mirror of
https://github.com/kellymichels/zscripts-token-savers
synced 2026-10-07 07:18:18 +00:00
fix(bash): make the port bash-3.2 / BSD-clean so it runs on stock macOS (#8)
The README advertises macOS support, but the port used constructs that fail on the userland macOS actually ships: - `mapfile` (bash 4+) in 10 spots — macOS ships bash 3.2 as /usr/bin/bash, so a mac user following the README hit "mapfile: command not found" and silently got empty project lists. Replace each with a portable `while IFS= read -r` loop (identical arrays; set -u safe on empty input). - `_z_commafy` used the GNU-only `sed :a;...;ta` label/branch idiom, which errors on BSD/macOS sed (the token footer's thousands separators). Reimplement with awk (already a dependency). - README: correct the macOS line — bash 4+ does NOT ship with macOS; the stock 3.2 now works, and only jq needs brew. Also two correctness nits found in the same review: - zkill/zrestart `--kill-all` was parsed but silently ignored; now it prints a "not implemented in the bash port" notice instead of no-op. - zrepair's smoke-test line said "https://$domain" but probes http://$HOST with a Host header; label now matches what it does. Verified on WSL (bash 5): read-loops produce the same 11 project / 7 domain keys as mapfile; awk commafy matches across 0..1,234,567; empty producer yields a 0-length array; footer renders. 3.2-compat is by static analysis — no bash-4-only constructs remain.
This commit is contained in:
parent
3fcc3546b7
commit
4ebde3ebcf
@ -11,12 +11,12 @@ same behavior — no PowerShell required.
|
||||
|
||||
## Requirements
|
||||
|
||||
- bash 4+, `jq`, `curl`, `zip`, OpenSSH (`ssh`/`scp`)
|
||||
- bash 3.2+ (the stock macOS bash works), `jq`, `curl`, `zip`, OpenSSH (`ssh`/`scp`)
|
||||
- `lsof` (for `zkill`/`zrestart`), `rsync` (for `zsync <project>` mirror mode)
|
||||
- Docker + docker compose on the remote host for the deploy scripts
|
||||
|
||||
Debian/Ubuntu: `sudo apt install jq curl zip lsof rsync`
|
||||
macOS: `brew install jq` (the rest ships with the OS)
|
||||
macOS: `brew install jq` (bash, curl, zip, ssh, lsof and rsync already ship with macOS)
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ done
|
||||
BACKUP_ROOT="$(z_path "$(zq '.paths.backupsLocal')")"
|
||||
include_scripts=0
|
||||
if [ "${#projects[@]}" -eq 0 ]; then
|
||||
mapfile -t projects < <(zproj_keys)
|
||||
projects=(); while IFS= read -r _zl || [ -n "$_zl" ]; do projects+=("$_zl"); done < <(zproj_keys)
|
||||
include_scripts=1
|
||||
else
|
||||
filtered=()
|
||||
@ -92,7 +92,7 @@ backup_project() { # <key>
|
||||
|
||||
warn " [2/3] Archiving source..."
|
||||
local excl=()
|
||||
mapfile -t excl < <(z_archive_excludes "$key" 1)
|
||||
excl=(); while IFS= read -r _zl || [ -n "$_zl" ]; do excl+=("$_zl"); done < <(z_archive_excludes "$key" 1)
|
||||
if ! z_archive "$root" "$zip_path" 0 "$extra" "${excl[@]}"; then
|
||||
rm -rf "$dump_dir"; return 1
|
||||
fi
|
||||
|
||||
@ -22,7 +22,8 @@ z_require ssh scp
|
||||
|
||||
projects=("$@")
|
||||
if [ "${#projects[@]}" -eq 0 ]; then
|
||||
mapfile -t projects < <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.remote.path != null) | .key' "$ZCONFIG")
|
||||
projects=(); while IFS= read -r _zl || [ -n "$_zl" ]; do projects+=("$_zl"); done \
|
||||
< <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.remote.path != null) | .key' "$ZCONFIG")
|
||||
fi
|
||||
|
||||
EC2_BACKUP_ROOT="$(z_path "$(zq '.paths.backupsEc2')")"
|
||||
|
||||
12
bash/zdeploy
12
bash/zdeploy
@ -46,8 +46,10 @@ mkdir -p "$TEMP_ROOT"
|
||||
|
||||
# 'all' -> every project, edge kinds first (the proxy must route before apps ship).
|
||||
if printf '%s\n' "${projects[@]}" | grep -qx 'all'; then
|
||||
mapfile -t edge_keys < <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.kind == "edge") | .key' "$ZCONFIG")
|
||||
mapfile -t rest_keys < <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.kind != "edge") | .key' "$ZCONFIG")
|
||||
edge_keys=(); while IFS= read -r _zl || [ -n "$_zl" ]; do edge_keys+=("$_zl"); done \
|
||||
< <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.kind == "edge") | .key' "$ZCONFIG")
|
||||
rest_keys=(); while IFS= read -r _zl || [ -n "$_zl" ]; do rest_keys+=("$_zl"); done \
|
||||
< <(jq -r '.projects | to_entries[] | select((.key | startswith("_") | not) and .value.kind != "edge") | .key' "$ZCONFIG")
|
||||
projects=("${edge_keys[@]+"${edge_keys[@]}"}" "${rest_keys[@]+"${rest_keys[@]}"}")
|
||||
info "Deploying all projects: $(printf '%s ' "${projects[@]}")"
|
||||
fi
|
||||
@ -203,7 +205,7 @@ deploy_python() { # <key>
|
||||
|
||||
printf '\n'; info "--- [1] Zipping $label ---"
|
||||
find "$root" -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null
|
||||
mapfile -t excl < <(z_archive_excludes "$key" 0)
|
||||
excl=(); while IFS= read -r _zl || [ -n "$_zl" ]; do excl+=("$_zl"); done < <(z_archive_excludes "$key" 0)
|
||||
z_archive "$root" "$zip_local" 0 "-" "${excl[@]}" || return 1
|
||||
|
||||
preflight_cleanup "$REMOTE_HOME/$zip_name" || { rm -f "$zip_local"; return 1; }
|
||||
@ -289,7 +291,7 @@ deploy_vite() { # <key>
|
||||
pre_bn="$(jq -r '.buildNumber // empty' "$root/build-version.json")"
|
||||
[ -n "$pre_pv" ] && dim " Pre-zip build label: v${pre_pv}.${pre_bn} (server-side build will bump +1)"
|
||||
fi
|
||||
mapfile -t excl < <(z_archive_excludes "$key" 0)
|
||||
excl=(); while IFS= read -r _zl || [ -n "$_zl" ]; do excl+=("$_zl"); done < <(z_archive_excludes "$key" 0)
|
||||
z_archive "$root" "$zip_local" 0 "-" "${excl[@]}" || return 1
|
||||
|
||||
preflight_cleanup "$REMOTE_HOME/$zip_name" || { rm -f "$zip_local"; return 1; }
|
||||
@ -347,7 +349,7 @@ deploy_next() { # <key>
|
||||
fi
|
||||
|
||||
printf '\n'; info "--- [1] Zipping project files ---"
|
||||
mapfile -t excl < <(z_archive_excludes "$key" 0)
|
||||
excl=(); while IFS= read -r _zl || [ -n "$_zl" ]; do excl+=("$_zl"); done < <(z_archive_excludes "$key" 0)
|
||||
z_archive "$root" "$zip_local" 0 "-" "${excl[@]}" || return 1
|
||||
|
||||
preflight_cleanup "$REMOTE_HOME/$zip_name" || { rm -f "$zip_local"; return 1; }
|
||||
|
||||
@ -28,7 +28,7 @@ done
|
||||
HOST="${host_override:-$(zec2_ip)}"
|
||||
|
||||
if [ "${#projects[@]}" -eq 0 ]; then
|
||||
mapfile -t projects < <(zproj_keys_with_domain)
|
||||
projects=(); while IFS= read -r _zl || [ -n "$_zl" ]; do projects+=("$_zl"); done < <(zproj_keys_with_domain)
|
||||
if [ "${#projects[@]}" -eq 0 ]; then
|
||||
warn "No projects with a 'domain' configured in zconfig.json."
|
||||
exit 1
|
||||
|
||||
@ -32,7 +32,7 @@ HOST="${host_override:-$(zec2_ip)}"
|
||||
EDGE_KEY="$(zedge_key)"
|
||||
|
||||
if [ "${#projects[@]}" -eq 0 ]; then
|
||||
mapfile -t projects < <(zproj_keys_with_domain)
|
||||
projects=(); while IFS= read -r _zl || [ -n "$_zl" ]; do projects+=("$_zl"); done < <(zproj_keys_with_domain)
|
||||
if [ "${#projects[@]}" -eq 0 ]; then
|
||||
warn "No projects with a 'domain' configured in zconfig.json."
|
||||
exit 1
|
||||
|
||||
@ -311,8 +311,16 @@ z_track_stop() {
|
||||
Z_TRACK_FILE=""
|
||||
}
|
||||
|
||||
# Portable thousands separators (avoids locale-dependent printf %'d).
|
||||
_z_commafy() { printf '%s' "$1" | sed -E ':a;s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/;ta'; }
|
||||
# Portable thousands separators for a non-negative integer string (avoids both
|
||||
# locale-dependent printf %'d and the GNU-only `sed :a;...;ta` idiom, which
|
||||
# errors on BSD/macOS sed).
|
||||
_z_commafy() {
|
||||
awk -v n="$1" 'BEGIN{
|
||||
s = n ""; out = ""
|
||||
while (length(s) > 3) { out = "," substr(s, length(s) - 2) out; s = substr(s, 1, length(s) - 3) }
|
||||
printf "%s%s", s, out
|
||||
}'
|
||||
}
|
||||
|
||||
# Append a run to the ztokens JSONL store. Data dir precedence: $ZTOKENS_DATA,
|
||||
# then config "ztokens.dataDir", then the sibling ../../ztokens/data if present
|
||||
|
||||
@ -31,6 +31,10 @@ if [ "${#projects[@]}" -eq 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --kill-all (kill stray project processes beyond the dev-port listener) isn't
|
||||
# ported yet; announce it rather than silently accepting the flag and doing nothing.
|
||||
[ "$kill_all" -eq 1 ] && warn "Note: --kill-all isn't implemented in the bash port yet - killing dev-port listeners only."
|
||||
|
||||
for key in "${projects[@]}"; do
|
||||
zproj_require "$key"
|
||||
label="$(zproj "$key" .label)"; [ -n "$label" ] || label="$key"
|
||||
|
||||
@ -60,7 +60,7 @@ for key in "$@"; do
|
||||
"cd $compose_dir || exit 1; if ! sudo docker compose ps | grep -q Up; then echo ' [Action] Stack is down. Starting...'; sudo docker compose up -d; else echo ' [OK] Stack is active and UP.'; fi" || exit 1
|
||||
|
||||
if [ -n "$domain" ]; then
|
||||
warn " [3/3] Smoke test for https://$domain..."
|
||||
warn " [3/3] Smoke test for $domain (via http://$HOST)..."
|
||||
code="$(http_code "http://$HOST/" "$domain")"
|
||||
if [ "$code" = "000" ]; then
|
||||
err " FAIL - smoke test failed: no HTTP response"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user