Clean Workflow Runs #72
This file contains 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
# | |
# SPDX-FileCopyrightText: 2021 Ching Chow. | |
# SPDX-FileCopyrightText: 2021-2025 Jens A. Koch. | |
# SPDX-License-Identifier: BSL-1.0 | |
# | |
# This file is part of https://github.com/jakoch/cpp-devbox | |
# | |
name: Clean Workflow Runs | |
on: | |
# This workflow runs at 03:17 daily. | |
# The irregular time is used to avoid Github high load times. | |
schedule: | |
- cron: "17 3 * * *" # GMT | |
# You can manually run this workflow. | |
workflow_dispatch: | |
jobs: | |
cleanup-workflows: | |
name: "Clean Workflows" | |
runs-on: ubuntu-24.04 | |
timeout-minutes: 10 | |
steps: | |
- name: ✂ Remove cancelled or skipped workflow runs | |
uses: actions/github-script@v7 # https://github.com/actions/github-script | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const cancelled = await github.rest.actions.listWorkflowRunsForRepo({ | |
owner: context.repo.owner, | |
per_page: 100, | |
repo: context.repo.repo, | |
status: 'cancelled', | |
}); | |
const skipped = await github.rest.actions.listWorkflowRunsForRepo({ | |
owner: context.repo.owner, | |
per_page: 100, | |
repo: context.repo.repo, | |
status: 'skipped', | |
}); | |
for (const response of [cancelled, skipped]) { | |
for (const run of response.data.workflow_runs) { | |
console.log(`[Deleting] Run id ${run.id} of '${run.name}' is a cancelled or skipped run.`); | |
await github.rest.actions.deleteWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: run.id | |
}); | |
} | |
} | |
- name: ✂ Remove 30 days old workflows runs | |
uses: actions/github-script@v7 # https://github.com/actions/github-script | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const days_to_expiration = 14; | |
const ms_in_day = 86400000; | |
const now = Date.now(); | |
const pages = 5; | |
// add the workflows runs to remove here | |
const workflows = [ | |
'release.yml', | |
'check-spelling.yml' | |
] | |
let runs_to_delete = []; | |
for (const workflow of workflows) { | |
for (let page = 0; page < pages; page += 1) { | |
let response = await github.rest.actions.listWorkflowRuns({ | |
owner: context.repo.owner, | |
page: page, | |
per_page: 100, | |
repo: context.repo.repo, | |
workflow_id: workflow | |
}); | |
if (response.data.workflow_runs.length > 0) { | |
for (const run of response.data.workflow_runs) { | |
if (now - Date.parse(run.created_at) > ms_in_day * days_to_expiration) { | |
runs_to_delete.push([run.id, run.name]); | |
} | |
} | |
} | |
} | |
} | |
for (const run of runs_to_delete) { | |
console.log(`[Deleting] Run id ${run[0]} of '${run[1]}' is older than ${days_to_expiration} days.`); | |
try { | |
await github.rest.actions.deleteWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: run[0] | |
}); | |
} catch (error) { | |
// ignore errors | |
} | |
} | |
# https://api.github.com/repos/jakoch/cpp-devbox/actions/workflows/clean-workflows.yml/runs | |
- name: ✂ Remove runs of the cleanup workflow itself | |
uses: actions/github-script@v7 # https://github.com/actions/github-script | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const pages = 5; | |
let runs_to_delete = []; | |
for (let page = 0; page < pages; page += 1) { | |
let response = await github.rest.actions.listWorkflowRuns({ | |
owner: context.repo.owner, | |
page: page, | |
per_page: 100, | |
repo: context.repo.repo, | |
workflow_id: 'clean-workflows.yml' | |
}); | |
if (response.data.workflow_runs.length > 0) { | |
for (const run of response.data.workflow_runs) { | |
runs_to_delete.push([run.id, run.name]); | |
} | |
} | |
} | |
for (const run of runs_to_delete) { | |
console.log(`[Deleting] Run id ${run[0]} of '${run[1]}'.`); | |
try { | |
await github.actions.deleteWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: run[0] | |
}); | |
} catch (error) { | |
// ignore errors | |
} | |
} | |
- name: ✂ Remove Dependabot workflow runs | |
uses: actions/github-script@v7 # https://github.com/actions/github-script | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const pages = 5; | |
let runs_to_delete = []; | |
// Get all workflows | |
const { data: workflowsData } = await github.rest.actions.listRepoWorkflows({ | |
owner: context.repo.owner, | |
repo: context.repo.repo | |
}); | |
// Find workflow with name "Dependabot Updates" | |
const dependabotWorkflow = workflowsData.workflows.find(w => w.name === "Dependabot Updates"); | |
if (!dependabotWorkflow) { | |
console.log("❌ No workflow named 'Dependabot Updates' found."); | |
return; | |
} | |
const workflowId = dependabotWorkflow.id; | |
console.log(`✅ Found Dependabot workflow ID: ${workflowId}`); | |
// Fetch workflow runs | |
for (let page = 0; page < pages; page += 1) { | |
const response = await github.rest.actions.listWorkflowRuns({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
workflow_id: workflowId, // Use dynamically fetched ID | |
page: page, | |
per_page: 100, | |
}); | |
if (response.data.workflow_runs.length > 0) { | |
for (const run of response.data.workflow_runs) { | |
if (run.triggering_actor?.login === 'dependabot[bot]') { | |
runs_to_delete.push([run.id, run.name]); | |
} | |
} | |
} | |
} | |
// Delete workflow runs | |
for (const run of runs_to_delete) { | |
console.log(`[Deleting] Run id ${run[0]} of '${run[1]}'.`); | |
try { | |
await github.rest.actions.deleteWorkflowRun({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
run_id: run[0] | |
}); | |
} catch (error) { | |
// ignore errors | |
} | |
} |