From 8d550e2ebcebb019d9693d3965d5a52557108078 Mon Sep 17 00:00:00 2001 From: Evan Bonsignori Date: Wed, 29 Jan 2025 16:04:33 -0800 Subject: [PATCH] Delete `close-dangling-prs` workflow & script (#54180) --- .github/workflows/close-dangling-prs.yml | 40 ------- package.json | 1 - src/workflows/close-dangling-prs.ts | 142 ----------------------- 3 files changed, 183 deletions(-) delete mode 100644 .github/workflows/close-dangling-prs.yml delete mode 100644 src/workflows/close-dangling-prs.ts diff --git a/.github/workflows/close-dangling-prs.yml b/.github/workflows/close-dangling-prs.yml deleted file mode 100644 index 0524d901ce75..000000000000 --- a/.github/workflows/close-dangling-prs.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Close dangling PRs - -# **What it does**: Searches for specific PRs that have been open for a certain number of days and closes them. -# **Why we have it**: Tidying up so humans don't need to do it. -# **Who does it impact**: docs-engineering - -on: - workflow_dispatch: - schedule: - - cron: '20 16 * * *' # Run every day at 16:20 UTC / 8:20 PST - pull_request: - paths: - - .github/workflows/close-dangling-prs.yml - - src/workflows/close-dangling-prs.ts - -permissions: - contents: read - pull-requests: write - -jobs: - close-dangling-prs: - if: github.repository == 'github/docs-internal' - runs-on: ubuntu-latest - steps: - - name: Check out repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - uses: ./.github/actions/node-npm-setup - - - name: Close dangling PRs - env: - GITHUB_TOKEN: ${{ secrets.DOCS_BOT_PAT_READPUBLICKEY }} - DRY_RUN: ${{ github.event_name == 'pull_request'}} - run: npm run close-dangling-prs - - - uses: ./.github/actions/slack-alert - if: ${{ failure() && github.event_name == 'schedule' }} - with: - slack_channel_id: ${{ secrets.DOCS_ALERTS_SLACK_CHANNEL_ID }} - slack_token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }} diff --git a/package.json b/package.json index 7bbe34a03665..15f3c9f94664 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "build": "next build", "check-content-type": "tsx src/workflows/check-content-type.ts", "check-github-github-links": "tsx src/links/scripts/check-github-github-links.ts", - "close-dangling-prs": "tsx src/workflows/close-dangling-prs.ts", "cmp-files": "tsx src/workflows/cmp-files.ts", "content-changes-table-comment": "tsx src/workflows/content-changes-table-comment.ts", "copy-fixture-data": "tsx src/tests/scripts/copy-fixture-data.js", diff --git a/src/workflows/close-dangling-prs.ts b/src/workflows/close-dangling-prs.ts deleted file mode 100644 index 43ced3d4a2d3..000000000000 --- a/src/workflows/close-dangling-prs.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { program, Option } from 'commander' - -import github from './github.js' -import { octoSecondaryRatelimitRetry } from './secondary-ratelimit-retry' - -const DRY_RUN = process.env.DRY_RUN || 'false' -const DEFAULT_MIN_DAYS = 10 - -const CLOSING_COMMENT = `Closing this PR because it's been open for too long without any activity. - -This is an automated event triggered by \`close-danging-prs\`. -` - -// This script is deliberately vague in that we might have multiple -// of what a "dangling PR" is. You can search PRs by title and find -// some that should match and some that shouldn't. Because the title -// search isn't verbatim. -// The `titleSearch` finds a "shortlist" of candidates but then when -// we loop over the more carefully, we look for more specifics (verbatim) -// matchings on the PR title. -type Config = { - titleSearch: string - titlePrefix: string - userLogin?: string -} -const CONFIGS: Config[] = [ - { - titleSearch: 'FOR PREVIEW ONLY', - titlePrefix: '[FOR PREVIEW ONLY] Preview of OpenAPI', - userLogin: 'docs-bot', - }, -] - -const owner = process.env.GITHUB_REPOSITORY_OWNER || 'github' -const repo = process.env.GITHUB_REPOSITORY?.split('/')[1] || 'docs-internal' - -program - .description('Finds PRs with "FOR PREVIEW ONLY" in the title that are too old and closes them.') - .option('--owner', 'Owner of the repository', owner) - .option('--repo', 'Name of the repository', repo) - .addOption( - new Option( - '--dry-run', - "Don't post any comment or close any PRs. Only print what it would do.", - ).default(Boolean(JSON.parse(DRY_RUN))), - ) - .option( - '--min-days ', - 'Minimum number of days since creation to be considered.', - `${DEFAULT_MIN_DAYS}`, - ) - .action(main) - -program.parse(process.argv) - -type Options = { - owner: string - repo: string - dryRun: boolean - minDays: number | string -} - -async function main(options: Options) { - const { dryRun, owner, repo } = options - - const minDays = - typeof options.minDays === 'string' ? parseInt(options.minDays, 10) : options.minDays - - if (!process.env.GITHUB_TOKEN) { - throw new Error( - `GITHUB_TOKEN is required to run this action and it needs read-write access to ${owner}/${repo}`, - ) - } - - const octokit = github() - - for (const config of CONFIGS) { - let q = `repo:${owner}/${repo} is:pr is:open ` - if (config.titleSearch) q += `in:title "${config.titleSearch}" ` - if (config.userLogin) q += `author:${config.userLogin} ` - q = q.trim() - - const { data } = await octoSecondaryRatelimitRetry(() => - octokit.rest.search.issuesAndPullRequests({ q }), - ) - - type PRData = { - updated_at: string - } - data.items.sort( - (pr1: PRData, pr2: PRData) => - new Date(pr1.updated_at).getTime() - new Date(pr2.updated_at).getTime(), - ) - - let countTooOld = 0 - for (const pr of data.items) { - if (config.titlePrefix && !pr.title.startsWith(config.titlePrefix)) { - console.log("PR doesn't have the right title prefix. Skipping.") - continue - } - - const L = (label: string) => `${label}:`.padEnd(20) - console.log(L('URL'), pr.html_url) - console.log(L('UPDATED'), pr.updated_at) - console.log(L('CREATED'), pr.created_at) - console.log(L('STATE'), pr.state) - console.log(L('DRAFT'), pr.draft) - const ageDays = (Date.now() - new Date(pr.updated_at).getTime()) / (1000 * 60 * 60 * 24) - console.log(L('AGE (days)'), Math.floor(ageDays)) - if (ageDays < minDays) { - console.log('Not old enough. Leave as is.') - } else { - countTooOld++ - - if (!dryRun) { - console.log('Updating PR with a comment...') - await octokit.rest.issues.createComment({ - owner, - repo, - issue_number: pr.number, - body: CLOSING_COMMENT, - }) - - console.log('Closing PR...') - await octokit.rest.pulls.update({ - owner, - repo, - pull_number: pr.number, - state: 'closed', - }) - - console.log('** Closed and commented because it was old enough.**') - } else { - console.warn("** Would close and comment on PR if it wasn't a dry run. **") - } - } - console.log('\n') // break between each print - } - - console.log(`${data.items.length} PRs found, ${countTooOld} old enough to close.`) - } -}