diff --git a/bin/release-check.sh b/bin/release-check.sh new file mode 100755 index 0000000..7cc974f --- /dev/null +++ b/bin/release-check.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Checks a published release the way a customer site sees it: no token, only public URLs. +# +# bin/release-check.sh [version] default: Plugin::VERSION +# +# Fails unless releases/latest is that version, ZIP and .sig are public downloads below the repository's release +# URL (the only place GiteaUpdater accepts a package from), and the downloaded ZIP passes the signature check against +# the key built into the plugin. +# +# Environment: LIT_MCP_GITEA (default https://git.loosen-it.de), only to try the script against another server. +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$root" + +gitea="${LIT_MCP_GITEA:-https://git.loosen-it.de}" +repo="janniclas.loosen/WP-LIT-MCP" +version="${1:-$(sed -n "s/.*const VERSION = '\(.*\)';/\1/p" src/Plugin.php)}" +name="wp-lit-mcp-$version.zip" + +fail() { echo "ABBRUCH: $*" >&2; exit 1; } + +latest="$(curl -fsS "$gitea/api/v1/repos/$repo/releases/latest")" \ + || fail "releases/latest ist ohne Anmeldung nicht abrufbar (noch kein veröffentlichtes Release? Repo nicht öffentlich?)" +tag="$(jq -r .tag_name <<<"$latest")" +[[ "$tag" == "v$version" ]] || fail "releases/latest liefert '$tag', erwartet v$version" + +asset_url() { jq -r --arg n "$1" '.assets[] | select(.name == $n) | .browser_download_url' <<<"$latest"; } +zip_url="$(asset_url "$name")" +sig_url="$(asset_url "$name.sig")" +for url in "$zip_url" "$sig_url"; do + [[ "$url" == "$gitea/$repo/releases/download/"* ]] || fail "Asset-URL fehlt oder liegt woanders: '$url'" +done + +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +curl -fsSL "$zip_url" -o "$work/$name" || fail "ZIP nicht ohne Anmeldung ladbar: $zip_url" +curl -fsSL "$sig_url" -o "$work/$name.sig" || fail "Signatur nicht ohne Anmeldung ladbar: $sig_url" +php bin/release-sign.php verify "$work/$name" "$work/$name.sig" >/dev/null \ + || fail "Das heruntergeladene Release besteht die Signaturprüfung nicht" + +echo "Release v$version in Ordnung: öffentlich ladbar, Signatur gültig" diff --git a/bin/release.sh b/bin/release.sh index 49e9917..5236736 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash -# Builds and signs a WP-LIT-MCP release from the committed state; with --publish it also tags and publishes it. +# Builds and signs a WP-LIT-MCP release from the committed state. It publishes nothing: tag, Gitea release and the +# upload of the two files in dist/ are done afterwards, then bin/release-check.sh looks at the result. # -# bin/release.sh dry run: checks, tests, build, signature, ZIP checks – publishes nothing -# bin/release.sh --publish additionally: git tag + push, Gitea release with ZIP and .sig, check of the result +# bin/release.sh # -# Needs: bsdtar, composer, curl, jq, php (sodium). For --publish a Gitea token with write access to the repository in -# $GITEA_TOKEN. The signing key (php bin/release-sign.php keygen) lives outside the repository. +# Needs: bsdtar, composer, php (sodium). The signing key (php bin/release-sign.php keygen) lives outside the +# repository. # # Environment: LIT_MCP_RELEASE_KEY (default ~/.config/wp-lit-mcp/release.key), LIT_MCP_DIVI_CHECK (command that must # succeed before a release, default: ddev wp --user=janniclas lit-mcp divi-check). @@ -14,12 +14,8 @@ set -euo pipefail root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$root" -repo_url="https://git.loosen-it.de/janniclas.loosen/WP-LIT-MCP" -api="https://git.loosen-it.de/api/v1/repos/janniclas.loosen/WP-LIT-MCP" key="${LIT_MCP_RELEASE_KEY:-$HOME/.config/wp-lit-mcp/release.key}" divi_check="${LIT_MCP_DIVI_CHECK:-ddev wp --user=janniclas lit-mcp divi-check}" -publish=0 -[[ "${1:-}" == "--publish" ]] && publish=1 fail() { echo "ABBRUCH: $*" >&2; exit 1; } step() { echo "==> $*"; } @@ -34,9 +30,6 @@ grep -q "^= $version =\$" readme.txt || fail "readme.txt hat keinen Changelog-Ei [[ -z "$(git status --porcelain)" ]] || fail "Arbeitsbaum nicht sauber: erst committen, das ZIP entsteht aus HEAD" ! git rev-parse -q --verify "refs/tags/v$version" >/dev/null || fail "Tag v$version existiert schon" [[ -r "$key" ]] || fail "Signierschlüssel fehlt: $key (php bin/release-sign.php keygen )" -if [[ $publish -eq 1 ]]; then - [[ -n "${GITEA_TOKEN:-}" ]] || fail "GITEA_TOKEN ist nicht gesetzt" -fi # --- 2. Tests and the Divi check must be green ------------------------------------------------------------------------ step "Tests" @@ -73,43 +66,9 @@ fi # --- 5. Sign, and prove the signature against the key built into the plugin ------------------------------------------- step "Signieren" php bin/release-sign.php sign "$zip" "$key" > "$zip.sig" -php bin/release-sign.php verify "$zip" "$zip.sig" >/dev/null || fail "Signatur passt nicht zum Schlüssel im Plugin (PackageSignature::RELEASE_KEY)" +php bin/release-sign.php verify "$zip" "$zip.sig" >/dev/null \ + || fail "Signatur passt nicht zum Schlüssel im Plugin (PackageSignature::RELEASE_KEY)" -if [[ $publish -eq 0 ]]; then - echo "Probelauf in Ordnung: dist/$name und dist/$name.sig" - echo "Veröffentlichen: GITEA_TOKEN=… bin/release.sh --publish" - exit 0 -fi - -# --- 6. Publish --------------------------------------------------------------------------------------------------------- -step "Tag v$version setzen und pushen" -git tag -a "v$version" -m "WP-LIT-MCP $version" -git push origin "v$version" - -step "Release auf Gitea anlegen" -auth=(-H "Authorization: token $GITEA_TOKEN") -notes="$(awk -v header="= $version =" '$0 == header { on = 1; next } /^= / { on = 0 } on' readme.txt)" -release_id="$(curl -fsS "${auth[@]}" -H 'Content-Type: application/json' \ - -d "$(jq -n --arg tag "v$version" --arg body "$notes" '{tag_name: $tag, name: $tag, body: $body}')" \ - "$api/releases" | jq -r .id)" -for file in "$zip" "$zip.sig"; do - curl -fsS "${auth[@]}" -F "attachment=@$file" "$api/releases/$release_id/assets?name=$(basename "$file")" >/dev/null -done - -# --- 7. Check what a customer site will see: latest release, public asset URLs, a signature that verifies ------------ -step "Ergebnis prüfen (ohne Token, wie eine Kundenseite)" -latest="$(curl -fsS "$api/releases/latest")" -[[ "$(jq -r .tag_name <<<"$latest")" == "v$version" ]] || fail "releases/latest liefert nicht v$version" -asset_url() { jq -r --arg n "$1" '.assets[] | select(.name == $n) | .browser_download_url' <<<"$latest"; } -zip_url="$(asset_url "$name")" -sig_url="$(asset_url "$name.sig")" -for url in "$zip_url" "$sig_url"; do - [[ "$url" == "$repo_url/releases/download/"* ]] || fail "Asset-URL nicht wie erwartet: '$url'" -done -mkdir "$work/check" -curl -fsSL "$zip_url" -o "$work/check/$name" -curl -fsSL "$sig_url" -o "$work/check/$name.sig" -php bin/release-sign.php verify "$work/check/$name" "$work/check/$name.sig" >/dev/null \ - || fail "Heruntergeladenes Release besteht die Signaturprüfung nicht" - -echo "Release v$version veröffentlicht: $repo_url/releases/tag/v$version" +echo "In Ordnung: dist/$name und dist/$name.sig" +echo "Weiter: Tag v$version und Release auf Gitea anlegen, beide Dateien hochladen, veröffentlichen." +echo "Danach: bin/release-check.sh $version"