WEBKT

彻底告别 GitHub 依赖:手把手教你定制 Changesets Changelog 生成器对接内网 GitLab

3 0 0 0

在现代前端 Monorepo 工程实践中,changesets 几乎是管理版本发布和 Changelog 生成的标准工具。然而,官方提供的 @changesets/changelog-github 插件深度绑定了 GitHub 的 URL 结构和 API。

对于大多数企业内网环境,我们使用的是私有部署的 GitLab。直接使用官方插件会导致 Changelog 中的提交记录链接、Merge Request 链接全部指向 GitHub 甚至变成死链。

本文将带你深度定制一个符合 GitLab 规范的 Changelog 生成器。

1. 核心原理:Changesets 需要什么?

@changesets/cli 在生成 CHANGELOG.md 时,会尝试调用配置中指定的 changelog 模块。这个模块必须导出一个符合 ChangelogFunctions 接口的对象,包含两个核心方法:

  1. getReleaseLine: 处理每个包在发布时生成的具体变更行(即我们在 .changeset/*.md 中写的描述)。
  2. getDependencyReleaseLine: 处理依赖更新时的描述行。

我们的目标就是重新实现这两个函数,使其能够解析 GitLab 的提交信息并生成正确的内网链接。

2. 环境准备

首先,在你的工程目录下(通常是 packages/ 目录下)创建一个新的工具包,例如 my-changelog-generator

mkdir -p packages/my-changelog-generator
cd packages/my-changelog-generator
npm init -y

安装必要的依赖:

npm install @changesets/types @changesets/get-github-info
# 虽然我们要对接 GitLab,但有时参考 GitHub 的解析逻辑会有帮助

3. 编写核心逻辑

创建 index.ts(或 index.js),我们将重点实现 GitLab 的链接拼接逻辑。

import { ChangelogFunctions } from "@changesets/types";

// 假设你的 GitLab 内网地址是 https://gitlab.company.com
const GITLAB_BASE_URL = "https://gitlab.company.com";

const changelogFunctions: ChangelogFunctions = {
  getReleaseLine: async (changeset, type, options) => {
    // 1. 获取项目路径,例如 "fe-group/my-project"
    const repo = options?.repo;
    if (!repo) {
      throw new Error('请在 .changeset/config.json 的 changelog 配置中提供 "repo" 参数');
    }

    // 2. 解析第一行描述
    const [firstLine, ...futureLines] = changeset.summary
      .split("\n")
      .map((l) => l.trimEnd());

    // 3. 获取提交者和 MR 信息 (此处可以通过 git log 获取,或从 CI 环境变量读取)
    // 为简化演示,我们假设 commit 信息可以从 changeset 对象中提取或通过 options 传入
    const commitLink = `[[${changeset.id.slice(0, 7)}](${GITLAB_BASE_URL}/${repo}/-/commit/${changeset.id})]`;

    // 4. 拼接最终的一行变更记录
    let res = `- ${commitLink} ${firstLine}`;

    if (futureLines.length > 0) {
      res += `\n${futureLines.map((l) => `  ${l}`).join("\n")}`;
    }

    return res;
  },

  getDependencyReleaseLine: async (changesets, dependenciesUpdated, options) => {
    if (dependenciesUpdated.length === 0) return "";

    const repo = options?.repo;
    const changesetLinks = changesets.map(
      (c) => `[[${c.id.slice(0, 7)}](${GITLAB_BASE_URL}/${repo}/-/commit/${c.id})]`
    );

    const updatedDepenenciesList = dependenciesUpdated.map(
      (dependency) => `  - ${dependency.name}@${dependency.newVersion}`
    );

    return [
      `- 依赖更新 ${changesetLinks.join(", ")}`,
      ...updatedDepenenciesList,
    ].join("\n");
  },
};

export default changelogFunctions;

4. 进阶:如何获取 GitLab Merge Request ID?

在 GitLab CI 流程中,我们往往希望能像 GitHub 那样自动标注 !123 这样的 MR 编号。

我们可以利用 GitLab CI 的环境变量 CI_MERGE_REQUEST_IID。但由于 Changeset 可能是在本地执行的,更通用的做法是通过 git 命令在本地解析:

import { execSync } from "child_process";

function getMergeRequestIid(commitHash: string) {
  try {
    // 搜索包含该 commit 的 merge commit 信息
    const message = execSync(`git log --merges --format=%s --contains ${commitHash}`).toString();
    // GitLab 默认的 merge 消息通常包含 "Merge branch '...' into '...' \n\n See merge request !123"
    const match = message.match(/merge request !(\d+)/);
    return match ? match[1] : null;
  } catch (e) {
    return null;
  }
}

将此逻辑集成进 getReleaseLine 中,就可以生成类似 [MR !123](https://gitlab.../-/merge_requests/123) 的链接。

5. 在项目中使用

由于是内网工具,你不需要将其发布到公共 NPM。你可以直接在根目录的 .changeset/config.json 中引用本地路径。

首先,编译你的 TS 代码为 JS,然后修改配置:

{
  "$schema": "https://unpkg.com/@changesets/config/schema.json",
  "changelog": ["./packages/my-changelog-generator/dist/index.js", { "repo": "my-group/my-repo" }],
  "commit": false,
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch"
}

注意: 路径必须以 ./../ 开头,Changesets 才会识别为本地文件引用。

6. 避坑指南

  1. 权限问题:如果你的脚本需要通过 GitLab API 获取用户信息,记得在 CI 环境变量中注入 GITLAB_TOKEN
  2. 换行符转换:在处理 changeset.summary 时,注意 Windows 和 Linux 环境下的换行符兼容性。
  3. Monorepo 引用:如果你使用的是 pnpm workspace,建议将自定义生成器作为一个正常的 workspace package,并在根目录执行编译。

总结

通过自定义 Changesets Changelog 生成器,我们不仅解决了内网链接失效的问题,还可以根据公司的规范(比如关联 Jira 单号、自动生成 Release Note 分类)进行深度定制。这种工程化小细节的优化,能显著提升团队全链路研发的幸福感。

工程化老兵 ChangesetsGitLab CI前端工程化

评论点评