目录
在使用git执行打包操作时,我们常常会根据场景在tag中增加一些标识。
以基准版本为1.0.0为例:软件开发初期可以定义1.0.0-alpha.0,开发阶段是1.0.0-beta.0,上预发布环境时可以打成1.0.0-release.0,最终上线可以打v1.0.0。
也许每个公司都有一套标准,是否能做一个工具适配这样的场景?
于是就有了这篇文章,我想借这篇文章与大家分享一下最近整的一个git标签工具git-tag-sh
在开始前,先分享一下我平时打tag的命令:
在代码commit和push完后,以当前版本1.0.0为例,先查询是否已有同名版本
git tag -l "1.0.0"
执行tag命令
git tag 1.0.0
push tag操作
git push origin 1.0.0
执行完成后,tag包就会触发CI/CD的构建功能,部署,发布
如果使用命令来操作,可能就是git-tag-sh,完事
那么思路有了,接下来就是实现了
我们在使用一些全局的npm依赖时,如cross-env,pnpm或一些cli时,时常能在node全局依赖的目录下看到 .cmd .ps1 的命令文件,便于在终端直接调用。

如何新建一个npm全局包?
流程有三步:
1.新建命令文件,在文件顶部新增 #!/usr/bin/env node
如:
#!/usr/bin/env node
console.log('hello world');
2.在package.json中新增bin属性,关联这个文件

3.运行npm link命令,将命令链接到全局npm目录下

此时执行hello-world时会直接执行index.js

helpers.shell:命令函数
const { exec } = require("child_process");
const { defer } = require("utils-lib-js");
// shell命令封装
exports.shell = (__shell, showLog = true, opts = {}) => {
const { resolve, reject, promise } = defer();
exec(__shell, opts, (err, str, errStr) => {
const __err = err ?? errStr;
showLog && console.info(`>>>>> ${__shell}: `, __err || str);
if (__err) reject(__err);
else resolve(str);
});
return promise;
};
helpers.git:执行git操作的函数
const { shell } = require("./helpers.shell");
const { catchAwait } = require("utils-lib-js");
// git操作
exports.git = {
// 校验有无git
hasGit: async () => catchAwait(shell("git --version", false)),
// 检测标签是否已存在
hasTag: async (tag) => {
const [err, tags] = await catchAwait(shell(`git tag -l "${tag}"`, false));
const len = tags?.length > 0;
if (err ?? len) return err ?? "标签已存在";
},
tag: async (tag, message) => {
const [err] = await catchAwait(
shell(`git tag -a ${tag} -m "${message}"`, false)
);
if (err) return err;
},
push: async (tag) => {
const [err] = await catchAwait(shell(`git push origin ${tag}`, false));
const succ = err.includes(tag);
if (err && !succ) return err;
},
};
helpers.tag:tag模板函数
const { git } = require("./helpers.git");
// git-tag
exports.gitTag = async ({ tag, message }) => {
console.log(`git-tag-start-------------`, tag);
message && console.log(`git-tag-message-------------`, message);
const err = await git.hasTag(tag);
if (err) return err;
const err2 = await git.tag(tag, message);
if (err2) return err2;
const err3 = await git.push(tag);
if (err3) return err3;
console.log(`git-tag-success-------------`, tag);
};
helpers.current:对当前项目的操作
// 获取当前文件夹路径
const getCurrentDir = () => process.cwd();
// 获取文件内容
const getFile = (dir, path) => require(`${dir}${path}`);
// 获取package文件内容
exports.currentPackage = getFile(getCurrentDir(), "/package.json");
exports.getFile = getFile;
exports.getCurrentDir = getCurrentDir;
helpers.others:其他函数封装
// 缩写一下reject函数
const rej = (err = "") => Promise.reject(err);
// 缩写一下resolve函数
const res = (result = "") => Promise.resolve(result);
// 对象转数组
const Obj2Arr = (obj = {}) => Reflect.ownKeys(obj).map((it) => obj[it]);
// 首字母转小写
const first2Lower = (str = "") =>
str.substring(0, 1).toLowerCase() + str.substring(1);
// 计数器,计算对象中有几个whiteList(白名单)的值(whiteList代表校验哪些key),maxCont表示最大计算到几个为止提前跳出循环
function countArgs(opts, whiteList = [], maxCont, count = 0) {
for (const it of whiteList) {
if (maxCont === count) return maxCont;
!!opts[it] && count++;
}
return count;
}
module.exports = {
res,
rej,
Obj2Arr,
first2Lower,
countArgs,
};
helpers.command:主函数
const { program } = require("commander");
const { options, prodKey, splitKey } = require("./helpers.config");
const { getType } = require("utils-lib-js");
const { rej, Obj2Arr, first2Lower, countArgs } = require("./helpers.others");
const { currentPackage } = require("./helpers.current");
const { gitTag } = require("./helpers.tag");
const { git } = require("./helpers.git");
const { version, name, description } = require("../package.json");
// 初始化命令函数
exports.initCmd = async () => {
const [err] = await git.hasGit();
if (err) return rej(err);
programOptions(options, program)
.name(name)
.version(version)
.description(description)
.parse(process.argv);
const err1 = await checkOptions(program);
if (err1) return rej(err1);
const { tag, message } = executeCommand(program);
const err2 = await gitTag({ tag, message });
if (err2) return rej(err2);
};
// 批量添加命令
function programOptions(config, program) {
config.forEach((it) => program.option(...Obj2Arr(it)));
return program;
}
// 校验命令
function checkOptions(program) {
const opts = program.opts();
if (countArgs(opts, ["Alpha", "Beta", "Release"]) > 1)
return "只能选择一个后缀,请修改后再操作";
}
// 执行命令
function executeCommand(program) {
const opts = program.opts();
let message = "";
let tagVersion = currentPackage.version;
let mixStr = "";
Reflect.ownKeys(opts).forEach((key) => {
const it = opts[key];
const isTypeIsStr = getType(it) === "string";
switch (key) {
case "Production":
const __prodKey = isTypeIsStr ? it : prodKey;
tagVersion = `${__prodKey}${tagVersion}`;
break;
case "CurVer":
console.log(currentPackage.version);
break;
case "Suffix":
if (!isTypeIsStr) break;
if (it.startsWith(splitKey)) {
mixStr = `${it}`;
} else {
mixStr = `${splitKey}${it}`;
}
break;
case "Alpha":
case "Beta":
case "Release":
tagVersion = `${tagVersion}${splitKey}${first2Lower(key)}`;
break;
case "Message":
isTypeIsStr && (message = it);
break;
}
});
return { tag: tagVersion + mixStr, message };
}
helpers.config:配置文件
exports.options = [
{
flags: "-cv, -curVer",
description:
"获取当前目录下程序版本号(Get the program version number in the current directory)",
},
{
flags: "-p, -production [string]",
description:
"是否打'生产环境'标签(Whether to label the 'production' environment)",
},
{
flags: "-s, -suffix [string]",
description: "增加标签后缀(Add tag suffix)",
},
{
flags: "-m, -message [string]",
description: "增加标签提交信息(Add tag submission information)",
},
{
flags: "-a, -alpha",
description:
"增加'alpha'后缀,标识为软件开发初期包(Add the suffix 'alpha' to identify the initial package of software development)",
},
{
flags: "-b, -beta",
description:
"增加'beta'后缀,标识为软件开发中期包(Add the suffix 'beta' and mark it as software development interim package)",
},
{
flags: "-r, -release",
description:
"增加'release'后缀,标识为软件开发完成包(Add the suffix 'release' to identify the software development completion package)",
},
];
exports.prodKey = "v"; // 生产环境标识
exports.splitKey = "-"; // tag后缀分隔符
最后在命令文件中增加以下代码
#!/usr/bin/env node
const { initCmd } = require("../utils/helpers.command");
initCmd().catch(console.error);
使用 npm link 进行本地调试(如果全局已经安装了这个包时,使用npm link会提示安装失败,此时需要卸载全局包,或者本地命令换个名字)

