From b8941e5b9f78bc91fbf4b5f741b74452796292ae Mon Sep 17 00:00:00 2001
From: "Andrei.Ovcharenko"
Date: Thu, 27 Aug 2026 15:33:25 +0300
Subject: [PATCH 1/4] ci: bound Actions artifact retention
---
.github/workflows/ci.yml | 1 +
.github/workflows/scorecard.yml | 1 +
2 files changed, 2 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 639a742..f986a15 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -61,6 +61,7 @@ jobs:
with:
name: jacoco-report-${{ github.run_id }}
path: build/reports/jacoco/jacocoAllReport/html
+ retention-days: 7
- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 983f3c6..83e856a 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -43,3 +43,4 @@ jobs:
name: openssf-scorecard
path: scorecard-results.sarif
if-no-files-found: error
+ retention-days: 7
From 31eac78aede79b87bce6a83aee79984f160b9e3f Mon Sep 17 00:00:00 2001
From: "Andrei.Ovcharenko"
Date: Sun, 30 Aug 2026 13:06:55 +0300
Subject: [PATCH 2/4] ci: probe private GHCR artifact package
---
.github/workflows/ghcr-canary.yml | 111 ++++++++++++++++++++++++++++++
1 file changed, 111 insertions(+)
create mode 100644 .github/workflows/ghcr-canary.yml
diff --git a/.github/workflows/ghcr-canary.yml b/.github/workflows/ghcr-canary.yml
new file mode 100644
index 0000000..d0b9b3a
--- /dev/null
+++ b/.github/workflows/ghcr-canary.yml
@@ -0,0 +1,111 @@
+name: Private GHCR canary
+
+on:
+ pull_request:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ probe:
+ name: Publish inert probe
+ if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - name: Set up ORAS 1.3.3
+ uses: oras-project/setup-oras@1d808f7d7f6995cc68b7bf507bfe5c5446e1dc9d
+ with:
+ version: 1.3.3
+
+ - name: Publish inert probe
+ id: probe
+ env:
+ GHCR_TOKEN: ${{ github.token }}
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ owner="${GITHUB_REPOSITORY_OWNER,,}"
+ repository="${GITHUB_REPOSITORY#*/}"
+ package="${repository,,}-ci-artifacts"
+ tag="probe-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
+ reference="ghcr.io/${owner}/${package}:${tag}"
+ probe_dir="$(mktemp -d)"
+ registry_config="${probe_dir}/registry.json"
+ cleanup() {
+ oras logout ghcr.io --registry-config "${registry_config}" >/dev/null 2>&1 || true
+ rm -rf -- "${probe_dir}"
+ }
+ trap cleanup EXIT
+
+ printf 'repository=%s\nrun_id=%s\nrun_attempt=%s\n' \
+ "${GITHUB_REPOSITORY}" "${GITHUB_RUN_ID}" "${GITHUB_RUN_ATTEMPT}" \
+ > "${probe_dir}/probe.txt"
+
+ printf '%s' "${GHCR_TOKEN}" | oras login ghcr.io \
+ --username "${GITHUB_ACTOR}" \
+ --password-stdin \
+ --registry-config "${registry_config}"
+
+ digest="$(
+ cd "${probe_dir}"
+ oras push "${reference}" \
+ --registry-config "${registry_config}" \
+ --artifact-type application/vnd.krotname.ci-canary.v1 \
+ --annotation "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
+ --annotation "org.opencontainers.image.revision=${GITHUB_HEAD_SHA:-${GITHUB_SHA}}" \
+ --format go-template \
+ --template '{{.digest}}' \
+ "probe.txt:text/plain"
+ )"
+
+ {
+ echo "package=${package}"
+ echo "reference=${reference}"
+ echo "digest=${digest}"
+ } >> "${GITHUB_OUTPUT}"
+
+ - name: Verify private package state
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
+ env:
+ PACKAGE_NAME: ${{ steps.probe.outputs.package }}
+ PACKAGE_REFERENCE: ${{ steps.probe.outputs.reference }}
+ PACKAGE_DIGEST: ${{ steps.probe.outputs.digest }}
+ with:
+ github-token: ${{ github.token }}
+ script: |
+ const response = await github.request(
+ 'GET /user/packages/{package_type}/{package_name}',
+ {
+ package_type: 'container',
+ package_name: process.env.PACKAGE_NAME,
+ headers: { 'X-GitHub-Api-Version': '2022-11-28' },
+ },
+ );
+ const packageInfo = response.data;
+ const expectedOwner = context.repo.owner.toLowerCase();
+
+ if (
+ packageInfo.name !== process.env.PACKAGE_NAME
+ || packageInfo.visibility !== 'private'
+ || packageInfo.owner?.login?.toLowerCase() !== expectedOwner
+ ) {
+ throw new Error(
+ `Unexpected package state: name=${packageInfo.name}, visibility=${packageInfo.visibility}, `
+ + `owner=${packageInfo.owner?.login ?? 'unknown'}`,
+ );
+ }
+
+ await core.summary
+ .addHeading('Private GHCR canary', 3)
+ .addList([
+ `Visibility: ${packageInfo.visibility}`,
+ `Owner: ${packageInfo.owner?.login}`,
+ `Reference: ${process.env.PACKAGE_REFERENCE}@${process.env.PACKAGE_DIGEST}`,
+ ])
+ .write();
From 5329cbde5bbbab5264849efd0db84c4b8101b89c Mon Sep 17 00:00:00 2001
From: "Andrei.Ovcharenko"
Date: Sun, 30 Aug 2026 13:14:15 +0300
Subject: [PATCH 3/4] ci: remove public canary package fail-closed
---
.github/workflows/ghcr-canary.yml | 49 +++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/.github/workflows/ghcr-canary.yml b/.github/workflows/ghcr-canary.yml
index d0b9b3a..33b4dbf 100644
--- a/.github/workflows/ghcr-canary.yml
+++ b/.github/workflows/ghcr-canary.yml
@@ -109,3 +109,52 @@ jobs:
`Reference: ${process.env.PACKAGE_REFERENCE}@${process.env.PACKAGE_DIGEST}`,
])
.write();
+
+ - name: Remove a public probe-only package
+ if: ${{ failure() }}
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
+ with:
+ github-token: ${{ github.token }}
+ script: |
+ const packageName = `${context.repo.repo.toLowerCase()}-ci-artifacts`;
+ const packageResponse = await github.request(
+ 'GET /user/packages/{package_type}/{package_name}',
+ {
+ package_type: 'container',
+ package_name: packageName,
+ headers: { 'X-GitHub-Api-Version': '2022-11-28' },
+ },
+ );
+ const packageInfo = packageResponse.data;
+ const versions = await github.paginate(
+ 'GET /user/packages/{package_type}/{package_name}/versions',
+ {
+ package_type: 'container',
+ package_name: packageName,
+ per_page: 100,
+ headers: { 'X-GitHub-Api-Version': '2022-11-28' },
+ },
+ );
+ const probeOnly = versions.length > 0 && versions.every((version) => {
+ const tags = version.metadata?.container?.tags ?? [];
+ return tags.length > 0 && tags.every((tag) => /^probe-\d+-\d+$/.test(tag));
+ });
+
+ if (
+ packageInfo.name !== packageName
+ || packageInfo.visibility !== 'public'
+ || packageInfo.owner?.login?.toLowerCase() !== context.repo.owner.toLowerCase()
+ || !probeOnly
+ ) {
+ throw new Error(`Refusing to delete non-probe package ${packageName}`);
+ }
+
+ await github.request(
+ 'DELETE /user/packages/{package_type}/{package_name}',
+ {
+ package_type: 'container',
+ package_name: packageName,
+ headers: { 'X-GitHub-Api-Version': '2022-11-28' },
+ },
+ );
+ core.notice(`Deleted public probe-only package ${packageName}`);
From 783d399be9cffe4776dbfb9ac8bd513a2109ccca Mon Sep 17 00:00:00 2001
From: "Andrei.Ovcharenko"
Date: Sun, 30 Aug 2026 13:16:06 +0300
Subject: [PATCH 4/4] ci: stop GHCR rollout for public repository
---
.github/workflows/ghcr-canary.yml | 160 ------------------------------
1 file changed, 160 deletions(-)
delete mode 100644 .github/workflows/ghcr-canary.yml
diff --git a/.github/workflows/ghcr-canary.yml b/.github/workflows/ghcr-canary.yml
deleted file mode 100644
index 33b4dbf..0000000
--- a/.github/workflows/ghcr-canary.yml
+++ /dev/null
@@ -1,160 +0,0 @@
-name: Private GHCR canary
-
-on:
- pull_request:
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- probe:
- name: Publish inert probe
- if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]') }}
- runs-on: ubuntu-latest
- timeout-minutes: 10
- permissions:
- contents: read
- packages: write
- steps:
- - name: Set up ORAS 1.3.3
- uses: oras-project/setup-oras@1d808f7d7f6995cc68b7bf507bfe5c5446e1dc9d
- with:
- version: 1.3.3
-
- - name: Publish inert probe
- id: probe
- env:
- GHCR_TOKEN: ${{ github.token }}
- shell: bash
- run: |
- set -euo pipefail
-
- owner="${GITHUB_REPOSITORY_OWNER,,}"
- repository="${GITHUB_REPOSITORY#*/}"
- package="${repository,,}-ci-artifacts"
- tag="probe-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
- reference="ghcr.io/${owner}/${package}:${tag}"
- probe_dir="$(mktemp -d)"
- registry_config="${probe_dir}/registry.json"
- cleanup() {
- oras logout ghcr.io --registry-config "${registry_config}" >/dev/null 2>&1 || true
- rm -rf -- "${probe_dir}"
- }
- trap cleanup EXIT
-
- printf 'repository=%s\nrun_id=%s\nrun_attempt=%s\n' \
- "${GITHUB_REPOSITORY}" "${GITHUB_RUN_ID}" "${GITHUB_RUN_ATTEMPT}" \
- > "${probe_dir}/probe.txt"
-
- printf '%s' "${GHCR_TOKEN}" | oras login ghcr.io \
- --username "${GITHUB_ACTOR}" \
- --password-stdin \
- --registry-config "${registry_config}"
-
- digest="$(
- cd "${probe_dir}"
- oras push "${reference}" \
- --registry-config "${registry_config}" \
- --artifact-type application/vnd.krotname.ci-canary.v1 \
- --annotation "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
- --annotation "org.opencontainers.image.revision=${GITHUB_HEAD_SHA:-${GITHUB_SHA}}" \
- --format go-template \
- --template '{{.digest}}' \
- "probe.txt:text/plain"
- )"
-
- {
- echo "package=${package}"
- echo "reference=${reference}"
- echo "digest=${digest}"
- } >> "${GITHUB_OUTPUT}"
-
- - name: Verify private package state
- uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
- env:
- PACKAGE_NAME: ${{ steps.probe.outputs.package }}
- PACKAGE_REFERENCE: ${{ steps.probe.outputs.reference }}
- PACKAGE_DIGEST: ${{ steps.probe.outputs.digest }}
- with:
- github-token: ${{ github.token }}
- script: |
- const response = await github.request(
- 'GET /user/packages/{package_type}/{package_name}',
- {
- package_type: 'container',
- package_name: process.env.PACKAGE_NAME,
- headers: { 'X-GitHub-Api-Version': '2022-11-28' },
- },
- );
- const packageInfo = response.data;
- const expectedOwner = context.repo.owner.toLowerCase();
-
- if (
- packageInfo.name !== process.env.PACKAGE_NAME
- || packageInfo.visibility !== 'private'
- || packageInfo.owner?.login?.toLowerCase() !== expectedOwner
- ) {
- throw new Error(
- `Unexpected package state: name=${packageInfo.name}, visibility=${packageInfo.visibility}, `
- + `owner=${packageInfo.owner?.login ?? 'unknown'}`,
- );
- }
-
- await core.summary
- .addHeading('Private GHCR canary', 3)
- .addList([
- `Visibility: ${packageInfo.visibility}`,
- `Owner: ${packageInfo.owner?.login}`,
- `Reference: ${process.env.PACKAGE_REFERENCE}@${process.env.PACKAGE_DIGEST}`,
- ])
- .write();
-
- - name: Remove a public probe-only package
- if: ${{ failure() }}
- uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
- with:
- github-token: ${{ github.token }}
- script: |
- const packageName = `${context.repo.repo.toLowerCase()}-ci-artifacts`;
- const packageResponse = await github.request(
- 'GET /user/packages/{package_type}/{package_name}',
- {
- package_type: 'container',
- package_name: packageName,
- headers: { 'X-GitHub-Api-Version': '2022-11-28' },
- },
- );
- const packageInfo = packageResponse.data;
- const versions = await github.paginate(
- 'GET /user/packages/{package_type}/{package_name}/versions',
- {
- package_type: 'container',
- package_name: packageName,
- per_page: 100,
- headers: { 'X-GitHub-Api-Version': '2022-11-28' },
- },
- );
- const probeOnly = versions.length > 0 && versions.every((version) => {
- const tags = version.metadata?.container?.tags ?? [];
- return tags.length > 0 && tags.every((tag) => /^probe-\d+-\d+$/.test(tag));
- });
-
- if (
- packageInfo.name !== packageName
- || packageInfo.visibility !== 'public'
- || packageInfo.owner?.login?.toLowerCase() !== context.repo.owner.toLowerCase()
- || !probeOnly
- ) {
- throw new Error(`Refusing to delete non-probe package ${packageName}`);
- }
-
- await github.request(
- 'DELETE /user/packages/{package_type}/{package_name}',
- {
- package_type: 'container',
- package_name: packageName,
- headers: { 'X-GitHub-Api-Version': '2022-11-28' },
- },
- );
- core.notice(`Deleted public probe-only package ${packageName}`);