GitHub Actions 竟能如此神奇,实现自动化发布 releases!

254 阅读1分钟

1. 创建 GitHub Actions

在项目中创建文件:.github/workflows/release.yml

name: Release

on:
  push:
    tags:
      - "v*" # 识别推送 v 开头的 tag 时触发

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "18"

      - name: Get version from tag
        id: get_version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Install dependencies
        run: npm install

      - name: Build
        run: npm run build

      # 生成需要上传的 release 文件
      - name: Rename and Compress dist
        run: |
          mv dist timeline-todolist-v${{ steps.get_version.outputs.VERSION }}
          tar -czf timeline-todolist-v${{ steps.get_version.outputs.VERSION }}.tar.gz timeline-todolist-v${{ steps.get_version.outputs.VERSION }}
          zip -r timeline-todolist-v${{ steps.get_version.outputs.VERSION }}.zip timeline-todolist-v${{ steps.get_version.outputs.VERSION }}

      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          # token 尤其需要注意,否则会出现 GitHub release failed with status: 403 undefined
          # token 的生成可以详见 tips
          token: ${{ secrets.GITHUBTOKEN }}
          name: "Version ${{ steps.get_version.outputs.VERSION }} 🚀"
          draft: false
          prerelease: false
          generate_release_notes: true
          # release 中的信息,一般为 changelog
          body_path: changelog/v${{ steps.get_version.outputs.VERSION }}.md
          # 你上传的 release 文件路径
          files: |
            timeline-todolist-v${{ steps.get_version.outputs.VERSION }}.tar.gz
            timeline-todolist-v${{ steps.get_version.outputs.VERSION }}.zip

2. 创建发布脚本

2.1 publish 脚本

publish.js 发布脚本

const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
const { generateChangelog } = require("./generate-changelog");

// 读取 package.json
const packagePath = path.resolve(__dirname, "../package.json");
const packageJson = fs.readFileSync(packagePath, "utf8");
let packageObj = JSON.parse(packageJson);

// 解析当前版本号
const [major, minor, patch] = packageObj.version.split(".").map(Number);

// 增加补丁版本号
const newVersion = `${major}.${minor}.${patch + 1}`;

// 根据你的 git 记录生成对应的 changlog
generateChangelog(newVersion);

// 更新 package.json
packageObj.version = newVersion;
fs.writeFileSync(packagePath, JSON.stringify(packageObj, null, 2) + "\n");
console.log("✨ Successfully create version " + newVersion);

try {
    // 提交更改
    execSync("git add package.json changelog/");
    execSync(`git commit -m "chore: bump version to ${newVersion}"`);

    // 创建新的 tag
    execSync(`git tag v${newVersion}`);

    // 推送更改和 tag
    execSync("git push");
    execSync("git push --tags");

    console.log(`✨ Successfully published version ${newVersion}`);
} catch (error) {
    console.error("❌ Failed to publish:", error.message);
    process.exit(1);
}

2.2 生成 changelog 脚本

generate-changelog.js 根据 git 记录,自动生成 changlog 脚本

const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");

function getLatestTag() {
  try {
    return execSync("git describe --tags --abbrev=0").toString().trim();
  } catch (error) {
    console.log("No tags found");
    return null;
  }
}

function getCommitsSinceLastTag() {
  const latestTag = getLatestTag();
  if (!latestTag) {
    return execSync('git log --pretty=format:"%h - %s (%an)"').toString();
  }

  return execSync(
    `git log ${latestTag}..HEAD --pretty=format:"%h - %s (%an)"`
  ).toString();
}

function generateChangelog(version) {
  const changelogDir = path.join(__dirname, "../changelog");

  // 确保 changelog 目录存在
  if (!fs.existsSync(changelogDir)) {
    fs.mkdirSync(changelogDir);
  }

  const commits = getCommitsSinceLastTag();
  const date = new Date().toISOString().split("T")[0];
  const filename = path.join(changelogDir, `v${version}.md`);

  const content = `# V${version} Changelog  (${date})\n\n${commits}`;
  fs.writeFileSync(filename, content);

  console.log(`Changelog has been generated at: changelog/${filename}`);
}

module.exports = {
  generateChangelog,
};

3. 发布

package.json 增加 "pub": "node scripts/publish.js" 命令

执行 pnpm pub 命令

等 Actions 跑完 github releases 就会生成对应的记录

image.png

4. tips:

4.1 secrets.GITHUBTOKEN 的生成和使用

  1. 生成

    • 进入 github.com/settings/to…
    • Generate new token (classic) For general use
    • 设置对应的权限和过期时间,生成完要保存好,之后不会明文显示。忘记只能重新生成 image.png
  2. 使用

    • 填写 GITHUBTOKEN 的位置

4.2 如何你想看完整项目

=> LuckRain7/test-release-publish