diff --git a/.github/workflows/code_freeze.yml b/.github/workflows/code_freeze.yml new file mode 100644 index 0000000000000..513390827cc88 --- /dev/null +++ b/.github/workflows/code_freeze.yml @@ -0,0 +1,44 @@ +on: + workflow_dispatch: + inputs: + type: + description: Enable/disable code freeze + required: true + type: choice + options: + - enable + - disable + + secrets: + CODE_FREEZE_TOKEN: + required: true + +name: Code Freeze + +env: + NAPI_CLI_VERSION: 2.14.7 + TURBO_VERSION: 1.10.9 + NODE_MAINTENANCE_VERSION: 16 + NODE_LTS_VERSION: 18 + +jobs: + start: + runs-on: ubuntu-latest + + environment: release-${{ github.event.inputs.releaseType }} + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 18 + check-latest: true + + - run: git clone https://github.com/vercel/next.js.git --depth=1 . + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - run: node ./scripts/code-freeze.js --type ${{ github.event.inputs.type }} + env: + CODE_FREEZE_TOKEN: ${{ secrets.CODE_FREEZE_TOKEN }} diff --git a/scripts/code-freeze.js b/scripts/code-freeze.js new file mode 100644 index 0000000000000..6d2d5996d6d33 --- /dev/null +++ b/scripts/code-freeze.js @@ -0,0 +1,117 @@ +const authToken = process.env.CODE_FREEZE_TOKEN + +if (!authToken) { + throw new Error(`missing CODE_FREEZE_TOKEN env`) +} + +const codeFreezeRule = { + context: 'Potentially publish release', + app_id: 15368, +} + +async function updateRules(newRules) { + const res = await fetch( + `https://api.github.com/repos/vercel/next.js/branches/canary/protection`, + { + method: 'PUT', + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${authToken}`, + 'X-GitHub-Api-Version': '2022-11-28', + }, + body: JSON.stringify(newRules), + } + ) + + if (!res.ok) { + throw new Error( + `Failed to check for rule ${res.status} ${await res.text()}` + ) + } +} + +async function getCurrentRules() { + const res = await fetch( + `https://api.github.com/repos/vercel/next.js/branches/canary/protection`, + { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${authToken}`, + 'X-GitHub-Api-Version': '2022-11-28', + }, + } + ) + + if (!res.ok) { + throw new Error( + `Failed to check for rule ${res.status} ${await res.text()}` + ) + } + const data = await res.json() + + return { + required_status_checks: { + strict: data.required_status_checks.strict, + // checks: data.required_status_checks.checks, + contexts: data.required_status_checks.contexts, + }, + enforce_admins: data.enforce_admins.enabled, + required_pull_request_reviews: { + dismiss_stale_reviews: + data.required_pull_request_reviews.dismiss_stale_reviews, + require_code_owner_reviews: + data.required_pull_request_reviews.require_code_owner_reviews, + require_last_push_approval: + data.required_pull_request_reviews.require_last_push_approval, + required_approving_review_count: + data.required_pull_request_reviews.required_approving_review_count, + }, + restrictions: data.restrictions || { + users: [], + teams: [], + apps: [], + }, + } +} + +async function main() { + const typeIdx = process.argv.indexOf('--type') + const type = process.argv[typeIdx + 1] + + if (type !== 'enable' && type !== 'disable') { + throw new Error(`--type should be enable or disable`) + } + const isEnable = type === 'enable' + const currentRules = await getCurrentRules() + const hasRule = currentRules.required_status_checks.contexts?.some((ctx) => { + return ctx === codeFreezeRule.context + }) + + console.log(currentRules) + + if (isEnable) { + if (hasRule) { + console.log(`Already enabled`) + return + } + currentRules.required_status_checks.contexts.push(codeFreezeRule.context) + await updateRules(currentRules) + console.log('Enabled code freeze') + } else { + if (!hasRule) { + console.log(`Already disabled`) + return + } + currentRules.required_status_checks.contexts = + currentRules.required_status_checks.contexts.filter( + (ctx) => ctx !== codeFreezeRule.context + ) + await updateRules(currentRules) + console.log('Disabled code freeze') + } +} + +main().catch((err) => { + console.error(err) + process.exit(1) +})