From a3a8794fed74bef5946d251571e3929c1d8d0618 Mon Sep 17 00:00:00 2001 From: Crash0v3rrid3 Date: Thu, 30 Jul 2026 15:07:38 +0530 Subject: [PATCH 1/3] fix(spm): serialize concurrent scans to protect synthetic Package.swift (DEVA11Y-483) Guard the setup/scan/cleanup cycle with an atomic per-directory mkdir lock so concurrent spm.sh invocations in the same working directory no longer race: the first instance to exit can no longer delete the shared synthetic Package.swift out from under a still-running peer. Stale locks left by killed peers are reclaimed after 5 minutes; mkdir is used instead of flock(1) for macOS portability. Applied to the bash, zsh and fish variants. Co-Authored-By: Claude Opus 4.8 --- scripts/bash/spm.sh | 35 +++++++++++++++++++++++++++++++---- scripts/fish/spm.sh | 35 +++++++++++++++++++++++++++++++---- scripts/zsh/spm.sh | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/scripts/bash/spm.sh b/scripts/bash/spm.sh index 0fcb69f..17f41c2 100644 --- a/scripts/bash/spm.sh +++ b/scripts/bash/spm.sh @@ -39,12 +39,39 @@ EOF } a11y_scan() { - # Ensure Package.swift is removed on exit (acts like a finally block) + # Serialize concurrent scans in the same directory (DEVA11Y-483). Without this the + # first instance to exit fires cleanup() and deletes the shared synthetic + # Package.swift out from under a still-running peer, so both scans fail + # non-deterministically. mkdir is atomic on POSIX filesystems and portable to + # macOS, which has no flock(1). Only lock when we own the synthetic file. + local lock_dir="${PWD}/.browserstack-a11y-spm.lock" + local have_lock=0 + if [ $PACKAGE_EXISTS -ne 0 ]; then + local waited=0 + while ! mkdir "$lock_dir" 2>/dev/null; do + # Reclaim a stale lock left by a peer killed before its trap could run. + if [ -n "$(find "$lock_dir" -maxdepth 0 -mmin +5 2>/dev/null)" ]; then + rm -rf -- "$lock_dir" + continue + fi + if [ "$waited" -ge 300 ]; then + echo "browserstack-a11y: timed out waiting for another scan in ${PWD}; remove ${lock_dir} if it is stale." >&2 + exit 1 + fi + sleep 1 + waited=$((waited + 1)) + done + have_lock=1 + fi + + # Ensure Package.swift and the lock are removed on exit (acts like a finally block) cleanup() { - if [ $PACKAGE_EXISTS -eq 0 ]; then - return + if [ $PACKAGE_EXISTS -ne 0 ]; then + rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" + fi + if [ "$have_lock" -eq 1 ]; then + rmdir "$lock_dir" 2>/dev/null fi - rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" } trap cleanup EXIT diff --git a/scripts/fish/spm.sh b/scripts/fish/spm.sh index 34b1079..c65806a 100644 --- a/scripts/fish/spm.sh +++ b/scripts/fish/spm.sh @@ -52,12 +52,39 @@ EOF } a11y_scan() { - # Ensure Package.swift is removed on exit (acts like a finally block) + # Serialize concurrent scans in the same directory (DEVA11Y-483). Without this the + # first instance to exit fires cleanup() and deletes the shared synthetic + # Package.swift out from under a still-running peer, so both scans fail + # non-deterministically. mkdir is atomic on POSIX filesystems and portable to + # macOS, which has no flock(1). Only lock when we own the synthetic file. + local lock_dir="${PWD}/.browserstack-a11y-spm.lock" + local have_lock=0 + if [ $PACKAGE_EXISTS -ne 0 ]; then + local waited=0 + while ! mkdir "$lock_dir" 2>/dev/null; do + # Reclaim a stale lock left by a peer killed before its trap could run. + if [ -n "$(find "$lock_dir" -maxdepth 0 -mmin +5 2>/dev/null)" ]; then + rm -rf -- "$lock_dir" + continue + fi + if [ "$waited" -ge 300 ]; then + echo "browserstack-a11y: timed out waiting for another scan in ${PWD}; remove ${lock_dir} if it is stale." >&2 + exit 1 + fi + sleep 1 + waited=$((waited + 1)) + done + have_lock=1 + fi + + # Ensure Package.swift and the lock are removed on exit (acts like a finally block) cleanup() { - if [ $PACKAGE_EXISTS -eq 0 ]; then - return + if [ $PACKAGE_EXISTS -ne 0 ]; then + rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" + fi + if [ "$have_lock" -eq 1 ]; then + rmdir "$lock_dir" 2>/dev/null fi - rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" } trap cleanup EXIT diff --git a/scripts/zsh/spm.sh b/scripts/zsh/spm.sh index ac963bd..f47b1a8 100644 --- a/scripts/zsh/spm.sh +++ b/scripts/zsh/spm.sh @@ -51,12 +51,39 @@ EOF } a11y_scan() { - # Ensure Package.swift is removed on exit (acts like a finally block) + # Serialize concurrent scans in the same directory (DEVA11Y-483). Without this the + # first instance to exit fires cleanup() and deletes the shared synthetic + # Package.swift out from under a still-running peer, so both scans fail + # non-deterministically. mkdir is atomic on POSIX filesystems and portable to + # macOS, which has no flock(1). Only lock when we own the synthetic file. + local lock_dir="${PWD}/.browserstack-a11y-spm.lock" + local have_lock=0 + if [ $PACKAGE_EXISTS -ne 0 ]; then + local waited=0 + while ! mkdir "$lock_dir" 2>/dev/null; do + # Reclaim a stale lock left by a peer killed before its trap could run. + if [ -n "$(find "$lock_dir" -maxdepth 0 -mmin +5 2>/dev/null)" ]; then + rm -rf -- "$lock_dir" + continue + fi + if [ "$waited" -ge 300 ]; then + echo "browserstack-a11y: timed out waiting for another scan in ${PWD}; remove ${lock_dir} if it is stale." >&2 + exit 1 + fi + sleep 1 + waited=$((waited + 1)) + done + have_lock=1 + fi + + # Ensure Package.swift and the lock are removed on exit (acts like a finally block) cleanup() { - if [ $PACKAGE_EXISTS -eq 0 ]; then - return + if [ $PACKAGE_EXISTS -ne 0 ]; then + rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" + fi + if [ "$have_lock" -eq 1 ]; then + rmdir "$lock_dir" 2>/dev/null fi - rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" } trap cleanup EXIT From 4d5ea978b940a5b988ee00a109fb01d286fae9c1 Mon Sep 17 00:00:00 2001 From: Crash0v3rrid3 Date: Thu, 30 Jul 2026 16:10:03 +0530 Subject: [PATCH 2/3] fix(spm): redesign concurrency lock to fully close the scan race (DEVA11Y-483) Addresses all code-review findings on the first cut: - #1 Decide manifest ownership UNDER the lock from the live filesystem, not a startup PACKAGE_EXISTS snapshot. The lock is now taken unconditionally, so a peer that starts after the synthetic Package.swift already exists still serializes instead of running unprotected and getting its file deleted. - #2 Reclaim a crashed peer's lock by PID liveness (kill -0), not a 5-min mtime that would steal a slow-but-alive long scan's lock. - #3 Claim a stale lock atomically via rename so two waiters can't both reclaim. - #4 Wait-timeout is non-fatal: it skips the scan (exit 0) with a visible 'waiting...'/'skipping' notice instead of hanging a git commit then aborting it. - #5 A non-EEXIST mkdir failure (unwritable/read-only/full TMPDIR) fails fast with an actionable message instead of waiting out the full timeout. - #6 The lock lives under TMPDIR keyed by the package path, never inside the working tree, so a crash can't leave it to be git-added. - #7 Consistent 'A11y scan:' message prefix. - Also fixes a latent bug from the first cut: cleanup state (lock_dir/have_lock/ created_package) is now global, since the EXIT trap fires after a11y_scan returns when its locals are out of scope (verified) -- previously the lock was never released on a normal run. Verified with an integration test (staggered concurrent runs serialize; both scans see Package.swift throughout; no tree/TMPDIR residue) and unit tests for stale reclaim, live-owner detection, and fail-fast. Applied to bash/zsh/fish. Co-Authored-By: Claude Opus 4.8 --- scripts/bash/spm.sh | 120 ++++++++++++++++++++++++++++---------------- scripts/fish/spm.sh | 120 ++++++++++++++++++++++++++++---------------- scripts/zsh/spm.sh | 120 ++++++++++++++++++++++++++++---------------- 3 files changed, 234 insertions(+), 126 deletions(-) diff --git a/scripts/bash/spm.sh b/scripts/bash/spm.sh index 17f41c2..2b4f446 100644 --- a/scripts/bash/spm.sh +++ b/scripts/bash/spm.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -il -[ -f "${PWD}/Package.swift" ] -PACKAGE_EXISTS="$?" GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) SCRIPT_PATH=$(realpath --relative-to="$GIT_ROOT" "$0" 2>/dev/null || realpath "$0") SUBCOMMAND="$1" @@ -38,49 +36,76 @@ EOF fi } -a11y_scan() { - # Serialize concurrent scans in the same directory (DEVA11Y-483). Without this the - # first instance to exit fires cleanup() and deletes the shared synthetic - # Package.swift out from under a still-running peer, so both scans fail - # non-deterministically. mkdir is atomic on POSIX filesystems and portable to - # macOS, which has no flock(1). Only lock when we own the synthetic file. - local lock_dir="${PWD}/.browserstack-a11y-spm.lock" - local have_lock=0 - if [ $PACKAGE_EXISTS -ne 0 ]; then - local waited=0 - while ! mkdir "$lock_dir" 2>/dev/null; do - # Reclaim a stale lock left by a peer killed before its trap could run. - if [ -n "$(find "$lock_dir" -maxdepth 0 -mmin +5 2>/dev/null)" ]; then - rm -rf -- "$lock_dir" - continue - fi - if [ "$waited" -ge 300 ]; then - echo "browserstack-a11y: timed out waiting for another scan in ${PWD}; remove ${lock_dir} if it is stale." >&2 - exit 1 - fi - sleep 1 - waited=$((waited + 1)) - done - have_lock=1 - fi +# Resolve a lock directory OUTSIDE the scanned package, keyed by the package path, +# so a crash can never leave a lock inside the user's working tree (nor let a +# pre-commit `git add -A` stage it). cksum is POSIX and always present. +_spm_lock_dir() { + local key + key=$(printf '%s' "$PWD" | cksum | cut -d' ' -f1) + printf '%s/browserstack-a11y-spm-%s.lock' "${TMPDIR:-/tmp}" "$key" +} - # Ensure Package.swift and the lock are removed on exit (acts like a finally block) - cleanup() { - if [ $PACKAGE_EXISTS -ne 0 ]; then - rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" - fi - if [ "$have_lock" -eq 1 ]; then - rmdir "$lock_dir" 2>/dev/null +# Acquire an exclusive per-directory lock via atomic mkdir. Serializes ALL +# concurrent scans of the same directory regardless of when each started, so no +# instance ever deletes a synthetic Package.swift another is still using. A lock +# left by a crashed peer is reclaimed by PID liveness (kill -0) -- never by +# wall-clock age, which cannot tell a slow-but-alive scan from a dead one -- and +# the reclaim is claimed atomically via rename so two waiters can't both take it. +# Returns: 0 acquired, 2 timed out (a peer is scanning), 1 lock dir unusable. +_spm_acquire_lock() { + local dir="$1" waited=0 announced=0 owner_pid stale + while ! mkdir "$dir" 2>/dev/null; do + # A non-EEXIST failure (unwritable/read-only/full TMPDIR) is not contention -- + # fail fast rather than waiting out the full timeout on a misleading message. + if [ ! -d "$dir" ]; then + echo "A11y scan: cannot create lock at ${dir} (check TMPDIR permissions/space)." >&2 + return 1 + fi + owner_pid=$(cat "${dir}/pid" 2>/dev/null) + if [ -n "$owner_pid" ] && ! kill -0 "$owner_pid" 2>/dev/null; then + # Owner is dead: claim the stale lock. Only one racer's mv can succeed; the + # losers fall through and retry mkdir. Never rm a path a peer may recreate. + stale="${dir}.stale.$$" + if mv "$dir" "$stale" 2>/dev/null; then + rm -rf -- "$stale" fi - } - trap cleanup EXIT + continue + fi + if [ "$announced" -eq 0 ]; then + echo "A11y scan: waiting for another scan in ${PWD} to finish..." >&2 + announced=1 + fi + if [ "$waited" -ge 300 ]; then + return 2 + fi + sleep 1 + waited=$((waited + 1)) + done + echo "$$" > "${dir}/pid" + return 0 +} - setup() { - if [ $PACKAGE_EXISTS -eq 0 ]; then - return - fi +# lock_dir/have_lock/created_package are GLOBAL on purpose: the EXIT trap fires +# after a11y_scan has returned, when its `local`s are already out of scope, so +# cleanup state must be global to survive. +a11y_scan() { + lock_dir=$(_spm_lock_dir) + have_lock=0 + created_package=0 + + _spm_acquire_lock "$lock_dir" + case "$?" in + 0) have_lock=1 ;; + 2) echo "A11y scan: another scan is already running in ${PWD}; skipping." >&2 + return 0 ;; + *) echo "A11y scan: proceeding without a lock; concurrent scans in ${PWD} may conflict." >&2 ;; + esac - cat > Package.swift < Package.swift </dev/null) SCRIPT_PATH=$(realpath --relative-to="$GIT_ROOT" "$0" 2>/dev/null || realpath "$0") SUBCOMMAND="$1" @@ -51,49 +49,76 @@ EOF fi } -a11y_scan() { - # Serialize concurrent scans in the same directory (DEVA11Y-483). Without this the - # first instance to exit fires cleanup() and deletes the shared synthetic - # Package.swift out from under a still-running peer, so both scans fail - # non-deterministically. mkdir is atomic on POSIX filesystems and portable to - # macOS, which has no flock(1). Only lock when we own the synthetic file. - local lock_dir="${PWD}/.browserstack-a11y-spm.lock" - local have_lock=0 - if [ $PACKAGE_EXISTS -ne 0 ]; then - local waited=0 - while ! mkdir "$lock_dir" 2>/dev/null; do - # Reclaim a stale lock left by a peer killed before its trap could run. - if [ -n "$(find "$lock_dir" -maxdepth 0 -mmin +5 2>/dev/null)" ]; then - rm -rf -- "$lock_dir" - continue - fi - if [ "$waited" -ge 300 ]; then - echo "browserstack-a11y: timed out waiting for another scan in ${PWD}; remove ${lock_dir} if it is stale." >&2 - exit 1 - fi - sleep 1 - waited=$((waited + 1)) - done - have_lock=1 - fi +# Resolve a lock directory OUTSIDE the scanned package, keyed by the package path, +# so a crash can never leave a lock inside the user's working tree (nor let a +# pre-commit `git add -A` stage it). cksum is POSIX and always present. +_spm_lock_dir() { + local key + key=$(printf '%s' "$PWD" | cksum | cut -d' ' -f1) + printf '%s/browserstack-a11y-spm-%s.lock' "${TMPDIR:-/tmp}" "$key" +} - # Ensure Package.swift and the lock are removed on exit (acts like a finally block) - cleanup() { - if [ $PACKAGE_EXISTS -ne 0 ]; then - rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" - fi - if [ "$have_lock" -eq 1 ]; then - rmdir "$lock_dir" 2>/dev/null +# Acquire an exclusive per-directory lock via atomic mkdir. Serializes ALL +# concurrent scans of the same directory regardless of when each started, so no +# instance ever deletes a synthetic Package.swift another is still using. A lock +# left by a crashed peer is reclaimed by PID liveness (kill -0) -- never by +# wall-clock age, which cannot tell a slow-but-alive scan from a dead one -- and +# the reclaim is claimed atomically via rename so two waiters can't both take it. +# Returns: 0 acquired, 2 timed out (a peer is scanning), 1 lock dir unusable. +_spm_acquire_lock() { + local dir="$1" waited=0 announced=0 owner_pid stale + while ! mkdir "$dir" 2>/dev/null; do + # A non-EEXIST failure (unwritable/read-only/full TMPDIR) is not contention -- + # fail fast rather than waiting out the full timeout on a misleading message. + if [ ! -d "$dir" ]; then + echo "A11y scan: cannot create lock at ${dir} (check TMPDIR permissions/space)." >&2 + return 1 + fi + owner_pid=$(cat "${dir}/pid" 2>/dev/null) + if [ -n "$owner_pid" ] && ! kill -0 "$owner_pid" 2>/dev/null; then + # Owner is dead: claim the stale lock. Only one racer's mv can succeed; the + # losers fall through and retry mkdir. Never rm a path a peer may recreate. + stale="${dir}.stale.$$" + if mv "$dir" "$stale" 2>/dev/null; then + rm -rf -- "$stale" fi - } - trap cleanup EXIT + continue + fi + if [ "$announced" -eq 0 ]; then + echo "A11y scan: waiting for another scan in ${PWD} to finish..." >&2 + announced=1 + fi + if [ "$waited" -ge 300 ]; then + return 2 + fi + sleep 1 + waited=$((waited + 1)) + done + echo "$$" > "${dir}/pid" + return 0 +} - setup() { - if [ $PACKAGE_EXISTS -eq 0 ]; then - return - fi +# lock_dir/have_lock/created_package are GLOBAL on purpose: the EXIT trap fires +# after a11y_scan has returned, when its `local`s are already out of scope, so +# cleanup state must be global to survive. +a11y_scan() { + lock_dir=$(_spm_lock_dir) + have_lock=0 + created_package=0 + + _spm_acquire_lock "$lock_dir" + case "$?" in + 0) have_lock=1 ;; + 2) echo "A11y scan: another scan is already running in ${PWD}; skipping." >&2 + return 0 ;; + *) echo "A11y scan: proceeding without a lock; concurrent scans in ${PWD} may conflict." >&2 ;; + esac - cat > Package.swift < Package.swift </dev/null) SCRIPT_PATH=$(realpath --relative-to="$GIT_ROOT" "$0" 2>/dev/null || realpath "$0") SUBCOMMAND="$1" @@ -50,49 +48,76 @@ EOF fi } -a11y_scan() { - # Serialize concurrent scans in the same directory (DEVA11Y-483). Without this the - # first instance to exit fires cleanup() and deletes the shared synthetic - # Package.swift out from under a still-running peer, so both scans fail - # non-deterministically. mkdir is atomic on POSIX filesystems and portable to - # macOS, which has no flock(1). Only lock when we own the synthetic file. - local lock_dir="${PWD}/.browserstack-a11y-spm.lock" - local have_lock=0 - if [ $PACKAGE_EXISTS -ne 0 ]; then - local waited=0 - while ! mkdir "$lock_dir" 2>/dev/null; do - # Reclaim a stale lock left by a peer killed before its trap could run. - if [ -n "$(find "$lock_dir" -maxdepth 0 -mmin +5 2>/dev/null)" ]; then - rm -rf -- "$lock_dir" - continue - fi - if [ "$waited" -ge 300 ]; then - echo "browserstack-a11y: timed out waiting for another scan in ${PWD}; remove ${lock_dir} if it is stale." >&2 - exit 1 - fi - sleep 1 - waited=$((waited + 1)) - done - have_lock=1 - fi +# Resolve a lock directory OUTSIDE the scanned package, keyed by the package path, +# so a crash can never leave a lock inside the user's working tree (nor let a +# pre-commit `git add -A` stage it). cksum is POSIX and always present. +_spm_lock_dir() { + local key + key=$(printf '%s' "$PWD" | cksum | cut -d' ' -f1) + printf '%s/browserstack-a11y-spm-%s.lock' "${TMPDIR:-/tmp}" "$key" +} - # Ensure Package.swift and the lock are removed on exit (acts like a finally block) - cleanup() { - if [ $PACKAGE_EXISTS -ne 0 ]; then - rm -f -- "${PWD}/Package.swift" "${PWD}/Package.resolved" - fi - if [ "$have_lock" -eq 1 ]; then - rmdir "$lock_dir" 2>/dev/null +# Acquire an exclusive per-directory lock via atomic mkdir. Serializes ALL +# concurrent scans of the same directory regardless of when each started, so no +# instance ever deletes a synthetic Package.swift another is still using. A lock +# left by a crashed peer is reclaimed by PID liveness (kill -0) -- never by +# wall-clock age, which cannot tell a slow-but-alive scan from a dead one -- and +# the reclaim is claimed atomically via rename so two waiters can't both take it. +# Returns: 0 acquired, 2 timed out (a peer is scanning), 1 lock dir unusable. +_spm_acquire_lock() { + local dir="$1" waited=0 announced=0 owner_pid stale + while ! mkdir "$dir" 2>/dev/null; do + # A non-EEXIST failure (unwritable/read-only/full TMPDIR) is not contention -- + # fail fast rather than waiting out the full timeout on a misleading message. + if [ ! -d "$dir" ]; then + echo "A11y scan: cannot create lock at ${dir} (check TMPDIR permissions/space)." >&2 + return 1 + fi + owner_pid=$(cat "${dir}/pid" 2>/dev/null) + if [ -n "$owner_pid" ] && ! kill -0 "$owner_pid" 2>/dev/null; then + # Owner is dead: claim the stale lock. Only one racer's mv can succeed; the + # losers fall through and retry mkdir. Never rm a path a peer may recreate. + stale="${dir}.stale.$$" + if mv "$dir" "$stale" 2>/dev/null; then + rm -rf -- "$stale" fi - } - trap cleanup EXIT + continue + fi + if [ "$announced" -eq 0 ]; then + echo "A11y scan: waiting for another scan in ${PWD} to finish..." >&2 + announced=1 + fi + if [ "$waited" -ge 300 ]; then + return 2 + fi + sleep 1 + waited=$((waited + 1)) + done + echo "$$" > "${dir}/pid" + return 0 +} - setup() { - if [ $PACKAGE_EXISTS -eq 0 ]; then - return - fi +# lock_dir/have_lock/created_package are GLOBAL on purpose: the EXIT trap fires +# after a11y_scan has returned, when its `local`s are already out of scope, so +# cleanup state must be global to survive. +a11y_scan() { + lock_dir=$(_spm_lock_dir) + have_lock=0 + created_package=0 + + _spm_acquire_lock "$lock_dir" + case "$?" in + 0) have_lock=1 ;; + 2) echo "A11y scan: another scan is already running in ${PWD}; skipping." >&2 + return 0 ;; + *) echo "A11y scan: proceeding without a lock; concurrent scans in ${PWD} may conflict." >&2 ;; + esac - cat > Package.swift < Package.swift < Date: Thu, 30 Jul 2026 16:11:02 +0530 Subject: [PATCH 3/3] fix(review): regenerate spm.sh checksum sidecars after lock redesign (DEVA11Y-483) The concurrency-lock redesign edited all three spm.sh launchers but left their .sha256 sidecars stale, which would make the self-update integrity check abort on every run (dead on arrival, same class as the DEVA11Y-475 fix). Regenerate all three sidecars to match. Co-Authored-By: Claude Opus 4.8 --- scripts/bash/spm.sh.sha256 | 2 +- scripts/fish/spm.sh.sha256 | 2 +- scripts/zsh/spm.sh.sha256 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/bash/spm.sh.sha256 b/scripts/bash/spm.sh.sha256 index c8541fa..f7c336d 100644 --- a/scripts/bash/spm.sh.sha256 +++ b/scripts/bash/spm.sh.sha256 @@ -1 +1 @@ -2d627fbe06991da445f7fbcd8bc8c4f5c4dd126c04372f74dd0f4ab39222c57e spm.sh +1987749e047ca43b15f10b0eb6a98d0adcfbf5f7de9f54f49831b486406c8ba1 spm.sh diff --git a/scripts/fish/spm.sh.sha256 b/scripts/fish/spm.sh.sha256 index 6617e4d..b812bfe 100644 --- a/scripts/fish/spm.sh.sha256 +++ b/scripts/fish/spm.sh.sha256 @@ -1 +1 @@ -8fe00a7635d3ad66d7197994631c1a58581d4b3a845453f72e5e237338dea24e spm.sh +87bf749b365b86157934671d913b3e4bcdb2b54e43b765d0850218d442ca4e26 spm.sh diff --git a/scripts/zsh/spm.sh.sha256 b/scripts/zsh/spm.sh.sha256 index 4c067f0..3702f78 100644 --- a/scripts/zsh/spm.sh.sha256 +++ b/scripts/zsh/spm.sh.sha256 @@ -1 +1 @@ -507bb44d5a17a4e15f59fcd3cdc04692a39a65abc2e4bf5375340939ce484a98 spm.sh +c798dc6be53c1dfdfa5113697cfe1a23ef48ccf33206e9484feb671d84bad45c spm.sh