Skip to content

feat(publish): recover original signed manifests versions - #454

Merged
devantler merged 1 commit into
mainfrom
codex/actions-recover-manifests-453
Oct 4, 2026
Merged

devantler merged 1 commit into
mainfrom
codex/actions-recover-manifests-453

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

An interrupted publication can leave a signed manifests artifact without its release version, and normal publication must refuse an existing version.

What

Add an explicit, default-off recovery request that restores a missing version from the verified original artifact, accepts an already matching version, and preserves the latest release. Conflicting versions or incomplete evidence stop recovery.

Fixes #453

@github-code-quality

github-code-quality Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Code Coverage Overview

Languages: Go, C#

Go / code-coverage/test

The overall line coverage in commit adcb97f in the codex/actions-recove... branch remains at 50%, unchanged from commit 4b00bd6 in the main branch.

C# / code-coverage/dotnet

The overall line coverage in commit adcb97f in the codex/actions-recove... branch remains at 100%, unchanged from commit 4b00bd6 in the main branch.

Go / code-coverage/go

The overall line coverage in commit adcb97f in the codex/actions-recove... branch remains at 50%, unchanged from commit 86bc632 in the main branch.


Updated October 04, 2026 21:16 UTC

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 2034938e-7a99-4c6c-aca2-e3683af9d18a
📥 Commits

Reviewing files that changed from the base of the PR and between 4b00bd6 and adcb97f.

📒 Files selected for processing (10)
  • .github/scripts/recover-manifests-version.sh
  • .github/tests/fixtures/registry-auth-proxy.go
  • .github/tests/test-manifests-recovery.sh
  • .github/tests/test-publish-manifests-promotion.sh
  • .github/tests/test-publish-preflight.sh
  • .github/tests/test-unpublished-version-native.sh
  • .github/workflows/ci.yaml
  • .github/workflows/publish-manifests.yaml
  • AGENTS.md
  • README.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
Source excerpt: Repo-specific watch-list for the daily engineer: **Agent-file freshness.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • AGENTS.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: devantler-tech/.github

Timestamp: 2026-10-04T21:20:42.498Z
Learning: Source excerpt:
# AGENTS.md — devantler-tech/.github (GitHub-as-code)

## Actions catalogue

### Maintaining the catalogue

**Validate before any PR:** `actionlint` on every changed workflow/action (else a thorough YAML parse); confirm `uses:` refs resolve and are pinned/aligned; check `inputs`/`outputs`/`shell:` are declared; for reusable workflows keep `on: workflow_call` inputs/secrets backward-compatible. No app build here — YAML correctness + pinning is the gate. Keep all remote actions pinned to full-length commit SHAs; first-party self-references resolve by local path after the same-commit self-checkout (never a remote ref — see the self-reference rule above). Never weaken a security control to pass a check.
Learnt from: CR
Repo: devantler-tech/.github

Timestamp: 2026-10-04T21:20:42.498Z
Learning: Source excerpt:
# AGENTS.md — devantler-tech/.github (GitHub-as-code)

## Actions catalogue

### Maintaining the catalogue

**Validate before any PR:** `actionlint` on every changed workflow/action (else a thorough YAML parse); confirm `uses:` refs resolve and are pinned/aligned; check `inputs`/`outputs`/`shell:` are declared; for reusable workflows keep `on: workflow_call` inputs/secrets backward-compatible. No app build here — YAML correctness + pinning is the gate. Keep all remote actions pinned to full-length commit SHAs; first-party self-references resolve by local path after the same-commit self-checkout (never a remote ref — see the self-reference rule above). Never weaken a security control to pass a check.
🪛 ast-grep (0.45.3)
.github/tests/test-unpublished-version-native.sh

[warning] 32-32: A credential-bearing variable (e.g. PASSWORD, PASSWD, SECRET, TOKEN, API_KEY) is assigned a hardcoded string literal. Secrets committed to a script are exposed in source control, process listings, and shell history, and cannot be rotated without a code change. Read the value from a secrets manager or an injected environment variable at runtime instead (e.g. PASSWORD="${DB_PASSWORD:?must be set}"), and never commit the literal.
Context: GH_TOKEN=synthetic-token
Note: [CWE-798] Use of Hard-coded Credentials.

(hardcoded-password-assignment-bash)

🔇 Additional comments (10)
.github/scripts/recover-manifests-version.sh (1)

70-78: Do not let a failed flux tag skip the readback.

The script runs with set -e, so a non-zero exit from flux tag on Line 77 stops it right away. That outcome is safe. There is a separate problem with how failures inside read_version reach the caller. read_version runs inside a command substitution, and its fail calls exit only that subshell. On Line 70, || exit 1 passes the failure on correctly. On Line 78, the substitution sits inside [[ ... ]], so a failed read becomes an empty string and the script reports "recovered version is not present" instead of the real cause. The run still fails closed. However, the ::error:: line that fail writes to stderr from the subshell still shows up, so the log contains two errors. This is a diagnostic issue only and does not need a change.

