ci: automate github release publishing

This commit is contained in:
Jacobinwwey 2026-04-14 00:23:20 -05:00
parent bf3a4b30f8
commit aee8b4e299
4 changed files with 104 additions and 0 deletions

63
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,63 @@
name: Release
on:
push:
tags:
- '*.*.*'
- 'v*.*.*'
- 'V*.*.*'
workflow_dispatch:
inputs:
tag:
description: Existing git tag to publish or repair
required: true
type: string
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Check out release ref
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }}
- name: Resolve tag name
shell: bash
run: |
TAG_NAME="${GITHUB_REF_NAME}"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG_NAME="${{ inputs.tag }}"
fi
echo "TAG_NAME=$TAG_NAME" >> "$GITHUB_ENV"
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Build plugin
run: npm run build
- name: Run test suite
run: npm test -- --runInBand
- name: Audit UI i18n surfaces
run: npm run audit:i18n-ui
- name: Check diff hygiene
run: git diff --check
- name: Publish or repair GitHub release assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run release:github -- "$TAG_NAME"

View file

@ -68,3 +68,13 @@ The helper enforces the required packaged assets and `docs/releases/<tag>.md` be
- If the release already exists, it runs `gh release upload ... --clobber`.
That second path is the repair path for cases where a release body was published but plugin assets were not uploaded.
## 6. CI Automation
The repository also ships `.github/workflows/release.yml`:
- Push a git tag to publish the release automatically.
- Use `workflow_dispatch` with a `tag` input to repair an existing release from CI.
- The workflow runs `npm ci`, `npm run build`, `npm test -- --runInBand`, `npm run audit:i18n-ui`, `git diff --check`, and finally `npm run release:github -- "$TAG_NAME"`.
The workflow intentionally reuses the checked-in release helper instead of duplicating asset lists or release-note logic inside YAML.

View file

@ -68,3 +68,13 @@ npm run release:github -- <tag>
- 如果 release 已存在,则执行 `gh release upload ... --clobber`
第二条路径就是这类问题的修复路径release 文案已发布,但插件安装资产未上传时,可直接补传。
## 6. CI 自动化
仓库现已内置 `.github/workflows/release.yml`
- 推送 git tag 时自动发布 release。
- 通过 `workflow_dispatch` 并传入 `tag` 参数,可在 CI 中修复已有 release。
- 工作流会执行 `npm ci`、`npm run build`、`npm test -- --runInBand`、`npm run audit:i18n-ui`、`git diff --check`,最后执行 `npm run release:github -- "$TAG_NAME"`
工作流刻意复用仓库内的 release 辅助脚本,而不是在 YAML 中重复维护资产清单或 release notes 逻辑,避免两套规则漂移。

View file

@ -8,6 +8,7 @@ describe('GitHub release workflow', () => {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const releaseScriptRelativePath = path.join('scripts', 'release', 'publish-github-release.js');
const releaseScriptPath = path.join(repoRoot, releaseScriptRelativePath);
const releaseWorkflowPath = path.join(repoRoot, '.github', 'workflows', 'release.yml');
test('exposes a checked-in GitHub release helper and notes for the current version', () => {
expect(packageJson.scripts['release:github']).toBe(`node ${releaseScriptRelativePath}`);
@ -17,6 +18,26 @@ describe('GitHub release workflow', () => {
expect(fs.existsSync(currentReleaseNotesPath)).toBe(true);
});
test('checks in a GitHub Actions workflow that reuses the release helper for tag and manual publishing', () => {
expect(fs.existsSync(releaseWorkflowPath)).toBe(true);
const workflow = fs.readFileSync(releaseWorkflowPath, 'utf8');
expect(workflow).toContain('workflow_dispatch:');
expect(workflow).toContain('push:');
expect(workflow).toContain("- '*.*.*'");
expect(workflow).toContain("- 'v*.*.*'");
expect(workflow).toContain("- 'V*.*.*'");
expect(workflow).toContain('contents: write');
expect(workflow).toContain('npm ci');
expect(workflow).toContain('npm run build');
expect(workflow).toContain('npm test -- --runInBand');
expect(workflow).toContain('npm run audit:i18n-ui');
expect(workflow).toContain('npm run release:github -- "$TAG_NAME"');
expect(workflow).toContain("github.event_name == 'workflow_dispatch'");
expect(workflow).toContain('inputs.tag');
});
const maybeDescribeReleaseScript = fs.existsSync(releaseScriptPath) ? describe : describe.skip;
maybeDescribeReleaseScript('publish-github-release helper', () => {