Cleaner #3341
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
| name: Cleaner | |
| on: | |
| schedule: | |
| # Run every hour at minute 0 | |
| - cron: '0 * * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| discussions: write | |
| jobs: | |
| cleanup: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Close test issues, PRs, and delete discussions | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prefix = "[Custom Engine Test]"; | |
| // Close issues with the prefix | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| per_page: 100 | |
| }); | |
| for (const issue of issues.data) { | |
| if (issue.title.startsWith(prefix) && !issue.pull_request) { | |
| console.log(`Closing issue: ${issue.title} (#${issue.number})`); | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| state: 'closed' | |
| }); | |
| } | |
| } | |
| // Close pull requests with the prefix | |
| const pulls = await github.rest.pulls.list({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| per_page: 100 | |
| }); | |
| for (const pr of pulls.data) { | |
| if (pr.title.startsWith(prefix)) { | |
| console.log( | |
| `Closing pull request: ${pr.title} (#${pr.number})` | |
| ); | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| state: 'closed' | |
| }); | |
| } | |
| } | |
| // Delete discussions with the prefix | |
| try { | |
| const discussionsQuery = ` | |
| query($owner: String!, $repo: String!) { | |
| repository(owner: $owner, name: $repo) { | |
| discussions(first: 100, states: [OPEN]) { | |
| nodes { | |
| id | |
| number | |
| title | |
| } | |
| } | |
| } | |
| } | |
| `; | |
| const discussionsResult = await github.graphql(discussionsQuery, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| }); | |
| const discussions = | |
| discussionsResult.repository.discussions.nodes || []; | |
| for (const discussion of discussions) { | |
| if (discussion.title.startsWith(prefix)) { | |
| console.log( | |
| `Deleting discussion: ` + | |
| `${discussion.title} (#${discussion.number})` | |
| ); | |
| const deleteDiscussionMutation = ` | |
| mutation($discussionId: ID!) { | |
| deleteDiscussion(input: { id: $discussionId }) { | |
| discussion { | |
| id | |
| } | |
| } | |
| } | |
| `; | |
| await github.graphql(deleteDiscussionMutation, { | |
| discussionId: discussion.id, | |
| }); | |
| } | |
| } | |
| } catch (error) { | |
| // Handle repositories without discussions enabled gracefully | |
| const errorMessage = | |
| error instanceof Error ? error.message : String(error); | |
| if ( | |
| errorMessage.includes("Not Found") || | |
| errorMessage.includes("not found") || | |
| errorMessage.includes("Could not resolve to a Repository") || | |
| errorMessage.includes("discussions") | |
| ) { | |
| console.log( | |
| "⚠ Cannot delete discussions: " + | |
| "Discussions are not enabled for this repository " + | |
| "or no discussions found" | |
| ); | |
| } else { | |
| console.error(`Error deleting discussions: ${errorMessage}`); | |
| throw error; | |
| } | |
| } | |
| // Delete branches starting with "test-safe-outputs-custom-engine/" | |
| const branchPrefix = "test-safe-outputs-custom-engine/"; | |
| try { | |
| const branchesResponse = await github.rest.repos.listBranches({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| per_page: 100 | |
| }); | |
| const branchesToDelete = branchesResponse.data.filter( | |
| branch => branch.name.startsWith(branchPrefix) | |
| ); | |
| for (const branch of branchesToDelete) { | |
| console.log(`Deleting branch: ${branch.name}`); | |
| try { | |
| await github.rest.git.deleteRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: `heads/${branch.name}` | |
| }); | |
| } catch (deleteError) { | |
| const errorMessage = deleteError instanceof Error | |
| ? deleteError.message | |
| : String(deleteError); | |
| console.error(`Failed to delete branch ${branch.name}: ${errorMessage}`); | |
| // Continue with other branches even if one fails | |
| } | |
| } | |
| if (branchesToDelete.length > 0) { | |
| console.log(`Processed ${branchesToDelete.length} branches with prefix "${branchPrefix}"`); | |
| } else { | |
| console.log(`No branches found with prefix "${branchPrefix}"`); | |
| } | |
| } catch (error) { | |
| const errorMessage = error instanceof Error ? error.message : String(error); | |
| console.error(`Error listing branches: ${errorMessage}`); | |
| // Don't throw error - we want other cleanup operations to continue | |
| } |