fix(tinacms): send the same TinaCloud token from editorial workflow a… #805
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: Publish | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled] | |
| workflow_dispatch: | |
| inputs: | |
| test_graphql_update: | |
| description: 'Test the @tinacms/graphql update flow to tinacloud' | |
| required: false | |
| default: false | |
| type: boolean | |
| test_version: | |
| description: 'Version to simulate (e.g., 2.2.0)' | |
| required: false | |
| default: '2.2.0' | |
| type: string | |
| concurrency: ${{ github.workflow }}-${{ github.ref }} | |
| # Least-privilege default; jobs that publish opt into the writes they need. | |
| permissions: | |
| contents: read | |
| jobs: | |
| publish-tagged-pr: | |
| if: > | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.head.repo.fork == false && | |
| contains(github.event.pull_request.labels.*.name, 'tagged') && | |
| github.event.pull_request.title != 'Version Packages' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for OIDC authentication with npm | |
| pull-requests: write | |
| env: | |
| TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} | |
| TURBO_TEAM: ${{ secrets.TURBO_TEAM }} | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 2 | |
| - name: Setup Node.js environment | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version-file: ".nvmrc" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Upgrade npm for OIDC support | |
| run: npm install -g npm@$(node -p "require('./package.json').engines.npm") | |
| - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 | |
| name: Install pnpm | |
| id: pnpm-install | |
| with: | |
| package_json_file: package.json | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| name: Setup pnpm cache | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm build | |
| - name: Build Types | |
| run: pnpm types | |
| - name: Publish tagged PR | |
| run: | | |
| echo running on branch ${BRANCH_NAME} | |
| pnpm version:snapshot $(echo ${{ github.sha }} | cut -c1-7) | |
| # Re-pin to workspace:* so snapshots publish exact internal versions — | |
| # caret prerelease ranges can resolve to a different snapshot batch. | |
| git diff --name-only -- '*package.json' | xargs -r sed -i 's/"workspace:\^"/"workspace:*"/g' | |
| pnpm publish -r --tag ${BRANCH_NAME} --no-git-checks --access public | |
| echo "Published NPM tag: $BRANCH_NAME" >> $GITHUB_STEP_SUMMARY | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| BRANCH_NAME: ${{ github.head_ref || github.ref_name }} | |
| NPM_CONFIG_PROVENANCE: true | |
| - name: Get published package versions | |
| id: package-versions | |
| run: | | |
| set -e | |
| echo "🔍 Checking for modified package.json files..." | |
| # Find all modified package.json files (uncommitted changes from version:snapshot) | |
| modified_packages=$(git diff --name-only | grep 'package\.json$' || true) | |
| if [ -z "$modified_packages" ]; then | |
| echo "⚠️ No modified package.json files found" | |
| echo "packages_csv=" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| echo "📦 Found modified package.json files:" | |
| echo "$modified_packages" | |
| # Build a CSV with one package per line (package,version) | |
| packages_csv="" | |
| for pkg_file in $modified_packages; do | |
| if [ -f "$pkg_file" ]; then | |
| pkg_name=$(jq -r '.name' "$pkg_file") | |
| pkg_version=$(jq -r '.version' "$pkg_file") | |
| if [ "$pkg_name" != "null" ] && [ "$pkg_version" != "null" ]; then | |
| echo " Found: $pkg_name@$pkg_version" | |
| if [ -n "$packages_csv" ]; then | |
| packages_csv="${packages_csv}"$'\n' | |
| fi | |
| packages_csv="${packages_csv}${pkg_name},${pkg_version}" | |
| fi | |
| fi | |
| done | |
| echo "📋 Generated CSV:" | |
| echo "$packages_csv" | |
| # Use heredoc for multiline output | |
| echo "packages_csv<<EOF" >> $GITHUB_OUTPUT | |
| echo "$packages_csv" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Comment on PR with install instructions | |
| if: steps.package-versions.outputs.packages_csv != '' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| PACKAGES_CSV: ${{ steps.package-versions.outputs.packages_csv }} | |
| with: | |
| script: | | |
| console.log('🔍 Starting PR comment script...'); | |
| if ( | |
| !context.payload || | |
| !context.payload.pull_request || | |
| !context.payload.pull_request.head || | |
| !context.payload.pull_request.head.ref || | |
| !context.payload.pull_request.number | |
| ) { | |
| throw new Error('Missing pull_request context. Cannot comment on PR.'); | |
| } | |
| const packagesCsv = process.env.PACKAGES_CSV; | |
| console.log('📦 Packages CSV:', packagesCsv); | |
| // Parse CSV format: one line per package "name,version" | |
| const packages = packagesCsv.trim().split('\n').map(line => { | |
| const [name, version] = line.split(','); | |
| return { name, version }; | |
| }); | |
| console.log('✅ Parsed packages:', packages); | |
| const branchName = context.payload.pull_request.head.ref; | |
| console.log('🌿 Branch name:', branchName); | |
| let packagesSection = '\n**Published Packages:**\n'; | |
| for (const pkg of packages) { | |
| packagesSection += `- \`${pkg.name}@${pkg.version}\`\n`; | |
| } | |
| console.log('📝 Generated packages section:', packagesSection); | |
| // Generate install commands using actual versions | |
| let installCommands = packages.map(pkg => `pnpm add ${pkg.name}@${pkg.version}`).join('\n'); | |
| const comment = `## 📦 Tagged Release Published | |
| Your tagged PR has been published! You can install the packages using their version tags: | |
| \`\`\`bash | |
| ${installCommands} | |
| \`\`\` | |
| ${packagesSection} | |
| **Commit:** \`${context.sha}\` | |
| `; | |
| console.log('💬 Posting comment to PR #' + context.payload.pull_request.number); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| body: comment | |
| }); | |
| console.log('✅ Comment posted successfully!'); | |
| build: | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| id-token: write # Required for OIDC authentication with npm | |
| pull-requests: write | |
| env: | |
| TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} | |
| TURBO_TEAM: ${{ secrets.TURBO_TEAM }} | |
| steps: | |
| - name: Generate a token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| id: generate-token | |
| with: | |
| # uses https://github.com/organizations/tinacms/settings/apps/release-bot-allow-prs-and-push | |
| app-id: ${{ secrets.BOT_APP_ID }} | |
| private-key: ${{ secrets.BOT_APP_SECRET }} | |
| - name: Check out code | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| token: ${{ steps.generate-token.outputs.token }} | |
| # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits | |
| fetch-depth: 0 | |
| - name: Setup Node.js environment | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version-file: ".nvmrc" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Upgrade npm for OIDC support | |
| run: npm install -g npm@$(node -p "require('./package.json').engines.npm") | |
| - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 | |
| name: Install pnpm | |
| id: pnpm-install | |
| with: | |
| package_json_file: package.json | |
| run_install: false | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| name: Setup pnpm cache | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm build | |
| - name: Build Types | |
| run: pnpm types | |
| - name: Setup git credentials | |
| run: | | |
| git config --global user.email "bot@tina.io" | |
| git config --global user.name "Tina Release Bot" | |
| - name: Create release pull request or publish packages | |
| uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0 | |
| id: changesets | |
| with: | |
| version: pnpm run version | |
| publish: pnpm run publish | |
| createGithubReleases: true | |
| setupGitUser: false | |
| # github-api mode makes GitHub create and sign the Version Packages | |
| # commit server-side, satisfying main's required-signatures rule | |
| commitMode: github-api | |
| env: | |
| GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} | |
| NPM_CONFIG_PROVENANCE: true | |
| - name: Release to @dev channel | |
| if: steps.changesets.outputs.hasChangesets == 'true' | |
| run: | | |
| # Hard reset (not checkout) so the snapshot runs against the pristine | |
| # release commit. changesets/action's github-api commit mode leaves the | |
| # version bumps uncommitted in the working tree; a plain checkout keeps | |
| # them, so `version:snapshot` finds no changesets and publishes the real | |
| # release version to the beta tag, which then blocks the latest publish. | |
| git reset --hard ${{ github.sha }} | |
| pnpm version:snapshot $(echo ${{ github.sha }} | cut -c1-7) | |
| # Re-pin to workspace:* so snapshots publish exact internal versions — | |
| # caret prerelease ranges can resolve to a different snapshot batch. | |
| git diff --name-only -- '*package.json' | xargs -r sed -i 's/"workspace:\^"/"workspace:*"/g' | |
| # Sync the lockfile with the re-pinned manifests so the deps check | |
| # in `pnpm run` passes. Offline: the sync must never resolve | |
| # anything new — workspace re-pins don't need the registry. | |
| pnpm install --lockfile-only --no-frozen-lockfile --offline | |
| pnpm run publish:beta | |
| echo "A release PR has been created: <https://github.com/tinacms/tinacms/pull/${{ steps.changesets.outputs.pullRequestNumber }}>" >> $GITHUB_STEP_SUMMARY | |
| env: | |
| GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} | |
| NPM_CONFIG_PROVENANCE: true | |
| - name: Has TinaCMS been published | |
| id: newTinaCMSVersion | |
| if: steps.changesets.outputs.published == 'true' | |
| run: | | |
| # $publishedPackages is a json array of [{"name": "@xx/xx", "version": "1.2.0"}] | |
| # work out if tinacms has been published and output the version number | |
| newTinaCMSVersion=$(echo $publishedPackages | jq -r '.[] | select(.name == "tinacms") | .version') | |
| if [ -z "$newTinaCMSVersion" ]; then | |
| echo "❌ TinaCMS not published" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "🚀 A new version of TinaCMS was released: $newTinaCMSVersion" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "newTinaCMSVersion=$newTinaCMSVersion" >> $GITHUB_OUTPUT | |
| env: | |
| publishedPackages: ${{ steps.changesets.outputs.publishedPackages }} | |
| - name: Publish release notes | |
| if: steps.newTinaCMSVersion.outputs.newTinaCMSVersion != '' | |
| uses: benc-uk/workflow-dispatch@31e2b3319479a63f0ab15bf800eff9e913504e26 # v1.3.2 | |
| with: | |
| ref: main | |
| workflow: main-generate-release-notes.yml | |
| inputs: | | |
| { | |
| "release_name": "${{ steps.newTinaCMSVersion.outputs.newTinaCMSVersion }}" | |
| } | |
| token: ${{ steps.generate-token.outputs.token }} | |
| - name: Trigger starter template builds | |
| if: steps.changesets.outputs.published == 'true' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }} | |
| with: | |
| github-token: ${{ steps.generate-token.outputs.token }} | |
| script: | | |
| await github.rest.actions.createWorkflowDispatch({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: 'build-starter-templates.yml', | |
| ref: 'main', | |
| inputs: { | |
| published_packages: process.env.PUBLISHED_PACKAGES || '[]' | |
| } | |
| }); | |
| console.log('Triggered starter template builds with published packages'); | |
| - name: Generate token for tinacloud | |
| if: steps.changesets.outputs.published == 'true' | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| id: tinacloud-token | |
| with: | |
| app-id: ${{ secrets.BOT_APP_ID }} | |
| private-key: ${{ secrets.BOT_APP_SECRET }} | |
| repositories: tinacloud | |
| - name: Notify tinacloud of published packages | |
| if: steps.changesets.outputs.published == 'true' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| publishedPackages: ${{ steps.changesets.outputs.publishedPackages }} | |
| with: | |
| github-token: ${{ steps.tinacloud-token.outputs.token }} | |
| script: | | |
| const publishedPackages = JSON.parse(process.env.publishedPackages); | |
| console.log(`Notifying tinacloud of ${publishedPackages.length} published packages`); | |
| console.log(publishedPackages.map(p => ` ${p.name}@${p.version}`).join('\n')); | |
| // Trigger a repository dispatch event in the tinacloud repo | |
| await github.rest.repos.createDispatchEvent({ | |
| owner: 'tinacms', | |
| repo: 'tinacloud', | |
| event_type: 'tinacms-release', | |
| client_payload: { | |
| packages: publishedPackages, | |
| source_repo: 'tinacms/tinacms', | |
| source_sha: context.sha | |
| } | |
| }); | |
| console.log('✅ Repository dispatch event sent to tinacloud'); | |
| core.summary.addRaw(`\n🔄 Notified tinacloud of ${publishedPackages.length} published packages`); | |
| await core.summary.write(); | |
| - name: Summary | |
| if: steps.newTinaCMSVersion.outputs.newTinaCMSVersion == '' && steps.changesets.outputs.hasChangesets == 'false' | |
| run: | | |
| echo "❌ Nothing was published" >> $GITHUB_STEP_SUMMARY | |
| # Test job for manually triggering the tinacloud update flow | |
| test-tinacloud-update: | |
| if: github.event_name == 'workflow_dispatch' && inputs.test_graphql_update == true | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Generate token for tinacloud | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| id: tinacloud-token | |
| with: | |
| app-id: ${{ secrets.BOT_APP_ID }} | |
| private-key: ${{ secrets.BOT_APP_SECRET }} | |
| repositories: tinacloud | |
| - name: Trigger tinacloud update (test mode) | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| github-token: ${{ steps.tinacloud-token.outputs.token }} | |
| script: | | |
| const version = '${{ inputs.test_version }}'; | |
| // Simulate a release with the test version | |
| const packages = [ | |
| { name: '@tinacms/graphql', version: version } | |
| ]; | |
| console.log(`[TEST] Notifying tinacloud of test packages:`); | |
| packages.forEach(p => console.log(` ${p.name}@${p.version}`)); | |
| await github.rest.repos.createDispatchEvent({ | |
| owner: 'tinacms', | |
| repo: 'tinacloud', | |
| event_type: 'tinacms-release', | |
| client_payload: { | |
| packages: packages, | |
| source_repo: 'tinacms/tinacms', | |
| source_sha: context.sha | |
| } | |
| }); | |
| console.log('✅ Repository dispatch event sent to tinacloud'); | |
| core.summary.addRaw(`## Test Mode\n\n🔄 Notified tinacloud of test release\n- @tinacms/graphql@${version}`); | |
| await core.summary.write(); |