使用 git-tag-sh 打默认开发包

git-tag-sh -p 打正式包

git-tag-sh -b 打测试包

git-tag-sh -s test.0 自定义后缀

git-tag-sh -r -m 上预发布环境 打预发布包带信息

最后试试多个场景打包 git-tag-sh -s test.0 -b -m 需求提测 -p b
远程的效果

完成上述代码及验证后使用npm publish进行包的发布,如果没有npm账号的话可以进行下面几步操作
在npm注册账号,在项目中打开终端执行 npm adduser 增加账户,npm login 登录账户,并输入用户名密码,最后执行npm publish进行包发布
感谢你看到了最后,如果文章有帮助,还请支持一下,感谢!
源码:git-tag-sh: 针对当前项目执行 git 打包操作
npm:git-tag-sh - npm
我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g
我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功
关于如何使用git设置类似Dropbox的服务,您有什么建议吗?您认为git是解决此问题的合适工具吗?我在考虑使用git+rush解决方案,你觉得怎么样? 最佳答案 检查这个开源项目:https://github.com/hbons/SparkleShare来自项目的自述文件:Howdoesitwork?SparkleSharecreatesaspecialfolderonyourcomputer.Youcanaddremotelyhostedfolders(or"projects")tothisfolder.Theseprojec
我编写了一个非常简单的“部署”脚本,作为我的裸git存储库中的post-updateHook运行。变量如下livedomain=~/mydomain.comstagingdomain=~/stage.mydomain.comgitrepolocation=~/git.mydomain.com/thisrepo.git(bare)core=~/git.mydomain.com/thisrepo.gitcore==addedremoteintoeachlive&stagegitslive和stage都初始化了gitrepos(非裸),我已经将我的裸仓库作为远程添加到它们中的每一个(名为co
我正在安装gitlabhq,并且在Gemfile中有对某些资源的“git://...”的引用。但是,我在公司防火墙后面,所以我必须使用http://。我可以手动编辑Gemfile,但我想知道是否有另一种方法告诉bundler使用http://作为git存储库? 最佳答案 您可以通过运行gitconfig--globalurl."https://".insteadOfgit://或通过将以下内容添加到~/.gitconfig:[url"https://"]insteadOf=git://
Activeadmingem已添加到我的rails项目中,但每次我尝试安装railsgactive_admin:install时,我都会收到类似的错误git://github.com/activeadmin/activeadmin.git(atmaster)isnotyetcheckedout.Runbundleinstallfirst.我肯定在运行“railsgactive_admin:install”之前运行了bundle。运行“bundleshow”后,我看到我已将“*activeadmin(1.0.0.pre3f916d6)”添加到我的项目中,但不断收到此错误消息。我的gem文