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
4 changes: 4 additions & 0 deletions .github/workflows/add-submodules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ on:
# Must match EVENT_ADD_SUBMODULES in assets/env.sh
types: [add-submodules]

concurrency:
group: add-submodules-${{ github.ref }}
cancel-in-progress: true

jobs:
add-submodules:
runs-on: ubuntu-latest
Expand Down
18 changes: 17 additions & 1 deletion .github/workflows/assets/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -198,16 +198,32 @@ update_translations_submodule() {
fi
}

git_push_supports_force_with_lease() {
git push -h 2>&1 | grep -qF 'force-with-lease'
}

commit_and_push_translations_branch() {
local dir="$1" branch="$2" libs_ref="$3" force="${4:-false}"
local push_rc remote_sha
git -C "$dir" status --short
if git -C "$dir" diff --cached --quiet; then
echo " No staged submodule changes on $branch; skipping commit." >&2
else
git -C "$dir" commit -m "Update libs submodules to $libs_ref"
fi
if [[ "$force" == "true" ]]; then
git -C "$dir" push --force-with-lease origin "$branch"
if ! git_push_supports_force_with_lease; then
phase_err "git push --force-with-lease is not supported by this Git installation"
return 1
Comment thread
whisper67265 marked this conversation as resolved.
fi
if git -C "$dir" push --force-with-lease origin "$branch"; then
:
else
push_rc=$?
remote_sha=$(git -C "$dir" ls-remote --heads origin "$branch" | awk '{print $1}')
phase_err "force-with-lease push rejected for branch $branch (remote HEAD=${remote_sha:-unknown}); remote may have advanced concurrently — re-run after fetch or resolve manually."
return "$push_rc"
fi
else
git -C "$dir" push origin "$branch"
fi
Expand Down
107 changes: 107 additions & 0 deletions tests/helpers/git_fixtures.bash
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,110 @@ cleanup_github_url_rewrite() {
unset GITHUB_URL_REWRITE_KEY
fi
}

# Push a new commit onto a remote branch from a side clone (concurrent writer simulation).
push_commit_to_remote_branch() {
local bare="$1" branch="$2" message="${3:-concurrent advance}"
local tmp
tmp="$(mktemp -d)"
git clone "$bare" "$tmp"
git -C "$tmp" config user.email "test@test.local"
git -C "$tmp" config user.name "Test"
git -C "$tmp" checkout "$branch"
echo "$message" >>"$tmp/README"
git -C "$tmp" add README
git -C "$tmp" commit -m "$message"
git -C "$tmp" push origin "$branch"
rm -rf "$tmp"
}

# Prepend a git wrapper to PATH that advances remote branch before push --force-with-lease.
install_git_push_pre_hook() {
local bare="$1" branch="$2" message="${3:-concurrent advance}"
local wrapper_dir="$GIT_FIXTURE_ROOT/bin"
REAL_GIT="$(command -v git)"
export REAL_GIT
export GIT_HOOK_BARE_REMOTE="$bare" GIT_HOOK_BRANCH="$branch" GIT_HOOK_MESSAGE="$message"
mkdir -p "$wrapper_dir"
cat >"$wrapper_dir/git" <<'EOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "-C" && "${3:-}" == "push" && "$*" == *"--force-with-lease"* ]]; then
tmp="$(mktemp -d)"
"$REAL_GIT" clone "$GIT_HOOK_BARE_REMOTE" "$tmp"
"$REAL_GIT" -C "$tmp" config user.email "test@test.local"
"$REAL_GIT" -C "$tmp" config user.name "Test"
"$REAL_GIT" -C "$tmp" checkout "$GIT_HOOK_BRANCH"
echo "$GIT_HOOK_MESSAGE" >>"$tmp/README"
"$REAL_GIT" -C "$tmp" add README
"$REAL_GIT" -C "$tmp" commit -m "$GIT_HOOK_MESSAGE"
"$REAL_GIT" -C "$tmp" push origin "$GIT_HOOK_BRANCH"
rm -rf "$tmp"
fi
exec "$REAL_GIT" "$@"
EOF
chmod +x "$wrapper_dir/git"
GIT_WRAPPER_DIR="$wrapper_dir"
export PATH="$wrapper_dir:$PATH"
}

restore_git_push_pre_hook() {
if [[ -n "${GIT_WRAPPER_DIR:-}" ]]; then
PATH="${PATH#"$GIT_WRAPPER_DIR:"}"
export PATH
rm -rf "$GIT_WRAPPER_DIR"
unset GIT_WRAPPER_DIR REAL_GIT
unset GIT_HOOK_BARE_REMOTE GIT_HOOK_BRANCH GIT_HOOK_MESSAGE GIT_FETCH_COUNTER_FILE
fi
}

# Prepend a git wrapper that hides --force-with-lease from push -h (simulates pre-2.8 git).
install_git_without_force_with_lease() {
local wrapper_dir="$GIT_FIXTURE_ROOT/bin"
REAL_GIT="$(command -v git)"
export REAL_GIT
mkdir -p "$wrapper_dir"
cat >"$wrapper_dir/git" <<'EOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "push" && "${2:-}" == "-h" ]]; then
"$REAL_GIT" push -h 2>&1 | grep -v 'force-with-lease'
exit 0
fi
exec "$REAL_GIT" "$@"
EOF
chmod +x "$wrapper_dir/git"
GIT_WRAPPER_DIR="$wrapper_dir"
export PATH="$wrapper_dir:$PATH"
}

# Prepend a git wrapper that counts fetch invocations and optionally injects concurrent push.
install_git_fetch_counter() {
local counter_file="$1"
local bare="${2:-}" branch="${3:-}" message="${4:-concurrent advance}"
local wrapper_dir="$GIT_FIXTURE_ROOT/bin"
REAL_GIT="$(command -v git)"
export REAL_GIT GIT_FETCH_COUNTER_FILE="$counter_file"
export GIT_HOOK_BARE_REMOTE="$bare" GIT_HOOK_BRANCH="$branch" GIT_HOOK_MESSAGE="$message"
mkdir -p "$wrapper_dir"
cat >"$wrapper_dir/git" <<'EOF'
#!/usr/bin/env bash
if [[ "${1:-}" == "-C" && "${3:-}" == "fetch" ]]; then
echo 1 >>"$GIT_FETCH_COUNTER_FILE"
fi
if [[ "${1:-}" == "-C" && "${3:-}" == "push" && "$*" == *"--force-with-lease"* && -n "${GIT_HOOK_BARE_REMOTE:-}" ]]; then
tmp="$(mktemp -d)"
"$REAL_GIT" clone "$GIT_HOOK_BARE_REMOTE" "$tmp"
"$REAL_GIT" -C "$tmp" config user.email "test@test.local"
"$REAL_GIT" -C "$tmp" config user.name "Test"
"$REAL_GIT" -C "$tmp" checkout "$GIT_HOOK_BRANCH"
echo "$GIT_HOOK_MESSAGE" >>"$tmp/README"
"$REAL_GIT" -C "$tmp" add README
"$REAL_GIT" -C "$tmp" commit -m "$GIT_HOOK_MESSAGE"
"$REAL_GIT" -C "$tmp" push origin "$GIT_HOOK_BRANCH"
rm -rf "$tmp"
fi
exec "$REAL_GIT" "$@"
EOF
chmod +x "$wrapper_dir/git"
GIT_WRAPPER_DIR="$wrapper_dir"
export PATH="$wrapper_dir:$PATH"
}
96 changes: 94 additions & 2 deletions tests/test_lib.bats
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ setup() {
load_lib
}

teardown() {
# shellcheck source=tests/helpers/git_fixtures.bash
source "$BATS_TEST_DIRNAME/helpers/git_fixtures.bash"
restore_git_push_pre_hook
cleanup_git_fixture_root
}

@test "parse_list: empty input produces no output" {
run parse_list ""
[ "$status" -eq 0 ]
Expand Down Expand Up @@ -242,8 +249,6 @@ setup() {
finalize_translations_local "$trans_dir" "develop" "en"
[ "${#SYNC_CALLS[@]}" -eq 1 ]
[ "${SYNC_CALLS[0]}" = "branch=${LOCAL_BRANCH_PREFIX}en force=true" ]

cleanup_git_fixture_root
}

@test "finalize_translations_repo: syncs master then each local branch" {
Expand Down Expand Up @@ -295,6 +300,93 @@ setup() {
cleanup_git_fixture_root
}

@test "finalize_translations_local: rejects stale force-with-lease push" {
# shellcheck source=tests/helpers/git_fixtures.bash
source "$BATS_TEST_DIRNAME/helpers/git_fixtures.bash"
init_git_fixture_root
create_bare_remote_with_clone "translations"
create_remote_branch "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" "$MASTER_BRANCH"
trans_dir="$GIT_FIXTURE_ROOT/translations-work"
git clone "$BARE_REMOTE" "$trans_dir"
set_git_bot_config "$trans_dir"

UPDATES=("algorithm")
update_translations_submodule() { :; }

install_git_push_pre_hook "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" "concurrent advance"

local stderr_file="$BATS_TMPDIR/finalize-lease-stderr"
set +e
finalize_translations_local "$trans_dir" "develop" "en" 2>"$stderr_file"
rc=$?
set -e

[ "$rc" -ne 0 ]
remote_sha=$(git ls-remote --heads "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" | awk '{print $1}')
grep -q "${LOCAL_BRANCH_PREFIX}en" "$stderr_file"
grep -q "$remote_sha" "$stderr_file"
grep -q "force-with-lease push rejected" "$stderr_file"
[ -z "$(git -C "$trans_dir" status --porcelain)" ]
[ ! -f "$trans_dir/.git/index.lock" ]
git --git-dir="$BARE_REMOTE" show -s --format=%s "$remote_sha" | grep -q "concurrent advance"
}

@test "finalize_translations_local: fail-fast on lease rejection (no retry)" {
# shellcheck source=tests/helpers/git_fixtures.bash
source "$BATS_TEST_DIRNAME/helpers/git_fixtures.bash"
init_git_fixture_root
create_bare_remote_with_clone "translations"
create_remote_branch "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" "$MASTER_BRANCH"
trans_dir="$GIT_FIXTURE_ROOT/translations-work"
git clone "$BARE_REMOTE" "$trans_dir"
set_git_bot_config "$trans_dir"

UPDATES=("algorithm")
update_translations_submodule() { :; }

local fetch_counter="$BATS_TMPDIR/fetch-count"
: >"$fetch_counter"
install_git_fetch_counter "$fetch_counter" "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" "concurrent advance"

remote_sha_before=$(git ls-remote --heads "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" | awk '{print $1}')

set +e
finalize_translations_local "$trans_dir" "develop" "en" 2>/dev/null
rc=$?
set -e

[ "$rc" -ne 0 ]
[ "$(wc -l <"$fetch_counter")" -eq 1 ]
remote_sha_after=$(git ls-remote --heads "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" | awk '{print $1}')
[ "$remote_sha_before" != "$remote_sha_after" ]
git --git-dir="$BARE_REMOTE" show -s --format=%s "$remote_sha_after" | grep -q "concurrent advance"
}

@test "commit_and_push_translations_branch: clear error when force-with-lease unsupported" {
# shellcheck source=tests/helpers/git_fixtures.bash
source "$BATS_TEST_DIRNAME/helpers/git_fixtures.bash"
init_git_fixture_root
create_bare_remote_with_clone "translations"
create_remote_branch "$BARE_REMOTE" "${LOCAL_BRANCH_PREFIX}en" "$MASTER_BRANCH"
trans_dir="$GIT_FIXTURE_ROOT/translations-work"
git clone "$BARE_REMOTE" "$trans_dir"
set_git_bot_config "$trans_dir"
git -C "$trans_dir" checkout "${LOCAL_BRANCH_PREFIX}en"

install_git_without_force_with_lease

local stderr_file="$BATS_TMPDIR/push-unsupported-stderr"
set +e
commit_and_push_translations_branch "$trans_dir" "${LOCAL_BRANCH_PREFIX}en" "develop" true \
2>"$stderr_file"
rc=$?
set -e

[ "$rc" -ne 0 ]
grep -q "force-with-lease is not supported" "$stderr_file"
grep -q "not supported by this Git installation" "$stderr_file"
}

@test "begin_phase and end_phase: emit group markers and track CURRENT_PHASE" {
local out_file="$BATS_TMPDIR/phase.out"

Expand Down