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

news2024/9/21 2:37:01

目录

前言

实现过程

起步

npm全局命令

功能实现

功能验证

代码发布

写在最后


前言

在使用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全局命令

我们在使用一些全局的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177657.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【面试克星】【公式少代码少话多】Vins-Mono预积分相关知识点总结与概述

前言 Vins-Mono预积分相关的公式和代码可以在各个博客中找到。本文将充当一个帮助大家快速理解预积分、中值积分相关概述的角色,带大家理解代码,应对面试。 下面会分成三步,来帮助大家充分理解这个过程。步骤是按照顺序来写的。 中值积分和…

学习MapStruct

概述 MapStruct是一个Java注释处理器,用于生成类型安全的bean映射类。 您要做的就是定义一个映射器接口,该接口声明任何必需的映射方法。在编译期间,MapStruct将生成此接口的实现。此实现使用简单的Java方法调用在源对象和目标对象之间进行映…

<<零入门容器云网络实战>>技术专栏之文章目录

建议: 1、资源 零入门容器云网络实战 链接: https://pan.baidu.com/s/1nPLRkAwjItAHmtEU2T1F4g 提取码: rrpd 2、技术沟通渠道 342498897 3、字体颜色说明 绿色字体&#xff0c; 表示已经发表&#xff0c;可以观看 灰色字体&#xff0c; 表示未发表 4、专栏介绍 1 <<…

【小知识点】如何在 git 中取消 __pycache__ 文件

文章目录如何在 git 中取消 **pycache** 文件CryptographyDeprecationWarning 异常如何在 git 中取消 pycache 文件 如果使用 PyCharm 运行代码&#xff0c;会在 Python 脚本所在目录生成 __pycache__ &#xff0c;每次提交到 git 都会包含该目录&#xff0c;本篇博客我们要将其…

插入排序算法的实现和优化~

插入排序的基本思想&#xff1a; 在一个已排好序的记录子集的基础上&#xff0c;每一步将下一个待排序的记录有序插入到已排好序的记录子集中&#xff0c;直到将所有待排记录全部插入为止 直接插入排序&#xff1a; 直接插入排序是一种最基本的插入排序方法&#xff0c;元素…

离线召回与排序介绍

3.3 离线召回与排序介绍 学习目标 目标 了解召回排序作用知道头条推荐召回排序设计应用 无 3.3.1 召回与排序介绍 召回&#xff1a;从海量文章数据中得到若干候选文章召回集合(数量较多) 排序&#xff1a;从召回集合中读取推荐文章&#xff0c;构建样本特征进行排序过滤筛选…

【SpringCloud21】面试题雪花算法

目录1. 问题2.ID生成规则部分硬性要求3.ID号生成系统的可用性要求4.一般通用方案4.1UUID4. 数据库自增主键5. 基于Redis生成全局id策略6. snowflake6.1 概述6.2 结构6.3 源码6.4 工程落地经验6.4.1 糊涂工具包6.4.2 springboot整合雪花算法1.POM2.核心代码IdGeneratorSnowflake…

ElasticSearch7.6.x 学习笔记

ElasticSearch7.6.x 学习笔记 目录 ElasticSearch概述 ElasticSearch&#xff0c;简称es&#xff0c;es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据。且本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理PB级别的数…

Spring事务、事务隔离级别、事务传播机制

Spring事务和事务传播机制一、为什么需要事务&#xff1f;(回顾)二、Spring中事务的实现2.1 MySQL中的事务使用 (回顾)2.2 Spring编程式事务2.3 Spring 声明式事务2.3.1 Transactional 使用2.3.2 Transactional 作用范围2.3.3 Transactional 参数说明2.3.4 注意事项2.3.4 Trans…

【微服务】Nacos注册中心

Nacos和Eureka一样也可以充当服务的注册中心&#xff0c;让我们一起看看有何区别&#xff1f; 点击跳转&#x1f449;【微服务】Eureka注册中心 一.引入 Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比于Eureka其功能更加丰富&#xff0c;在国内受欢迎…

2. 获取数字证书,搭建nginx服务器,验证https请求

文章目录 一、 前提知识二、获取数字证书三、搭建nginx服务器3.1 安装nginx操作步骤3.2 导入证书3.3 修改nginx配置文件四、验证一、 前提知识 首先我们知道访问域名时,http请求默认端口为80,https为443。那么我们现在就需要对这两个端口进行监听,这里我们就要用到nginx服务…

数据结构 最短路径课设(源码+实验报告+视频讲解)(用了自取)

XIAN TECHNOLOGICAL UNIVERSITY 课程设计报告 实验课程名称 算法与数据结构 专 业&#xff1a; 班 级&#xff1a; 姓 名&#xff1a; 学 号&#xff1a; 实验学时&#xff1a; 指导…

插入排序实现

场景&#xff1a; 插入排序&#xff0c;一般也被称为直接插入排序。 对于少量元素的排序&#xff0c;它是一个有效的算法 。 插入排序是一种最简单的排序方法&#xff0c;它的基本思想是将一个记录插入到已经排好序的有序表中&#xff0c;从而一个新的、记录数增1的有序表。在…

分享135个ASP源码,总有一款适合您

ASP源码 分享135个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 135个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1rHFniMK56P-_qXNY9kKihg?pwdl95g 提取码&#x…

day11文件夹导航条+文件从数据库和cos删除

获取临时凭证&&上传文件 1创建COS_OBJECT对象 new一个&#xff0c;然后就会向你写的url地址获取临时凭证&#xff08;需要引入一个JS&#xff09; 2.写url地址和url的函数&#xff0c;后台写函数获取到临时凭证&#xff0c;有文档直接调用就可&#xff0c;然后后台返回…

【C++】右值引用和移动语义 | 新的类功能 | 可变参数模板

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;左值引用…

HTTPS 是这样握手的

HTTP协议默认是明文传输&#xff0c;存在一定的安全隐患&#xff0c;容易被中间人窃听和攻击&#xff0c;在 加密解决HTTP协议带来的安全问题 中提到使用哈希、对称加密、非对称加密等方式对数据加密&#xff0c;能解决数据安全的问题。 以上加密方式需要我们手动的使用加密算…

python基础——列表切片操作

python基础——列表切片操作 文章目录python基础——列表切片操作一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤一、实验目的 掌握列表切片操作 二、实验原理 1、列表是写在方括号[]之间、用逗号分隔开的元素列表。列表可以完成大多数集合类的数据结构实现…

23种设计模式(二十三)——解释器模式【邻域问题】

文章目录 意图什么时候使用解释器真实世界类比解释器模式的实现文法抽象语法树解释器模式的优缺点亦称:Interpreter 意图 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。 在软件系统中,如果有一些特殊的领域问题较为复杂,疑…

基于springboot物资管理系统源码含论文

摘要 目前&#xff0c;大型物资作为社会零售业态中最为重要的组成部分&#xff0c;处于社会零售商业 进入高速发展的轨道阶段&#xff0c;其在社会经济发展的作用日益明显。国内各大大型基本 上都拥有自己的社会网&#xff0c;将社会物资管理纳入网络管理系统&#xff0c;实现…