From 1267b9177071a15a7171d475cf28fd35987941c1 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 19:53:15 -0400 Subject: [PATCH] fix(#346): extract signing cert SHA-256 by shape, not label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First live signing run (#345) failed at APK cert verification. The APK signed correctly, but the label parse (grep "Signer #1 certificate SHA-256 digest" | awk) returned empty on the runner's apksigner, whose output labels that line differently than the local build-tools. Empty != expected produced a false mismatch. The release correctly did NOT publish (fail-safe held). Now extracts by shape: the cert SHA-256 is the only 64-hex string in the output (SHA-1 is 40, MD5 is 32), so grep -ioE '[0-9a-f]{64}' is label-independent. Verified against a real signed APK (e7da8cd5…). Echoes the raw apksigner + keytool output for diagnosability, and hardens the AAB parse the same way (it was skipped last run, so its runner format is still unconfirmed — the echo will show it). Part of epic #312. Agent: SapphireCompass (session 8d755b5e) --- .github/workflows/release-signed.yml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-signed.yml b/.github/workflows/release-signed.yml index 5d6ae8b..f1eda06 100644 --- a/.github/workflows/release-signed.yml +++ b/.github/workflows/release-signed.yml @@ -118,11 +118,23 @@ jobs: APK=build/app/outputs/flutter-apk/app-release.apk BUILD_TOOLS=$(ls -d "${ANDROID_HOME}"/build-tools/* | sort -V | tail -1) CERTS=$("${BUILD_TOOLS}/apksigner" verify --print-certs "${APK}") + echo "--- apksigner --print-certs output ---" + echo "${CERTS}" + echo "--------------------------------------" if echo "${CERTS}" | grep -qi "CN=Android Debug"; then echo "::error::APK is DEBUG-SIGNED. key.properties not picked up; see #111." exit 1 fi - ACTUAL=$(echo "${CERTS}" | grep -i "Signer #1 certificate SHA-256 digest" | head -1 | awk -F': ' '{print $2}' | tr -d '[:space:]') + # Extract the fingerprint by shape, not by label: only the cert's + # SHA-256 digest is 64 hex chars (SHA-1 is 40, MD5 is 32), so this is + # unambiguous and independent of how a given apksigner version phrases + # the line. The label-based awk parse broke on the runner's apksigner + # (#345 first live run: label differed, ACTUAL came back empty). + ACTUAL=$(echo "${CERTS}" | grep -ioE '[0-9a-f]{64}' | head -1 | tr 'A-F' 'a-f') + if [ -z "${ACTUAL}" ]; then + echo "::error::Could not read a SHA-256 fingerprint from the APK (see output above)." + exit 1 + fi if [ "${ACTUAL}" != "${EXPECTED_SHA256}" ]; then echo "::error::Signing cert MISMATCH. expected ${EXPECTED_SHA256} actual ${ACTUAL}" exit 1 @@ -140,11 +152,15 @@ jobs: set -eu AAB=build/app/outputs/bundle/release/app-release.aab # keytool comes from the JDK that setup-java put on PATH in this job. + echo "--- keytool -printcert -jarfile output ---" + keytool -printcert -jarfile "${AAB}" | grep -iE 'Owner|SHA256' || true + echo "------------------------------------------" + # Strip the label, keep only hex (robust to spacing/case), lowercase. AAB_SHA=$(keytool -printcert -jarfile "${AAB}" \ | grep -i 'SHA256:' | head -1 \ - | sed 's/.*SHA256:[[:space:]]*//' | tr -d ': ' | tr 'A-F' 'a-f') + | sed 's/.*SHA256://' | tr -cd '0-9a-fA-F' | tr 'A-F' 'a-f') if [ -z "${AAB_SHA}" ]; then - echo "::error::Could not read a SHA-256 from the AAB signing cert." + echo "::error::Could not read a SHA-256 from the AAB signing cert (see output above)." exit 1 fi if [ "${AAB_SHA}" != "${EXPECTED_SHA256}" ]; then