Skip to content

Commit

Permalink
New input: allow deletion of workflows older than a given timeframe (#37
Browse files Browse the repository at this point in the history
)

* Adds documentation for new feature
* Implements new input 'remove-older-than'
* Adds cleaning up of old workflow runs to this repo
  • Loading branch information
hanseartic authored Sep 23, 2024
1 parent 6ffb9b7 commit a00a1b5
Show file tree
Hide file tree
Showing 10 changed files with 6,748 additions and 440 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/purge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ jobs:
- uses: otto-de/purge-deprecated-workflow-runs@v2
with:
token: ${{ github.token }}
- uses: otto-de/purge-deprecated-workflow-runs@v2
with:
token: ${{ github.token }}
remove-obsolete: false
remove-older-than: 30d
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
60 changes: 51 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ filter can be applied to deleted workflow runs by status/conclusion - see input

## Inputs

| Name | Description | Default | Optional |
|--------------------|--------------------------------|-----------------------|----------|
| `token` | The token used to authenticate | `${{ github.token }}` | `true` |
| `remove-obsolete` | Remove obsolete workflows | `true` | `true` |
| `remove-cancelled` | Remove cancelled workflows | `false` | `true` |
| `remove-failed` | Remove failed workflows | `false` | `true` |
| `remove-skipped` | Remove skipped workflows | `false` | `true` |

### remarks on the input fields
| Name | Description | Default | Is optional |
|---------------------|---------------------------------------|-----------------------|-------------|
| `token` | The token used to authenticate | `${{ github.token }}` | `true` |
| `remove-obsolete` | Remove obsolete workflows | `true` | `true` |
| `remove-cancelled` | Remove cancelled workflows | `false` | `true` |
| `remove-failed` | Remove failed workflows | `false` | `true` |
| `remove-older-than` | Remove workflows older than timeframe | `<null>` | `true` |
| `remove-skipped` | Remove skipped workflows | `false` | `true` |

### Remarks on the input fields
All inputs are optional - if any input is not given, the default value will be used.

<dl>
<dt>

Expand Down Expand Up @@ -48,6 +51,24 @@ filter can be applied to deleted workflow runs by status/conclusion - see input
</dd>
<dt>

`remove-older-than`</dt>
<dd>

- Remove workflows from the list that are older than the given timeframe (e.g. '10S', '30M', '12H', '7d', '2w', '1m', '6y')
- Accepts a (multiline) `string` in the format of `NU [W]` where `N` is a number, `U` is a time unit and optionally `W` is the workflow name.
The following units are supported:
- `s` for seconds
- `m` for minutes
- `h` for hours
- `d` for days
- `w` for weeks
- `y` for years
- When given as a multiline string, each line will be parsed as a separate input
- When given with `W = *` or without a workflow name, all workflows will be checked
- When given with a workflow name, only the matching workflows will be checked
</dd>
<dt>

`remove-skipped`</dt>
<dd>

Expand All @@ -56,6 +77,9 @@ filter can be applied to deleted workflow runs by status/conclusion - see input
</dd>
</dl>

> [!IMPORTANT]
> All given inputs are applied (i.e. combined with a logical `OR`).
## Example usage

