diff --git a/README.md b/README.md index 106ce34..71bb4e2 100644 --- a/README.md +++ b/README.md @@ -55,21 +55,29 @@ Firefox signing uses `web-ext` and AMO credentials from an env file. The script requires `WEB_EXT_API_PROXY`; AMO upload and approval polling run through that proxy. -Create the signed XPI and publish it, together with `updates.json`, as the -current GitHub Release: +Create the signed XPI locally, without publishing it: ```bash -VERSTAK_BROWSER_ENV=/path/to/.env npm run publish:firefox +VERSTAK_BROWSER_ENV=/path/to/.env npm run release:firefox ``` -The first invocation publishes tag `v2.0.3` and bootstraps the -`releases/latest/download/updates.json` endpoint. Re-running the command -replaces those two release assets for the same tag. - -To sign and prepare release files locally without changing GitHub: +Publish the signed XPI and `updates.json` as the current GitHub Release: ```bash -VERSTAK_BROWSER_ENV=/home/mirivlad/git/verstak/.env npm run release:firefox +VERSTAK_BROWSER_ENV=/path/to/.env npm run publish:github +``` + +The publisher reads the version from `package.json`. It requires an +authenticated GitHub CLI, a clean local `main` equal to `origin/main`, and a +tag pointing at that commit. It creates and pushes the tag if needed, then +creates or updates the GitHub Release. Re-running it for the same tag replaces +the XPI and `updates.json` assets. `npm run publish:firefox` remains as a +compatible alias for the same Firefox publishing flow. + +For an explicit version check, pass the current tag after `--`: + +```bash +VERSTAK_BROWSER_ENV=/path/to/.env npm run publish:github -- v2.0.3 ``` Release output: diff --git a/package.json b/package.json index 1f7ccce..eb98c7e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "sign:firefox": "./scripts/sign-firefox-xpi.sh", "release:firefox": "./scripts/release-firefox-xpi.sh", "publish:firefox": "./scripts/publish-firefox-github-release.sh", + "publish:github": "./scripts/publish-github-release.sh", "release:package": "./scripts/package-release.sh" }, "devDependencies": { diff --git a/scripts/publish-firefox-github-release.sh b/scripts/publish-firefox-github-release.sh index c025060..cb51230 100755 --- a/scripts/publish-firefox-github-release.sh +++ b/scripts/publish-firefox-github-release.sh @@ -7,13 +7,29 @@ cd "$ROOT_DIR" REPOSITORY="mirivlad/verstak-browser-extension" SOURCE_DIR="${VERSTAK_FIREFOX_SOURCE_DIR:-dist/firefox}" RELEASE_DIR="${VERSTAK_FIREFOX_RELEASE_DIR:-release/firefox}" +GIT_BIN="${GIT_BIN:-git}" +GH_BIN="${GH_BIN:-gh}" -if ! command -v gh >/dev/null; then +if ! command -v "$GH_BIN" >/dev/null; then echo "ERROR: gh CLI is required to publish a GitHub Release" >&2 exit 1 fi +if [[ "$("$GIT_BIN" branch --show-current)" != "main" ]]; then + echo "ERROR: GitHub releases must be published from main" >&2 + exit 1 +fi +if [[ -n "$("$GIT_BIN" status --porcelain)" ]]; then + echo "ERROR: working tree must be clean before publishing a release" >&2 + exit 1 +fi -gh auth status +"$GH_BIN" auth status +"$GIT_BIN" fetch origin main --tags +HEAD="$("$GIT_BIN" rev-parse HEAD)" +if [[ "$HEAD" != "$("$GIT_BIN" rev-parse origin/main)" ]]; then + echo "ERROR: local main must match origin/main before publishing a release" >&2 + exit 1 +fi ./scripts/release-firefox-xpi.sh VERSION="$(node -e "console.log(require('./${SOURCE_DIR}/manifest.json').version)")" @@ -28,16 +44,26 @@ for artifact in "$XPI" "$UPDATES"; do fi done -if gh release view "$TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then - gh release upload "$TAG" "$XPI" "$UPDATES" --repo "$REPOSITORY" --clobber +if "$GIT_BIN" rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then + if [[ "$("$GIT_BIN" rev-parse "${TAG}^{commit}")" != "$HEAD" ]]; then + echo "ERROR: existing tag $TAG does not point at HEAD" >&2 + exit 1 + fi else - gh release create "$TAG" "$XPI" "$UPDATES" \ + "$GIT_BIN" tag -a "$TAG" -m "Release $TAG" + "$GIT_BIN" push origin "refs/tags/$TAG" +fi + +if "$GH_BIN" release view "$TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then + "$GH_BIN" release upload "$TAG" "$XPI" "$UPDATES" --repo "$REPOSITORY" --clobber +else + "$GH_BIN" release create "$TAG" "$XPI" "$UPDATES" \ --repo "$REPOSITORY" \ --title "Verstak Browser Extension $VERSION" \ --generate-notes \ --latest \ - --target "$(git rev-parse HEAD)" + --verify-tag fi echo "GitHub release:" -gh release view "$TAG" --repo "$REPOSITORY" --json url --jq .url +"$GH_BIN" release view "$TAG" --repo "$REPOSITORY" --json url --jq .url diff --git a/scripts/publish-github-release.sh b/scripts/publish-github-release.sh new file mode 100755 index 0000000..57a898e --- /dev/null +++ b/scripts/publish-github-release.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +VERSION="$(node -p "require('$ROOT/package.json').version")" + +if [[ $# -gt 1 || ( $# -eq 1 && "$1" != "v$VERSION" && "$1" != "$VERSION" ) ]]; then + echo "usage: $0 [v$VERSION]" >&2 + exit 2 +fi + +exec "$ROOT/scripts/publish-firefox-github-release.sh" diff --git a/scripts/test-firefox-github-release.js b/scripts/test-firefox-github-release.js index 83d622e..7869574 100644 --- a/scripts/test-firefox-github-release.js +++ b/scripts/test-firefox-github-release.js @@ -35,10 +35,16 @@ assert.equal( ); const publisher = fs.readFileSync('scripts/publish-firefox-github-release.sh', 'utf8'); -assert.match(publisher, /gh auth status/); -assert.match(publisher, /gh release create/); -assert.match(publisher, /gh release upload/); +assert.match(publisher, /auth status/); +assert.match(publisher, /branch --show-current/); +assert.match(publisher, /tag -a/); +assert.match(publisher, /push origin/); +assert.match(publisher, /release create/); +assert.match(publisher, /release upload/); assert.match(publisher, /--clobber/); assert.match(publisher, /--latest/); +const genericPublisher = fs.readFileSync('scripts/publish-github-release.sh', 'utf8'); +assert.match(genericPublisher, /publish-firefox-github-release\.sh/); + console.log('Firefox GitHub release metadata tests passed');