Compare commits

...
Author SHA1 Message Date
james.maes 33f505c592 chore(ci): diagnose npm trust without publishing (#912) 2026-09-29 15:05:37 -05:00
+48 -76
View File
@@ -1,82 +1,54 @@
---
version: 2.1
orbs:
qqq-orb: kingsrook/qqq-orb@0.6.7
jobs:
verify_npm_trust:
docker:
- image: cimg/node:24.21
steps:
- run:
name: Check public identity claims and npm trust exchange without publishing
environment:
QQQ_NPM_TRUSTED_PUBLISHING: true
command: |
set +x
set -euo pipefail
printf 'Trusted-mode environment value: %s\n' "$QQQ_NPM_TRUSTED_PUBLISHING"
npm --version
NPM_ID_TOKEN=$(circleci run oidc get --claims '{"aud":"npm:registry.npmjs.org"}')
export NPM_ID_TOKEN
node \<<'NODE'
const expected = {
'oidc.circleci.com/org-id': 'bb329fea-03cd-4fb5-b530-15e85703598d',
'oidc.circleci.com/project-id': '5bd1e544-282a-4080-afc1-36ffdebf5487',
'oidc.circleci.com/pipeline-definition-id': '3bd87740-26e6-5f18-8600-10685a13a81e',
'oidc.circleci.com/vcs-origin': 'github.com/QRun-IO/qqq-frontend-core',
};
(async () => {
const token = process.env.NPM_ID_TOKEN;
const parts = token.split('.');
if (parts.length !== 3) throw new Error('Unexpected identity format');
const claims = JSON.parse(Buffer.from(parts[1], 'base64url').toString());
for (const key of Object.keys(expected)) {
console.log(JSON.stringify({claim:key,value:claims[key] ?? null,matches:claims[key] === expected[key]}));
}
console.log(JSON.stringify({audience:claims.aud,issuer:claims.iss}));
const response = await fetch('https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/@qrunio%2fqqq-frontend-core', {
method: 'POST', headers: {Authorization: `Bearer ${token}`}, signal: AbortSignal.timeout(20000),
});
const data = await response.json();
const accepted = response.ok && typeof data.token === 'string' && data.token.length > 0;
console.log(JSON.stringify({exchangeStatus:response.status,accepted}));
if (!accepted) {
const message = String(data.message ?? data.error ?? 'No message').replace(/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED]').replace(/npm_[A-Za-z0-9]+/g, '[REDACTED]');
console.error(message.slice(0, 600));
process.exitCode = 1;
}
})().catch(() => { console.error('npm trust diagnostic failed; no credentials logged'); process.exitCode = 1; });
NODE
workflows:
test_only:
trust_diagnostic:
jobs:
- qqq-orb/node_test_only:
node_version: "24.21"
- verify_npm_trust:
context: [qqq-maven-registry-credentials, build-qqq-sample-app]
filters:
branches:
ignore: /(develop|main|release/.*|hotfix/.*|integration.*)/
tags:
only: []
publish_snapshot:
jobs:
- qqq-orb/node_publish:
node_version: "24.21"
npm_trusted_publishing: true
context: [qqq-maven-registry-credentials, build-qqq-sample-app]
branch_type: snapshot
filters:
branches:
only:
- develop
publish_feature:
jobs:
- qqq-orb/node_publish:
node_version: "24.21"
npm_trusted_publishing: true
context: [qqq-maven-registry-credentials, build-qqq-sample-app]
branch_type: feature
filters:
branches:
only:
- feature/.*
tags:
only: [/publish.*/]
publish_release_candidate:
jobs:
- qqq-orb/node_publish:
node_version: "24.21"
npm_trusted_publishing: true
context: [qqq-maven-registry-credentials, build-qqq-sample-app]
branch_type: release_candidate
filters:
branches:
only:
- /release\/.*/
publish_release:
jobs:
- qqq-orb/node_publish:
node_version: "24.21"
npm_trusted_publishing: true
context: [qqq-maven-registry-credentials, build-qqq-sample-app]
branch_type: release
filters:
branches:
only: [main]
tags:
only: [/v.*/]
publish_hotfix_release:
jobs:
- qqq-orb/node_publish:
node_version: "24.21"
npm_trusted_publishing: true
context: [qqq-maven-registry-credentials, build-qqq-sample-app]
branch_type: hotfix
filters:
branches:
only:
- /hotfix\/.*/
only: feature/GH-912-trust-diagnostic