From 438b48fb33a23ea4246f9ecb7a973cdc7ac0aa10 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 11 Nov 2026 13:15:40 +0100 Subject: [PATCH 1/6] Build TypeScript declarations before packing the npm package package.json points "types" to types/ece.d.ts, which is generated by tsc and gitignored, so nothing guaranteed it existed when publishing. --- nodejs/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/nodejs/package.json b/nodejs/package.json index 4004c63..7583c26 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -29,6 +29,7 @@ }, "scripts": { "build": "tsc", + "prepack": "tsc", "typecheck": "tsc --noEmit", "test": "node ./test.js", "test:verbose": "node ./test.js dump verbose" From b18d769e3e43baff3ebb895f8954c2d79369a3c2 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 11 Nov 2026 13:15:55 +0100 Subject: [PATCH 2/6] Run build and twine through uvx when available The script required the build and twine packages to be installed in the system Python, and failed with "No module named build" otherwise. --- release.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/release.sh b/release.sh index c4e281e..5b83c16 100755 --- a/release.sh +++ b/release.sh @@ -32,8 +32,17 @@ case "$1" in exit 2 esac +# Run Python tools through uvx when available, so they don't need to be installed. +if command -v uvx >/dev/null; then + pybuild() { uvx --from build pyproject-build "$@"; } + twine() { uvx twine "$@"; } +else + pybuild() { python3 -m build "$@"; } + twine() { python3 -m twine "$@"; } +fi + pushd "$root"/python -python -m build --sdist --wheel +pybuild --sdist --wheel twine upload dist/http_ece-"$new".tar.gz dist/http_ece-"$new"-*.whl popd From f61913fe6c0e70291f4b77e456a0791912a23518 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 11 Nov 2026 13:15:56 +0100 Subject: [PATCH 3/6] Push the release to the canonical remote, including the version commit The tag was always pushed to "origin", which may be a personal fork, and the version bump commit was never pushed at all. Push both to the remote pointing at web-push-libs (overridable with RELEASE_REMOTE). --- release.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/release.sh b/release.sh index 5b83c16..9743cb0 100755 --- a/release.sh +++ b/release.sh @@ -5,6 +5,10 @@ set -e root=$(cd $(dirname "$0"); pwd -P) cd "$root" python_metadata=python/pyproject.toml + +# Push to the canonical repository, not a personal fork. +remote=${RELEASE_REMOTE:-$(git remote -v | awk '/web-push-libs\/encrypted-content-encoding/ { print $1; exit }')} +remote=${remote:-origin} sub='{ s/^.*["'"'"']\([0-9]*\.[0-9]*\.[0-9]*\)["'"'"'].*$/\1/;p; }' old1=$(sed -n -e '/version *=/'"$sub"'' "$python_metadata") old2=$(sed -n -e '/"version" *:/'"$sub"'' nodejs/package.json) @@ -52,6 +56,7 @@ popd if [[ "$1" == "+" ]]; then git commit -m "Update version to $new" "$python_metadata" nodejs/package.json + git push "$remote" HEAD fi git tag -a v"$new" -m "Release version $new" -git push origin v"$new" +git push "$remote" v"$new" From a6c6be8bab04aea20625ccd1648c65909809099e Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 11 Nov 2026 13:15:56 +0100 Subject: [PATCH 4/6] Update package-lock.json when bumping the version --- release.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release.sh b/release.sh index 9743cb0..ff6afd0 100755 --- a/release.sh +++ b/release.sh @@ -25,6 +25,7 @@ case "$1" in sub='s/\(["'"'"']\)'"$old1"'["'"'"']/\1'"$new"'\1/' sed -i~ -e '/version *=/'"$sub" "$python_metadata" sed -i~ -e '/"version" *:/'"$sub" nodejs/package.json + (cd nodejs && npm install --package-lock-only --ignore-scripts >/dev/null) ;; =) @@ -55,7 +56,7 @@ npm publish popd if [[ "$1" == "+" ]]; then - git commit -m "Update version to $new" "$python_metadata" nodejs/package.json + git commit -m "Update version to $new" "$python_metadata" nodejs/package.json nodejs/package-lock.json git push "$remote" HEAD fi git tag -a v"$new" -m "Release version $new" From 8e15bfa9a3689549624d38af46cfdb1fb1cc7b3a Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 11 Nov 2026 13:16:04 +0100 Subject: [PATCH 5/6] Check release prerequisites before publishing anything Previously a failure (e.g. not being logged in to npm) could happen after the PyPI upload, leaving a half-done release. Now the script verifies up front that the working tree is clean, the tag doesn't exist yet and npm is logged in, and only then bumps the version. Also enable strict mode (set -euo pipefail), quote paths, split the version with read instead of a global IFS change, anchor the pyproject version match and stop leaving "~" backup files behind. --- release.sh | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/release.sh b/release.sh index ff6afd0..7d73db8 100755 --- a/release.sh +++ b/release.sh @@ -1,33 +1,29 @@ #!/usr/bin/env bash -set -e +set -euo pipefail -root=$(cd $(dirname "$0"); pwd -P) +root=$(cd "$(dirname "$0")"; pwd -P) cd "$root" python_metadata=python/pyproject.toml +node_metadata=nodejs/package.json # Push to the canonical repository, not a personal fork. remote=${RELEASE_REMOTE:-$(git remote -v | awk '/web-push-libs\/encrypted-content-encoding/ { print $1; exit }')} remote=${remote:-origin} + sub='{ s/^.*["'"'"']\([0-9]*\.[0-9]*\.[0-9]*\)["'"'"'].*$/\1/;p; }' -old1=$(sed -n -e '/version *=/'"$sub"'' "$python_metadata") -old2=$(sed -n -e '/"version" *:/'"$sub"'' nodejs/package.json) +old1=$(sed -n -e '/^version *=/'"$sub"'' "$python_metadata") +old2=$(sed -n -e '/"version" *:/'"$sub"'' "$node_metadata") if [[ "$old1" != "$old2" ]]; then echo "Versions aren't the same: $old1 != $old2" 1>&2 exit 1 fi -case "$1" in +case "${1:-}" in +) - IFS=. v=($old1) - new="${v[0]}.${v[1]}.$((${v[2]} + 1))" - - sub='s/\(["'"'"']\)'"$old1"'["'"'"']/\1'"$new"'\1/' - sed -i~ -e '/version *=/'"$sub" "$python_metadata" - sed -i~ -e '/"version" *:/'"$sub" nodejs/package.json - (cd nodejs && npm install --package-lock-only --ignore-scripts >/dev/null) + IFS=. read -r -a v <<< "$old1" + new="${v[0]}.${v[1]}.$((v[2] + 1))" ;; - =) new="$old1" ;; @@ -46,6 +42,29 @@ else twine() { python3 -m twine "$@"; } fi +# Pre-flight checks, before anything irreversible happens. +if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then + echo "Working tree has uncommitted changes" 1>&2 + exit 1 +fi +if git rev-parse -q --verify "refs/tags/v$new" >/dev/null || + git ls-remote --exit-code --tags "$remote" "refs/tags/v$new" >/dev/null; then + echo "Tag v$new already exists" 1>&2 + exit 1 +fi +if ! npm whoami >/dev/null 2>&1; then + echo "Not logged in to npm; run 'npm login' first" 1>&2 + exit 1 +fi +echo "Releasing $new (pushing to remote '$remote')" + +if [[ "$1" == "+" ]]; then + sub='s/\(["'"'"']\)'"$old1"'["'"'"']/\1'"$new"'\1/' + sed -i -e '/^version *=/'"$sub" "$python_metadata" + sed -i -e '/"version" *:/'"$sub" "$node_metadata" + (cd nodejs && npm install --package-lock-only --ignore-scripts >/dev/null) +fi + pushd "$root"/python pybuild --sdist --wheel twine upload dist/http_ece-"$new".tar.gz dist/http_ece-"$new"-*.whl @@ -56,7 +75,7 @@ npm publish popd if [[ "$1" == "+" ]]; then - git commit -m "Update version to $new" "$python_metadata" nodejs/package.json nodejs/package-lock.json + git commit -m "Update version to $new" "$python_metadata" "$node_metadata" nodejs/package-lock.json git push "$remote" HEAD fi git tag -a v"$new" -m "Release version $new" From ae859a8c9b0fff6fcc8d61163b6bbe64cb2323c1 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Wed, 11 Nov 2026 13:16:04 +0100 Subject: [PATCH 6/6] Validate both packages before uploading either of them Remove stale dist/ and build/ output, run npm ci, the test suite and npm pack (which builds the TypeScript declarations), and validate the Python distributions with twine check. All of this happens before the first upload, so a local failure can't leave a partially published release. If a check fails after the version bump, the bump is reverted so the release can be retried from a clean tree. --- release.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/release.sh b/release.sh index 7d73db8..940cac7 100755 --- a/release.sh +++ b/release.sh @@ -59,20 +59,31 @@ fi echo "Releasing $new (pushing to remote '$remote')" if [[ "$1" == "+" ]]; then + # Undo the version bump if anything fails before publishing starts, + # so the release can be retried from a clean tree. + trap 'git checkout -- "$python_metadata" "$node_metadata" nodejs/package-lock.json' EXIT sub='s/\(["'"'"']\)'"$old1"'["'"'"']/\1'"$new"'\1/' sed -i -e '/^version *=/'"$sub" "$python_metadata" sed -i -e '/"version" *:/'"$sub" "$node_metadata" (cd nodejs && npm install --package-lock-only --ignore-scripts >/dev/null) fi +# Build and validate both packages before publishing either of them. +pushd "$root"/nodejs +npm ci +npm test +npm pack --dry-run # "prepack" builds the TypeScript declarations +popd + pushd "$root"/python +rm -rf dist build pybuild --sdist --wheel -twine upload dist/http_ece-"$new".tar.gz dist/http_ece-"$new"-*.whl +twine check dist/* popd -pushd "$root"/nodejs -npm publish -popd +trap - EXIT +twine upload python/dist/http_ece-"$new".tar.gz python/dist/http_ece-"$new"-*.whl +(cd nodejs && npm publish) if [[ "$1" == "+" ]]; then git commit -m "Update version to $new" "$python_metadata" "$node_metadata" nodejs/package-lock.json