Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ jobs:
- run: cargo fmt --check
- run: cargo test
- run: python3 scripts/package_component.py target/map-cli-component.tar.gz
- run: python3 scripts/test_publish_component_release.py
106 changes: 64 additions & 42 deletions .github/workflows/component-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ on:
branches: [main]
workflow_dispatch:

concurrency:
# The publisher is not internally serialised. Queue same-revision writers so
# they cannot create duplicate drafts for the same immutable release tag.
# Key this on github.sha, not github.ref: the contended resource is the tag.
# Keep cancel-in-progress false so a publisher is never cancelled mid-publish.
group: ${{ github.workflow }}-${{ github.sha }}
cancel-in-progress: false

permissions:
contents: read

Expand Down Expand Up @@ -57,7 +65,8 @@ jobs:

# ADR 0022: durable, immutable Release assets so the pin never expires.
# Per-commit, per-family tag; SHA256SUMS.txt recorded for the consume/re-pin
# side. Immutable = first-publish-wins. macOS runner -> shasum(1).
# side. Immutable, and published from a draft that already holds its assets
# (#55): a published release refuses uploads. macOS runner -> shasum(1).
- name: Publish durable Release assets
if: github.ref == 'refs/heads/main'
env:
Expand All @@ -67,48 +76,61 @@ jobs:
# signing is skipped and the release still publishes SHA256SUMS.txt.
AEGIS_COMPONENT_SIGNING_KEY: ${{ secrets.AEGIS_COMPONENT_SIGNING_KEY }}
run: |
# The vendored publisher subsumes the old existence guard and is stronger.
# On an existing published tag it validates the release against the intended
# assets -- refusing extras, refusing missing assets, and comparing asset
# bytes against a rebuild. The old guard skipped on mere existence, which is
# how the empty releases accumulated unnoticed. Their disposition is recorded
# in #55: superseded, not deleted and not recreated.
#
# Do not re-add an existence check that skips when the tag is present. A
# red re-dispatch is the publisher refusing to agree that a release is what
# it claims to be, and that guard restores exactly the defect #55 fixed.
tag="map-cli-macos/${GITHUB_SHA}"
if gh release view "$tag" >/dev/null 2>&1; then
echo "Release $tag already exists (immutable); leaving assets untouched."
else
( cd dist/map-cli || exit 1
shasum -a 256 -- *.tar.gz > SHA256SUMS.txt
# Self-signed component provenance (aegis#1110): detached raw Ed25519
# signature over the exact SHA256SUMS.txt bytes. Inert until the org secret
# AEGIS_COMPONENT_SIGNING_KEY is provisioned; a release without it still
# publishes SHA256SUMS.txt and does not fail. Verified aegis-side with
# `openssl pkeyutl -verify -pubin -rawin -sigfile SHA256SUMS.txt.sig`.
if [ -n "${AEGIS_COMPONENT_SIGNING_KEY:-}" ]; then
# Resolve an OpenSSL 3.x `openssl` (only 3.x has `pkeyutl -rawin`). macOS system
# openssl is LibreSSL and lacks it; prefer Homebrew openssl@3 when present.
openssl_bin=""
if command -v brew >/dev/null 2>&1; then
brew_openssl="$(brew --prefix openssl@3 2>/dev/null)/bin/openssl"
if [ -x "$brew_openssl" ] && "$brew_openssl" version 2>/dev/null | grep -qE '^OpenSSL [3-9]'; then
openssl_bin="$brew_openssl"
fi
fi
if [ -z "$openssl_bin" ] && command -v openssl >/dev/null 2>&1 && openssl version 2>/dev/null | grep -qE '^OpenSSL [3-9]'; then
openssl_bin=openssl
fi
if [ -n "$openssl_bin" ]; then
# A real signing failure here is a hard error (fails the release, by design).
printf '%s\n' "$AEGIS_COMPONENT_SIGNING_KEY" > "$RUNNER_TEMP/component-signing.key"
"$openssl_bin" pkeyutl -sign -inkey "$RUNNER_TEMP/component-signing.key" -rawin -in SHA256SUMS.txt -out SHA256SUMS.txt.sig
rm -f "$RUNNER_TEMP/component-signing.key"
else
# Capability gap (e.g. a self-hosted runner without openssl@3): degrade
# gracefully. Producer signs best-effort; the aegis consumer's required mode
# blocks anything unsigned at re-pin, so a skipped signature is caught
# downstream, never a silent gap. Still publish SHA256SUMS.txt (unsigned).
echo "::warning::component-signing skipped: no OpenSSL 3.x on this runner; SHA256SUMS.txt published unsigned - consumer required-mode will flag it"
( cd dist/map-cli || exit 1
shasum -a 256 -- *.tar.gz > SHA256SUMS.txt
# Self-signed component provenance (aegis#1110): detached raw Ed25519
# signature over the exact SHA256SUMS.txt bytes. Inert until the org secret
# AEGIS_COMPONENT_SIGNING_KEY is provisioned; a release without it still
# publishes SHA256SUMS.txt and does not fail. Verified aegis-side with
# `openssl pkeyutl -verify -pubin -rawin -sigfile SHA256SUMS.txt.sig`.
if [ -n "${AEGIS_COMPONENT_SIGNING_KEY:-}" ]; then
# Resolve an OpenSSL 3.x `openssl` (only 3.x has `pkeyutl -rawin`). macOS system
# openssl is LibreSSL and lacks it; prefer Homebrew openssl@3 when present.
openssl_bin=""
if command -v brew >/dev/null 2>&1; then
brew_openssl="$(brew --prefix openssl@3 2>/dev/null)/bin/openssl"
if [ -x "$brew_openssl" ] && "$brew_openssl" version 2>/dev/null | grep -qE '^OpenSSL [3-9]'; then
openssl_bin="$brew_openssl"
fi
fi
)
gh release create "$tag" \
--prerelease \
--target "$GITHUB_SHA" \
--title "map-cli macOS component ${GITHUB_SHA:0:12}" \
--notes "Durable macOS MAP CLI (\`map\`) component assets (ADR 0022 / aegis#980). Immutable per commit; consumed via aegis component-pins by tag + sha256."
gh release upload "$tag" dist/map-cli/*.tar.gz dist/map-cli/SHA256SUMS.txt*
if [ -z "$openssl_bin" ] && command -v openssl >/dev/null 2>&1 && openssl version 2>/dev/null | grep -qE '^OpenSSL [3-9]'; then
openssl_bin=openssl
fi
if [ -n "$openssl_bin" ]; then
# A real signing failure here is a hard error (fails the release, by design).
printf '%s\n' "$AEGIS_COMPONENT_SIGNING_KEY" > "$RUNNER_TEMP/component-signing.key"
"$openssl_bin" pkeyutl -sign -inkey "$RUNNER_TEMP/component-signing.key" -rawin -in SHA256SUMS.txt -out SHA256SUMS.txt.sig
rm -f "$RUNNER_TEMP/component-signing.key"
else
# Capability gap (e.g. a self-hosted runner without openssl@3): degrade
# gracefully. Producer signs best-effort; the aegis consumer's required mode
# blocks anything unsigned at re-pin, so a skipped signature is caught
# downstream, never a silent gap. Still publish SHA256SUMS.txt (unsigned).
echo "::warning::component-signing skipped: no OpenSSL 3.x on this runner; SHA256SUMS.txt published unsigned - consumer required-mode will flag it"
fi
fi
)
# Build the exact intended asset list. SHA256SUMS.txt.sig exists only when
# AEGIS_COMPONENT_SIGNING_KEY is provisioned and an OpenSSL 3.x was found.
release_assets=(dist/map-cli/*.tar.gz dist/map-cli/SHA256SUMS.txt)
if [ -f dist/map-cli/SHA256SUMS.txt.sig ]; then
release_assets+=(dist/map-cli/SHA256SUMS.txt.sig)
fi

python3 "$GITHUB_WORKSPACE/scripts/publish_component_release.py" \
--tag "$tag" \
--target-commit "$GITHUB_SHA" \
--name "map-cli macOS component ${GITHUB_SHA:0:12}" \
--body "Durable macOS MAP CLI (\`map\`) component assets (ADR 0022 / aegis#980). Immutable per commit; consumed via aegis component-pins by tag + sha256." \
"${release_assets[@]}"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/target/
/dist
__pycache__/
.worktrees/
Loading
Loading