.github/workflows/publish-manifests.yaml (1)

36-60: LGTM!

Also applies to: 85-95, 207-207, 252-252, 314-335

.github/tests/test-publish-preflight.sh (1)

168-170: LGTM!

Also applies to: 505-518

.github/tests/test-publish-manifests-promotion.sh (1)

37-37: LGTM!

.github/tests/test-manifests-recovery.sh (1)

1-136: LGTM!

.github/tests/fixtures/registry-auth-proxy.go (1)

39-45: LGTM!

.github/tests/test-unpublished-version-native.sh (1)

26-32: LGTM!

Also applies to: 46-46, 130-196

.github/workflows/ci.yaml (1)

3255-3264: LGTM!

AGENTS.md (1)

481-482: LGTM!

README.md (1)

679-685: LGTM!


📝 Walkthrough

Walkthrough

The Publish Manifests workflow adds default-off signed recovery inputs and requires signed promotion and caller pinning for recovery. The recovery helper verifies the original digest, signature identity, and publication claims before checking the version tag. It restores only a confirmed missing tag, accepts a matching tag without writing, and fails on conflicting or ambiguous registry results. Recovery leaves latest unchanged. Stubbed tests, native-registry tests, CI coverage, and documentation are added or updated.

Estimated code review effort

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to adcb9

Recovery remains opt-in, verifies the original signed artifact, and avoids changing latest. No identified issue currently blocks merging after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to adcb9

Recovery is explicitly enabled, verifies the original signed artifact and publication claims, refuses conflicting versions, and leaves latest unchanged. No new authorization bypass was established. Residual risk concerns concurrent registry writers and production behavior not demonstrated by the local tests.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Each recovery invocation selects one manifests repository and one version alias. Effective writable scope remains bounded by the caller's GITHUB_TOKEN package authority, whose production grants were not supplied. The workflow introduces neither broader declared permissions nor a latest-tag write in recovery.

Security Findings and Attack Paths

  • inferred — Changing caller-supplied recovery provenance alone does not establish a path to tagging arbitrary bytes: the selected digest must pass the expected signature identity and signed claims before registry access. Test source includes wrong-claim and conflicting-version refusal scenarios, but these are not evidence of live production execution.

Trust Boundaries and Controls

  • observed — Registry reads use HTTPS with bounded time and response size, without redirect following. Authentication failures, partial transfers, malformed token responses, unrecognized statuses, mixed denial errors, and conflicting manifest hashes stop recovery. Temporary response files use restrictive permissions and exit cleanup.

Resilience and Maintainability Implications

  • observed — The existing shared publisher queue is retained, with cancellation disabled. Registry tagging has no compare-and-set: readback can detect a conflicting observed result but cannot prevent an external writer racing the absence check. This limitation predates recovery and is documented for both normal publication and recovery; runtime serialization of every normalized target was not demonstrated.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 6 files. (4 skipped: 4 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed [ #453 ] The recovery helper verifies the original Cosign identity and publication claims before registry access. It restores only a missing manifests version, accepts matching bytes without a write, …
Out of Scope Changes check ✅ Passed The workflow inputs, admission checks, documentation, CI wiring, and registry-fixture updates support the #453 recovery path and its tests. The recovery helper changes only the manifests version alias…
Title check ✅ Passed The title clearly summarizes the main change: recovery of original signed manifest versions.
Description check ✅ Passed The description explains why recovery is needed and how it restores a missing version while preserving the latest release and rejecting conflicts.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 6 files. (4 skipped: 4 unsupported.)

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Readiness at adcb97f54c09c4727bde610c4bace0abc007509b: all native checks are complete and green, including CI run 37234995928. Its lint-ci-coverage-parity job exercised the production recovery helper with actual Flux, an authenticated TLS registry and independently signed original publication claims. It restored the original bytes, made a matching retry a no-op, rejected every wrong original claim and a conflicting version, and retained latest. Offline controls additionally exercised ambiguous registry/auth results, interruption and retry.

CodeRabbit reviewed this exact head with no actionable findings; all review threads are absent. Fresh main remains the reviewed base. The fixture uses a synthetic local credential and local-key signature transport; it does not claim production OIDC proof or atomic registry writes. Existing real signed publication behavior is retained, and recovery remains explicit and default-off.

@devantler
devantler marked this pull request as ready for review October 4, 2026 21:33
@devantler
devantler merged commit e271b7f into main Oct 4, 2026
270 checks passed
@devantler
devantler deleted the codex/actions-recover-manifests-453 branch October 4, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recover missing manifests version tags from the original signed digest

1 participant