From 1094afce11486f6c8cd80476021535ac4b35edfe Mon Sep 17 00:00:00 2001 From: Aaron Bull Schaefer Date: Tue, 10 Feb 2026 07:44:36 -0800 Subject: [PATCH 1/2] Update verify-checksums to handle multiple patterns --- README.md | 2 +- bin/common/verify-checksums | 131 +++++++++++++++++------------------- 2 files changed, 64 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index f1307cb..26b1a42 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ General-purpose scripts intended for use by any repository: - `create-checksums` – Generate `sha256sums.txt` for release assets - `screenshot-mode` – Apply or reset canonical iOS/Android screenshot state - `tag-if-missing` – Create and push a missing git tag (SemVer aware) -- `verify-checksums` – Verify an asset checksum and emit CI outputs +- `verify-checksums` – Verify asset checksums for one or more patterns ### Flutter diff --git a/bin/common/verify-checksums b/bin/common/verify-checksums index b6c6fba..b590f43 100755 --- a/bin/common/verify-checksums +++ b/bin/common/verify-checksums @@ -4,21 +4,15 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - verify-checksums --pattern [--dir ] [--checksums ] [--output-prefix ] + verify-checksums [--dir ] [--pattern ...] -Verify a single asset's SHA-256 checksum against a checksums file. +Verify asset SHA-256 checksums against sha256sums.txt. Options: - -p, --pattern Glob pattern to match exactly one asset file (required) - -d, --dir Directory containing the assets (default: dist) - -c, --checksums Checksums file name (default: sha256sums.txt) - -o, --output-prefix Output prefix for CI (default: asset) + -d, --dir Directory containing the assets (default: dist) + -p, --pattern Only verify files matching (default: *) + May be specified multiple times. -h, --help Show this help message. - -Notes: - In CI (when GITHUB_OUTPUT is set), emits: - - _filename - - _path USAGE } @@ -27,20 +21,10 @@ die() { exit 1 } -emit_github_output() { - local key="$1" - local value="$2" - - if [[ -n ${GITHUB_OUTPUT:-} ]]; then - printf '%s=%s\n' "$key" "$value" >>"$GITHUB_OUTPUT" - fi -} - # Defaults (populated/overridden by parse_args) dist_dir="dist" -pattern="" +patterns=() checksums_file="sha256sums.txt" -output_prefix="asset" parse_args() { while (($#)); do @@ -54,18 +38,7 @@ parse_args() { shift pattern="${1-}" [[ -n $pattern ]] || die "--pattern requires a value" - ;; - -c | --checksums) - shift - checksums_file="${1-}" - [[ -n $checksums_file ]] || die "--checksums requires a value" - ;; - -o | --output-prefix) - shift - output_prefix="${1-}" - [[ -n $output_prefix ]] || die "--output-prefix requires a value" - [[ $output_prefix =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || - die "--output-prefix must match ^[A-Za-z_][A-Za-z0-9_]*$" + patterns+=("$pattern") ;; -h | --help) usage @@ -78,37 +51,60 @@ parse_args() { } validate_inputs() { - [[ -n $pattern ]] || { - usage - die "--pattern is required" - } + if ((${#patterns[@]} == 0)); then + patterns=("*") + fi [[ -d $dist_dir ]] || die "Dist directory not found: $dist_dir" [[ -f "$dist_dir/$checksums_file" ]] || die "$checksums_file not found in $dist_dir" } # Outputs: -# sets global ASSET_PATH -resolve_single_asset() { - ASSET_PATH="" - local -a files=() - +# sets global ASSET_PATHS +# sets global ASSET_FILENAMES +collect_assets() { shopt -s nullglob - # shellcheck disable=SC2206 # Intentional: allow glob expansion from user-provided --pattern. - files=("$dist_dir"/$pattern) + local pattern + for pattern in "${patterns[@]}"; do + local -a pattern_matches=() + # shellcheck disable=SC2206 # Intentional: allow glob expansion from user-provided --pattern. + pattern_matches=("$dist_dir"/$pattern) + + if ((${#pattern_matches[@]} == 0)); then + die "No files matched pattern: $pattern" + fi + + local matched_any=0 + local f base + for f in "${pattern_matches[@]}"; do + [[ -f $f ]] || continue + base="${f##*/}" + [[ $base == "$checksums_file" ]] && continue + matched_any=1 + add_unique_asset "$f" + done + + if ((matched_any == 0)); then + die "No files matched pattern (after excluding $checksums_file): $pattern" + fi + done shopt -u nullglob - if ((${#files[@]} == 0)); then - die "No files matched pattern: $pattern" - fi + ((${#ASSET_PATHS[@]} > 0)) || die "No assets matched patterns" +} - if ((${#files[@]} != 1)); then - printf "ERROR: Expected exactly 1 file for pattern %s, found %s.\n" "$pattern" "${#files[@]}" >&2 - printf '%s\n' "${files[@]}" >&2 - exit 1 +add_unique_asset() { + local path="$1" + local existing + + if ((${#ASSET_PATHS[@]} > 0)); then + for existing in "${ASSET_PATHS[@]}"; do + [[ $existing == "$path" ]] && return 0 + done fi - ASSET_PATH="${files[0]}" + ASSET_PATHS+=("$path") + ASSET_FILENAMES+=("${path##*/}") } # Outputs: @@ -120,15 +116,15 @@ lookup_checksum_line() { local asset_filename="$1" local line="" - local matches=0 + local match_count=0 while IFS= read -r line; do # Match the common "HASH filename" format used by sha256sum, # and the binary mode "HASH *filename" format. case "$line" in *" $asset_filename" | *" *$asset_filename") - matches=$((matches + 1)) - if ((matches == 1)); then + match_count=$((match_count + 1)) + if ((match_count == 1)); then CHECKSUM_LINE="$line" else # Defer error output until after we've collected enough to know it's multiple. @@ -138,11 +134,11 @@ lookup_checksum_line() { esac done <"$checksums_path" - if ((matches == 0)); then + if ((match_count == 0)); then die "No checksum entry found for $asset_filename in $checksums_file" fi - if ((matches != 1)); then + if ((match_count != 1)); then printf "ERROR: Multiple checksum entries found for %s in %s.\n" "$asset_filename" "$checksums_file" >&2 while IFS= read -r line; do case "$line" in @@ -169,18 +165,17 @@ main() { parse_args "$@" validate_inputs - resolve_single_asset - - local asset_filename - asset_filename="${ASSET_PATH##*/}" # filename only + ASSET_PATHS=() + ASSET_FILENAMES=() + collect_assets - lookup_checksum_line "$asset_filename" - verify_checksum_line "$CHECKSUM_LINE" + local i asset_filename + for i in "${!ASSET_PATHS[@]}"; do + asset_filename="${ASSET_FILENAMES[$i]}" + lookup_checksum_line "$asset_filename" + verify_checksum_line "$CHECKSUM_LINE" + done - if [[ -n ${GITHUB_OUTPUT:-} ]]; then - emit_github_output "${output_prefix}_filename" "$asset_filename" - emit_github_output "${output_prefix}_path" "$dist_dir/$asset_filename" - fi } main "$@" From 7fa85a199d29c9d1a87623d773a73c9705f1a41e Mon Sep 17 00:00:00 2001 From: Aaron Bull Schaefer Date: Tue, 10 Feb 2026 08:14:56 -0800 Subject: [PATCH 2/2] Standardize format for usage placeholder values --- bin/common/bump-calver | 9 +++++---- bin/common/bump-changelog-version | 3 ++- bin/common/bump-semver | 16 ++++++++-------- bin/common/create-checksums | 4 ++-- bin/common/screenshot-mode | 19 ++++++++++--------- bin/common/tag-if-missing | 3 ++- bin/flutter/android-signing-setup | 8 +++++--- bin/flutter/asc-auth-key-setup | 7 ++++--- bin/flutter/ios-signing-setup | 3 ++- bin/flutter/set-project-version | 6 +++--- bin/flutter/stage-release-assets | 6 ++++-- bin/media/diagmerge | 14 +++++++++----- bin/media/shotproc | 11 +++++++---- 13 files changed, 63 insertions(+), 46 deletions(-) diff --git a/bin/common/bump-calver b/bin/common/bump-calver index 540c5ca..d18d2e5 100755 --- a/bin/common/bump-calver +++ b/bin/common/bump-calver @@ -4,16 +4,17 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - bump-calver [--format FMT] [DATE] + bump-calver [--format ] [] -Print a CalVer for a date (UTC). Reads DATE from stdin or arg; if empty, uses today. +Print a CalVer for a date (UTC). +Reads date from stdin or arg; if empty, uses today. Accepted DATE forms: YYYY-MM-DD, YYYY.MM.DD, YYYY/MM/DD, YYYYMMDD Options: - --format FMT Override output format (default: YYYY.MM.DD) - -h, --help Show this help message. + --format Override output format (default: YYYY.MM.DD) + -h, --help Show this help message. Tokens: YYYY Full year (e.g. 2006, 2016, 2106) diff --git a/bin/common/bump-changelog-version b/bin/common/bump-changelog-version index d7c002e..4136566 100755 --- a/bin/common/bump-changelog-version +++ b/bin/common/bump-changelog-version @@ -8,7 +8,8 @@ Usage: get-project-version | bump-changelog-version Update CHANGELOG.md (Keep a Changelog style): -- Move "## [Unreleased]" to "## [] - " +- Move "## [Unreleased]" to + "## [] - " - Insert a fresh "## [Unreleased]" above it - Update reference links for [Unreleased] and [] USAGE diff --git a/bin/common/bump-semver b/bin/common/bump-semver index 89c7ceb..48bb9b2 100755 --- a/bin/common/bump-semver +++ b/bin/common/bump-semver @@ -4,18 +4,18 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - bump-semver (--major | --minor | --patch | --set X.Y.Z) - [--prerelease ID] [--build META] - [VERSION] + bump-semver (--major | --minor | --patch | --set ) + [--prerelease ] [--build ] + [] -Bump or set a SemVer version. Reads VERSION from stdin or arg. +Bump or set a SemVer version. Reads version from stdin or arg. Options: --major | --minor | --patch Increment core version - --set X.Y.Z Set core version - --prerelease ID Append prerelease (e.g. rc.1) - --build META Append build metadata - -h, --help Show this help message. + --set Set core version + --prerelease Append prerelease (e.g. rc.1) + --build Append build metadata + -h, --help Show this help message. USAGE } diff --git a/bin/common/create-checksums b/bin/common/create-checksums index fb13322..e51b949 100755 --- a/bin/common/create-checksums +++ b/bin/common/create-checksums @@ -9,8 +9,8 @@ Usage: Create sha256sums.txt for all regular files in a directory. Options: - -d, --dir Directory containing the assets (default: dist) - -h, --help Show this help message. + -d, --dir Directory containing the assets (default: dist) + -h, --help Show this help message. USAGE } diff --git a/bin/common/screenshot-mode b/bin/common/screenshot-mode index 6286222..8df944e 100755 --- a/bin/common/screenshot-mode +++ b/bin/common/screenshot-mode @@ -4,19 +4,20 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - screenshot-mode apply --platform ios [--device DEVICE] [--dry-run] - screenshot-mode reset --platform ios [--device DEVICE] [--dry-run] - screenshot-mode apply --platform android [--serial SERIAL] [--dry-run] - screenshot-mode reset --platform android [--serial SERIAL] [--dry-run] + screenshot-mode apply --platform + [--device | --serial ] [--dry-run] + screenshot-mode reset --platform + [--device | --serial ] [--dry-run] screenshot-mode (-h|--help) Canonical screenshot mode for iOS Simulator and Android emulators. Options: - -p, --platform PLATFORM One of: ios, android - -n, --dry-run Print the command(s) without running them - --device DEVICE iOS only. simctl device selector (default: booted) - -s, --serial SERIAL Android only. adb device serial + -p, --platform One of: ios, android + -n, --dry-run Print the command(s) without running them + -d, --device iOS only. simctl device selector + (default: booted) + -s, --serial Android only. adb device serial USAGE } @@ -83,7 +84,7 @@ parse_args() { DRY_RUN="1" shift ;; - --device) + -d | --device) [[ $# -ge 2 ]] || die "--device requires a value" IOS_DEVICE="$2" shift 2 diff --git a/bin/common/tag-if-missing b/bin/common/tag-if-missing index fb185d5..2033591 100755 --- a/bin/common/tag-if-missing +++ b/bin/common/tag-if-missing @@ -7,7 +7,8 @@ Usage: tag-if-missing [--remote ] [--dry-run] [] Create and push a git tag if it does not already exist. -Reads the tag/version from stdin or arg. If input is SemVer, prefixes "v". +Reads the tag/version from stdin or arg. +If input is SemVer, prefixes "v". Options: --remote Remote to check/push to (default: origin) diff --git a/bin/flutter/android-signing-setup b/bin/flutter/android-signing-setup index 7612e3e..450826e 100755 --- a/bin/flutter/android-signing-setup +++ b/bin/flutter/android-signing-setup @@ -6,7 +6,8 @@ usage() { Usage: android-signing-setup -Prepare Android signing files under ANDROID_SIGNING_DIR (default: ./android). +Prepare Android signing files under ANDROID_SIGNING_DIR +(default: ./android). Required environment variables: ANDROID_KEYSTORE_PASSWORD @@ -14,10 +15,11 @@ Required environment variables: ANDROID_KEY_PASSWORD Optional: - ANDROID_KEYSTORE_B64 Base64-encoded PKCS12/JKS keystore (CI typical) + ANDROID_KEYSTORE_B64 Base64-encoded PKCS12/JKS keystore + (CI typical) ANDROID_KEYSTORE_FILENAME Default: android-keystore.p12 ANDROID_SIGNING_DIR Default: android - ANDROID_SIGNING_FORCE_DECODE=1 Decode even if keystore file already exists + ANDROID_SIGNING_FORCE_DECODE=1 Decode even if keystore file exists ANDROID_SIGNING_ROLE Log label only (e.g. upload, appsigning) Notes: diff --git a/bin/flutter/asc-auth-key-setup b/bin/flutter/asc-auth-key-setup index eee0b7e..b53ea1a 100755 --- a/bin/flutter/asc-auth-key-setup +++ b/bin/flutter/asc-auth-key-setup @@ -10,11 +10,12 @@ Set up an App Store Connect API auth key from env vars. Required: ASC_AUTH_KEY_B64 Base64-encoded AuthKey .p8 contents - ASC_KEY_ID Key ID used in filename AuthKey_.p8 + ASC_KEY_ID Key ID used in filename AuthKey_.p8 Optional: - ASC_KEY_DIR Directory to place the key (default: temp dir) - ASC_AUTH_KEY_FORCE_DECODE=1 Decode even if key file already exists + ASC_KEY_DIR Directory to place the key + (default: temp dir) + ASC_AUTH_KEY_FORCE_DECODE=1 Decode even if key file exists ASC_USE_STANDARD_DIR=1 Use ~/.appstoreconnect/private_keys Outputs: diff --git a/bin/flutter/ios-signing-setup b/bin/flutter/ios-signing-setup index 2f4e0ca..6ba9198 100755 --- a/bin/flutter/ios-signing-setup +++ b/bin/flutter/ios-signing-setup @@ -9,7 +9,8 @@ Usage: Set up iOS signing in CI from env vars. Required environment variables: - IOS_SIGNING_CERT_B64 Base64-encoded P12 signing certificate + IOS_SIGNING_CERT_B64 Base64-encoded P12 signing + certificate IOS_SIGNING_CERT_PASSWORD Password for the P12 IOS_PROVISIONING_PROFILE_B64 Base64-encoded .mobileprovision IOS_KEYCHAIN_PASSWORD Password for the ephemeral keychain diff --git a/bin/flutter/set-project-version b/bin/flutter/set-project-version index 21054b1..dcd90fd 100755 --- a/bin/flutter/set-project-version +++ b/bin/flutter/set-project-version @@ -4,9 +4,9 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - set-project-version [--bump-build-on-same | --fail-on-same] [X.Y.Z] + set-project-version [--bump-build-on-same | --fail-on-same] [] -Set the pubspec version. Reads X.Y.Z from stdin or arg. +Set the pubspec version. Reads x.y.z from stdin or arg. Options: --bump-build-on-same Bump build even when core version is unchanged @@ -73,7 +73,7 @@ parse_args() { } validate_inputs() { - [[ ${#positional[@]} -le 1 ]] || die "At most one X.Y.Z argument may be provided" + [[ ${#positional[@]} -le 1 ]] || die "At most one x.y.z argument may be provided" } read_input_version() { diff --git a/bin/flutter/stage-release-assets b/bin/flutter/stage-release-assets index c145e1c..7821e47 100755 --- a/bin/flutter/stage-release-assets +++ b/bin/flutter/stage-release-assets @@ -9,8 +9,10 @@ Usage: Stage Flutter release assets into dist/ with deterministic names. Options: - --slug Slug for output filenames (required; lowercase alnum) - --tag Tag to use locally (required outside CI) + --slug Slug for output filenames + (required; lowercase alnum) + --tag Tag to use locally + (required outside CI) --dist Output directory (default: dist) -h, --help Show this help message USAGE diff --git a/bin/media/diagmerge b/bin/media/diagmerge index 5c54b21..f2a6e4d 100755 --- a/bin/media/diagmerge +++ b/bin/media/diagmerge @@ -4,15 +4,19 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - diagmerge [--width N] [--feather S] [--quality Q] [output] + diagmerge [--width ] [--feather ] + [--quality ] [] Create a diagonal merge: upper-left from image1, lower-right from image2. Options: - -w, --width N Resize both inputs to width N (px), keeping aspect ratio - -f, --feather S Feather sigma (px) for the diagonal edge (default: 0.8) - -q, --quality Q Output quality for lossy formats (WebP/JPEG) (default: 90) - -h, --help Show this help message + -w, --width Resize both inputs to width in px + (keeps aspect ratio) + -f, --feather Feather sigma in px for the diagonal edge + (default: 0.8) + -q, --quality Output quality for lossy formats like WebP/JPEG + (default: 90) + -h, --help Show this help message USAGE } diff --git a/bin/media/shotproc b/bin/media/shotproc index cab1243..be6ea87 100755 --- a/bin/media/shotproc +++ b/bin/media/shotproc @@ -4,14 +4,17 @@ set -euo pipefail usage() { cat >&2 <<'USAGE' Usage: - shotproc [--width N] [--quality Q] [output] + shotproc [--width ] [--quality ] + [] Process a screenshot: optional resize, strip metadata, set output quality. Options: - -w, --width N Resize to width N (px), keeping aspect ratio - -q, --quality Q Output quality for lossy formats (WebP/JPEG) (default: 90) - -h, --help Show this help message + -w, --width Resize to width in px + (keeps aspect ratio) + -q, --quality Output quality for lossy formats like WebP/JPEG + (default: 90) + -h, --help Show this help message USAGE }