From 19d1f2785f4fa30ab0414423d4c25378376e42a7 Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 15:47:26 +0200 Subject: [PATCH 1/9] vcpkg registry: automate version publish (collector-v* tag -> PR) --- .../hsm-collector-registry-publish.yml | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 .github/workflows/hsm-collector-registry-publish.yml diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml new file mode 100644 index 000000000..3daae9923 --- /dev/null +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -0,0 +1,116 @@ +name: hsm-collector registry publish + +# Automates publishing a new hsm-collector version into this repo's vcpkg registry, so there is no +# manual "recipe" to remember. Push a `collector-v` tag (or run this manually) and it: +# 1. computes the source tarball SHA512 for that tag, +# 2. updates ports/hsm-collector/ (portfile REF + SHA512, vcpkg.json version), +# 3. refreshes the version database (versions/), and +# 4. opens a PR — which the hsm-collector-registry lane validates before a human merges it. + +on: + push: + tags: + - 'collector-v*' + workflow_dispatch: + inputs: + version: + description: 'Version to publish, e.g. 0.6.3 (a matching collector-v tag must already exist).' + required: true + dry_run: + description: 'Only compute + show the diff; do not open a PR.' + type: boolean + default: true + +permissions: + contents: write + pull-requests: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: master + fetch-depth: 0 + + - name: Resolve version + tag + id: v + run: | + if [ "${{ github.event_name }}" = "push" ]; then + tag="$GITHUB_REF_NAME" + else + tag="collector-v${{ github.event.inputs.version }}" + fi + ver="${tag#collector-v}" + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "ver=$ver" >> "$GITHUB_OUTPUT" + echo "Publishing hsm-collector $ver (source tag $tag)" + + - name: Compute source tarball SHA512 + id: sha + run: | + url="https://github.com/${{ github.repository }}/archive/${{ steps.v.outputs.tag }}.tar.gz" + sha=$(curl -fsSL "$url" | sha512sum | awk '{print $1}') + [ "${#sha}" -eq 128 ] || { echo "unexpected sha512: $sha"; exit 1; } + echo "sha=$sha" >> "$GITHUB_OUTPUT" + + - name: Prepare registry update + id: prep + run: | + set -euo pipefail + tag='${{ steps.v.outputs.tag }}'; ver='${{ steps.v.outputs.ver }}'; sha='${{ steps.sha.outputs.sha }}' + br="registry/hsm-collector-${ver}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$br" + + sed -i -E "s|REF collector-v[0-9]+\.[0-9]+\.[0-9]+|REF ${tag}|" ports/hsm-collector/portfile.cmake + sed -i -E "s|SHA512 [0-9a-f]{128}|SHA512 ${sha}|" ports/hsm-collector/portfile.cmake + sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[0-9]+\.[0-9]+\.[0-9]+(\")|\1${ver}\2|" ports/hsm-collector/vcpkg.json + + # Commit the port first so the recorded git-tree is the NEW port content. + git add ports/hsm-collector + git commit -q -m "vcpkg registry: publish hsm-collector ${ver}" + tree=$(git rev-parse "HEAD:ports/hsm-collector") + + python3 - "$ver" "$tree" <<'PY' + import json, sys + ver, tree = sys.argv[1], sys.argv[2] + b = json.load(open('versions/baseline.json')) + b['default']['hsm-collector']['baseline'] = ver + json.dump(b, open('versions/baseline.json', 'w'), indent=2); open('versions/baseline.json', 'a').write('\n') + f = 'versions/h-/hsm-collector.json' + d = json.load(open(f)) + if not any(e.get('version') == ver for e in d['versions']): + d['versions'].insert(0, {"version": ver, "git-tree": tree}) + json.dump(d, open(f, 'w'), indent=2); open(f, 'a').write('\n') + PY + + # Fold the versions/ change into the same commit (ports/ tree — and thus the recorded + # git-tree — is unchanged by touching versions/). + git add versions + git commit -q --amend --no-edit + echo "branch=$br" >> "$GITHUB_OUTPUT" + + - name: Sanity-check version DB git-tree + run: | + recorded=$(grep -oiE '[0-9a-f]{40}' versions/h-/hsm-collector.json | head -1) + actual=$(git rev-parse "HEAD:ports/hsm-collector") + echo "recorded=$recorded actual=$actual" + [ "$recorded" = "$actual" ] + + - name: Show the computed change + run: git show --stat HEAD + + - name: Open PR + if: github.event_name == 'push' || github.event.inputs.dry_run == 'false' + env: + GH_TOKEN: ${{ github.token }} + run: | + br='${{ steps.prep.outputs.branch }}'; ver='${{ steps.v.outputs.ver }}' + git push -f -u origin "$br" + gh pr create --base master --head "$br" \ + --title "vcpkg registry: hsm-collector ${ver}" \ + --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${ver}\` (SHA512 refreshed) and the version database is updated. Re-run / ensure the **hsm-collector-registry** check passes, then merge to make ${ver} available to consumers." \ + || echo "A PR for $br may already exist." From 9199be5874e75648e45cc15bf5418e33d59a0d1e Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 16:26:45 +0200 Subject: [PATCH 2/9] registry-publish: fix review findings (empty-hash guard, input injection, validation) Addresses the #1268 review: - (bug) SHA512 step had no pipefail, so a missing tag (curl 404 -> empty stream) hashed to the empty-string SHA512 (128 chars) and passed the length guard, silently recording a bad hash. Now: set -euo pipefail, download to a file (curl -f fails the step on 404), and explicitly reject the empty-stream hash. - (security) the workflow_dispatch `version` input was interpolated as ${{ }} into the shell body (command injection). Pass it via env and reference $INPUT_VERSION. - (robustness) validate the version is X.Y.Z up front; malformed input now fails fast instead of producing partial sed edits. This also makes all downstream values safe to embed. - (robustness) re-publishing an existing version now drops the stale versions[] entry before inserting, so the git-tree can't go inconsistent. - (validation) the publish run now builds + installs the updated port via vcpkg (overlay) before opening the PR, so the auto-PR is known-good even though a GITHUB_TOKEN-opened PR doesn't auto-trigger the separate registry check; the PR body now says so explicitly. Co-Authored-By: Claude Opus 4.8 --- .../hsm-collector-registry-publish.yml | 98 +++++++++++++------ 1 file changed, 70 insertions(+), 28 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 3daae9923..a79268848 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -4,8 +4,9 @@ name: hsm-collector registry publish # manual "recipe" to remember. Push a `collector-v` tag (or run this manually) and it: # 1. computes the source tarball SHA512 for that tag, # 2. updates ports/hsm-collector/ (portfile REF + SHA512, vcpkg.json version), -# 3. refreshes the version database (versions/), and -# 4. opens a PR — which the hsm-collector-registry lane validates before a human merges it. +# 3. refreshes the version database (versions/), +# 4. builds+installs the updated port to prove it's valid, then +# 5. opens a PR to master. on: push: @@ -17,7 +18,7 @@ on: description: 'Version to publish, e.g. 0.6.3 (a matching collector-v tag must already exist).' required: true dry_run: - description: 'Only compute + show the diff; do not open a PR.' + description: 'Only compute + validate + show the diff; do not open a PR.' type: boolean default: true @@ -28,78 +29,117 @@ permissions: jobs: publish: runs-on: ubuntu-latest + env: + VCPKG_DISABLE_METRICS: '1' steps: - uses: actions/checkout@v4 with: ref: master fetch-depth: 0 - - name: Resolve version + tag + - name: Resolve + validate version id: v + # Pass the dispatch input through env (never interpolate ${{ }} into the shell body — that + # would let an input like $(...) run on the runner). GITHUB_REF_NAME is already a safe env var. + env: + EVENT: ${{ github.event_name }} + INPUT_VERSION: ${{ github.event.inputs.version }} run: | - if [ "${{ github.event_name }}" = "push" ]; then + set -euo pipefail + if [ "$EVENT" = "push" ]; then tag="$GITHUB_REF_NAME" else - tag="collector-v${{ github.event.inputs.version }}" + tag="collector-v${INPUT_VERSION}" fi ver="${tag#collector-v}" + if ! printf '%s' "$ver" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::bad version '$ver' — expected X.Y.Z (tag collector-vX.Y.Z)"; exit 1 + fi echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "ver=$ver" >> "$GITHUB_OUTPUT" echo "Publishing hsm-collector $ver (source tag $tag)" - name: Compute source tarball SHA512 id: sha + env: + TAG: ${{ steps.v.outputs.tag }} run: | - url="https://github.com/${{ github.repository }}/archive/${{ steps.v.outputs.tag }}.tar.gz" - sha=$(curl -fsSL "$url" | sha512sum | awk '{print $1}') - [ "${#sha}" -eq 128 ] || { echo "unexpected sha512: $sha"; exit 1; } + set -euo pipefail + url="https://github.com/${{ github.repository }}/archive/${TAG}.tar.gz" + # Download to a file (with -f so a missing tag → non-zero exit under set -e) rather than piping + # curl into sha512sum, where a 404 would otherwise be hashed as an empty stream. + curl -fsSL "$url" -o source.tgz + sha=$(sha512sum source.tgz | awk '{print $1}') + empty="cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" + if [ "${#sha}" -ne 128 ] || [ "$sha" = "$empty" ]; then + echo "::error::bad SHA512 for $url (does the tag exist?)"; exit 1 + fi + rm -f source.tgz echo "sha=$sha" >> "$GITHUB_OUTPUT" - name: Prepare registry update id: prep + env: + TAG: ${{ steps.v.outputs.tag }} + VER: ${{ steps.v.outputs.ver }} + SHA: ${{ steps.sha.outputs.sha }} run: | set -euo pipefail - tag='${{ steps.v.outputs.tag }}'; ver='${{ steps.v.outputs.ver }}'; sha='${{ steps.sha.outputs.sha }}' - br="registry/hsm-collector-${ver}" + br="registry/hsm-collector-${VER}" git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git checkout -b "$br" - sed -i -E "s|REF collector-v[0-9]+\.[0-9]+\.[0-9]+|REF ${tag}|" ports/hsm-collector/portfile.cmake - sed -i -E "s|SHA512 [0-9a-f]{128}|SHA512 ${sha}|" ports/hsm-collector/portfile.cmake - sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[0-9]+\.[0-9]+\.[0-9]+(\")|\1${ver}\2|" ports/hsm-collector/vcpkg.json + sed -i -E "s|REF collector-v[0-9]+\.[0-9]+\.[0-9]+|REF ${TAG}|" ports/hsm-collector/portfile.cmake + sed -i -E "s|SHA512 [0-9a-f]{128}|SHA512 ${SHA}|" ports/hsm-collector/portfile.cmake + sed -i -E "s|(\"version\"[[:space:]]*:[[:space:]]*\")[0-9]+\.[0-9]+\.[0-9]+(\")|\1${VER}\2|" ports/hsm-collector/vcpkg.json # Commit the port first so the recorded git-tree is the NEW port content. git add ports/hsm-collector - git commit -q -m "vcpkg registry: publish hsm-collector ${ver}" + git commit -q -m "vcpkg registry: publish hsm-collector ${VER}" tree=$(git rev-parse "HEAD:ports/hsm-collector") - python3 - "$ver" "$tree" <<'PY' - import json, sys - ver, tree = sys.argv[1], sys.argv[2] + VER="$VER" TREE="$tree" python3 - <<'PY' + import json, os + ver, tree = os.environ['VER'], os.environ['TREE'] b = json.load(open('versions/baseline.json')) b['default']['hsm-collector']['baseline'] = ver json.dump(b, open('versions/baseline.json', 'w'), indent=2); open('versions/baseline.json', 'a').write('\n') f = 'versions/h-/hsm-collector.json' d = json.load(open(f)) - if not any(e.get('version') == ver for e in d['versions']): - d['versions'].insert(0, {"version": ver, "git-tree": tree}) + # Idempotent: drop any existing entry for this version, then prepend the fresh one. + d['versions'] = [e for e in d['versions'] if e.get('version') != ver] + d['versions'].insert(0, {"version": ver, "git-tree": tree}) json.dump(d, open(f, 'w'), indent=2); open(f, 'a').write('\n') PY - # Fold the versions/ change into the same commit (ports/ tree — and thus the recorded - # git-tree — is unchanged by touching versions/). + # Fold versions/ into the same commit (ports/ tree — and thus the recorded git-tree — is + # unchanged by touching versions/). git add versions git commit -q --amend --no-edit echo "branch=$br" >> "$GITHUB_OUTPUT" - name: Sanity-check version DB git-tree run: | + set -euo pipefail recorded=$(grep -oiE '[0-9a-f]{40}' versions/h-/hsm-collector.json | head -1) actual=$(git rev-parse "HEAD:ports/hsm-collector") echo "recorded=$recorded actual=$actual" [ "$recorded" = "$actual" ] + - name: Set up vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: 6f29f12e82a8293156836ad81cc9bf5af41fe836 + + - name: Validate the updated port builds + installs + # Proves the refreshed SHA512 + REF actually resolve and the collector builds from them, so the + # PR is known-good even though (see below) the separate registry check won't auto-run on it. + env: + VCPKG_BINARY_SOURCES: clear + run: | + "$VCPKG_ROOT/vcpkg" install hsm-collector --overlay-ports=ports + - name: Show the computed change run: git show --stat HEAD @@ -107,10 +147,12 @@ jobs: if: github.event_name == 'push' || github.event.inputs.dry_run == 'false' env: GH_TOKEN: ${{ github.token }} + BR: ${{ steps.prep.outputs.branch }} + VER: ${{ steps.v.outputs.ver }} run: | - br='${{ steps.prep.outputs.branch }}'; ver='${{ steps.v.outputs.ver }}' - git push -f -u origin "$br" - gh pr create --base master --head "$br" \ - --title "vcpkg registry: hsm-collector ${ver}" \ - --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${ver}\` (SHA512 refreshed) and the version database is updated. Re-run / ensure the **hsm-collector-registry** check passes, then merge to make ${ver} available to consumers." \ - || echo "A PR for $br may already exist." + set -euo pipefail + git push -f -u origin "$BR" + gh pr create --base master --head "$BR" \ + --title "vcpkg registry: hsm-collector ${VER}" \ + --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${VER}\` (SHA512 refreshed) and the version DB is updated. The updated port was **built + installed in the publish run** ('Validate the updated port builds + installs'), so it is known-good. Note: this PR is opened by \`GITHUB_TOKEN\`, so the separate **hsm-collector-registry** check does NOT auto-run here — re-run it manually (Actions -> hsm-collector-registry -> Run workflow on this branch) or push an empty commit if you want it on the PR before merging." \ + || echo "A PR for $BR may already exist." From f1e0b0bedcdfed82d08df319e6398879bf61bba1 Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 16:32:34 +0200 Subject: [PATCH 3/9] registry-publish: harden per 2nd review (immutability guard, port-version, concurrency) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refuse to rewrite a version's git-tree if it's already published with different content (vcpkg versions are immutable) — bump instead. - Reset baseline port-version to 0 on a new version. - Replace `gh pr create ... || echo` (which masked all failures) with an explicit "PR already open?" check. - Add a concurrency group so two tag pushes can't race the version database. Co-Authored-By: Claude Opus 4.8 --- .../hsm-collector-registry-publish.yml | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index a79268848..0ce1ea305 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -26,6 +26,11 @@ permissions: contents: write pull-requests: write +# Serialize publishes so two tag pushes can't race on the shared version database. +concurrency: + group: hsm-collector-registry-publish + cancel-in-progress: false + jobs: publish: runs-on: ubuntu-latest @@ -100,14 +105,22 @@ jobs: tree=$(git rev-parse "HEAD:ports/hsm-collector") VER="$VER" TREE="$tree" python3 - <<'PY' - import json, os + import json, os, sys ver, tree = os.environ['VER'], os.environ['TREE'] + f = 'versions/h-/hsm-collector.json' + d = json.load(open(f)) + # vcpkg version->git-tree mappings are immutable. If this version is already published with a + # DIFFERENT tree (e.g. its source tag was recreated with new content), refuse — bump instead. + existing = next((e for e in d['versions'] if e.get('version') == ver), None) + if existing and existing.get('git-tree') != tree: + print(f"::error::hsm-collector {ver} is already published with git-tree " + f"{existing['git-tree']}; refusing to rewrite it to {tree}. Bump the version.") + sys.exit(1) b = json.load(open('versions/baseline.json')) b['default']['hsm-collector']['baseline'] = ver + b['default']['hsm-collector']['port-version'] = 0 # reset on a new version json.dump(b, open('versions/baseline.json', 'w'), indent=2); open('versions/baseline.json', 'a').write('\n') - f = 'versions/h-/hsm-collector.json' - d = json.load(open(f)) - # Idempotent: drop any existing entry for this version, then prepend the fresh one. + # Idempotent (pre-merge re-runs): drop any existing entry for this version, then prepend it. d['versions'] = [e for e in d['versions'] if e.get('version') != ver] d['versions'].insert(0, {"version": ver, "git-tree": tree}) json.dump(d, open(f, 'w'), indent=2); open(f, 'a').write('\n') @@ -152,7 +165,11 @@ jobs: run: | set -euo pipefail git push -f -u origin "$BR" - gh pr create --base master --head "$BR" \ - --title "vcpkg registry: hsm-collector ${VER}" \ - --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${VER}\` (SHA512 refreshed) and the version DB is updated. The updated port was **built + installed in the publish run** ('Validate the updated port builds + installs'), so it is known-good. Note: this PR is opened by \`GITHUB_TOKEN\`, so the separate **hsm-collector-registry** check does NOT auto-run here — re-run it manually (Actions -> hsm-collector-registry -> Run workflow on this branch) or push an empty commit if you want it on the PR before merging." \ - || echo "A PR for $BR may already exist." + # Check existence explicitly rather than masking every gh failure with `|| echo`. + if gh pr view "$BR" --json number >/dev/null 2>&1; then + echo "A PR for $BR is already open; pushed the refreshed branch to it." + else + gh pr create --base master --head "$BR" \ + --title "vcpkg registry: hsm-collector ${VER}" \ + --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${VER}\` (SHA512 refreshed) and the version DB is updated. The updated port was **built + installed in the publish run** ('Validate the updated port builds + installs'), so it is known-good. Note: this PR is opened by \`GITHUB_TOKEN\`, so the separate **hsm-collector-registry** check does NOT auto-run here — re-run it manually (Actions -> hsm-collector-registry -> Run workflow on this branch) or push an empty commit if you want it on the PR before merging." + fi From bec535c0d9c33724f257956236b25345c5dfe28d Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 18:09:23 +0200 Subject: [PATCH 4/9] registry-publish: only advance the vcpkg baseline, never roll it back --- .../workflows/hsm-collector-registry-publish.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 0ce1ea305..6957232b6 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -117,9 +117,16 @@ jobs: f"{existing['git-tree']}; refusing to rewrite it to {tree}. Bump the version.") sys.exit(1) b = json.load(open('versions/baseline.json')) - b['default']['hsm-collector']['baseline'] = ver - b['default']['hsm-collector']['port-version'] = 0 # reset on a new version - json.dump(b, open('versions/baseline.json', 'w'), indent=2); open('versions/baseline.json', 'a').write('\n') + cur = b['default']['hsm-collector'].get('baseline', '0.0.0') + semver = lambda v: tuple(int(x) for x in v.split('.')) + # Only advance the baseline — a backport / re-publish of an OLDER version still gets its + # versions/ entry (below) but must not roll the default consumers see backward. + if semver(ver) >= semver(cur): + b['default']['hsm-collector']['baseline'] = ver + b['default']['hsm-collector']['port-version'] = 0 # reset on a new version + json.dump(b, open('versions/baseline.json', 'w'), indent=2); open('versions/baseline.json', 'a').write('\n') + else: + print(f"::warning::published {ver}, but the baseline stays at {cur} (newer) — not rolling back.") # Idempotent (pre-merge re-runs): drop any existing entry for this version, then prepend it. d['versions'] = [e for e in d['versions'] if e.get('version') != ver] d['versions'].insert(0, {"version": ver, "git-tree": tree}) From fd01e0854674263c7284f699ba078c3b47880800 Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 18:15:04 +0200 Subject: [PATCH 5/9] registry-publish: scope PR check to open PRs; env-pass repo per convention --- .github/workflows/hsm-collector-registry-publish.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 6957232b6..605bfbe4a 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -68,9 +68,10 @@ jobs: id: sha env: TAG: ${{ steps.v.outputs.tag }} + REPO: ${{ github.repository }} run: | set -euo pipefail - url="https://github.com/${{ github.repository }}/archive/${TAG}.tar.gz" + url="https://github.com/${REPO}/archive/${TAG}.tar.gz" # Download to a file (with -f so a missing tag → non-zero exit under set -e) rather than piping # curl into sha512sum, where a 404 would otherwise be hashed as an empty stream. curl -fsSL "$url" -o source.tgz @@ -172,8 +173,9 @@ jobs: run: | set -euo pipefail git push -f -u origin "$BR" - # Check existence explicitly rather than masking every gh failure with `|| echo`. - if gh pr view "$BR" --json number >/dev/null 2>&1; then + # Check for an OPEN PR explicitly (gh pr view also resolves merged/closed ones) rather than + # masking every gh failure with `|| echo`. + if [ -n "$(gh pr list --head "$BR" --state open --json number --jq '.[].number')" ]; then echo "A PR for $BR is already open; pushed the refreshed branch to it." else gh pr create --base master --head "$BR" \ From f6054f277e1cba70dd5c7951f2dedb13c389e32f Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 18:21:33 +0200 Subject: [PATCH 6/9] registry-publish: keep port-version on same-version re-run; validate [http]; with-blocks --- .../hsm-collector-registry-publish.yml | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 605bfbe4a..55a59b550 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -120,18 +120,24 @@ jobs: b = json.load(open('versions/baseline.json')) cur = b['default']['hsm-collector'].get('baseline', '0.0.0') semver = lambda v: tuple(int(x) for x in v.split('.')) - # Only advance the baseline — a backport / re-publish of an OLDER version still gets its - # versions/ entry (below) but must not roll the default consumers see backward. - if semver(ver) >= semver(cur): + # Only ADVANCE the baseline. A strictly newer version resets port-version; re-publishing the + # SAME baseline version leaves port-version alone (a manual port-only bump must not be + # clobbered); an OLDER version (backport) still gets its versions/ entry (below) but never + # rolls the default that consumers resolve backward. + if semver(ver) > semver(cur): b['default']['hsm-collector']['baseline'] = ver - b['default']['hsm-collector']['port-version'] = 0 # reset on a new version - json.dump(b, open('versions/baseline.json', 'w'), indent=2); open('versions/baseline.json', 'a').write('\n') - else: + b['default']['hsm-collector']['port-version'] = 0 + with open('versions/baseline.json', 'w') as fh: + json.dump(b, fh, indent=2); fh.write('\n') + elif semver(ver) < semver(cur): print(f"::warning::published {ver}, but the baseline stays at {cur} (newer) — not rolling back.") - # Idempotent (pre-merge re-runs): drop any existing entry for this version, then prepend it. + # ver == cur: baseline already correct; leave baseline.json (and its port-version) untouched. + # Idempotent (pre-merge re-runs): drop any existing entry for this version, then prepend it + # (newest-first — the sanity-check step's `grep ... | head -1` relies on this ordering). d['versions'] = [e for e in d['versions'] if e.get('version') != ver] d['versions'].insert(0, {"version": ver, "git-tree": tree}) - json.dump(d, open(f, 'w'), indent=2); open(f, 'a').write('\n') + with open(f, 'w') as fh: + json.dump(d, fh, indent=2); fh.write('\n') PY # Fold versions/ into the same commit (ports/ tree — and thus the recorded git-tree — is @@ -159,7 +165,7 @@ jobs: env: VCPKG_BINARY_SOURCES: clear run: | - "$VCPKG_ROOT/vcpkg" install hsm-collector --overlay-ports=ports + "$VCPKG_ROOT/vcpkg" install "hsm-collector[http]" --overlay-ports=ports - name: Show the computed change run: git show --stat HEAD From c5447d07f0da227a1014a03298cd932a1a10e4dc Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 18:28:08 +0200 Subject: [PATCH 7/9] registry-publish: honest Linux-only wording, curl retry, order-independent check, doc limits --- .../workflows/hsm-collector-registry-publish.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 55a59b550..73dee67b2 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -7,6 +7,11 @@ name: hsm-collector registry publish # 3. refreshes the version database (versions/), # 4. builds+installs the updated port to prove it's valid, then # 5. opens a PR to master. +# +# Scope / limits: publishes SOURCE versions only — a port-only fix (no source change) needs a manual +# port-version bump. Two publishes opened before either merges will conflict in versions/ (rebase the +# second). Build validation runs on Linux; the Windows/MSVC consumer path is the hsm-collector-registry +# check, which a human runs before merge (a GITHUB_TOKEN-opened PR doesn't auto-trigger it). on: push: @@ -74,7 +79,7 @@ jobs: url="https://github.com/${REPO}/archive/${TAG}.tar.gz" # Download to a file (with -f so a missing tag → non-zero exit under set -e) rather than piping # curl into sha512sum, where a 404 would otherwise be hashed as an empty stream. - curl -fsSL "$url" -o source.tgz + curl -fsSL --retry 3 --retry-connrefused "$url" -o source.tgz sha=$(sha512sum source.tgz | awk '{print $1}') empty="cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" if [ "${#sha}" -ne 128 ] || [ "$sha" = "$empty" ]; then @@ -147,9 +152,12 @@ jobs: echo "branch=$br" >> "$GITHUB_OUTPUT" - name: Sanity-check version DB git-tree + env: + VER: ${{ steps.v.outputs.ver }} run: | set -euo pipefail - recorded=$(grep -oiE '[0-9a-f]{40}' versions/h-/hsm-collector.json | head -1) + # Read the git-tree recorded for THIS version (order-independent — not `head -1`). + recorded=$(python3 -c "import json,os; d=json.load(open('versions/h-/hsm-collector.json')); print(next(e['git-tree'] for e in d['versions'] if e['version']==os.environ['VER']))") actual=$(git rev-parse "HEAD:ports/hsm-collector") echo "recorded=$recorded actual=$actual" [ "$recorded" = "$actual" ] @@ -186,5 +194,5 @@ jobs: else gh pr create --base master --head "$BR" \ --title "vcpkg registry: hsm-collector ${VER}" \ - --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${VER}\` (SHA512 refreshed) and the version DB is updated. The updated port was **built + installed in the publish run** ('Validate the updated port builds + installs'), so it is known-good. Note: this PR is opened by \`GITHUB_TOKEN\`, so the separate **hsm-collector-registry** check does NOT auto-run here — re-run it manually (Actions -> hsm-collector-registry -> Run workflow on this branch) or push an empty commit if you want it on the PR before merging." + --body "Automated by \`hsm-collector-registry-publish\`: the port now fetches \`collector-v${VER}\` (SHA512 refreshed) and the version DB is updated. The updated port was **built + installed on Linux in the publish run** (validates the SHA512/REF resolve and the collector compiles with \`[http]\`). The consumer-facing **hsm-collector-registry** check is Windows/MSVC and does NOT auto-run on a \`GITHUB_TOKEN\` PR — **run it manually** (Actions -> hsm-collector-registry -> Run workflow on this branch) before merging to cover the Windows path." fi From be843831740183279abf9916de21bf15ad6b05df Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 18:33:44 +0200 Subject: [PATCH 8/9] registry-publish: fetch-depth 1, force-push caution, same-version port-version warning --- .github/workflows/hsm-collector-registry-publish.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 73dee67b2..96f79f273 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -12,6 +12,7 @@ name: hsm-collector registry publish # port-version bump. Two publishes opened before either merges will conflict in versions/ (rebase the # second). Build validation runs on Linux; the Windows/MSVC consumer path is the hsm-collector-registry # check, which a human runs before merge (a GITHUB_TOKEN-opened PR doesn't auto-trigger it). +# Re-runs force-push the version's branch, so any manual commit pushed onto an open auto-PR is lost. on: push: @@ -45,7 +46,7 @@ jobs: - uses: actions/checkout@v4 with: ref: master - fetch-depth: 0 + fetch-depth: 1 - name: Resolve + validate version id: v @@ -136,7 +137,11 @@ jobs: json.dump(b, fh, indent=2); fh.write('\n') elif semver(ver) < semver(cur): print(f"::warning::published {ver}, but the baseline stays at {cur} (newer) — not rolling back.") - # ver == cur: baseline already correct; leave baseline.json (and its port-version) untouched. + else: # ver == cur: baseline already correct; leave baseline.json (and its port-version) alone. + if b['default']['hsm-collector'].get('port-version', 0): + print(f"::warning::re-published {ver} while baseline port-version is " + f"{b['default']['hsm-collector']['port-version']}; the versions/ entry records port-version 0. " + f"Port-only changes need a manual port-version bump (see the header note).") # Idempotent (pre-merge re-runs): drop any existing entry for this version, then prepend it # (newest-first — the sanity-check step's `grep ... | head -1` relies on this ordering). d['versions'] = [e for e in d['versions'] if e.get('version') != ver] From b2521226f6d91227adb077264299c535effe210c Mon Sep 17 00:00:00 2001 From: Andrei Date: Thu, 16 Jul 2026 18:39:47 +0200 Subject: [PATCH 9/9] registry-publish: cap source download with curl --max-time --- .github/workflows/hsm-collector-registry-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hsm-collector-registry-publish.yml b/.github/workflows/hsm-collector-registry-publish.yml index 96f79f273..05e73801b 100644 --- a/.github/workflows/hsm-collector-registry-publish.yml +++ b/.github/workflows/hsm-collector-registry-publish.yml @@ -80,7 +80,7 @@ jobs: url="https://github.com/${REPO}/archive/${TAG}.tar.gz" # Download to a file (with -f so a missing tag → non-zero exit under set -e) rather than piping # curl into sha512sum, where a 404 would otherwise be hashed as an empty stream. - curl -fsSL --retry 3 --retry-connrefused "$url" -o source.tgz + curl -fsSL --retry 3 --retry-connrefused --max-time 300 "$url" -o source.tgz sha=$(sha512sum source.tgz | awk '{print $1}') empty="cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" if [ "${#sha}" -ne 128 ] || [ "$sha" = "$empty" ]; then