为了方便项目打包,我用Node写了个git-tag工具

简介: 为了方便项目打包,我用Node写了个git-tag工具

前言

在使用git执行打包操作时,我们常常会根据场景在tag中增加一些标识。

以基准版本为1.0.0为例:软件开发初期可以定义1.0.0-alpha.0,开发阶段是1.0.0-beta.0,上预发布环境时可以打成1.0.0-release.0,最终上线可以打v1.0.0。

也许每个公司都有一套标准,是否能做一个工具适配这样的场景?

于是就有了这篇文章,我想借这篇文章与大家分享一下最近整的一个git标签工具

实现过程

起步

在开始前,先分享一下我平时打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全局命令

我们在使用一些全局的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进行包发布

写在最后

感谢你看到了最后,如果文章有帮助,还请支持一下,感谢!


相关文章
|
23天前
|
消息中间件 JavaScript 中间件
函数计算产品使用问题之WebIDE编写的Node.js代码是否会自动进行打包部署
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
5天前
|
JavaScript Linux 开发者
一个用于管理多个 Node.js 版本的安装和切换开源工具
【9月更文挑战第14天】nvm(Node Version Manager)是一个开源工具,用于便捷地管理多个 Node.js 版本。其特点包括:版本安装便捷,支持 LTS 和最新版本;版本切换简单,不影响开发流程;多平台支持,包括 Windows、macOS 和 Linux;社区活跃,持续更新。通过 nvm,开发者可以轻松安装、切换和管理不同项目的 Node.js 版本,提高开发效率。
|
22天前
|
JavaScript 小程序 Java
【工具】用nvm管理nodejs版本切换,真香!
本文详细介绍了如何使用 nvm(node.js 版本管理工具)解决在不同项目间频繁切换 Node.js 版本的问题。通过实例展示了 A、B 两个项目分别依赖 v14.19.1 和 v16.15.0 版本时的解决方案,并提供了 nvm 的下载、安装及常用命令等实用信息,帮助读者轻松管理 Node.js 版本。文章还包括了卸载已安装的 Node.js、配置环境变量等步骤,确保切换顺畅无阻。
45 0
【工具】用nvm管理nodejs版本切换,真香!
|
2天前
|
JavaScript 中间件 Shell
Node.js JXcore 打包
Node.js 是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore 是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 这篇文章主要是要向大家介绍 JXcore 的打包功能。
|
1月前
|
JavaScript IDE 前端开发
前端开发工具配置 nodejs & git & IDE
前端开发工具配置 nodejs & git & IDE
|
1月前
|
架构师 开发工具 git
项目去除git版本控制 去除版本控制
文章提供了去除本地项目Git版本控制的步骤,包括删除`.git`文件夹和`.idea`目录下的`vcs.xml`文件。
项目去除git版本控制 去除版本控制
|
1月前
|
JavaScript
成功解决node、node-sass和sass-loader版本冲突问题、不需要降低node版本。如何在vue项目中安装node-sass,以及安装node-sass可能遇到的版本冲突问题
这篇文章介绍了在Vue项目中安装node-sass和sass-loader时遇到的版本冲突问题,并提供了解决这些问题的方法,包括在不降低node版本的情况下成功安装node-sass。
成功解决node、node-sass和sass-loader版本冲突问题、不需要降低node版本。如何在vue项目中安装node-sass,以及安装node-sass可能遇到的版本冲突问题
|
1月前
|
jenkins 测试技术 开发工具
协同开发的艺术:Git 在团队项目中的高效应用
【8月更文第16天】在现代软件开发中,团队成员之间的高效协作是至关重要的。Git 作为一种分布式版本控制系统,为开发者提供了强大的工具来管理代码的变化和协作。本文将介绍如何利用 Git 来优化团队的工作流程,并提供实际操作的代码示例。
41 1
|
1月前
|
开发工具 git
成功解决:fatal: detected dubious ownership in repository at ‘E:/workspace/CSMarket‘。如何使用git工具通过命令行的形式
这篇文章分享了作者在使用Git工具初始化本地仓库时遇到的权限问题,提供了通过命令行解决Git仓库权限问题的方案,并介绍了如何使用Git命令行初始化项目、添加文件、提交以及关联远程仓库的步骤。
成功解决:fatal: detected dubious ownership in repository at ‘E:/workspace/CSMarket‘。如何使用git工具通过命令行的形式
|
1月前
|
网络安全 开发工具 数据安全/隐私保护
Win10使用Git克隆项目出现fatal: Authentication failed for异常
Windows 10系统中使用Git克隆项目时出现"fatal: Authentication failed for"异常的解决方法,主要是通过修改凭据管理器中的Git凭据密码来解决因密码过期导致的身份验证失败问题。
33 0
Win10使用Git克隆项目出现fatal: Authentication failed for异常