mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
refactor(ci): move the Winget bump out of CI onto a maintainer machine
Mirrors the Homebrew cask change. The old design stored a classic `public_repo` PAT as WINGET_TOKEN and handed it to the third-party vedantmgoyal9/winget-releaser action: a token with write access to every public repo the owning account can reach, given to code we do not control, in a place any push-access collaborator could read it from (a pushed branch containing a workflow runs with repo secrets). - CI (bump-winget.yml, now "Sync Winget Manifest Reference") uses GITHUB_TOKEN only: downloads the MSI, computes the sha256, reads the ProductCode from the MSI Property table via msitools, and opens an in-repo PR syncing desktopApp/packaging/winget/. Runs on ubuntu (1x billing) rather than Windows since msitools reads the Property table fine. - scripts/bump-winget.sh does the upstream PR. It needs NO new token: it drives `gh`, which a maintainer already has authenticated, and it does not need wingetcreate (Windows-only) because the manifests are plain YAML — so it runs from macOS or Linux. Add the three reference manifests under desktopApp/packaging/winget/, matching the schema 1.12.0 shape used upstream. All three validate against Microsoft's published JSON schemas. Validation caught one real bug worth noting: an all-digit 64-char InstallerSha256 parses as a YAML *integer* and fails the schema's `string` type, so it is written quoted. ProductCode is re-read every release because jpackage regenerates it per build; it is the ARP key `winget upgrade` matches on. Drops the last package-manager PAT from the secret inventory.
This commit is contained in:
Executable
+159
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Push the VitorPamplona.Amethyst manifests upstream to microsoft/winget-pkgs.
|
||||
#
|
||||
# Like scripts/bump-homebrew-cask.sh, this is deliberately not automated: it
|
||||
# needs push access to a fork of winget-pkgs, and the previous design kept that
|
||||
# as a classic `public_repo` PAT in the WINGET_TOKEN Actions secret, handed to a
|
||||
# third-party action. That scope grants write to every public repo the account
|
||||
# can reach, and any Actions secret is usable by anyone with push access to this
|
||||
# repo. See BUILDING.md § Winget.
|
||||
#
|
||||
# Unlike the Homebrew script this needs NO new token: it drives `gh`, which you
|
||||
# are already authenticated with. It also does not need `wingetcreate` (which is
|
||||
# Windows-only) — winget manifests are plain YAML, so this works from macOS or
|
||||
# Linux.
|
||||
#
|
||||
# Everything error-prone (version, sha256, ProductCode) is already done in CI by
|
||||
# .github/workflows/bump-winget.yml, which opens a PR syncing
|
||||
# desktopApp/packaging/winget/. Merge that PR first; this script reads the merged
|
||||
# values so the two can never disagree.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/bump-winget.sh # uses the manifests as-is
|
||||
# scripts/bump-winget.sh v1.13.2 # asserts the manifests match this tag
|
||||
# DRY_RUN=1 scripts/bump-winget.sh # print what would happen, do nothing
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
SRC="$REPO_ROOT/desktopApp/packaging/winget"
|
||||
PKG_ID="VitorPamplona.Amethyst"
|
||||
UPSTREAM="microsoft/winget-pkgs"
|
||||
EXPECTED_TAG="${1:-}"
|
||||
DRY_RUN="${DRY_RUN:-}"
|
||||
|
||||
die() { echo "error: $*" >&2; exit 1; }
|
||||
|
||||
command -v gh >/dev/null || die "gh not found — https://cli.github.com"
|
||||
gh auth status >/dev/null 2>&1 || die "gh is not authenticated; run: gh auth login"
|
||||
[[ -d "$SRC" ]] || die "manifests not found at $SRC"
|
||||
|
||||
VERSION=$(awk '/^PackageVersion: /{print $2; exit}' "$SRC/$PKG_ID.yaml")
|
||||
[[ -n "$VERSION" ]] || die "could not parse PackageVersion out of $SRC/$PKG_ID.yaml"
|
||||
|
||||
if [[ -n "$EXPECTED_TAG" && "v$VERSION" != "$EXPECTED_TAG" ]]; then
|
||||
die "manifests are at v$VERSION but you asked for $EXPECTED_TAG.
|
||||
Merge the 'chore: sync winget manifests to $EXPECTED_TAG' PR first, then pull."
|
||||
fi
|
||||
|
||||
# InstallerSha256 and ProductCode are single-quoted in the YAML (see the
|
||||
# comments there); strip the quotes when reading them back.
|
||||
SHA=$(awk '/^ InstallerSha256: /{print $2; exit}' "$SRC/$PKG_ID.installer.yaml" | tr -d "'")
|
||||
URL=$(awk '/^ InstallerUrl: /{print $2; exit}' "$SRC/$PKG_ID.installer.yaml")
|
||||
PC=$(awk '/^ ProductCode: /{print $2; exit}' "$SRC/$PKG_ID.installer.yaml" | tr -d "'")
|
||||
|
||||
[[ "$SHA" =~ ^0+$ ]] && die "InstallerSha256 is still the placeholder.
|
||||
Run the sync workflow first: gh workflow run bump-winget.yml -f tag=v$VERSION"
|
||||
|
||||
echo "package : $PKG_ID"
|
||||
echo "version : $VERSION"
|
||||
echo "sha256 : $SHA"
|
||||
echo "product : $PC"
|
||||
echo "url : $URL"
|
||||
echo
|
||||
|
||||
# Re-verify against the live asset. CI already did, but the whole point of a
|
||||
# manual gate is that a human confirms what is about to be published.
|
||||
echo "==> verifying the release MSI"
|
||||
TMP=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
curl -fsSL -o "$TMP/amethyst.msi" "$URL" || die "could not download $URL"
|
||||
|
||||
if command -v sha256sum >/dev/null; then
|
||||
ACTUAL=$(sha256sum "$TMP/amethyst.msi" | awk '{print $1}')
|
||||
else
|
||||
ACTUAL=$(shasum -a 256 "$TMP/amethyst.msi" | awk '{print $1}')
|
||||
fi
|
||||
ACTUAL=$(echo "$ACTUAL" | tr '[:lower:]' '[:upper:]')
|
||||
[[ "$ACTUAL" == "$SHA" ]] || die "sha256 mismatch!
|
||||
manifest says : $SHA
|
||||
actual : $ACTUAL"
|
||||
echo " sha256 matches"
|
||||
echo
|
||||
|
||||
DEST="manifests/v/VitorPamplona/Amethyst/$VERSION"
|
||||
BRANCH="$PKG_ID-$VERSION"
|
||||
|
||||
if [[ -n "$DRY_RUN" ]]; then
|
||||
echo "DRY_RUN set — would now:"
|
||||
echo " fork $UPSTREAM (if needed), branch $BRANCH"
|
||||
echo " add $DEST/{$PKG_ID.yaml,$PKG_ID.installer.yaml,$PKG_ID.locale.en-US.yaml}"
|
||||
echo " open a PR against $UPSTREAM"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# A new package needs a human-reviewed first submission; after that this script
|
||||
# handles every version bump.
|
||||
if ! gh api "repos/$UPSTREAM/contents/manifests/v/VitorPamplona/Amethyst" >/dev/null 2>&1; then
|
||||
echo "note: $PKG_ID is not in $UPSTREAM yet — this will be the NEW-PACKAGE submission."
|
||||
echo " Expect a slower, human-reviewed first pass."
|
||||
echo
|
||||
fi
|
||||
|
||||
WORK="$TMP/winget-pkgs"
|
||||
echo "==> preparing the fork"
|
||||
gh repo fork "$UPSTREAM" --clone=false --remote=false >/dev/null 2>&1 || true
|
||||
FORK="$(gh api user --jq .login)/winget-pkgs"
|
||||
|
||||
# Shallow clone of the fork only — winget-pkgs full history is enormous.
|
||||
gh repo clone "$FORK" "$WORK" -- --depth=1 >/dev/null 2>&1 \
|
||||
|| die "could not clone $FORK"
|
||||
|
||||
cd "$WORK"
|
||||
git remote add upstream "https://github.com/$UPSTREAM.git" 2>/dev/null || true
|
||||
git fetch --depth=1 upstream master >/dev/null 2>&1 || git fetch --depth=1 upstream main >/dev/null 2>&1
|
||||
DEFAULT_BRANCH=$(gh api "repos/$UPSTREAM" --jq .default_branch)
|
||||
git checkout -B "$BRANCH" "upstream/$DEFAULT_BRANCH" >/dev/null 2>&1
|
||||
|
||||
mkdir -p "$DEST"
|
||||
cp "$SRC/$PKG_ID.yaml" "$SRC/$PKG_ID.installer.yaml" "$SRC/$PKG_ID.locale.en-US.yaml" "$DEST/"
|
||||
|
||||
# The reference copies carry an Amethyst-repo header comment; upstream manifests
|
||||
# should not. Strip leading comment lines, keep the yaml-language-server line.
|
||||
for f in "$DEST"/*.yaml; do
|
||||
python3 - "$f" <<'PY'
|
||||
import sys
|
||||
p = sys.argv[1]
|
||||
lines = open(p).read().splitlines(keepends=True)
|
||||
out, started = [], False
|
||||
for ln in lines:
|
||||
if not started and ln.startswith('#') and 'yaml-language-server' not in ln:
|
||||
continue
|
||||
started = True
|
||||
out.append(ln)
|
||||
open(p, 'w').writelines(out)
|
||||
PY
|
||||
done
|
||||
|
||||
git add "$DEST"
|
||||
git -c user.name="$(gh api user --jq '.name // .login')" \
|
||||
-c user.email="$(gh api user --jq .id)+$(gh api user --jq .login)@users.noreply.github.com" \
|
||||
commit -q -m "$PKG_ID version $VERSION"
|
||||
git push -q -u origin "$BRANCH" --force
|
||||
|
||||
echo "==> opening the PR"
|
||||
gh pr create --repo "$UPSTREAM" \
|
||||
--base "$DEFAULT_BRANCH" \
|
||||
--head "$(gh api user --jq .login):$BRANCH" \
|
||||
--title "$PKG_ID version $VERSION" \
|
||||
--body "Automated manifest sync for [Amethyst](https://github.com/vitorpamplona/amethyst) $VERSION.
|
||||
|
||||
- Installer: \`amethyst-desktop-$VERSION-windows-x64.msi\` from the official GitHub release
|
||||
- SHA256 verified against the published asset
|
||||
- ProductCode read from the MSI Property table
|
||||
|
||||
Manifests are generated from \`desktopApp/packaging/winget/\` in the upstream repo."
|
||||
|
||||
echo
|
||||
echo "Done. Watch the PR at https://github.com/$UPSTREAM/pulls"
|
||||
Reference in New Issue
Block a user