Skip to content

Clean Workflow Runs #72

Clean Workflow Runs

Clean Workflow Runs #72

#
# 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
}
}