Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 5 additions & 4 deletions bin/common/bump-calver
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ set -euo pipefail
usage() {
cat >&2 <<'USAGE'
Usage:
bump-calver [--format FMT] [DATE]
bump-calver [--format <format>] [<date>]

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 <format> Override output format (default: YYYY.MM.DD)
-h, --help Show this help message.

Tokens:
YYYY Full year (e.g. 2006, 2016, 2106)
Expand Down
3 changes: 2 additions & 1 deletion bin/common/bump-changelog-version
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Usage:
get-project-version | bump-changelog-version

Update CHANGELOG.md (Keep a Changelog style):
- Move "## [Unreleased]" to "## [<new>] - <today>"
- Move "## [Unreleased]" to
"## [<new>] - <today>"
- Insert a fresh "## [Unreleased]" above it
- Update reference links for [Unreleased] and [<new>]
USAGE
Expand Down
16 changes: 8 additions & 8 deletions bin/common/bump-semver
Original file line number Diff line number Diff line change
Expand Up @@ -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 <x.y.z>)
[--prerelease <id>] [--build <meta>]
[<version>]

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 <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.
USAGE
}

Expand Down
4 changes: 2 additions & 2 deletions bin/common/create-checksums
Original file line number Diff line number Diff line change
Expand Up @@ -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 <dir> Directory containing the assets (default: dist)
-h, --help Show this help message.
USAGE
}

Expand Down
19 changes: 10 additions & 9 deletions bin/common/screenshot-mode
Original file line number Diff line number Diff line change
Expand Up @@ -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 <platform>
[--device <device> | --serial <serial>] [--dry-run]
screenshot-mode reset --platform <platform>
[--device <device> | --serial <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 <platform> One of: ios, android
-n, --dry-run Print the command(s) without running them
-d, --device <device> iOS only. simctl device selector
(default: booted)
-s, --serial <serial> Android only. adb device serial
USAGE
}

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion bin/common/tag-if-missing
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Usage:
tag-if-missing [--remote <name>] [--dry-run] [<tag-or-version>]

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 <name> Remote to check/push to (default: origin)
Expand Down
131 changes: 63 additions & 68 deletions bin/common/verify-checksums
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@ set -euo pipefail
usage() {
cat >&2 <<'USAGE'
Usage:
verify-checksums --pattern <glob> [--dir <dir>] [--checksums <file>] [--output-prefix <prefix>]
verify-checksums [--dir <dir>] [--pattern <glob> ...]

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 <dir> Directory containing the assets (default: dist)
-p, --pattern <glob> Only verify files matching <glob> (default: *)
May be specified multiple times.
-h, --help Show this help message.

Notes:
In CI (when GITHUB_OUTPUT is set), emits:
- <prefix>_filename
- <prefix>_path
USAGE
}

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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 "$@"
8 changes: 5 additions & 3 deletions bin/flutter/android-signing-setup
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,20 @@ 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
ANDROID_KEY_ALIAS
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:
Expand Down
7 changes: 4 additions & 3 deletions bin/flutter/asc-auth-key-setup
Original file line number Diff line number Diff line change
Expand Up @@ -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_<ID>.p8
ASC_KEY_ID Key ID used in filename AuthKey_<id>.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:
Expand Down
3 changes: 2 additions & 1 deletion bin/flutter/ios-signing-setup
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading