From 09f6b5d22297ed933bc75d2e426a72194f4a85c0 Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Sat, 8 Aug 2026 15:31:21 -0400 Subject: [PATCH] build: add deploy-ext.sh release packaging script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preflight (clean tree, typecheck, tests), package.json/manifest version agreement, and a requirement that tag v already exists at HEAD — version decisions and tagging live outside this script. Then a clean build + verify:dist and a zip of dist/ contents with sanity checks. Co-Authored-By: Claude Fable 5 --- .gitignore | 1 + deploy-ext.sh | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100755 deploy-ext.sh diff --git a/.gitignore b/.gitignore index e2510fc..93d6c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ dist/ +release/ .astro/ *.pem .env diff --git a/deploy-ext.sh b/deploy-ext.sh new file mode 100755 index 0000000..b53c954 --- /dev/null +++ b/deploy-ext.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# Package a store-uploadable release of the extension: +# preflight checks, clean build, zip of dist/ contents. Requires the release +# tag v to already exist at HEAD — version decisions and tagging +# live outside this script. Nothing is pushed or uploaded; that is the +# human's step. +set -euo pipefail + +cd "$(dirname "$0")" + +die() { + echo "deploy-ext: error: $*" >&2 + exit 1 +} + +# 1. Preflight: clean tree, typecheck, tests. +if [[ -n "$(git status --porcelain)" ]]; then + git status --short >&2 + die "working tree is dirty; commit or stash before releasing" +fi +npm run check +npm test + +# 2. Version: this script makes no version decisions. It requires that +# package.json and the manifest agree, and that the release tag for that +# version already exists and points at HEAD. +version="$(node -p "JSON.parse(require('fs').readFileSync('public/manifest.json','utf8')).version")" +pkg_version="$(node -p "JSON.parse(require('fs').readFileSync('package.json','utf8')).version")" +[[ -n "$version" && "$version" != "undefined" ]] || die "could not read version from public/manifest.json" +if [[ "$version" != "$pkg_version" ]]; then + die "version drift: public/manifest.json has $version but package.json has $pkg_version — align them by hand (this script does not auto-stamp)" +fi + +tag="v$version" +if ! git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then + die "no tag $tag — create the release tag first (version decisions live outside this script)" +fi +if [[ "$(git rev-parse "$tag^{commit}")" != "$(git rev-parse HEAD)" ]]; then + die "tag $tag does not point at HEAD — packaging this tree would mislabel the artifact" +fi + +# 3. Clean production build: no watch-mode leftovers. +rm -rf dist +npm run build +npm run verify:dist + +# 4. Zip the CONTENTS of dist/ at the archive root (Chrome Web Store wants +# manifest.json at the top level, not nested in a dist/ folder). The manifest +# keeps its "key" field: the first CWS upload needs it to preserve the pinned +# extension ID. +mkdir -p release +zipfile="release/substandard-$tag.zip" +rm -f "$zipfile" +abs_zipfile="$PWD/$zipfile" +if command -v zip >/dev/null; then + (cd dist && zip -q -r -X "$abs_zipfile" .) +else + # Fallback: python3's zipfile module, no extra dependencies. + python3 - "$abs_zipfile" <<'PY' +import os, sys, zipfile +out = sys.argv[1] +with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z: + for root, dirs, files in os.walk("dist"): + for f in files: + path = os.path.join(root, f) + z.write(path, os.path.relpath(path, "dist")) +PY +fi + +# Sanity-check the archive: manifest at the root, no private key, +# no dotfiles, no sourcemaps. +entries="$(python3 - "$abs_zipfile" <<'PY' +import sys, zipfile +print("\n".join(zipfile.ZipFile(sys.argv[1]).namelist())) +PY +)" +grep -qx 'manifest.json' <<<"$entries" || die "$zipfile has no manifest.json at its root" +! grep -q 'key\.pem' <<<"$entries" || die "$zipfile contains key.pem — never ship the private key" +! grep -Eq '(^|/)\.' <<<"$entries" || die "$zipfile contains dotfiles" +! grep -q '\.map$' <<<"$entries" || die "$zipfile contains sourcemaps" + +echo +echo "deploy-ext: done" +echo " artifact: $zipfile" +echo " tag: $tag (at HEAD)" +echo "Next steps (yours, not this script's):" +echo " - if $tag is not pushed yet: git push origin $tag" +echo " - upload $zipfile to the Chrome Web Store developer dashboard" -- 2.51.2