Skip to content

Commit

Permalink
feat: add cleanup job
Browse files Browse the repository at this point in the history
- #1
  • Loading branch information
viceice committed Jan 27, 2025
1 parent e6dd738 commit 6b4e565
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/prune.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: prune
# based on https://github.com/airtower-luna/hello-ghcr

on:
schedule:
- cron: '@weekly'
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
prune:
runs-on: ubuntu-22.04

strategy:
fail-fast: false
matrix:
image:
- docker-build-cache

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup PDM
uses: pdm-project/setup-pdm@b2472ca4258a9ea3aee813980a0100a2261a42fc # v4.2
with:
python-version-file: .python-version
version: 2.22.3 # renovate: datasource=pypi depName=pdm
cache: true

- name: Install pdm dependencies
run: pdm install --prod

- name: Prune untagged images
env:
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
python ghcr-prune.py --container ${{ matrix.image }} --prune-age 30 -n
1 change: 1 addition & 0 deletions .pdm-python
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
C:/Users/kriese/projects/gh/renovatebot/docker-build-cache/.venv/Scripts/python.exe
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13.1
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright 2021 WhiteSource Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
99 changes: 99 additions & 0 deletions ghcr-prune.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/python3
# PYTHON_ARGCOMPLETE_OK
import argparse
import getpass
import os
import requests
from datetime import datetime, timedelta

__author__ = "Fiona Klute"
__version__ = "0.1"
__copyright__ = "Copyright (C) 2021 Fiona Klute"
__license__ = "MIT"
# https://github.com/airtower-luna/hello-ghcr/blob/main/ghcr-prune.py

# GitHub API documentation: https://docs.github.com/en/rest/reference/packages
github_api_accept = 'application/vnd.github.v3+json'
# https://docs.github.com/en/rest/overview/api-versions?apiVersion=2022-11-28
github_api_version = '2022-11-28'


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='List versions of a GHCR container image you own, and '
'optionally delete (prune) old, untagged versions.')
parser.add_argument('--token', '-t', action='store_true',
help='ask for token input instead of using the '
'GHCR_TOKEN environment variable')
parser.add_argument('--container', default='hello-ghcr-meow',
help='name of the container image')
parser.add_argument('--verbose', '-v', action='store_true',
help='print extra debug info')
parser.add_argument('--prune-age', type=float, metavar='DAYS',
default=None,
help='delete untagged images older than DAYS days')
parser.add_argument('--dry-run', '-n', action='store_true',
help='do not actually prune images, just list which '
'would be pruned')

# enable bash completion if argcomplete is available
try:
import argcomplete
argcomplete.autocomplete(parser)
except ImportError:
pass

args = parser.parse_args()

if args.token:
token = getpass.getpass('Enter Token: ')
elif 'GHCR_TOKEN' in os.environ:
token = os.environ['GHCR_TOKEN']
else:
raise ValueError('missing authentication token')

s = requests.Session()
s.headers.update({'Authorization': f'token {token}',
'Accept': github_api_accept,
'X-GitHub-Api-Version': github_api_version})

del_before = datetime.now().astimezone() - timedelta(days=args.prune_age) \
if args.prune_age is not None else None
if del_before:
print(f'Pruning images created before {del_before}')

list_url: str | None = 'https://api.github.com/user/packages/' \
f'container/{args.container}/versions'

while list_url is not None:
r = s.get(list_url)
if 'link' in r.headers and 'next' in r.links:
list_url = r.links['next']['url']
if args.verbose:
print(f'More result pages, next is <{list_url}>')
else:
list_url = None

versions = r.json()
if args.verbose:
reset = datetime.fromtimestamp(int(r.headers["x-ratelimit-reset"]))
print(f'{r.headers["x-ratelimit-remaining"]} requests remaining '
f'until {reset}')
print(versions)

for v in versions:
created = datetime.fromisoformat(v['created_at'])
metadata = v["metadata"]["container"]
print(f'{v["id"]}\t{v["name"]}\t{created}\t{metadata["tags"]}')

# prune old untagged images if requested
if del_before is not None and created < del_before \
and len(metadata['tags']) == 0:
if args.dry_run:
print(f'would delete {v["id"]}')
else:
r = s.delete(
'https://api.github.com/user/packages/'
f'container/{args.container}/versions/{v["id"]}')
r.raise_for_status()
print(f'deleted {v["id"]}')
96 changes: 96 additions & 0 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[project]
dependencies = [
"requests==2.32.3"
]
requires-python = ">=3.13"

[tool.pdm]
distribution = false

[dependency-groups]
dev = [
"argcomplete>=3.5.3",
]

0 comments on commit 6b4e565

Please sign in to comment.