diff --git a/.github/workflows/sync-main-to-develop.yml b/.github/workflows/sync-main-to-develop.yml index 7aa4159b75..3707b33658 100644 --- a/.github/workflows/sync-main-to-develop.yml +++ b/.github/workflows/sync-main-to-develop.yml @@ -7,7 +7,9 @@ on: description: 'Release version to sync, e.g. 1.2.3. Defaults to packages/vchart version on main.' required: false type: string - pull_request: + # Release PR heads use [skip ci], which suppresses pull_request workflows. + # pull_request_target still runs on merge; this job only checks out main. + pull_request_target: types: - closed branches: @@ -17,7 +19,11 @@ jobs: sync_main_to_develop: if: >- github.event_name == 'workflow_dispatch' || - (github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/')) + ( + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.head.ref, 'release/') && + github.event.pull_request.head.repo.full_name == github.repository + ) runs-on: macos-latest @@ -44,7 +50,7 @@ jobs: - name: Configure git remote for workflow-created branches env: - GH_TOKEN: ${{ secrets.CREATE_TAG_RELEASE_TOKEN || github.token }} + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" @@ -128,7 +134,7 @@ jobs: - name: Create Pull Request to develop env: - GH_TOKEN: ${{ secrets.CREATE_TAG_RELEASE_TOKEN || github.token }} + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail BRANCH="${{ steps.sync_branch.outputs.sync_branch }}" diff --git a/common/scripts/verify-release-workflows.js b/common/scripts/verify-release-workflows.js index 2ca168814f..813689cb46 100644 --- a/common/scripts/verify-release-workflows.js +++ b/common/scripts/verify-release-workflows.js @@ -58,6 +58,10 @@ function assertFallbackGhToken(step, context) { assert(step && step.env && step.env.GH_TOKEN === '${{ secrets.CREATE_TAG_RELEASE_TOKEN || github.token }}', `${context} must use PAT fallback GH_TOKEN`); } +function assertGithubToken(step, context) { + assert(step && step.env && step.env.GH_TOKEN === '${{ github.token }}', `${context} must use GitHub Actions bot token`); +} + function assertStepIfIncludes(step, expected, context) { assert(step, `${context} step must exist`); assert(typeof step.if === 'string' && step.if.includes(expected), `${context} if must include ${expected}`); @@ -164,21 +168,26 @@ function run() { const syncOn = getOn(syncMain); assert(hasOwn(syncOn, 'workflow_dispatch'), 'sync-main-to-develop.yml must support workflow_dispatch recovery'); + assert(!hasOwn(syncOn, 'pull_request'), 'sync-main-to-develop.yml must not use pull_request because release heads use [skip ci]'); + assert(hasOwn(syncOn, 'pull_request_target'), 'sync-main-to-develop.yml must use pull_request_target for release PR merge events'); + assertArrayIncludes(syncOn.pull_request_target.types, ['closed'], 'sync-main-to-develop.yml pull_request_target types'); + assertArrayIncludes(syncOn.pull_request_target.branches, ['main'], 'sync-main-to-develop.yml pull_request_target branches'); assert( syncMain.data.jobs.sync_main_to_develop.if && - syncMain.data.jobs.sync_main_to_develop.if.includes("github.event_name == 'workflow_dispatch'"), - 'sync-main-to-develop.yml job must allow workflow_dispatch' + syncMain.data.jobs.sync_main_to_develop.if.includes("github.event_name == 'workflow_dispatch'") && + syncMain.data.jobs.sync_main_to_develop.if.includes("github.event.pull_request.head.repo.full_name == github.repository"), + 'sync-main-to-develop.yml job must allow workflow_dispatch and only trust same-repo release PRs' ); const syncCheckout = getStep(syncMain, 'sync_main_to_develop', 'Checkout'); assert(syncCheckout && syncCheckout.with && syncCheckout.with.ref === 'main', 'sync-main-to-develop.yml must checkout main explicitly'); - assertFallbackGhToken(getStep(syncMain, 'sync_main_to_develop', 'Configure git remote for workflow-created branches'), 'sync-main remote setup'); + assertGithubToken(getStep(syncMain, 'sync_main_to_develop', 'Configure git remote for workflow-created branches'), 'sync-main remote setup'); assertRunIncludes( getStep(syncMain, 'sync_main_to_develop', 'Compute sync branch name and check existence'), '^[0-9]+\\.[0-9]+\\.[0-9]+$', 'sync-main-to-develop.yml version validation' ); const syncPrStep = getStep(syncMain, 'sync_main_to_develop', 'Create Pull Request to develop'); - assertFallbackGhToken(syncPrStep, 'sync-main PR creation'); + assertGithubToken(syncPrStep, 'sync-main PR creation'); assert(!syncPrStep.if, 'sync-main PR creation must run even when the sync branch already exists'); const dispatchOn = getOn(developDispatch);