### Remove failed workflows
Expand Down Expand Up @@ -101,3 +125,21 @@ jobs:
Unit Tests
Deploy
```

The following example will remove all workflow runs that are older than 4 weeks and all runs of the current workflow older than 1 day:
```yaml
name: Weekly purge of workflow runs older than a week
on:
schedule:
- cron: '0 0 * * 0'
jobs:
purge_old_workflows:
runs-on: ubuntu-latest
steps:
- uses: otto-de/purge-deprecated-workflow-runs@v2
with:
remove-older-than: |
4w *
1d ${{ github.workflow }}
```
6 changes: 5 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Purge deprecated workflow runs
description: >-
Delete GH workflow runs that don't have a definition (anymore).
Optionally delete cancelled, failed or skipped runs.
Optionally delete cancelled, failed, outdated or skipped runs.
author: hanseartic
inputs:
token:
Expand All @@ -20,6 +20,10 @@ inputs:
description: Remove failed workflow runs
required: false
default: false
remove-older-than:
description: Remove workflow runs older than specified timeframe
required: false
default: ''
remove-skipped:
description: Remove skipped workflow runs
required: false
Expand Down
47 changes: 30 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
const core = require('@actions/core');
const github = require('@actions/github');
const { Octokit } = require('@octokit/rest');
const { extract: extractOlderThanInMs, filterWorkflowRuns: filterWorkflowRunsOlderThan} = require('./process_older_than');


const getInput = (name, fallback = undefined) => {
const getInput = (name, fallback = undefined, extractor = (data) => data) => {
try {
return core.getBooleanInput(name) ?? fallback
return extractor(core.getBooleanInput(name) ?? fallback)
} catch {
const ml = core.getMultilineInput(name)
const ml = core.getMultilineInput(name).map(extractor).filter(Boolean)
return ml.length ? ml : fallback
}
}

const run2Id = (run) => run.id

const run = async () => {
try {
const token = core.getInput('token') || process.env.GITHUB_TOKEN;
Expand All @@ -23,7 +25,8 @@ const run = async () => {
failure: getInput('remove-failed', false),
skipped: getInput('remove-skipped', false),
}
const {owner, repo} = github.context.repo;
const deleteOlderThan = getInput('remove-older-than', [], extractOlderThanInMs)
const {owner, repo} = github.context.repo
const octokit = new Octokit({auth: token});

const workflowIds = await (octokit.paginate(
Expand All @@ -45,29 +48,39 @@ const run = async () => {
},
page => page.data.map(run => ({
conclusion: run.conclusion,
created_at: run.created_at,
id: run.id,
name: run.name,
workflow_id: run.workflow_id,
}))
)

const idsToDelete = []
if (deleteObsolete) {
const workflowRunsWithoutWorkflow = workflowRuns.filter(run => !workflowIds.includes(run.workflow_id));
core.info(`Found ${workflowRunsWithoutWorkflow.length} obsolete workflow runs.`);
idsToDelete.push(...workflowRunsWithoutWorkflow.map(run => run.id))
const workflowRunIdsWithoutWorkflow = workflowRuns
.filter(run => !workflowIds.includes(run.workflow_id))
.map(run2Id)
core.info(`Found ${workflowRunIdsWithoutWorkflow.length} obsolete workflow runs.`)
idsToDelete.push(...workflowRunIdsWithoutWorkflow)
}

for (const status in deleteByStatus) {
if (deleteByStatus[status]) {
const IdsToDeleteByStatus = workflowRuns.filter(run => {
if (run.conclusion !== status) { return false }
return deleteByStatus[status] === true || deleteByStatus[status].includes(run.name)
})
core.info(`Found ${IdsToDeleteByStatus.length} workflow runs with status [${status}].`);
idsToDelete.push(...IdsToDeleteByStatus.map(run => run.id))
const idsToDeleteByStatus = workflowRuns
.filter(run => {
if (run.conclusion !== status) { return false }
return deleteByStatus[status] === true || deleteByStatus[status].includes(run.name)
})
.map(run2Id)
core.info(`Found ${idsToDeleteByStatus.length} workflow runs with status [${status}].`);
idsToDelete.push(...idsToDeleteByStatus)
}
}

if (deleteOlderThan.length) {
idsToDelete.push(...filterWorkflowRunsOlderThan(workflowRuns, deleteOlderThan).map(run2Id))
}

const uniqueRunIdsToDelete = [...new Set(idsToDelete)]
await Promise.all(uniqueRunIdsToDelete.map(run_id => octokit.request(
"DELETE /repos/{owner}/{repo}/actions/runs/{run_id}",
Expand All @@ -79,8 +92,8 @@ const run = async () => {
.then(() => core.info("Removed run with id: " + run_id))
));
} catch (error) {
core.error(error);
core.error(error)
}
};
}

run();
run()
Loading

0 comments on commit a00a1b5

Please sign in to comment.