From ad4aa327e929e190de729c1749f97d8ced70b1a4 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:18 -0500 Subject: [PATCH 01/16] fix(config): use npm publish with oidc trusted publishing instead of pnpm --- .github/workflows/publish.yml | 62 ++---------- common/scripts/npm-publish.js | 185 ++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 56 deletions(-) create mode 100644 common/scripts/npm-publish.js diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1267da87..bf56d4a8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,7 +29,7 @@ jobs: with: token: ${{ secrets.PAT_TOKEN }} - - name: Setup registry + - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' @@ -46,69 +46,19 @@ jobs: echo "is_main=false" >> $GITHUB_OUTPUT fi + - name: Update npm for trusted publishing + run: npm install -g npm@latest + - name: Build run: rush build - - name: Get npm token via OIDC - run: | - echo "Requesting OIDC token from GitHub..." - OIDC_TOKEN=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ - "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=https://registry.npmjs.org" | jq -r '.value') - - if [ -z "$OIDC_TOKEN" ] || [ "$OIDC_TOKEN" = "null" ]; then - echo "::error::Failed to get OIDC token from GitHub" - exit 1 - fi - echo "OIDC token obtained successfully" - - echo "Exchanging OIDC token for npm token..." - RESPONSE=$(curl -sS -w "\n%{http_code}" -X POST "https://registry.npmjs.org/-/npm/v1/security/oidc/token" \ - -H "Content-Type: application/json" \ - -d "{\"oidcToken\": \"${OIDC_TOKEN}\"}") - - HTTP_CODE=$(echo "$RESPONSE" | tail -1) - BODY=$(echo "$RESPONSE" | sed '$d') - - echo "npm OIDC exchange HTTP status: $HTTP_CODE" - - if [ "$HTTP_CODE" != "200" ]; then - echo "::error::npm OIDC exchange failed with status $HTTP_CODE" - echo "Response: $BODY" - exit 1 - fi - - NPM_TOKEN=$(echo "$BODY" | jq -r '.token') - if [ -z "$NPM_TOKEN" ] || [ "$NPM_TOKEN" = "null" ]; then - echo "::error::npm returned empty token" - echo "Response: $BODY" - exit 1 - fi - - echo "::add-mask::$NPM_TOKEN" - echo "NPM_AUTH_TOKEN=$NPM_TOKEN" >> $GITHUB_ENV - echo "npm token obtained successfully" - - - name: Verify npm auth - run: | - echo "=== .npmrc content (masked) ===" - cat ~/.npmrc 2>/dev/null | sed 's/_authToken=.*/_authToken=***/' || echo "No ~/.npmrc" - echo "" - echo "=== NPM_AUTH_TOKEN set? ===" - if [ -n "$NPM_AUTH_TOKEN" ]; then echo "YES (length: ${#NPM_AUTH_TOKEN})"; else echo "NO"; fi - echo "" - echo "=== npm whoami ===" - npm whoami 2>&1 || echo "npm whoami failed - NOT authenticated" - echo "" - echo "=== publish .npmrc ===" - cat common/temp/publish-home/.npmrc 2>/dev/null | sed 's/_authToken=.*/_authToken=***/' || echo "No publish .npmrc yet" - - name: Publish (main) if: steps.branch.outputs.is_main == 'true' - run: rush publish --publish --target-branch main --include-all --set-access-level=public + run: node common/scripts/npm-publish.js - name: Publish (prerelease) if: steps.branch.outputs.is_main == 'false' - run: rush publish --publish --tag ${{ steps.branch.outputs.name }} --include-all --set-access-level=public --apply + run: node common/scripts/npm-publish.js --tag ${{ steps.branch.outputs.name }} - name: Commit version bumps run: | diff --git a/common/scripts/npm-publish.js b/common/scripts/npm-publish.js new file mode 100644 index 00000000..6a7772a4 --- /dev/null +++ b/common/scripts/npm-publish.js @@ -0,0 +1,185 @@ +/** + * Custom publish script that uses `npm publish` instead of `pnpm publish`. + * This enables npm's built-in OIDC Trusted Publishing support. + * + * Usage: + * node common/scripts/npm-publish.js [--tag ] [--dry-run] + * + * Reads rush.json to find all packages with shouldPublish: true, + * checks if the local version differs from the published version, + * and publishes using `npm publish --provenance --access public`. + */ +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '..', '..'); + +function exec(cmd, opts = {}) { + return execSync(cmd, { encoding: 'utf-8', cwd: ROOT, ...opts }).trim(); +} + +function getPublishedVersion(packageName) { + try { + return exec(`npm view ${packageName} version 2>/dev/null`); + } catch { + return null; + } +} + +function getLocalVersion(packageJsonPath) { + const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + return pkg.version; +} + +function parseArgs() { + const args = process.argv.slice(2); + const result = { tag: null, dryRun: false }; + for (let i = 0; i < args.length; i++) { + if (args[i] === '--tag' && args[i + 1]) { + result.tag = args[++i]; + } + if (args[i] === '--dry-run') { + result.dryRun = true; + } + } + return result; +} + +async function main() { + const { tag, dryRun } = parseArgs(); + const rushJson = JSON.parse(fs.readFileSync(path.join(ROOT, 'rush.json'), 'utf-8')); + + const publishable = rushJson.projects.filter(p => p.shouldPublish === true); + + // Debug info + console.log('='.repeat(60)); + console.log(' npm-publish.js - Debug Info'); + console.log('='.repeat(60)); + console.log(`npm version: ${exec('npm --version')}`); + console.log(`node version: ${exec('node --version')}`); + console.log(`tag: ${tag || 'latest'}`); + console.log(`dry-run: ${dryRun}`); + console.log(`publishable packages: ${publishable.length}`); + console.log(''); + + // Check npm auth + console.log('--- npm auth check ---'); + try { + const whoami = exec('npm whoami 2>&1'); + console.log(`npm whoami: ${whoami}`); + } catch (err) { + console.log(`npm whoami: FAILED - ${err.stderr || err.message}`); + console.log('WARNING: npm is not authenticated. Trusted Publishing OIDC will be used during publish.'); + } + console.log(''); + + // Check .npmrc + console.log('--- .npmrc files ---'); + const homeNpmrc = path.join(process.env.HOME || '~', '.npmrc'); + if (fs.existsSync(homeNpmrc)) { + const content = fs.readFileSync(homeNpmrc, 'utf-8'); + console.log(`~/.npmrc exists (${content.split('\n').length} lines)`); + content.split('\n').forEach(line => { + if (line.includes('authToken') || line.includes('_auth')) { + console.log(` ${line.replace(/=.*/, '=***')}`); + } else if (line.trim()) { + console.log(` ${line}`); + } + }); + } else { + console.log('~/.npmrc: not found'); + } + console.log(''); + + // Check env + console.log('--- Environment ---'); + console.log(`ACTIONS_ID_TOKEN_REQUEST_URL: ${process.env.ACTIONS_ID_TOKEN_REQUEST_URL ? 'SET' : 'NOT SET'}`); + console.log(`ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN ? 'SET' : 'NOT SET'}`); + console.log(`NPM_AUTH_TOKEN: ${process.env.NPM_AUTH_TOKEN ? 'SET' : 'NOT SET'}`); + console.log(`NODE_AUTH_TOKEN: ${process.env.NODE_AUTH_TOKEN ? 'SET' : 'NOT SET'}`); + console.log(`NPM_CONFIG_PROVENANCE: ${process.env.NPM_CONFIG_PROVENANCE || 'NOT SET'}`); + console.log(''); + console.log('='.repeat(60)); + console.log(' Starting publish'); + console.log('='.repeat(60)); + console.log(''); + + let published = 0; + let skipped = 0; + let failed = 0; + + for (const project of publishable) { + const pkgJsonPath = path.join(ROOT, project.projectFolder, 'package.json'); + const localVersion = getLocalVersion(pkgJsonPath); + const publishedVersion = getPublishedVersion(project.packageName); + + if (localVersion === publishedVersion) { + console.log(`SKIP ${project.packageName}@${localVersion} (already published)`); + skipped++; + continue; + } + + console.log(`PUBLISH ${project.packageName}@${localVersion} (npm: ${publishedVersion || 'not found'})`); + + const distPath = path.join(ROOT, project.projectFolder, 'dist'); + if (!fs.existsSync(distPath)) { + console.log(` WARN: dist/ folder not found at ${distPath}`); + } else { + const files = fs.readdirSync(distPath); + console.log(` dist/ contains: ${files.join(', ')}`); + } + + const publishCmd = [ + 'npm publish', + '--provenance', + '--access public', + tag ? `--tag ${tag}` : '', + dryRun ? '--dry-run' : '', + ].filter(Boolean).join(' '); + + console.log(` CMD: ${publishCmd}`); + console.log(` CWD: ${project.projectFolder}`); + + try { + const output = exec(publishCmd, { + cwd: path.join(ROOT, project.projectFolder), + }); + console.log(` OUTPUT: ${output}`); + console.log(` OK ${project.packageName}@${localVersion}`); + published++; + } catch (err) { + console.error(` FAIL ${project.packageName}@${localVersion}`); + if (err.stdout) console.error(` STDOUT: ${err.stdout}`); + if (err.stderr) console.error(` STDERR: ${err.stderr}`); + if (!err.stdout && !err.stderr) console.error(` ERROR: ${err.message}`); + failed++; + } + + console.log(''); + } + + console.log('='.repeat(60)); + console.log(` Results`); + console.log('='.repeat(60)); + console.log(`Published: ${published}`); + console.log(`Skipped: ${skipped}`); + console.log(`Failed: ${failed}`); + + if (failed > 0) { + process.exit(1); + } +} + +function getPublishedVersionWithTag(packageName, tag) { + try { + return exec(`npm view ${packageName}@${tag} version 2>/dev/null`); + } catch { + return null; + } +} + +main().catch(err => { + console.error('Error:', err.message); + process.exit(1); +}); From 3ab1ffaad72502d912275e0e80f53c91cace7519 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:20 -0500 Subject: [PATCH 02/16] feat(xml): trigger publish From fc914376990de7af9197495c6015f9c63da44ca6 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:21 -0500 Subject: [PATCH 03/16] feat(complementos): trigger publish From 7b81438e99688439600ba4e067d067cce0264754 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:22 -0500 Subject: [PATCH 04/16] feat(xsd): trigger publish From 98ae8f15e1a97c2d77d3de9141d775491e2d532f Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:23 -0500 Subject: [PATCH 05/16] feat(csd): trigger publish From 99bbb223950a7e48a921f70e15dba898c14c45b7 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:24 -0500 Subject: [PATCH 06/16] feat(csf): trigger publish From 3e6e2f5d887797464589da94f605e36462d15a75 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:25 -0500 Subject: [PATCH 07/16] feat(catalogs): trigger publish From 2659669adc11ec2e2e74aa107a5c27cca5e83e0a Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:26 -0500 Subject: [PATCH 08/16] feat(utils): trigger publish From d029d12b3f13f74c5fb48f40175a100f9f18c79a Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:28 -0500 Subject: [PATCH 09/16] feat(rfc): trigger publish From fa4e2604702d2831c57929e1b47976a2a5b6f885 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:29 -0500 Subject: [PATCH 10/16] feat(types): trigger publish From 7a61d61c8d25f3d310a065b1f238bbb2ecb34fb2 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:30 -0500 Subject: [PATCH 11/16] feat(transform): trigger publish From f8fd9419a90b143ab87fe3424717e48c22737f26 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:31 -0500 Subject: [PATCH 12/16] feat(2json): trigger publish From bc85bf245ef38475d2f8c841a366594e1aa73863 Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:32 -0500 Subject: [PATCH 13/16] feat(expresiones): trigger publish From d9198e8112ed71f0d53c777d89b24182815fb46f Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:33 -0500 Subject: [PATCH 14/16] feat(elements): trigger publish From 5f4b435658851e529273be06e8b1f7da74f9192b Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:34 -0500 Subject: [PATCH 15/16] feat(openssl): trigger publish From f8681ef5a5a961ea478df0afb1337d45dc76b7ea Mon Sep 17 00:00:00 2001 From: MisaelMa Date: Mon, 6 Apr 2026 11:59:35 -0500 Subject: [PATCH 16/16] feat(saxon): trigger publish