Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace
Arklean

Arklean

GitHub Marketplace CI GitHub Release License: Apache-2.0

Safe and explainable lifecycle policies for GitHub Container Registry. Arklean is a JavaScript GitHub Action written in strict TypeScript, with no runtime npm dependencies.

Safety notice: with protect-multi-arch and protect-referrers (both on by default), Arklean inspects registry manifests and protects platform children of retained multi-arch indexes and their signature/attestation/SBOM referrers; relations it cannot prove fail closed (PROTECTED_UNKNOWN_RELATION). Still start with dry-run: true and review the plan before enabling deletion β€” delete-untagged and delete-orphaned-referrers stay false by default.

🌍 Languages: USA Flag English (this file) Β· Brazil Flag PortuguΓͺs

✨ Features

  • πŸ” Dry-run by default β€” plans first, deletes only when explicitly confirmed with confirm-delete.
  • 🧾 Explainable decisions β€” every version gets a stable, machine-readable reason code in a canonical JSON plan.
  • πŸ›‘οΈ OCI graph protection β€” retains platform children of multi-arch indexes and signature/attestation/SBOM referrers; unprovable relations fail closed.
  • 🚧 Safety budgets β€” absolute and percentage deletion limits, inventory re-check before the first DELETE, and post-apply validation.
  • πŸͺΆ Zero runtime dependencies β€” Node built-ins only, committed reproducible bundle, SHA-pinned CI.
  • πŸ“¦ One package per run β€” fan out over multiple packages with a workflow matrix.

πŸš€ Getting started

name: Cleanup GHCR
on:
  workflow_dispatch:
    inputs:
      dry-run: { type: boolean, default: true }
  schedule:
    - cron: "0 3 * * 1"
permissions:
  contents: read
  packages: write
jobs:
  cleanup:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        package: [aws-cli, gcloud-cli, tofu]
    steps:
      - uses: Tooark/action-ark-clean@v0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          owner: ${{ github.repository_owner }}
          package: ${{ matrix.package }}
          dry-run: ${{ github.event_name != 'workflow_dispatch' || inputs.dry-run }}
          # Required when dry-run is false; must equal owner/package exactly:
          # confirm-delete: ${{ github.repository_owner }}/${{ matrix.package }}
          keep-latest: 10
          max-deletions: 20
          max-delete-percentage: 25

The executing repository needs package administration access. A classic PAT used for deletion needs package read/delete scopes. Prefer GITHUB_TOKEN with explicit package Actions access or a dedicated GitHub App/token.

More examples under examples/:

βš™οΈ Inputs

Required:

Input Description
token GitHub token with package administration permission
owner Organization or user owning the package
package Exact GHCR package name

Retention policy:

Input Default Description
protected-tags latest, stable, production, SemVer regex Newline-separated exact tags or /regex/; a match retains the version
ephemeral-tags SHA, branch-prefix, and scan regexes Newline-separated exact tags or /regex/ eligible by age
ephemeral-retention-days 30 Minimum age for ephemeral tagged versions
untagged-retention-days 7 Minimum age for untagged versions
keep-latest 10 Newest otherwise-eligible tagged versions to retain
delete-untagged false Permit deletion of old untagged versions
always-keep-newest true Always preserve the newest package version

OCI safety:

Input Default Description
protect-multi-arch true Protect platform children of retained multi-arch indexes
protect-referrers true Protect signature, attestation, and SBOM referrers of retained versions
delete-orphaned-referrers false Delete referrers whose subject is confirmed absent (missing from inventory and 404 in the registry)

Safety and execution:

Input Default Description
dry-run true Plan only; no DELETE requests
confirm-delete β€” Required in apply mode; must equal owner/package exactly
verify-inventory-before-apply true Re-read inventory and abort if it changed before deletion
validate-after-cleanup true Re-read inventory after apply; fail if a protected version is gone
max-deletions 20 Absolute deletion safety budget
max-delete-percentage 25 Percentage deletion safety budget
budget-mode abort abort fails on exceeded budgets; cap defers the excess
fail-on-empty false Fail when the package has no versions
owner-type auto auto, organization, or user
concurrency 2 Concurrent requests, 1–10
retry-count 3 Retries for transient API failures, 0–5

