diff --git a/.github/workflows/check_approvals.yml b/.github/workflows/check_approvals.yml index 7f8ce8beca9..b10743b7bdc 100644 --- a/.github/workflows/check_approvals.yml +++ b/.github/workflows/check_approvals.yml @@ -12,6 +12,9 @@ jobs: runs-on: ubuntu-latest # Skip draft PRs. if: github.event.pull_request.draft == false && github.repository_owner == 'warpdotdev' + permissions: + contents: read # actions/checkout clones the repo so the script can read OWNERS and diff the changed files + pull-requests: write # `gh pr view` reads the PR's reviews, and `gh pr comment` posts the list of missing approvals steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37e9aa668d8..5e595574cb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,9 @@ jobs: github.event.pull_request.draft == false && (!startsWith(github.event.pull_request.head.ref, 'repo-sync/') || contains(github.event.pull_request.labels.*.name, 'repo-sync:conflict')) && github.repository_owner == 'warpdotdev' + permissions: + contents: read # actions/checkout clones the repo + pull-requests: read # dorny/paths-filter asks the GitHub API for the PR's changed-files list to decide which CI jobs run; reading PR metadata needs this scope outputs: affects-database-schema: ${{ github.ref == 'master' || steps.filter.outputs.affects-database-schema }} affects-rust-sources: ${{ github.ref == 'master' || steps.filter.outputs.affects-rust-sources }} diff --git a/.github/workflows/close_stale_fix_prs.yml b/.github/workflows/close_stale_fix_prs.yml deleted file mode 100644 index df0d728e758..00000000000 --- a/.github/workflows/close_stale_fix_prs.yml +++ /dev/null @@ -1,76 +0,0 @@ - -# ====================================================================================== -# Workflow: Close Stale Fix PRs -# ====================================================================================== -# Usage: -# - This workflow runs daily and closes agent-opened CI fix PRs that have been -# open for more than 3 days. -# -# Expected Output: -# - Any open PR whose branch matches the oz-agent-fix/run-* convention and that -# was opened more than 3 days ago will be closed with an explanatory comment. -# -# ====================================================================================== - -name: Close Stale Fix PRs -on: - schedule: - - cron: '0 12 * * *' # 8am EST - workflow_dispatch: - -jobs: - close-stale-fix-prs: - runs-on: ubuntu-latest - if: github.repository_owner == 'warpdotdev' - steps: - - name: Close Stale Fix PRs - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { owner, repo } = context.repo; - const staleCutoffMs = 3 * 24 * 60 * 60 * 1000; // 3 days in milliseconds - const now = Date.now(); - - // Paginate through all open PRs to find ones matching the branch convention - const openPRs = await github.paginate(github.rest.pulls.list, { - owner, - repo, - state: 'open', - per_page: 100, - }); - - const staleFixPRs = openPRs.filter(pr => { - const branchMatches = /^oz-agent-fix\/run-/.test(pr.head.ref); - const openedAt = new Date(pr.created_at).getTime(); - const isStale = (now - openedAt) > staleCutoffMs; - return branchMatches && isStale; - }); - - if (staleFixPRs.length === 0) { - console.log('No stale fix PRs found.'); - return; - } - - console.log(`Found ${staleFixPRs.length} stale fix PR(s). Closing them...`); - - for (const pr of staleFixPRs) { - const ageMs = now - new Date(pr.created_at).getTime(); - const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000)); - console.log(`Closing PR #${pr.number} (branch: ${pr.head.ref}, open for ${ageDays} day(s)): ${pr.title}`); - - await github.rest.issues.createComment({ - owner, - repo, - issue_number: pr.number, - body: `Closing this automated CI fix PR because it has been open for more than 3 days (${ageDays} day(s)) without being merged. Please review the underlying CI failures manually if they are still relevant.`, - }); - - await github.rest.pulls.update({ - owner, - repo, - pull_number: pr.number, - state: 'closed', - }); - } - - console.log('Done.'); diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 2f494bac7f1..f8215e24feb 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -57,6 +57,9 @@ on: type: boolean default: true +permissions: + contents: write # script/create_release_tag_and_branch pushes the release branch and tag; softprops/action-gh-release creates the GitHub release; each build job runs `gh release upload` to attach its artifacts + env: CARGO_TERM_COLOR: always CONFIG_FILE: ".github/workflows/release_configurations.json" diff --git a/.github/workflows/cut_new_release_candidate.yml b/.github/workflows/cut_new_release_candidate.yml index d670044c75f..5b73d170e9b 100644 --- a/.github/workflows/cut_new_release_candidate.yml +++ b/.github/workflows/cut_new_release_candidate.yml @@ -34,6 +34,8 @@ jobs: create_release_candidate: name: Create Release Candidate uses: ./.github/workflows/create_release.yml + permissions: + contents: write # create_release.yml pushes the release branch/tag and creates and uploads the GitHub release; a reusable-workflow caller must grant everything its callee declares needs: compute_channel with: channel: ${{ needs.compute_channel.outputs.channel }} diff --git a/.github/workflows/cut_new_releases.yml b/.github/workflows/cut_new_releases.yml index cd620d814e6..9f5299f3fb9 100644 --- a/.github/workflows/cut_new_releases.yml +++ b/.github/workflows/cut_new_releases.yml @@ -71,6 +71,8 @@ jobs: create_release: name: Create Release uses: ./.github/workflows/create_release.yml + permissions: + contents: write # create_release.yml pushes the release branch/tag and creates and uploads the GitHub release; a reusable-workflow caller must grant everything its callee declares needs: get_config strategy: fail-fast: false diff --git a/.github/workflows/delete_release.yml b/.github/workflows/delete_release.yml index 6c0cf43bedf..5033e67916e 100644 --- a/.github/workflows/delete_release.yml +++ b/.github/workflows/delete_release.yml @@ -26,6 +26,8 @@ jobs: delete_release: name: Delete Release Branch runs-on: ubuntu-latest + permissions: + contents: write # `git push` renames the release branch to deleted/ and deletes the original steps: - name: Verify confirmation run: | diff --git a/.github/workflows/populate_build_cache.yml b/.github/workflows/populate_build_cache.yml index 00b93c6ebc7..82011b006bb 100644 --- a/.github/workflows/populate_build_cache.yml +++ b/.github/workflows/populate_build_cache.yml @@ -24,4 +24,8 @@ jobs: name: CI if: github.repository_owner == 'warpdotdev' uses: ./.github/workflows/ci.yml + permissions: + contents: read # ci.yml jobs clone the repo + id-token: write # ci.yml's tests jobs mint an OIDC token to authenticate to GCP + pull-requests: read # ci.yml's params job reads the PR changed-files list; a reusable-workflow caller must grant everything its callee's jobs declare secrets: inherit diff --git a/.github/workflows/repo-sync.yml b/.github/workflows/repo-sync.yml index 0948d652254..2521a3458c1 100644 --- a/.github/workflows/repo-sync.yml +++ b/.github/workflows/repo-sync.yml @@ -38,6 +38,9 @@ jobs: ) && github.repository_owner == 'warpdotdev' uses: warpdotdev/repo-sync/.github/workflows/restack.yml@main + permissions: + contents: write # repo-sync's restack workflow rebases and force-pushes the sync branches + pull-requests: write # it retargets the sync PRs and enables auto-merge on them with: repo_sync_ref: main app_id: ${{ vars.REPO_SYNC_APP_ID }} @@ -54,6 +57,9 @@ jobs: startsWith(github.event.pull_request.head.ref, 'repo-sync/') && github.repository_owner == 'warpdotdev' uses: warpdotdev/repo-sync/.github/workflows/approve.yml@main + permissions: + contents: write # repo-sync's approve workflow pushes conflict resolutions to the sync branch + pull-requests: write # it approves the sync PR and enables auto-merge with: repo_sync_ref: main approver_app_id: ${{ vars.REPO_SYNC_APPROVER_APP_ID }} @@ -65,6 +71,10 @@ jobs: escalation: if: inputs.action == 'escalation' && github.repository_owner == 'warpdotdev' uses: warpdotdev/repo-sync/.github/workflows/escalation.yml@main + permissions: + actions: write # repo-sync's escalation workflow re-dispatches the restack workflow for stuck sync stacks + contents: read # it clones the repo to inspect the stack + pull-requests: write # it requests reviewers and disables auto-merge on the stuck sync PRs with: repo_sync_ref: main app_id: ${{ vars.REPO_SYNC_APP_ID }} diff --git a/.github/workflows/warp_cleanup_fix_prs.yml b/.github/workflows/warp_cleanup_fix_prs.yml deleted file mode 100644 index 38db3b0a313..00000000000 --- a/.github/workflows/warp_cleanup_fix_prs.yml +++ /dev/null @@ -1,71 +0,0 @@ - -# ====================================================================================== -# Workflow: Cleanup Fix PRs -# ====================================================================================== -# Usage: -# - This workflow cleans up PRs generated by Fix Failing Checks -# -# Expected Output: -# - The workflow should close all PRs created by the github-actions bot that are based on a branch with the auto-fix-ci label after it closes -# -# ====================================================================================== - -name: Cleanup Fix PRs -on: - pull_request: - types: [closed] - -jobs: - close-fix-prs: - runs-on: ubuntu-latest - if: contains(github.event.pull_request.labels.*.name, 'auto-fix-ci') && github.repository_owner == 'warpdotdev' - steps: - - name: Close Dependent Fix PRs - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - script: | - const { pull_request: pr, repository } = context.payload; - const { owner, repo } = context.repo; - - const closedBranch = pr.head.ref; - console.log(`Original PR #${pr.number} closed. Head ref was: ${closedBranch}`); - - // Find open PRs that target the closed PR's branch - const { data: openPRs } = await github.rest.pulls.list({ - owner, - repo, - state: 'open', - base: closedBranch - }); - - if (openPRs.length === 0) { - console.log('No open fix PRs found targeting this branch.'); - return; - } - - console.log(`Found ${openPRs.length} open PRs targeting ${closedBranch}. Closing them...`); - - for (const openPR of openPRs) { - if (openPR.user.login !== 'github-actions[bot]') { - console.log(`Skipping PR #${openPR.number} created by ${openPR.user.login} (not github-actions[bot])`); - continue; - } - - console.log(`Closing PR #${openPR.number}: ${openPR.title}`); - - // Comment on the fix PR - await github.rest.issues.createComment({ - owner, - repo, - issue_number: openPR.number, - body: `Closing this fix PR because the parent PR #${pr.number} has been merged.` - }); - - // Close the fix PR - await github.rest.pulls.update({ - owner, - repo, - pull_number: openPR.number, - state: 'closed' - }); - }