Skip to content

Threads Close

Threads Close #17

Workflow file for this run

name: 'Threads Close'
on:
workflow_dispatch:
inputs:
job_type:
description: 'Type to Close'
type: choice
required: true
default: 'close-any'
options:
- 'close-any'
- 'close-issues'
- 'close-discussions'
schedule:
- cron: '5 5 */3 * *'
permissions:
issues: write
discussions: write
jobs:
# Handle Stale Threads - https://github.com/actions/stale
stale-issues:
name: Flag or Close Issues
runs-on: ubuntu-latest
if: |
github.repository == 'RefindPlusRepo/RefindPlus' && (
github.event_name == 'schedule' ||
github.event.inputs.job_type == 'close-any' ||
github.event.inputs.job_type == 'close-issues'
)
steps:
- id: app-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Repository Checkout
uses: actions/checkout@v6
- name: Handle Rate Limits
uses: ./.github/actions/handle-rate-limits
with:
token: ${{ steps.app-token.outputs.token }}
- name: Handle Cancelled/Duplicate/Invalid/Wierd/Not-Planned Issues
uses: actions/stale@v10
with:
ignore-updates: true
close-issue-reason: 'completed'
close-issue-message: ''
days-before-stale: 0
days-before-close: 1
exempt-assignees: 'dakanji'
stale-issue-label: 'Tagged'
any-of-labels: 'Cancelled, Duplicate, Known-Issue, Invalid, Cannot-Replicate, Not-Planned, Noted, Config'
remove-issue-stale-when-updated: false
repo-token: ${{ steps.app-token.outputs.token }}
- name: Handle Rate Limits
uses: ./.github/actions/handle-rate-limits
with:
token: ${{ steps.app-token.outputs.token }}
- name: Handle Incomplete Issues
uses: actions/stale@v10
with:
ignore-updates: false
close-issue-reason: 'completed'
stale-issue-message: |-
This item has been flagged for closure.
Trigger: Missing Requirement.
close-issue-message: |-
This item is being closed.
days-before-stale: 0
days-before-close: 2
exempt-assignees: 'dakanji'
stale-issue-label: 'Stale'
any-of-labels: 'Incomplete'
repo-token: ${{ steps.app-token.outputs.token }}
- name: Handle Rate Limits
uses: ./.github/actions/handle-rate-limits
with:
token: ${{ steps.app-token.outputs.token }}
- name: Handle No-Response Issues
uses: actions/stale@v10
with:
ignore-updates: false
close-issue-reason: 'completed'
stale-issue-message: |-
This item has been flagged for closure.
Trigger: Failure to Respond.
close-issue-message: |-
This item is being closed.
days-before-stale: 1
days-before-close: 2
exempt-assignees: 'dakanji'
stale-issue-label: 'Stale'
any-of-labels: 'Queries'
remove-issue-stale-when-updated: true
repo-token: ${{ steps.app-token.outputs.token }}
- name: Handle Rate Limits
uses: ./.github/actions/handle-rate-limits
with:
token: ${{ steps.app-token.outputs.token }}
- name: Catchall Closer
uses: actions/stale@v10
with:
ignore-updates: false
close-issue-reason: 'completed'
stale-issue-message: |-
This item has been flagged for closure.
Trigger: Inactivity Threshold.
close-issue-message: |-
This item is being closed.
days-before-stale: 21
days-before-close: 21
exempt-assignees: 'dakanji'
stale-issue-label: 'Stale'
remove-issue-stale-when-updated: true
repo-token: ${{ steps.app-token.outputs.token }}
# Handle Stale Threads - https://github.com/actions/stale
stale-discussions:
name: Close Discussions
runs-on: ubuntu-latest
if: |
github.repository == 'RefindPlusRepo/RefindPlus' && (
github.event_name == 'schedule' ||
github.event.inputs.job_type == 'close-any' ||
github.event.inputs.job_type == 'close-discussions'
)
steps:
- id: app-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Repository Checkout
uses: actions/checkout@v6
- name: Handle Rate Limits
uses: ./.github/actions/handle-rate-limits
with:
token: ${{ steps.app-token.outputs.token }}
- name: Close Inactive Discussions
uses: actions/github-script@v9
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const createdAfter = new Date('2026-07-01T00:00:00Z');
const inactiveDays = 60;
const inactiveBefore = new Date(Date.now() - inactiveDays * 24 * 60 * 60 * 1000);
const query = `
query($owner: String!, $repo: String!, $after: String) {
repository(owner: $owner, name: $repo) {
discussions(first: 100, after: $after, orderBy: {field: UPDATED_AT, direction: ASC}) {
pageInfo { hasNextPage endCursor }
nodes { id number createdAt updatedAt closed locked }
}
}
}
`;
const mutation = `
mutation($discussionId: ID!, $reason: DiscussionCloseReason) {
closeDiscussion(input: {discussionId: $discussionId, reason: $reason}) {
discussion { id number }
}
}
`;
let after = null;
let hasNextPage = true;
const toClose = [];
while (hasNextPage) {
const result = await github.graphql(query, { owner, repo, after });
const page = result.repository.discussions;
for (const d of page.nodes) {
const updatedAt = new Date(d.updatedAt);
// Discussions are ordered oldest-updated first, so once we hit one
// inside the inactivity window, every later node will also be too.
if (updatedAt > inactiveBefore) {
hasNextPage = false;
break;
}
const createdAt = new Date(d.createdAt);
if (!d.closed && !d.locked && createdAt >= createdAfter) {
toClose.push(d);
}
}
if (hasNextPage) {
hasNextPage = page.pageInfo.hasNextPage;
after = page.pageInfo.endCursor;
}
}
core.info(`Found ${toClose.length} discussion(s) to close.`);
for (const d of toClose) {
try {
await github.graphql(mutation, { discussionId: d.id, reason: 'OUTDATED' });
core.info(`Closed discussion #${d.number}`);
} catch (err) {
core.warning(`Failed to close discussion #${d.number}: ${err.message}`);
}
}