Skip to content

Commit 0dc9974

Browse files
scripts: re-vendor release harness (pipefail grep fix, nested helper signing)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2105a61 commit 0dc9974

1 file changed

Lines changed: 42 additions & 14 deletions

File tree

scripts/release-lib.sh

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@ rl_note() { printf ' %s\n' "$*"; }
7979
rl_step() { printf '==> %s\n' "$*"; }
8080
rl_skip() { RL_SKIPPED+=("$1"); printf ' skipped: %s\n' "$1"; }
8181

82+
# `producer | grep -q pattern` is a trap in this file, because `set -o pipefail`
83+
# is on: grep exits at the first match, the producer is killed by SIGPIPE, and
84+
# the pipeline reports that failure — so a SUCCESSFUL match returns non-zero.
85+
# It only bites when the producer is still writing when grep leaves, which is
86+
# why it looks intermittent: `file -b` on one binary is fine, `unzip -l` on a
87+
# 5,000-entry Electron bundle is not. That silently skipped the payload inside
88+
# WebLinked's launcher and produced an unsignable release.
89+
#
90+
# So: never pipe into grep -q here. Capture first, match against a here-string.
91+
rl_grep() { grep -qE -- "$1" <<<"$2"; } # rl_grep <ere> <text>
92+
rl_grepF() { grep -qF -- "$1" <<<"$2"; } # rl_grepF <literal> <text>
93+
rl_grepi() { grep -qiE -- "$1" <<<"$2"; } # rl_grepi <ere, case-insensitive> <text>
94+
8295
# NSIS wants a 4-part numeric version (1.2.3 -> 1.2.3.0) for VIProductVersion.
8396
rl_numver() {
8497
local v="${RL_VERSION%%-*}" n
@@ -400,10 +413,11 @@ rl_sign_file() { # rl_sign_file <path-to-exe-or-dll>
400413
# malformed signature is exactly the failure that stays invisible until a
401414
# user reports it weeks later.
402415
if command -v osslsigncode >/dev/null 2>&1; then
403-
if ! osslsigncode verify "$f" 2>&1 | grep -q 'Signature verification: ok'; then
416+
local vout; vout="$(osslsigncode verify "$f" 2>&1 || true)"
417+
if ! rl_grepF 'Signature verification: ok' "$vout"; then
404418
echo "signature did not verify: $f" >&2; return 1
405419
fi
406-
if ! osslsigncode verify "$f" 2>&1 | grep -qi 'timestamp'; then
420+
if ! rl_grepi 'timestamp' "$vout"; then
407421
echo "signed but NOT timestamped (expires in 72h): $f" >&2; return 1
408422
fi
409423
fi
@@ -813,25 +827,39 @@ rl_mac_sign() { # rl_mac_sign <path-to-.app-or-binary>
813827
# when APPLE_SIGNING_IDENTITY is exported. Do NOT re-sign: replacing the
814828
# signature changes the CDHash, and any DMG built from the earlier copy
815829
# would no longer be covered by this app's notarisation.
816-
if codesign --verify --strict --deep "$app" >/dev/null 2>&1 \
817-
&& codesign -dvv "$app" 2>&1 | grep -q "Authority=$RL_MAC_SIGN_IDENTITY" \
818-
&& codesign -d --verbose=2 "$app" 2>&1 | grep -q 'flags=.*runtime'; then
830+
# RL_MAC_FORCE_SIGN=1 bypasses this — for repairing something this identity
831+
# previously signed wrongly (a valid signature says nothing about whether
832+
# the entitlements inside it are right).
833+
local desc; desc="$(codesign -dvv "$app" 2>&1 || true)"
834+
if [[ "${RL_MAC_FORCE_SIGN:-0}" != "1" ]] \
835+
&& codesign --verify --strict --deep "$app" >/dev/null 2>&1 \
836+
&& rl_grepF "Authority=$RL_MAC_SIGN_IDENTITY" "$desc" \
837+
&& rl_grep 'flags=.*runtime' "$desc"; then
819838
RL_MAC_SIGNED_COUNT=$((RL_MAC_SIGNED_COUNT + 1))
820839
rl_note "already signed with this identity, left untouched"
821840
return 0
822841
fi
823842

824843
if [[ -d "$app" ]]; then
825-
# Nested code: everything executable or Mach-O shaped that is not the main
826-
# binary. file(1) is the arbiter — resources are not re-signed.
844+
# Nested .app bundles first (Electron/CEF helper apps), recursively and
845+
# WITH the same entitlements: a renderer helper without allow-jit means V8
846+
# aborts at launch — the app notarises fine and then crashes on first run,
847+
# which is strictly worse than the warning this replaces. The -prune below
848+
# keeps the flat passes out of these bundles so this signature survives.
849+
while IFS= read -r f; do
850+
rl_mac_sign "$f" || return 1
851+
done < <(find "$app/Contents" -mindepth 2 -type d -name '*.app' -prune 2>/dev/null | sort)
852+
# Nested loose code: everything executable or Mach-O shaped that is not the
853+
# main binary. file(1) is the arbiter — resources are not re-signed.
827854
while IFS= read -r f; do
828-
file -b "$f" 2>/dev/null | grep -q 'Mach-O' || continue
855+
rl_grep 'Mach-O' "$(file -b "$f" 2>/dev/null || true)" || continue
829856
"${sign[@]}" "$f" >/dev/null 2>&1 \
830857
|| { echo "codesign failed on nested $f" >&2; return 1; }
831-
done < <(find "$app/Contents" -type f \
832-
\( -perm -u+x -o -name '*.dylib' -o -name '*.so' \) \
833-
! -path "$app/Contents/MacOS/*" 2>/dev/null | sort)
834-
find "$app/Contents" -name '*.framework' -maxdepth 3 -type d 2>/dev/null \
858+
done < <(find "$app/Contents" -type d -name '*.app' -prune -o -type f \
859+
\( -perm -u+x -o -name '*.dylib' -o -name '*.so' -o -name '*.node' \) \
860+
! -path "$app/Contents/MacOS/*" -print 2>/dev/null | sort)
861+
find "$app/Contents" -maxdepth 3 -type d -name '*.app' -prune -o \
862+
-maxdepth 3 -type d -name '*.framework' -print 2>/dev/null \
835863
| while IFS= read -r f; do
836864
"${sign[@]}" "$f" >/dev/null 2>&1 \
837865
|| { echo "codesign failed on framework $f" >&2; exit 1; }
@@ -906,9 +934,9 @@ rl_mac_sign_tree() { # rl_mac_sign_tree <dir>
906934
rl_mac_sign_ready || return 0
907935
local f
908936
while IFS= read -r f; do
909-
file -b "$f" 2>/dev/null | grep -q 'Mach-O' || continue
937+
rl_grep 'Mach-O' "$(file -b "$f" 2>/dev/null || true)" || continue
910938
if codesign --verify --strict "$f" >/dev/null 2>&1 \
911-
&& codesign -dvv "$f" 2>&1 | grep -q 'Authority='; then
939+
&& rl_grepF 'Authority=' "$(codesign -dvv "$f" 2>&1 || true)"; then
912940
continue
913941
fi
914942
rl_mac_sign "$f" || return 1

0 commit comments

Comments
 (0)