Document trusted publishing and align CI for OIDC npm publish
- Add trusted publishing guidance to SKILL.md, including npm and GitHub Actions requirements, migration steps, and Rust toolchain handling - Update release-npm.yml: - Set permissions for OIDC (id-token: write, contents: read) - Remove NPM_TOKEN usage in publish steps - Read Rust toolchain from rust-toolchain.toml and use consistently - Pin dtolnay/rust-toolchain action and pass explicit toolchain input - Use latest OS images for build matrix - Skip npm publish if version already exists - Use actions/checkout@v6
Seto Elkahfi committed
Mar 28, 2026 at 22:37 UTC
ce32bb4fa6acc7976b6a5de275d324d7b18d305e
2 files changed
+139
-19
.agents/skills/smbcloud-cli-release/SKILL.md
+97
@@ -97,6 +97,103 @@ When that happens, retry the exact `npm publish` command with:
97
98
Do not assume the first publish failed before the package upload step. Check the exit result or npm registry state before retrying repeatedly.
99
100
+## Trusted publishing
101
+
102
+Prefer npm trusted publishers for CI over `NPM_TOKEN`.
103
+
104
+Official references:
105
+
106
+- https://docs.npmjs.com/trusted-publishers/
107
+- https://docs.npmjs.com/cli/v11/commands/npm-trust/
108
+
109
+### npm-side constraints
110
+
111
+Trusted publishing can only be configured for packages that already exist on npm.
112
+
113
+That means:
114
+
115
+- publish a package once manually if it does not exist yet
116
+- then configure trust for that package
117
+- each package can have only one trusted publisher configuration at a time
118
+
119
+For `smbcloud-cli`, configure trust per package, not only for `@smbcloud/cli`.
120
+
121
+Example packages:
122
+
123
+- `@smbcloud/cli`
124
+- `@smbcloud/cli-darwin-arm64`
125
+- later, each additional published `@smbcloud/cli-*` package
126
+
127
+### GitHub Actions setup
128
+
129
+The workflow must include:
130
+
131
+- `permissions:`
132
+- `id-token: write`
133
+- `contents: read`
134
+
135
+Do not pass `NODE_AUTH_TOKEN` to `npm publish` when using trusted publishing.
136
+
137
+The npm CLI detects the GitHub OIDC environment automatically and exchanges it for a short-lived publish credential.
138
+
139
+### Rust toolchain consistency
140
+
141
+When cross-compiling in CI, the Rust toolchain used for `rustup target add` must match the toolchain used by `cargo build`.
142
+
143
+For this repo, `rust-toolchain.toml` is the source of truth.
144
+
145
+Do not duplicate the Rust version in the matrix unless the workflow intentionally tests multiple toolchains.
146
+
147
+If the workflow installs a target for one toolchain but Cargo builds with another, CI can fail with:
148
+
149
+- `error[E0463]: can't find crate for core`
150
+- note that the target may not be installed
151
+
152
+This can happen even when `rustup target add <target>` already ran successfully.
153
+
154
+### dtolnay action behavior
155
+
156
+For the pinned `dtolnay/rust-toolchain` revision used in this repo, do not assume the action will infer the toolchain from `rust-toolchain.toml` without input.
157
+
158
+A workflow can fail with:
159
+
160
+- `'toolchain' is a required input`
161
+
162
+Preferred pattern in this repo:
163
+
164
+- read `channel = "..."` from `rust-toolchain.toml`
165
+- write it to `RUST_TOOLCHAIN` in `GITHUB_ENV`
166
+- pass `toolchain: ${{ env.RUST_TOOLCHAIN }}` to `dtolnay/rust-toolchain`
167
+- run `rustup target add <target> --toolchain ${{ env.RUST_TOOLCHAIN }}`
168
+- run `cargo build --target <target>` after the action sets that toolchain active
169
+
170
+This keeps `rust-toolchain.toml` as the only Rust version source while avoiding implicit action behavior.
171
+
172
+### Trusted publisher command
173
+
174
+For this repo, the trust relationship should point at:
175
+
176
+- repository: `smbcloudXYZ/smbcloud-cli`
177
+- workflow file: `release-npm.yml`
178
+
179
+Example:
180
+
181
+- `npm trust github @smbcloud/cli --repo smbcloudXYZ/smbcloud-cli --file release-npm.yml --yes`
182
+- `npm trust github @smbcloud/cli-darwin-arm64 --repo smbcloudXYZ/smbcloud-cli --file release-npm.yml --yes`
183
+
184
+These commands may require npm 2FA and support `--otp=<code>`.
185
+
186
+### Migration guidance
187
+
188
+Recommended order:
189
+
190
+1. enable trusted publishing for existing packages
191
+2. verify a CI publish works through OIDC
192
+3. then remove or stop using `NPM_TOKEN`
193
+4. after the migration is proven, tighten npm package publishing access if desired
194
+
195
+Do not remove the manual publish path for brand-new package names, because npm trust cannot be configured before the first publish.
196
+
197
## CI/CD workflow rules
198
199
The release workflow lives in `.github/workflows/release-npm.yml`.
.github/workflows/release-npm.yml
+42
-19
@@ -6,6 +6,10 @@ on:
6
- "v*.*.*"
7
workflow_dispatch:
8
9
+permissions:
10
+ id-token: write
11
+ contents: read
12
+
13
env:
14
SMB_CLIENT_ID: ${{ secrets.SMB_CLIENT_ID }}
15
SMB_CLIENT_SECRET: ${{ secrets.SMB_CLIENT_SECRET }}
@@ -26,44 +30,39 @@ jobs:
30
build:
31
- {
32
NAME: linux-x64-glibc,
29
- OS: ubuntu-20.04,
30
- TOOLCHAIN: 1.92,
33
+ OS: ubuntu-latest,
34
TARGET: x86_64-unknown-linux-gnu,
35
}
36
# Cannot cross-compile for now
37
#- {
38
# NAME: linux-arm64-glibc,
36
- # OS: ubuntu-20.04,
39
+ # OS: ubuntu-latest,
40
# TOOLCHAIN: stable,
41
# TARGET: aarch64-unknown-linux-gnu,
42
# }
43
- {
44
NAME: win32-x64-msvc,
45
OS: windows-2022,
43
- TOOLCHAIN: 1.92,
46
TARGET: x86_64-pc-windows-msvc,
47
}
48
- {
49
NAME: win32-arm64-msvc,
50
OS: windows-2022,
49
- TOOLCHAIN: 1.92,
51
TARGET: aarch64-pc-windows-msvc,
52
}
53
- {
54
NAME: darwin-x64,
54
- OS: macos-11,
55
- TOOLCHAIN: 1.92,
55
+ OS: macos-15-intel,
56
TARGET: x86_64-apple-darwin,
57
}
58
- {
59
NAME: darwin-arm64,
60
- OS: macos-11,
61
- TOOLCHAIN: 1.92,
60
+ OS: macos-latest,
61
TARGET: aarch64-apple-darwin,
62
}
63
steps:
64
- name: Checkout
66
- uses: actions/checkout@v3
65
+ uses: actions/checkout@v6
66
67
- name: Set the release version
68
shell: bash
@@ -76,11 +75,27 @@ jobs:
75
76
echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
77
78
+ - name: Read Rust toolchain
79
+ shell: bash
80
+ run: |
81
+ rust_toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml | head -n 1)"
82
+ if [ -z "$rust_toolchain" ]; then
83
+ echo "Failed to read Rust toolchain from rust-toolchain.toml" >&2
84
+ exit 1
85
+ fi
86
+
87
+ echo "RUST_TOOLCHAIN=${rust_toolchain}" >> "$GITHUB_ENV"
88
+
89
- name: Install toolkit
80
- uses: dtolnay/rust-toolchain@master
90
+ uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9
91
with:
82
- targets: ${{ matrix.build.TARGET }}
83
- toolchain: ${{ matrix.build.TOOLCHAIN}}
92
+ toolchain: ${{ env.RUST_TOOLCHAIN }}
93
+
94
+ - name: Install Rust target
95
+ shell: bash
96
+ run: |
97
+ rustup target add ${{ matrix.build.TARGET }} --toolchain ${{ env.RUST_TOOLCHAIN }}
98
+ rustup target list --installed
99
100
- name: Create .env file
101
run: |
@@ -142,17 +157,20 @@ jobs:
157
cp "../target/${{ matrix.build.TARGET }}/release/${cargo_bin}" "${node_pkg}/bin"
158
# publish the package
159
cd "${node_pkg}"
160
+ npm_package_name="@smbcloud/${node_pkg}"
161
+ if npm view "${npm_package_name}@${node_version}" version >/dev/null 2>&1; then
162
+ echo "${npm_package_name}@${node_version} already exists on npm, skipping publish"
163
+ exit 0
164
+ fi
165
npm publish --access public
146
- env:
147
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
166
167
publish-npm-base:
168
name: Publish the base NPM package
169
needs: publish-npm-binaries
152
- runs-on: ubuntu-20.04
170
+ runs-on: ubuntu-latest
171
steps:
172
- name: Checkout
155
- uses: actions/checkout@v3
173
+ uses: actions/checkout@v6
174
175
- name: Set the release version
176
shell: bash
@@ -178,8 +196,13 @@ jobs:
196
197
node ../scripts/render-main-package.cjs "./package.json" "${{ env.RELEASE_VERSION }}"
198
199
+ npm_package_name="@smbcloud/cli"
200
+ npm_package_version="${{ env.RELEASE_VERSION }}"
201
+ if npm view "${npm_package_name}@${npm_package_version}" version >/dev/null 2>&1; then
202
+ echo "${npm_package_name}@${npm_package_version} already exists on npm, skipping publish"
203
+ exit 0
204
+ fi
205
+
206
npm install
207
npm run build
208
npm publish --access public
184
- env:
185
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}