fix: remove website-generator from prepublishOnly (#126) #11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Check NPM Provenance | |
| on: | |
| pull_request: | |
| paths: | |
| - '**/package.json' | |
| - '.github/workflows/check-provenance.yml' | |
| push: | |
| branches: | |
| - master | |
| paths: | |
| - '**/package.json' | |
| workflow_dispatch: | |
| permissions: {} | |
| jobs: | |
| check-provenance: | |
| name: Check Package Provenance | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| has_missing: ${{ steps.check.outputs.has_missing }} | |
| has_unpublished: ${{ steps.check.outputs.has_unpublished }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 | |
| with: | |
| persist-credentials: false | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: lts/* | |
| - name: Check npm provenance | |
| id: check | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | |
| with: | |
| script: | | |
| const fs = require('node:fs'); | |
| const path = require('node:path'); | |
| const { execSync } = require('node:child_process'); | |
| // Get workspace packages using pnpm | |
| function getWorkspacePackages() { | |
| try { | |
| // Use pnpm to list all workspace packages | |
| const output = execSync('pnpm list -r --json --depth -1', { | |
| encoding: 'utf-8', | |
| stdio: ['pipe', 'pipe', 'pipe'] | |
| }); | |
| const packages = JSON.parse(output); | |
| // Filter out private packages and return package info | |
| return packages | |
| .filter(pkg => !pkg.private) | |
| .map(pkg => ({ | |
| name: pkg.name, | |
| version: pkg.version, | |
| private: pkg.private || false | |
| })); | |
| } catch (error) { | |
| console.error('Error getting workspace packages:', error); | |
| // Fallback to reading lerna.json or root package.json | |
| const rootPkg = JSON.parse(fs.readFileSync('package.json', 'utf-8')); | |
| const workspaces = rootPkg.workspaces || []; | |
| const packages = []; | |
| for (const pattern of workspaces) { | |
| const basePath = pattern.replace('/*', ''); | |
| if (!fs.existsSync(basePath)) continue; | |
| const dirs = fs.readdirSync(basePath); | |
| for (const dir of dirs) { | |
| const packagePath = path.join(basePath, dir, 'package.json'); | |
| if (fs.existsSync(packagePath)) { | |
| const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8')); | |
| if (!packageJson.private) { | |
| packages.push(packageJson); | |
| } | |
| } | |
| } | |
| } | |
| return packages; | |
| } | |
| } | |
| // Check package status (published + provenance) in a single fetch | |
| async function checkPackageStatus(packageName) { | |
| try { | |
| const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`); | |
| if (!response.ok) { | |
| return { published: false, hasProvenance: false }; | |
| } | |
| const data = await response.json(); | |
| // Get latest version | |
| const latestVersion = data['dist-tags']?.latest; | |
| if (!latestVersion) { | |
| return { published: true, hasProvenance: false }; | |
| } | |
| // Check if the latest version has attestations | |
| const versionData = data.versions?.[latestVersion]; | |
| const hasProvenance = !!(versionData?.dist?.attestations); | |
| return { published: true, hasProvenance }; | |
| } catch (error) { | |
| console.error(`Error checking status for ${packageName}:`, error); | |
| return { published: false, hasProvenance: false }; | |
| } | |
| } | |
| console.log('🔍 Checking npm provenance for public packages...\n'); | |
| // Get all public packages from workspace | |
| const publicPackages = getWorkspacePackages(); | |
| const results = { | |
| withProvenance: [], | |
| withoutProvenance: [], | |
| notPublished: [] | |
| }; | |
| for (const pkg of publicPackages) { | |
| const status = await checkPackageStatus(pkg.name); | |
| if (!status.published) { | |
| results.notPublished.push(pkg.name); | |
| console.log(`⏭️ ${pkg.name}: Not published yet`); | |
| } else if (status.hasProvenance) { | |
| results.withProvenance.push(pkg.name); | |
| console.log(`✅ ${pkg.name}: Has provenance`); | |
| } else { | |
| results.withoutProvenance.push(pkg.name); | |
| console.log(`❌ ${pkg.name}: Missing provenance`); | |
| } | |
| } | |
| // Summary | |
| console.log('\n📊 Summary:'); | |
| console.log(` Total public packages: ${publicPackages.length}`); | |
| console.log(` With provenance: ${results.withProvenance.length}`); | |
| console.log(` Without provenance: ${results.withoutProvenance.length}`); | |
| console.log(` Not published: ${results.notPublished.length}`); | |
| // Save results for next steps | |
| fs.writeFileSync('provenance-results.json', JSON.stringify(results, null, 2)); | |
| // Set outputs | |
| core.setOutput('has_missing', results.withoutProvenance.length > 0); | |
| core.setOutput('has_unpublished', results.notPublished.length > 0); | |
| core.setOutput('missing_packages', results.withoutProvenance); | |
| core.setOutput('unpublished_packages', results.notPublished); | |
| - name: Upload results | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: provenance-results | |
| path: provenance-results.json | |
| retention-days: 1 | |
| post-comment: | |
| name: Post PR Comment | |
| runs-on: ubuntu-latest | |
| needs: check-provenance | |
| if: (needs.check-provenance.outputs.has_missing == 'true' || needs.check-provenance.outputs.has_unpublished == 'true') && github.event_name == 'pull_request' | |
| permissions: | |
| pull-requests: write | |
| steps: | |
| - name: Download results | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: provenance-results | |
| - name: Comment on PR | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('node:fs'); | |
| const results = JSON.parse(fs.readFileSync('provenance-results.json', 'utf8')); | |
| let comment = '## 📦 NPM Package Status\n\n'; | |
| if (results.notPublished.length > 0) { | |
| comment += '### New Packages (Not Published Yet)\n\n'; | |
| comment += 'Run the following commands to set up OIDC and publish:\n\n'; | |
| results.notPublished.forEach(pkg => { | |
| comment += `- [ ] \`npx setup-npm-trusted-publish ${pkg}\`\n`; | |
| }); | |
| comment += '\n'; | |
| } | |
| if (results.withoutProvenance.length > 0) { | |
| comment += '### Published Packages Missing OIDC Configuration\n\n'; | |
| comment += 'Configure OIDC for these packages:\n\n'; | |
| results.withoutProvenance.forEach(pkg => { | |
| comment += `- [ ] [${pkg}](https://www.npmjs.com/package/${pkg}/access)\n`; | |
| }); | |
| comment += '\n**Setup Instructions:**\n'; | |
| comment += '1. Click each package link above\n'; | |
| comment += '2. Click "Add trusted publisher"\n'; | |
| comment += '3. Configure with:\n'; | |
| comment += ` - Repository: \`${context.repo.owner}/${context.repo.repo}\`\n`; | |
| comment += ' - Workflow: `.github/workflows/release.yml`\n'; | |
| comment += ' - Environment: npm\n'; | |
| } | |
| // Find existing comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const botComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes('NPM Package Status') | |
| ); | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body: comment | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: comment | |
| }); | |
| } |