@@ -277,15 +277,18 @@ Deno.test('release: superseded theme is only recorded on a real version change',
277277 ) ;
278278} ) ;
279279
280- Deno . test ( 'release: local plan includes publish, smoke, gates, and GitHub release when credentials are present' , ( ) => {
281- // Simulate a local/manual environment that has the credentials required for
282- // npm publish and GitHub release creation. Force CI off so the plan follows
283- // the dev -> main path.
280+ Deno . test ( 'release: local plan runs gates and GitHub release but never publishes npm (OIDC-only publication)' , ( ) => {
281+ // #1187: npm publication authenticates exclusively via Trusted
282+ // Publishing/OIDC, which exists only in the GitHub Actions release lane.
283+ // A local/manual run never includes the npm publish steps — even when a
284+ // legacy token variable is set in the environment, it is ignored.
284285 const originalNpmToken = Deno . env . get ( 'NPM_TOKEN' ) ;
285286 const originalGitHubToken = Deno . env . get ( 'GITHUB_TOKEN' ) ;
287+ const originalGitHubActions = Deno . env . get ( 'GITHUB_ACTIONS' ) ;
286288 const originalCi = Deno . env . get ( 'CI' ) ;
287289 Deno . env . set ( 'NPM_TOKEN' , 'test-token' ) ;
288290 Deno . env . set ( 'GITHUB_TOKEN' , 'test-token' ) ;
291+ Deno . env . delete ( 'GITHUB_ACTIONS' ) ;
289292 Deno . env . delete ( 'CI' ) ;
290293 try {
291294 const commands = createReleasePlan ( '0.39.1' ) . map ( ( step ) => [
@@ -296,28 +299,31 @@ Deno.test('release: local plan includes publish, smoke, gates, and GitHub releas
296299 assert ( commands . some ( ( [ name ] ) => name === 'package artifact gate' ) ) ;
297300 assert ( commands . some ( ( [ name ] ) => name === 'push dev' ) ) ;
298301 assert ( commands . some ( ( [ name ] ) => name === 'sync main from dev (fast-forward)' ) ) ;
299- assert (
302+ assertFalse (
300303 commands . some ( ( [ , command ] ) => command . includes ( 'deno task publish:npm' ) ) ,
304+ 'local runs must not publish to npm: publication is OIDC-only in the Actions lane (#1187)' ,
301305 ) ;
302- assert (
303- commands . some ( ( [ , command ] ) => command . includes ( 'tools/consumer-smoke.ts --version 0.39.1' ) ) ,
304- ) ;
306+ assertFalse ( commands . some ( ( [ name ] ) => name === 'post-publish npm consumer smoke' ) ) ;
305307 assert ( commands . some ( ( [ name ] ) => name === 'create GitHub release' ) ) ;
306308 } finally {
307309 if ( originalNpmToken === undefined ) Deno . env . delete ( 'NPM_TOKEN' ) ;
308310 else Deno . env . set ( 'NPM_TOKEN' , originalNpmToken ) ;
309311 if ( originalGitHubToken === undefined ) Deno . env . delete ( 'GITHUB_TOKEN' ) ;
310312 else Deno . env . set ( 'GITHUB_TOKEN' , originalGitHubToken ) ;
313+ if ( originalGitHubActions === undefined ) Deno . env . delete ( 'GITHUB_ACTIONS' ) ;
314+ else Deno . env . set ( 'GITHUB_ACTIONS' , originalGitHubActions ) ;
311315 if ( originalCi === undefined ) Deno . env . delete ( 'CI' ) ;
312316 else Deno . env . set ( 'CI' , originalCi ) ;
313317 }
314318} ) ;
315319
316320Deno . test ( 'release: CI plan publishes from main without touching dev' , ( ) => {
317- const originalNpmToken = Deno . env . get ( 'NPM_TOKEN' ) ;
321+ // Publication capability in CI comes from the Actions OIDC environment
322+ // (npm Trusted Publishing, #1187), not from a token variable.
323+ const originalGitHubActions = Deno . env . get ( 'GITHUB_ACTIONS' ) ;
318324 const originalGitHubToken = Deno . env . get ( 'GITHUB_TOKEN' ) ;
319325 const originalCi = Deno . env . get ( 'CI' ) ;
320- Deno . env . set ( 'NPM_TOKEN ' , 'test-token ' ) ;
326+ Deno . env . set ( 'GITHUB_ACTIONS ' , 'true ' ) ;
321327 Deno . env . set ( 'GITHUB_TOKEN' , 'test-token' ) ;
322328 Deno . env . set ( 'CI' , 'true' ) ;
323329 try {
@@ -338,23 +344,22 @@ Deno.test('release: CI plan publishes from main without touching dev', () => {
338344 assert ( names . includes ( 'push tag' ) ) ;
339345 assert ( names . includes ( 'create GitHub release' ) ) ;
340346 } finally {
341- if ( originalNpmToken === undefined ) Deno . env . delete ( 'NPM_TOKEN ' ) ;
342- else Deno . env . set ( 'NPM_TOKEN ' , originalNpmToken ) ;
347+ if ( originalGitHubActions === undefined ) Deno . env . delete ( 'GITHUB_ACTIONS ' ) ;
348+ else Deno . env . set ( 'GITHUB_ACTIONS ' , originalGitHubActions ) ;
343349 if ( originalGitHubToken === undefined ) Deno . env . delete ( 'GITHUB_TOKEN' ) ;
344350 else Deno . env . set ( 'GITHUB_TOKEN' , originalGitHubToken ) ;
345351 if ( originalCi === undefined ) Deno . env . delete ( 'CI' ) ;
346352 else Deno . env . set ( 'CI' , originalCi ) ;
347353 }
348354} ) ;
349355
350- Deno . test ( 'release: patch release plan omits publish and GitHub release without credentials' , ( ) => {
351- const originalNpmToken = Deno . env . get ( 'NPM_TOKEN' ) ;
356+ Deno . test ( 'release: patch release plan omits publish and GitHub release outside the Actions lane' , ( ) => {
357+ // No GitHub credentials and no Actions OIDC environment: neither npm
358+ // publish nor GitHub release creation may enter the plan (#1187).
352359 const originalGitHubToken = Deno . env . get ( 'GITHUB_TOKEN' ) ;
353360 const originalGhToken = Deno . env . get ( 'GH_TOKEN' ) ;
354361 const originalGitHubActions = Deno . env . get ( 'GITHUB_ACTIONS' ) ;
355362 const originalCi = Deno . env . get ( 'CI' ) ;
356- Deno . env . delete ( 'NPM_TOKEN' ) ;
357- Deno . env . delete ( 'NODE_AUTH_TOKEN' ) ;
358363 Deno . env . delete ( 'GITHUB_TOKEN' ) ;
359364 Deno . env . delete ( 'GH_TOKEN' ) ;
360365 Deno . env . delete ( 'GITHUB_ACTIONS' ) ;
@@ -366,8 +371,6 @@ Deno.test('release: patch release plan omits publish and GitHub release without
366371 assert ( names . includes ( 'tag release' ) ) ;
367372 assert ( names . includes ( 'push tag' ) ) ;
368373 } finally {
369- if ( originalNpmToken === undefined ) Deno . env . delete ( 'NPM_TOKEN' ) ;
370- else Deno . env . set ( 'NPM_TOKEN' , originalNpmToken ) ;
371374 if ( originalGitHubToken === undefined ) Deno . env . delete ( 'GITHUB_TOKEN' ) ;
372375 else Deno . env . set ( 'GITHUB_TOKEN' , originalGitHubToken ) ;
373376 if ( originalGhToken === undefined ) Deno . env . delete ( 'GH_TOKEN' ) ;
0 commit comments