From 3ffd965d2984484a666e178de89ab7428a08ac4d Mon Sep 17 00:00:00 2001 From: Bartok9 <259807879+Bartok9@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:02:59 -0400 Subject: [PATCH 1/2] Stamp new migrations with wall-clock time omarchy-dev-add-migration used HEAD's committer date, so every branch cut from the same commit minted the same id and later migrations silently never ran. Use date +%s and bump on collision. --- bin/omarchy-dev-add-migration | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bin/omarchy-dev-add-migration b/bin/omarchy-dev-add-migration index bed8af3ffd2..ccd47e572ad 100755 --- a/bin/omarchy-dev-add-migration +++ b/bin/omarchy-dev-add-migration @@ -31,7 +31,16 @@ done cd "${OMARCHY_PATH:-$(pwd)}" mkdir -p migrations -migration_file="migrations/$(git log -1 --format=%cd --date=unix).sh" + +# Use wall-clock time, not HEAD's committer date, so two branches cut from the +# same commit do not mint the same migration id (#13516). +id=$(date +%s) +migration_file="migrations/${id}.sh" +while [[ -e $migration_file ]]; do + id=$((id + 1)) + migration_file="migrations/${id}.sh" +done + touch "$migration_file" if (( ! no_edit )); then From d4797b344610439b11ffed1faac0a68e5da508d8 Mon Sep 17 00:00:00 2001 From: Bartok9 <259807879+Bartok9@users.noreply.github.com> Date: Mon, 28 Sep 2026 18:26:36 -0400 Subject: [PATCH 2/2] Make migration id claim atomic and add regression tests Use noclobber O_EXCL create so concurrent helper runs in one tree cannot mint the same path. Cover wall-clock naming and the race. --- bin/omarchy-dev-add-migration | 8 ++-- test/shell.d/dev-add-migration-race-test.sh | 33 +++++++++++++++++ test/shell.d/dev-add-migration-test.sh | 41 +++++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) create mode 100755 test/shell.d/dev-add-migration-race-test.sh create mode 100755 test/shell.d/dev-add-migration-test.sh diff --git a/bin/omarchy-dev-add-migration b/bin/omarchy-dev-add-migration index ccd47e572ad..87de6e0bf92 100755 --- a/bin/omarchy-dev-add-migration +++ b/bin/omarchy-dev-add-migration @@ -36,12 +36,14 @@ mkdir -p migrations # same commit do not mint the same migration id (#13516). id=$(date +%s) migration_file="migrations/${id}.sh" -while [[ -e $migration_file ]]; do +# noclobber creates the file with O_EXCL, so concurrent runs cannot claim one id. +set -o noclobber +until { : >"$migration_file"; } 2>/dev/null; do + [[ -e $migration_file ]] || { echo "Cannot create $PWD/$migration_file" >&2; exit 1; } id=$((id + 1)) migration_file="migrations/${id}.sh" done - -touch "$migration_file" +set +o noclobber if (( ! no_edit )); then nvim "$migration_file" diff --git a/test/shell.d/dev-add-migration-race-test.sh b/test/shell.d/dev-add-migration-race-test.sh new file mode 100755 index 00000000000..46b1bffb902 --- /dev/null +++ b/test/shell.d/dev-add-migration-race-test.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +# Pin the clock so both runs start from the same second. +stub_bin="$test_tmp/bin" +mkdir -p "$stub_bin" +cat >"$stub_bin/date" <<'SH' +#!/bin/bash +[[ ${1:-} == "+%s" ]] || exit 1 +echo 1790700000 +SH +chmod +x "$stub_bin/date" + +for trial in $(seq 1 20); do + checkout="$test_tmp/omarchy-$trial" + mkdir -p "$checkout/migrations" + + PATH="$stub_bin:$PATH" OMARCHY_PATH="$checkout" "$ROOT/bin/omarchy-dev-add-migration" --no-edit >"$test_tmp/$trial.a" & + first=$! + PATH="$stub_bin:$PATH" OMARCHY_PATH="$checkout" "$ROOT/bin/omarchy-dev-add-migration" --no-edit >"$test_tmp/$trial.b" & + second=$! + wait "$first" "$second" + + [[ $(<"$test_tmp/$trial.a") != "$(<"$test_tmp/$trial.b")" ]] || + fail "two concurrent runs in one checkout create distinct migrations" "trial $trial: both printed $(<"$test_tmp/$trial.a")" +done +pass "two concurrent runs in one checkout create distinct migrations" diff --git a/test/shell.d/dev-add-migration-test.sh b/test/shell.d/dev-add-migration-test.sh new file mode 100755 index 00000000000..3859560bb68 --- /dev/null +++ b/test/shell.d/dev-add-migration-test.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +# A checkout whose HEAD was committed at 1789095456, the id #13516 saw reused. +checkout="$test_tmp/omarchy" +mkdir -p "$checkout/migrations" +git -C "$checkout" init -q +GIT_AUTHOR_NAME=test GIT_AUTHOR_EMAIL=test@example.invalid \ +GIT_COMMITTER_NAME=test GIT_COMMITTER_EMAIL=test@example.invalid \ +GIT_AUTHOR_DATE=@1789095456 GIT_COMMITTER_DATE=@1789095456 \ + git -C "$checkout" commit --allow-empty -q -m base + +# Pin the clock so the expected ids are exact. +stub_bin="$test_tmp/bin" +mkdir -p "$stub_bin" +cat >"$stub_bin/date" <<'SH' +#!/bin/bash +[[ ${1:-} == "+%s" ]] || exit 1 +echo 1790700000 +SH +chmod +x "$stub_bin/date" + +add_migration() { + PATH="$stub_bin:$PATH" OMARCHY_PATH="$checkout" "$ROOT/bin/omarchy-dev-add-migration" --no-edit +} + +created=$(add_migration) +[[ $created == "$checkout/migrations/1790700000.sh" && -f $created ]] || + fail "a new migration is named after the current time, not HEAD's commit time" "created: $created" +pass "a new migration is named after the current time, not HEAD's commit time" + +created=$(add_migration) +[[ $created == "$checkout/migrations/1790700001.sh" && -f $created ]] || + fail "a second migration in the same second takes the next free id" "created: $created" +pass "a second migration in the same second takes the next free id"