Util: Sync master to 3.x #72
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
| # Syncs the master branch into the long-lived 3.x branch daily. | |
| # | |
| # During the v3 development window, master carries normal feature work (behind opt-in | |
| # flags) and 3.x is "master + breaking-change commits". This workflow keeps that literally | |
| # true by REPLAYING the 3.x-only commits onto master and force-pushing 3.x — so a clean | |
| # sync adds no commit at all, and every replayed commit is kept as-is (nothing squashed, | |
| # authors preserved). The content pushed is always verified to be exactly what a merge of | |
| # 3.x and master would produce. Per-commit stalls the replay cannot settle with a strategy | |
| # option alone (e.g. modify/delete) are resolved toward 3.x's side, under that same | |
| # verification. | |
| # | |
| # Conflicts confined to mechanical, tool-generated files (the pnpm lockfile, bot-maintained | |
| # data files) are resolved in place during the replay — no PR, no commit, no human. When | |
| # master genuinely conflicts with 3.x on real code, 3.x is left untouched: a draft conflict | |
| # PR carrying the conflict markers (mechanical files pre-resolved) is opened on the sync | |
| # branch and #alerts-v3-sync is notified. Delete/modify conflicts, which git cannot express | |
| # as markers, are resolved toward 3.x's side and listed in that PR as an explicit decision. | |
| # The resolver fixes the markers in a commit of their own and merges that PR with the normal | |
| # merge button — never closes it, since closing resolves nothing and the conflict returns. | |
| # No further syncs run until it is merged. 3.x itself never has markers at its tip (nightly | |
| # images build from it) and the merge commit holding them drops out of its history at the | |
| # next replay. | |
| # | |
| # See .github/DEVELOPING_V3.md for the full v3 development model. | |
| name: 'Util: Sync master to 3.x' | |
| on: | |
| schedule: | |
| - cron: '0 6 * * *' | |
| workflow_dispatch: | |
| # Serialize syncs — never run two at once. | |
| concurrency: | |
| group: sync-master-to-3x | |
| cancel-in-progress: false | |
| # Least privilege by default; each job opts into exactly what it needs. | |
| permissions: {} | |
| jobs: | |
| sync: | |
| name: Sync master into 3.x | |
| if: github.repository == 'n8n-io/n8n' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| outputs: | |
| conflict_pr: ${{ steps.sync.outputs.conflict_pr }} | |
| conflict_owners: ${{ steps.sync.outputs.conflict_owners }} | |
| steps: | |
| - name: Generate GitHub App Token | |
| id: app-token | |
| uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 | |
| with: | |
| app-id: ${{ secrets.N8N_ASSISTANT_APP_ID }} | |
| private-key: ${{ secrets.N8N_ASSISTANT_PRIVATE_KEY }} | |
| # Scope the installation token to only what the sync needs. | |
| permission-contents: write # force-push 3.x / push the conflict branch | |
| permission-pull-requests: write # open the conflict PR | |
| permission-issues: write # create the conflict label | |
| - name: Checkout 3.x | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: 3.x | |
| fetch-depth: 0 | |
| persist-credentials: false # we push with an explicit token URL instead | |
| # Node + pnpm toolchain for the mechanical lockfile resolution | |
| # (`pnpm install --lockfile-only`). No dependency install, no build. | |
| - name: Setup Node.js | |
| uses: ./.github/actions/setup-nodejs | |
| with: | |
| install-command: '' | |
| build-command: '' | |
| - name: Sync master into 3.x | |
| id: sync | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: node .github/scripts/sync-master-to-3x.mjs | |
| notify-conflict: | |
| name: Notify Slack about conflict PR | |
| needs: [sync] | |
| if: ${{ needs.sync.outputs.conflict_pr != '' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # checkout the slack scripts | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| sparse-checkout: .github/scripts/slack | |
| sparse-checkout-cone-mode: false | |
| persist-credentials: false | |
| - name: Notify Slack | |
| env: | |
| SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }} | |
| PR_URL: ${{ needs.sync.outputs.conflict_pr }} | |
| OWNERS_TEXT: ${{ needs.sync.outputs.conflict_owners }} | |
| run: | | |
| node .github/scripts/slack/notify.mjs \ | |
| --channel '#alerts-v3-sync' \ | |
| --text "<${PR_URL}|master→3.x sync hit a conflict — resolve and merge this PR>. 3.x is untouched and daily syncs are paused until it is merged. ${OWNERS_TEXT}" | |
| notify-on-failure: | |
| name: Notify Slack on failure | |
| needs: [sync] | |
| if: ${{ always() && needs.sync.result == 'failure' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # checkout the slack scripts | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| sparse-checkout: .github/scripts/slack | |
| sparse-checkout-cone-mode: false | |
| persist-credentials: false | |
| - name: Notify Slack | |
| env: | |
| SLACK_TOKEN: ${{ secrets.QBOT_SLACK_TOKEN }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| run: | | |
| node .github/scripts/slack/notify.mjs \ | |
| --channel '#alerts-v3-sync' \ | |
| --text "<${RUN_URL}|master→3.x sync workflow failed unexpectedly>" |