The normative description of every input, output, and reason code is the action contract.

πŸ“€ Outputs

Output Description
scanned Number of versions scanned
protected Number of versions retained
eligible Number eligible for deletion
deleted Number deleted
absent Number already gone when deletion was attempted (idempotent 404)
failed Number whose deletion failed
estimated-reclaimed-bytes Best-effort bytes the eligible versions would reclaim; empty when registry inspection did not run
plan-sha256 SHA-256 of the canonical cleanup plan
plan-path Path of the JSON plan (always written)
result-path Path of the JSON apply report; empty string in dry-run

Upload the plan and report as workflow artifacts if you need durable audit records.

🧠 Policy semantics

  • A protected tag protects the whole package version.
  • Tagged versions not matching an ephemeral rule are retained (PROTECTED_UNMATCHED_TAG).
  • Old matching ephemeral versions become candidates.
  • Old untagged versions become candidates only when explicitly enabled; default is false.
  • With OCI protection enabled and eligible versions present, Arklean fetches one registry manifest per scanned version (bounded by concurrency): children of retained indexes become PROTECTED_OCI_CHILD, referrers (OCI 1.1 subject or cosign sha256-<digest>.* tags) become PROTECTED_OCI_REFERRER, and anything unprovable becomes PROTECTED_UNKNOWN_RELATION.
  • keep-latest protects the newest tagged candidates; always-keep-newest preserves the newest package version (PROTECTED_NEWEST).
  • Mutable branch tags (main, master, develop) are not ephemeral by default: they may point at the image currently in use. Add them to ephemeral-tags explicitly if you understand the risk.
  • owner-type defaults to auto and is resolved through the GitHub API.
  • Before the first DELETE, Arklean requires safety budgets, exact confirm-delete: owner/package, and an unchanged inventory re-check.
  • After apply, validate-after-cleanup re-reads the inventory and fails the run if any protected version disappeared.
  • Every decision carries a stable reason code; run-level aborts use ABORTED_BUDGET_EXCEEDED, ABORTED_NO_MATCH, ABORTED_INVENTORY_CHANGED, and VALIDATION_FAILED.

πŸ§ͺ Development

corepack enable
pnpm install
pnpm check   # lint + typecheck + build + test

The committed dist/ bundle must always match a fresh build; CI fails otherwise. Tests use the Node built-in test runner with an HTTP mock of the GitHub API β€” no network access needed.

πŸ“š Documentation

The maintained versions of the original planning documents live under docs/.

πŸ” Supply chain

CI actions are pinned by full commit SHA. CodeQL, dependency review, and Dependabot run on every change. Releases are built from source, checked for bundle reproducibility, published with SHA-256 checksums, a CycloneDX SBOM, and build provenance attestation; the moving major tag (currently v0) is only updated by the protected release workflow. Consumers with a strict posture should pin a full commit SHA.

Repository-level controls (branch protection, protected release environment, secret scanning) are pending until the repository is public β€” see docs/REPO-SETUP.md for status and the exact commands.

🀝 Contributing

Contributions are welcome! Start with CONTRIBUTING.md β€” it covers the repository layout, the development workflow, the commit convention, the DCO sign-off requirement, and the release process.

  • πŸ› Found a bug? Open an issue.
  • ✨ Have an idea? Open an issue describing the problem you want to solve.

By participating you agree to our Code of Conduct.

πŸ†˜ Help & Security

  • πŸ’¬ Questions and help β€” see SUPPORT.md for all channels and response targets.
  • πŸ”’ Security vulnerabilities β€” never open a public issue; report privately per SECURITY.md.

πŸ’– Support

If Arklean saves you storage bills or cleanup scripts, consider supporting the project:

Every contribution helps keep the Tooark family maintained. Thank you! πŸ’™

πŸ“ License

Distributed under the Apache License 2.0.


Made with πŸ’™ by Tooark.

About

Safe and explainable lifecycle policies for GitHub Container Registry. Arklean is a JavaScript GitHub Action written in strict TypeScript, with no runtime npm dependencies.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages