Skip to content

Commit 98d5749

Browse files
authored
feat: support npm trusted publisher (#15)
close #14 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced an automated npm release workflow using GitHub Actions, supporting dry runs, testing, and configurable install commands. * Added a script to handle semantic-release automation, including changelog updates, npm publishing, and GitHub release creation. * Provided a dedicated package for release automation with all required dependencies. * **Documentation** * Updated release workflow documentation to reflect the new npm release process and streamlined token configuration instructions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 84dbcd9 commit 98d5749

File tree

5 files changed

+207
-23
lines changed

5 files changed

+207
-23
lines changed

.github/workflows/npm-release.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: NPM Trusted Publisher
2+
3+
on:
4+
workflow_call:
5+
6+
inputs:
7+
checkTest:
8+
type: boolean
9+
description: whether run test before release
10+
default: false
11+
12+
dryRun:
13+
type: boolean
14+
description: pass dry-run to semantic-release
15+
default: false
16+
17+
install:
18+
type: string
19+
description: 'Install dependencies script'
20+
default: 'npm i --no-package-lock --no-fund --force && rm -rf package-lock.json'
21+
22+
action_ref:
23+
type: string
24+
description: 'Branch name for node-modules/github-actions, for test purpose'
25+
default: master
26+
27+
jobs:
28+
Release:
29+
permissions:
30+
contents: write
31+
id-token: write # Required for OIDC
32+
deployments: write
33+
issues: write
34+
pull-requests: write
35+
runs-on: ubuntu-latest
36+
defaults:
37+
run:
38+
working-directory: main_repo
39+
40+
concurrency:
41+
group: ${{ github.workflow }}-${{ github.ref }}
42+
cancel-in-progress: true
43+
44+
steps:
45+
# Checkout action repository
46+
- name: Checkout action repository
47+
uses: actions/checkout@v4
48+
with:
49+
repository: node-modules/github-actions
50+
path: action_repo
51+
ref: ${{ inputs.action_ref }}
52+
53+
# Checkout project repository
54+
- name: Checkout project repository
55+
uses: actions/checkout@v4
56+
with:
57+
path: main_repo
58+
token: ${{ secrets.GITHUB_TOKEN }}
59+
60+
# Setup Node.js environment
61+
- name: Setup Node.js
62+
uses: actions/setup-node@v4
63+
with:
64+
node-version: lts/*
65+
66+
# Ensure npm 11.5.1 or later is installed
67+
- name: Update npm
68+
run: npm install -g npm@latest
69+
70+
# Install action dependencies
71+
- name: Install action dependencies
72+
run: npm i --no-package-lock --no-fund --force --omit=dev
73+
working-directory: action_repo/scripts/npm-release
74+
75+
# Install dependencies
76+
- name: Install project dependencies
77+
run: ${{ inputs.install }}
78+
79+
# Run Test Only
80+
- name: Run Test
81+
run: npm test
82+
if: inputs.checkTest
83+
84+
- name: Semantic Release
85+
id: release
86+
run: node ../action_repo/scripts/npm-release/index.js
87+
env:
88+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
89+
DRYRUN: ${{ inputs.dryRun }}
90+
91+
- name: Publish ${{ steps.release.outputs.name }}@${{ steps.release.outputs.release_version }}
92+
if: steps.release.outputs.release_version && !inputs.dryRun
93+
run: |
94+
echo ${{ steps.release.outputs.name }}
95+
echo ${{ steps.release.outputs.release_version }}
96+
echo ${{ steps.release.outputs.registry }}
97+
echo ${{ steps.release.outputs.cnpm_sync_url }}

README.md

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,6 @@ BREAKING CHANGE: Drop Node.js < 18 support
145145

146146
### 配置方式
147147

148-
- 创建 NPM Token
149-
- NPM Registry 需创建 Automation Token,参见[文档](https://docs.npmjs.com/creating-and-viewing-access-tokens)
150-
- GitHub Package 无需创建,默认支持
151-
152-
- 创建 GitHub Token
153-
- 因为生成的 CHANGELOG.md 和 package.json 需回写 GitHub,而默认的 `GITHUB_TOKEN` 没有该权限
154-
- 因此需要创建一个新的 Token,参见[文档](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token)
155-
156-
- 配置 Token
157-
- 在项目或组织维度配置 2 个 `secrets``NPM_TOKEN``GIT_TOKEN`
158-
- 参见[文档](https://docs.github.com/en/codespaces/managing-codespaces-for-your-organization/managing-encrypted-secrets-for-your-repository-and-organization-for-github-codespaces)
159-
160148
- 创建 `.github/workflows/release.yml`
161149

162150
```yaml
@@ -171,24 +159,14 @@ on:
171159
jobs:
172160
release:
173161
name: Node.js
174-
uses: node-modules/github-actions/.github/workflows/node-release.yml@master
175-
secrets:
176-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
177-
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
162+
uses: node-modules/github-actions/.github/workflows/npm-release.yml@master
178163
# with:
179164
# checkTest: false
180165
# dryRun: true
181166
```
182167

183168
### 发布到 GitHub Package
184169

185-
修改 `release.yml` 的 secrets:
186-
187-
```yaml
188-
secrets:
189-
NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }}
190-
```
191-
192170
修改 `package.json`
193171

194172
```json

scripts/npm-release/index.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const path = require('path');
2+
const fs = require('fs');
3+
const core = require('@actions/core');
4+
const semanticRelease = require('semantic-release').default;
5+
const { request } = require('undici');
6+
7+
async function run() {
8+
const mainRepoPath = process.cwd();
9+
const pkgInfo = require(`${mainRepoPath}/package.json`);
10+
const registry = pkgInfo.publishConfig?.registry || 'https://registry.npmjs.org';
11+
core.setOutput('name', pkgInfo.name);
12+
core.setOutput('registry', registry);
13+
14+
try {
15+
const configFiles = [
16+
path.join(__dirname, 'release.config.js'),
17+
path.join(mainRepoPath, 'release.config.js'),
18+
].filter(file => fs.existsSync(file));
19+
20+
core.info(`Using config files: ${configFiles.join(', ')}`);
21+
22+
const result = await semanticRelease({
23+
dryRun: process.env.DRYRUN === 'true',
24+
extends: configFiles,
25+
});
26+
27+
const { nextRelease, lastRelease } = result;
28+
29+
if (!nextRelease) {
30+
core.notice('No release need to be published.');
31+
core.summary.addRaw('No release need to be published.');
32+
await core.summary.write();
33+
} else {
34+
core.info(`Published release: ${nextRelease.version}`);
35+
core.setOutput('release_version', nextRelease.version);
36+
37+
// cnpm sync
38+
try {
39+
const res = await request(`https://registry-direct.npmmirror.com/-/package/${pkgInfo.name}/syncs`, {
40+
method: 'PUT',
41+
timeout: 30000,
42+
});
43+
const { id } = await res.body.json();
44+
const logUrl = `https://registry.npmmirror.com/-/package/${pkgInfo.name}/syncs/${id}/log`;
45+
core.setOutput('cnpm_sync_url', logUrl);
46+
core.info(`cnpm sync log url: ${logUrl}`);
47+
48+
// write summary
49+
core.summary.addRaw(`## [${pkgInfo.name}](https://github.com/${process.env.GITHUB_REPOSITORY})\n`);
50+
core.summary.addRaw(`- Release: ${lastRelease?.version ?? ''} -> ${nextRelease.version}\n`);
51+
core.summary.addRaw(`- Registry: ${registry}\n`);
52+
core.summary.addRaw(`- CNPM Sync: ${logUrl}\n`);
53+
core.summary.addRaw(`- DryRun: ${process.env.DRYRUN}\n`);
54+
} catch (err) {
55+
core.info(`cnpm sync ${pkgInfo.name} fail, ${err.message}`);
56+
core.summary.addRaw(`- CNPM Sync ${pkgInfo.name} error: ${err.message}\n`);
57+
}
58+
core.summary.addRaw(nextRelease.notes);
59+
await core.summary.write();
60+
}
61+
console.log('Result:', result);
62+
} catch (error) {
63+
console.error(error);
64+
core.setFailed(error);
65+
}
66+
}
67+
68+
run();

scripts/npm-release/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "ci-release",
3+
"dependencies": {
4+
"@actions/core": "^1.10.0",
5+
"@actions/exec": "^1.1.1",
6+
"@semantic-release/npm": "13.0.0-alpha.5",
7+
"@semantic-release/changelog": "^6.0.3",
8+
"@semantic-release/exec": "^7.1.0",
9+
"@semantic-release/git": "^10.0.1",
10+
"@semantic-release/commit-analyzer": "^13.0.1",
11+
"@semantic-release/release-notes-generator": "^14.0.3",
12+
"@semantic-release/github": "^11.0.3",
13+
"conventional-changelog-conventionalcommits": "^5.0.0",
14+
"semantic-release": "25.0.0-alpha.4",
15+
"undici": "^5.14.0"
16+
},
17+
"devDependencies": {
18+
"@types/node": "24",
19+
"@types/semantic-release": "21"
20+
}
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
plugins: [
3+
[ '@semantic-release/commit-analyzer', { preset: 'conventionalcommits' } ],
4+
[ '@semantic-release/release-notes-generator', { preset: 'conventionalcommits' } ],
5+
[ '@semantic-release/changelog', { changelogTitle: '# Changelog' } ],
6+
[ '@semantic-release/npm', {} ],
7+
8+
[ '@semantic-release/git',
9+
{
10+
message: 'Release <%= nextRelease.version %>\n\n[skip ci]\n\n<%= nextRelease.notes %>',
11+
},
12+
],
13+
14+
[ '@semantic-release/github',
15+
{
16+
addReleases: 'bottom',
17+
},
18+
],
19+
],
20+
};

0 commit comments

Comments
 (0)