From b531c69d9048b9ecccbbeafd1e4d11b1789a08d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:29:58 +0000 Subject: [PATCH 1/6] Initial plan From bb9a228852492a125195b3e0caec97b1ad2a9eb8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:50:37 +0000 Subject: [PATCH 2/6] Automate cleanup of attestations alongside image cleanup Co-authored-by: rjaegers <45816308+rjaegers@users.noreply.github.com> --- .github/workflows/image-cleanup.yml | 80 ++++++++++++++++++++++++++ .github/workflows/pr-image-cleanup.yml | 44 ++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/.github/workflows/image-cleanup.yml b/.github/workflows/image-cleanup.yml index 880e04b6..f8a7ae5e 100644 --- a/.github/workflows/image-cleanup.yml +++ b/.github/workflows/image-cleanup.yml @@ -9,8 +9,88 @@ on: permissions: {} jobs: + cleanup-attestations: + name: ๐Ÿ” Cleanup Attestations (${{ matrix.package }}) + runs-on: ubuntu-latest + permissions: + attestations: write # is needed to delete attestations + packages: read # is needed to list package versions to find digests + pull-requests: read # is needed to determine which pull requests are open + strategy: + fail-fast: false + matrix: + package: [amp-devcontainer-base, amp-devcontainer-cpp, amp-devcontainer-rust] + steps: + - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + with: + disable-sudo-and-containers: true + egress-policy: audit + allowed-endpoints: api.github.com:443 + - name: Delete outdated attestations + run: | + set -Eeuo pipefail + + ORG="${GH_REPO%%/*}" + + # Get all open PR numbers to determine which pr-N tags to keep + open_pr_numbers=$(gh api "/repos/${GH_REPO}/pulls?state=open&per_page=100" \ + --paginate \ + --jq '.[].number' 2>/dev/null || true) + + # Get all package versions with their digests and tags + while IFS=$'\t' read -r digest tags_csv; do + [[ -z "$digest" ]] && continue + + keep=false + + # Check each tag to determine if this digest should be kept + IFS=',' read -r -a tags <<< "$tags_csv" + for tag in "${tags[@]}"; do + [[ -z "$tag" ]] && continue + + # Keep release semver tags (e.g., v6.5.2, 6.5.2, 6.5, 6) + if [[ "$tag" =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then + keep=true + break + fi + + # Keep edge tag + if [[ "$tag" == "edge" ]]; then + keep=true + break + fi + + # Keep pr-N tags for open pull requests + if [[ "$tag" =~ ^pr-([0-9]+)$ ]]; then + pr_number="${BASH_REMATCH[1]}" + if echo "$open_pr_numbers" | grep -qx "$pr_number"; then + keep=true + break + fi + fi + done + + if [[ "$keep" == "false" ]]; then + echo "Deleting attestations for ${GH_PACKAGE}@${digest} (tags: ${tags_csv})" + encoded_digest="${digest//:/%3A}" + gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ + 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" + fi + done < <( + gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ + --paginate \ + --jq '.[] | "\(.name)\t\(.metadata.container.tags // [] | join(","))"' \ + 2>/dev/null || true + ) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + GH_PACKAGE: ${{ matrix.package }} + cleanup-images: name: ๐Ÿงน Clean Images + if: always() + needs: cleanup-attestations runs-on: ubuntu-latest permissions: packages: write # is needed by dataaxiom/ghcr-cleanup-action to delete untagged and orphaned images diff --git a/.github/workflows/pr-image-cleanup.yml b/.github/workflows/pr-image-cleanup.yml index 59ca7105..0d6140e9 100644 --- a/.github/workflows/pr-image-cleanup.yml +++ b/.github/workflows/pr-image-cleanup.yml @@ -8,6 +8,50 @@ on: permissions: {} jobs: + delete-attestations: + name: ๐Ÿ” Delete PR Attestations (${{ matrix.package }}) + runs-on: ubuntu-latest + permissions: + attestations: write # is needed to delete attestations + packages: read # is needed to list package versions to find the PR digest + strategy: + fail-fast: false + matrix: + package: [amp-devcontainer-base, amp-devcontainer-cpp, amp-devcontainer-rust] + steps: + - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + with: + disable-sudo-and-containers: true + egress-policy: audit + allowed-endpoints: api.github.com:443 + - name: Delete attestations for PR ${{ github.event.pull_request.number }} + run: | + set -Eeuo pipefail + + ORG="${GH_REPO%%/*}" + PR_TAG="pr-${PR_NUMBER}" + + # Find the digest for the PR tag + digest=$(gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ + --paginate \ + --jq ".[] | select((.metadata.container.tags // []) | contains([\"${PR_TAG}\"]) ) | .name" \ + 2>/dev/null | head -1 || true) + + if [[ -z "$digest" ]]; then + echo "No version found with tag ${PR_TAG} for ${GH_PACKAGE}, skipping" + exit 0 + fi + + echo "Deleting attestations for ${GH_PACKAGE}@${digest} (tag: ${PR_TAG})" + encoded_digest="${digest//:/%3A}" + gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ + 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + GH_PACKAGE: ${{ matrix.package }} + PR_NUMBER: ${{ github.event.pull_request.number }} + delete-images: name: ๐Ÿ—‘๏ธ Delete PR Images runs-on: ubuntu-latest From 6419753c095283bccc97b34723b9066ed23b57b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:27:17 +0000 Subject: [PATCH 3/6] Refactor: make image deletion leading, then clean orphaned attestations Co-authored-by: rjaegers <45816308+rjaegers@users.noreply.github.com> --- .github/workflows/image-cleanup.yml | 121 +++++++++++++------------ .github/workflows/pr-image-cleanup.yml | 76 ++++++++++++---- 2 files changed, 118 insertions(+), 79 deletions(-) diff --git a/.github/workflows/image-cleanup.yml b/.github/workflows/image-cleanup.yml index f8a7ae5e..57504fca 100644 --- a/.github/workflows/image-cleanup.yml +++ b/.github/workflows/image-cleanup.yml @@ -9,88 +9,43 @@ on: permissions: {} jobs: - cleanup-attestations: - name: ๐Ÿ” Cleanup Attestations (${{ matrix.package }}) + collect-digests: + name: ๐Ÿ“ฆ Collect Digests (${{ matrix.package }}) runs-on: ubuntu-latest - permissions: - attestations: write # is needed to delete attestations - packages: read # is needed to list package versions to find digests - pull-requests: read # is needed to determine which pull requests are open strategy: fail-fast: false matrix: package: [amp-devcontainer-base, amp-devcontainer-cpp, amp-devcontainer-rust] + permissions: + packages: read # is needed to list package versions steps: - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo-and-containers: true egress-policy: audit allowed-endpoints: api.github.com:443 - - name: Delete outdated attestations + - name: Collect package digests run: | set -Eeuo pipefail - ORG="${GH_REPO%%/*}" - - # Get all open PR numbers to determine which pr-N tags to keep - open_pr_numbers=$(gh api "/repos/${GH_REPO}/pulls?state=open&per_page=100" \ + gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ --paginate \ - --jq '.[].number' 2>/dev/null || true) - - # Get all package versions with their digests and tags - while IFS=$'\t' read -r digest tags_csv; do - [[ -z "$digest" ]] && continue - - keep=false - - # Check each tag to determine if this digest should be kept - IFS=',' read -r -a tags <<< "$tags_csv" - for tag in "${tags[@]}"; do - [[ -z "$tag" ]] && continue - - # Keep release semver tags (e.g., v6.5.2, 6.5.2, 6.5, 6) - if [[ "$tag" =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then - keep=true - break - fi - - # Keep edge tag - if [[ "$tag" == "edge" ]]; then - keep=true - break - fi - - # Keep pr-N tags for open pull requests - if [[ "$tag" =~ ^pr-([0-9]+)$ ]]; then - pr_number="${BASH_REMATCH[1]}" - if echo "$open_pr_numbers" | grep -qx "$pr_number"; then - keep=true - break - fi - fi - done - - if [[ "$keep" == "false" ]]; then - echo "Deleting attestations for ${GH_PACKAGE}@${digest} (tags: ${tags_csv})" - encoded_digest="${digest//:/%3A}" - gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ - 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" - fi - done < <( - gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ - --paginate \ - --jq '.[] | "\(.name)\t\(.metadata.container.tags // [] | join(","))"' \ - 2>/dev/null || true - ) + --jq '.[].name' 2>/dev/null > digests.txt || touch digests.txt env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} GH_PACKAGE: ${{ matrix.package }} + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: digests-before-cleanup-${{ matrix.package }} + path: digests.txt + if-no-files-found: warn + retention-days: 1 cleanup-images: name: ๐Ÿงน Clean Images if: always() - needs: cleanup-attestations + needs: collect-digests runs-on: ubuntu-latest permissions: packages: write # is needed by dataaxiom/ghcr-cleanup-action to delete untagged and orphaned images @@ -106,3 +61,51 @@ jobs: delete-orphaned-images: true delete-untagged: true packages: amp-devcontainer,amp-devcontainer-cpp,amp-devcontainer-rust + + cleanup-attestations: + name: ๐Ÿ” Cleanup Orphaned Attestations (${{ matrix.package }}) + needs: cleanup-images + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: [amp-devcontainer-base, amp-devcontainer-cpp, amp-devcontainer-rust] + permissions: + attestations: write # is needed to delete attestations + packages: read # is needed to list remaining package versions after cleanup + steps: + - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + with: + disable-sudo-and-containers: true + egress-policy: audit + allowed-endpoints: api.github.com:443 + - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + id: download-digests + continue-on-error: true + with: + name: digests-before-cleanup-${{ matrix.package }} + - name: Delete orphaned attestations + if: steps.download-digests.outcome == 'success' + run: | + set -Eeuo pipefail + ORG="${GH_REPO%%/*}" + + # Get remaining digests after image cleanup + current_digests=$(gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ + --paginate \ + --jq '.[].name' 2>/dev/null || echo "") + + # Delete attestations for digests that no longer have a package version + while read -r digest; do + [[ -z "$digest" ]] && continue + if ! echo "$current_digests" | grep -qx "$digest"; then + echo "Deleting attestations for removed digest: ${digest}" + encoded_digest="${digest//:/%3A}" + gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ + 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" + fi + done < digests.txt + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + GH_PACKAGE: ${{ matrix.package }} diff --git a/.github/workflows/pr-image-cleanup.yml b/.github/workflows/pr-image-cleanup.yml index 0d6140e9..c9224f11 100644 --- a/.github/workflows/pr-image-cleanup.yml +++ b/.github/workflows/pr-image-cleanup.yml @@ -8,52 +8,47 @@ on: permissions: {} jobs: - delete-attestations: - name: ๐Ÿ” Delete PR Attestations (${{ matrix.package }}) + collect-pr-digests: + name: ๐Ÿ“ฆ Collect PR Digests (${{ matrix.package }}) runs-on: ubuntu-latest - permissions: - attestations: write # is needed to delete attestations - packages: read # is needed to list package versions to find the PR digest strategy: fail-fast: false matrix: package: [amp-devcontainer-base, amp-devcontainer-cpp, amp-devcontainer-rust] + permissions: + packages: read # is needed to find the digest for the PR tag steps: - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 with: disable-sudo-and-containers: true egress-policy: audit allowed-endpoints: api.github.com:443 - - name: Delete attestations for PR ${{ github.event.pull_request.number }} + - name: Find PR image digest run: | set -Eeuo pipefail - ORG="${GH_REPO%%/*}" PR_TAG="pr-${PR_NUMBER}" - - # Find the digest for the PR tag digest=$(gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ --paginate \ --jq ".[] | select((.metadata.container.tags // []) | contains([\"${PR_TAG}\"]) ) | .name" \ - 2>/dev/null | head -1 || true) - - if [[ -z "$digest" ]]; then - echo "No version found with tag ${PR_TAG} for ${GH_PACKAGE}, skipping" - exit 0 - fi - - echo "Deleting attestations for ${GH_PACKAGE}@${digest} (tag: ${PR_TAG})" - encoded_digest="${digest//:/%3A}" - gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ - 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" + 2>/dev/null | head -1 || echo "") + echo "${digest:-}" > digest.txt env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} GH_PACKAGE: ${{ matrix.package }} PR_NUMBER: ${{ github.event.pull_request.number }} + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: pr-digest-${{ matrix.package }} + path: digest.txt + if-no-files-found: warn + retention-days: 1 delete-images: name: ๐Ÿ—‘๏ธ Delete PR Images + if: always() + needs: collect-pr-digests runs-on: ubuntu-latest permissions: packages: write # is needed by dataaxiom/ghcr-cleanup-action to delete images @@ -67,6 +62,47 @@ jobs: delete-tags: pr-${{ github.event.pull_request.number }} packages: amp-devcontainer,amp-devcontainer-cpp,amp-devcontainer-rust + delete-attestations: + name: ๐Ÿ” Delete PR Attestations (${{ matrix.package }}) + needs: [collect-pr-digests, delete-images] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + package: [amp-devcontainer-base, amp-devcontainer-cpp, amp-devcontainer-rust] + permissions: + attestations: write # is needed to delete attestations + steps: + - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + with: + disable-sudo-and-containers: true + egress-policy: audit + allowed-endpoints: api.github.com:443 + - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + id: download-digest + continue-on-error: true + with: + name: pr-digest-${{ matrix.package }} + - name: Delete attestations for PR ${{ github.event.pull_request.number }} + if: steps.download-digest.outcome == 'success' + run: | + set -Eeuo pipefail + ORG="${GH_REPO%%/*}" + digest=$(cat digest.txt) + if [[ -z "$digest" ]]; then + echo "No digest found for pr-${PR_NUMBER} in ${GH_PACKAGE}, skipping" + exit 0 + fi + echo "Deleting attestations for ${GH_PACKAGE}@${digest}" + encoded_digest="${digest//:/%3A}" + gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ + 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + GH_PACKAGE: ${{ matrix.package }} + PR_NUMBER: ${{ github.event.pull_request.number }} + cleanup-cache: name: ๐Ÿงน Cleanup Cache runs-on: ubuntu-latest From b46a5f2b55646977bbfa527707b821c826418c67 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:03:17 +0000 Subject: [PATCH 4/6] ci: refactor attestation clean-up --- .github/workflows/image-cleanup.yml | 50 ++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/.github/workflows/image-cleanup.yml b/.github/workflows/image-cleanup.yml index 8f6b86df..4d3fd13f 100644 --- a/.github/workflows/image-cleanup.yml +++ b/.github/workflows/image-cleanup.yml @@ -5,6 +5,10 @@ on: schedule: - cron: "0 0 * * 3" workflow_dispatch: + inputs: + dry-run: + default: false + type: boolean permissions: {} @@ -19,7 +23,7 @@ jobs: permissions: packages: read # is needed to list package versions steps: - - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo-and-containers: true egress-policy: audit @@ -28,6 +32,7 @@ jobs: run: | set -Eeuo pipefail ORG="${GH_REPO%%/*}" + gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ --paginate \ --jq '.[].name' 2>/dev/null > digests.txt || touch digests.txt @@ -60,7 +65,8 @@ jobs: with: delete-orphaned-images: true delete-untagged: true - packages: amp-devcontainer,amp-devcontainer-cpp,amp-devcontainer-rust + dry-run: ${{ inputs.dry-run == true }} + packages: amp-devcontainer-base,amp-devcontainer-cpp,amp-devcontainer-rust cleanup-attestations: name: ๐Ÿ” Cleanup Orphaned Attestations (${{ matrix.package }}) @@ -74,7 +80,7 @@ jobs: attestations: write # is needed to delete attestations packages: read # is needed to list remaining package versions after cleanup steps: - - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo-and-containers: true egress-policy: audit @@ -91,21 +97,35 @@ jobs: ORG="${GH_REPO%%/*}" # Get remaining digests after image cleanup - current_digests=$(gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ + if ! gh api "/orgs/${ORG}/packages/container/${GH_PACKAGE}/versions" \ --paginate \ - --jq '.[].name' 2>/dev/null || echo "") + --jq '.[].name' > current-digests.txt; then + echo "Package not found or API error, skipping attestation cleanup" + exit 0 + fi + + # Find orphaned digests (present before cleanup but no longer in current) + orphaned=$(comm -23 <(grep -v '^$' digests.txt | sort -u) <(sort -u current-digests.txt)) + + if [[ -z "$orphaned" ]]; then + echo "No orphaned digests found" + exit 0 + fi + + count=$(echo "$orphaned" | wc -l) + echo "Found ${count} orphaned digests" + echo "$orphaned" + + if [[ "${DRY_RUN}" == "true" ]]; then + echo "Dry-run mode: skipping attestation deletion" + exit 0 + fi - # Delete attestations for digests that no longer have a package version - while read -r digest; do - [[ -z "$digest" ]] && continue - if ! echo "$current_digests" | grep -qx "$digest"; then - echo "Deleting attestations for removed digest: ${digest}" - encoded_digest="${digest//:/%3A}" - gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ - 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" - fi - done < digests.txt + echo "Deleting attestations for ${count} orphaned digests" + echo "$orphaned" | jq -R . | jq -sc '{subject_digests: .}' | \ + gh api --method POST "/orgs/${ORG}/attestations/delete-request" --input - env: + DRY_RUN: ${{ inputs.dry-run == true }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} GH_PACKAGE: ${{ matrix.package }} From 34fe70ef9cd7c8314a993057b4ebb00f46f93c87 Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:45:21 +0000 Subject: [PATCH 5/6] ci: individual dry run selection --- .github/workflows/image-cleanup.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/image-cleanup.yml b/.github/workflows/image-cleanup.yml index 4d3fd13f..0e38913d 100644 --- a/.github/workflows/image-cleanup.yml +++ b/.github/workflows/image-cleanup.yml @@ -6,7 +6,10 @@ on: - cron: "0 0 * * 3" workflow_dispatch: inputs: - dry-run: + image-cleanup-dry-run: + default: false + type: boolean + attestation-cleanup-dry-run: default: false type: boolean @@ -26,7 +29,6 @@ jobs: - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo-and-containers: true - egress-policy: audit allowed-endpoints: api.github.com:443 - name: Collect package digests run: | @@ -65,7 +67,7 @@ jobs: with: delete-orphaned-images: true delete-untagged: true - dry-run: ${{ inputs.dry-run == true }} + dry-run: ${{ inputs.image-cleanup-dry-run == true }} packages: amp-devcontainer-base,amp-devcontainer-cpp,amp-devcontainer-rust cleanup-attestations: @@ -83,7 +85,6 @@ jobs: - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo-and-containers: true - egress-policy: audit allowed-endpoints: api.github.com:443 - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 id: download-digests @@ -125,7 +126,7 @@ jobs: echo "$orphaned" | jq -R . | jq -sc '{subject_digests: .}' | \ gh api --method POST "/orgs/${ORG}/attestations/delete-request" --input - env: - DRY_RUN: ${{ inputs.dry-run == true }} + DRY_RUN: ${{ inputs.attestation-cleanup-dry-run == true }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} GH_PACKAGE: ${{ matrix.package }} From df577b672b04011134a33e4293f7ad2f2514ee1e Mon Sep 17 00:00:00 2001 From: Ron <45816308+rjaegers@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:16:45 +0000 Subject: [PATCH 6/6] ci: also update pr-image-cleanup --- .github/workflows/pr-image-cleanup.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr-image-cleanup.yml b/.github/workflows/pr-image-cleanup.yml index 53c6052e..d0291489 100644 --- a/.github/workflows/pr-image-cleanup.yml +++ b/.github/workflows/pr-image-cleanup.yml @@ -18,10 +18,9 @@ jobs: permissions: packages: read # is needed to find the digest for the PR tag steps: - - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo-and-containers: true - egress-policy: audit allowed-endpoints: api.github.com:443 - name: Find PR image digest run: | @@ -56,11 +55,13 @@ jobs: - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo: true - egress-policy: audit + allowed-endpoints: > + api.github.com:443 + ghcr.io:443 - uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 # v1.0.16 with: delete-tags: pr-${{ github.event.pull_request.number }} - packages: amp-devcontainer,amp-devcontainer-cpp,amp-devcontainer-rust + packages: amp-devcontainer-base,amp-devcontainer-cpp,amp-devcontainer-rust delete-attestations: name: ๐Ÿ” Delete PR Attestations (${{ matrix.package }}) @@ -73,10 +74,9 @@ jobs: permissions: attestations: write # is needed to delete attestations steps: - - uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1 + - uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: disable-sudo-and-containers: true - egress-policy: audit allowed-endpoints: api.github.com:443 - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 id: download-digest @@ -94,9 +94,8 @@ jobs: exit 0 fi echo "Deleting attestations for ${GH_PACKAGE}@${digest}" - encoded_digest="${digest//:/%3A}" - gh api --method DELETE "/orgs/${ORG}/attestations/digest/${encoded_digest}" \ - 2>/dev/null && echo "Deleted" || echo "No attestations found (already cleaned up)" + echo "$digest" | jq -R . | jq -sc '{subject_digests: .}' | \ + gh api --method POST "/orgs/${ORG}/attestations/delete-request" --input - env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }}