ci(publish): wire winget job via komac (D3 §3d)

Add the 4th publish.yaml sibling job: opens a PR to microsoft/winget-pkgs
with komac (pinned 2.16.0 prebuilt) for LazyEvaluation.RdbmsPlayground
(portable, x64 + arm64). Unlike scoop/homebrew it's a PR into Microsoft's
central, human-gated catalog - async and validated on their side.

- Auth: a classic public_repo GitHub PAT on a dedicated bot account
  (lazyeval-ci; fine-grained tokens can't open the cross-fork PR - komac
  #310), as the WINGET_GITHUB_TOKEN secret, job-scoped and passed to the API
  guards via a 0600 curl config file (never argv).
- Idempotent via two guards before submitting (already-merged version +
  already-open PR) so a repeated publish dispatch can't open a duplicate.
- No actions/checkout (komac works off URLs + the GitHub API).

Docs: ADR-0056 Amendment 4 - the model, the one-time manual `komac new`
bootstrap recipe (flags verified vs komac 2.16.0), and first-run learnings:
the fork must pre-exist, and a direct single-exe portable takes its PATH
alias from Commands[0] (not PortableCommandAlias, which is nested-only).
Plus README index + requirements D3.

Wiring only; going live needs the bootstrap PR (#391335, submitted) to merge.
This commit is contained in:
claude@clouddev1
2026-06-21 21:58:47 +00:00
parent 6bb2288470
commit 0208c67e59
4 changed files with 171 additions and 7 deletions
+84 -3
View File
@@ -198,6 +198,87 @@ jobs:
git push origin HEAD:main
echo "homebrew: tap updated to rdbms-playground $VER."
# winget remains a future sibling job here (komac on Linux CI, or a manual PR
# to microsoft/winget-pkgs). No `needs:` between jobs — each is independent and
# idempotent, so one failing or being added never breaks another.
# Update the winget package (Windows) by opening a PR to microsoft/winget-pkgs
# with komac. Unlike scoop-bucket/homebrew-tap (which push to OUR repos and are
# live at once), winget is a PR into Microsoft's central, human-gated catalog —
# asynchronous, and re-submitting the same version would open a DUPLICATE PR. So
# this job guards on both already-merged versions AND already-open PRs before
# submitting, which keeps a repeated `publish` dispatch safe.
#
# Auth: komac needs a CLASSIC GitHub PAT with `public_repo` (fine-grained tokens
# cannot open the cross-fork PR — komac #310). It is held on a dedicated GitHub
# bot account (a leak can't reach other repos — same reasoning as lazyeval-ci)
# and referenced ONLY in this job's env (job-level secret scoping keeps it out
# of the other publish jobs).
#
# PREREQUISITE: the package `LazyEvaluation.RdbmsPlayground` must already exist
# in winget-pkgs via a one-time `komac new` (interactive — run manually once;
# see ADR-0056 Amendment 4). This job only does the per-release `komac update`.
winget:
runs-on: ci-public
container:
image: git.lazyeval.net/oli/rdbms-playground-ci:latest
steps:
- name: submit the winget update PR (idempotent)
shell: bash
env:
TAG: ${{ inputs.tag }}
# Classic public_repo PAT for the winget bot account. komac reads it
# from GITHUB_TOKEN; the API guards below read it via a curl config file
# so the token never appears in a command line / process list.
GITHUB_TOKEN: ${{ secrets.WINGET_GITHUB_TOKEN }}
run: |
set -euo pipefail
VER="${TAG#v}"
PKG="LazyEvaluation.RdbmsPlayground"
echo "winget: targeting $PKG $VER ($TAG)"
# Auth header in a 0600 curl config (keeps the token out of argv/logs).
umask 077
printf 'header = "Authorization: Bearer %s"\nheader = "Accept: application/vnd.github+json"\n' \
"$GITHUB_TOKEN" > /tmp/gh-curlrc
api="https://api.github.com"
# Guard 1 — already merged into winget-pkgs?
# (manifests/<first-letter>/<Publisher>/<Package>/<version>)
merged=$(curl -sS -o /dev/null -w '%{http_code}' --config /tmp/gh-curlrc \
"$api/repos/microsoft/winget-pkgs/contents/manifests/l/LazyEvaluation/RdbmsPlayground/$VER" || echo 000)
if [ "$merged" = "200" ]; then
echo "winget: $PKG $VER already in winget-pkgs — nothing to do."
exit 0
fi
# Guard 2 — an open PR for this exact id+version already? (avoid a dup)
# curl -G --data-urlencode does the URL-encoding (no jq in the image).
curl -sS -G --config /tmp/gh-curlrc \
--data-urlencode "q=repo:microsoft/winget-pkgs type:pr state:open in:title \"$PKG\" \"$VER\"" \
"$api/search/issues" -o /tmp/winget-search.json
open=$(node -e 'process.stdout.write(String(JSON.parse(require("fs").readFileSync("/tmp/winget-search.json","utf8")).total_count||0))')
if [ "$open" != "0" ]; then
echo "winget: an open PR for $PKG $VER already exists — skipping."
exit 0
fi
# Install pinned komac (prebuilt glibc binary; the CI image has no
# cargo/komac). Pinned for reproducibility — bump deliberately.
KOMAC_VER=2.16.0
curl -fsSL -o /tmp/komac.tgz \
"https://github.com/russellbanks/Komac/releases/download/v$KOMAC_VER/komac-$KOMAC_VER-x86_64-unknown-linux-gnu.tar.gz"
tar -xzf /tmp/komac.tgz -C /tmp
komac_bin=$(find /tmp -maxdepth 2 -type f -name komac | head -1)
[ -n "$komac_bin" ] || { echo "ERROR: komac binary not found after extract" >&2; exit 1; }
base="https://git.lazyeval.net/oli/rdbms-playground/releases/download/$TAG"
echo "winget: submitting update PR via komac $KOMAC_VER"
# NB: confirm flags against `komac update --help` on first run — komac
# evolves; --version/--urls/--submit are the stable core. komac infers
# architecture + the `portable` installer type from the binaries.
"$komac_bin" update "$PKG" \
--version "$VER" \
--urls "$base/rdbms-playground-$TAG-x86_64-pc-windows-gnu.exe" \
"$base/rdbms-playground-$TAG-aarch64-pc-windows-gnullvm.exe" \
--submit
echo "winget: update PR submitted for $PKG $VER (Microsoft review is async)."
# No `needs:` between jobs — each is independent and idempotent, so one failing
# or being added never breaks another.