name: Publish to npm on: release: types: [released] workflow_dispatch: inputs: tag_name: description: "Release tag (e.g., v1.2.3)" required: true release_id: description: "GitHub release ID" required: true concurrency: group: publish cancel-in-progress: true permissions: contents: write packages: write env: NODE_VERSION: 22 PNPM_VERSION: 10.8.1 jobs: publish: runs-on: ubuntu-latest # Only run if this was converted from a pre-release if: github.event.release.prerelease == false steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.release.tag_name || inputs.tag_name }} - name: Setup Node uses: ./.github/actions/setup-node - name: Configure npm authentication run: | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc - name: Download release assets uses: actions/github-script@v7 env: RELEASE_ID: ${{ inputs.release_id }} with: script: | const fs = require('fs'); const path = require('path'); const releaseId = context.payload.release?.id || process.env.RELEASE_ID; console.log("releaseId:", releaseId); if (!releaseId) { core.setFailed('No release ID found.'); return; } const release = await github.rest.repos.getRelease({ owner: context.repo.owner, repo: context.repo.repo, release_id: releaseId }); // Find the .tgz file const tgzAsset = release.data.assets.find(asset => asset.name.endsWith('.tgz')); if (!tgzAsset) { core.setFailed('No .tgz file found in release assets'); return; } // Download the asset const response = await github.rest.repos.getReleaseAsset({ owner: context.repo.owner, repo: context.repo.repo, asset_id: tgzAsset.id, headers: { Accept: 'application/octet-stream' } }); // Save to npx-cli directory const filePath = path.join('npx-cli', tgzAsset.name); fs.writeFileSync(filePath, Buffer.from(response.data)); console.log(`Downloaded ${tgzAsset.name} to ${filePath}`); // Set output for next step core.setOutput('package-file', filePath); core.setOutput('package-name', tgzAsset.name); - name: Verify package integrity id: verify run: | cd npx-cli # List files to confirm download ls -la *.tgz # Verify the package can be read npm pack --dry-run || echo "Note: This is expected to show differences since we're using the pre-built package" # Extract package name from the downloaded file PACKAGE_FILE=$(ls *.tgz | head -n1) echo "package-file=$PACKAGE_FILE" >> $GITHUB_OUTPUT - name: Publish to npm run: | cd npx-cli # Publish the exact same package that was tested PACKAGE_FILE="${{ steps.verify.outputs.package-file }}" echo "Publishing $PACKAGE_FILE to npm..." npm publish "$PACKAGE_FILE" echo "āœ… Successfully published to npm!" env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Update release description uses: actions/github-script@v7 env: RELEASE_ID: ${{ inputs.release_id }} with: script: | const releaseId = context.payload.release?.id || process.env.RELEASE_ID;; // Fetch the release to get the current body const release = await github.rest.repos.getRelease({ owner: context.repo.owner, repo: context.repo.repo, release_id: releaseId }); const currentBody = release.data.body || ''; await github.rest.repos.updateRelease({ owner: context.repo.owner, repo: context.repo.repo, release_id: releaseId, body: currentBody + '\n\nāœ… **Published to npm registry**' });