Skip to content

Commit c098a18

Browse files
authored
Make start-translation fail loud instead of exiting 0 when submodule discovery finds nothing. (#69)
* make start-translation fail loud instead of exiting 0 when submodule discovery finds nothing * fix the coderabbitai review comments * fix the second reviewer comments * fix the coderabbitai review comments
1 parent 76e6aab commit c098a18

5 files changed

Lines changed: 100 additions & 28 deletions

File tree

.github/workflows/assets/lib.sh

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,19 @@ ensure_local_branch_in_translations() {
154154
local branch="${LOCAL_BRANCH_PREFIX}${lang_code}"
155155
if git -C "$dir" ls-remote --exit-code --heads origin "$branch" &>/dev/null; then
156156
echo " Branch $branch already exists in $TRANSLATIONS_REPO." >&2
157-
else
158-
echo " Creating branch $branch in $TRANSLATIONS_REPO from $MASTER_BRANCH..." >&2
159-
git -C "$dir" checkout -B "$MASTER_BRANCH" "origin/$MASTER_BRANCH"
160-
git -C "$dir" checkout -b "$branch"
161-
rm -rf "$dir/libs" "$dir/.gitmodules"
162-
git -C "$dir" rm -rf --cached libs .gitmodules 2>/dev/null || true
163-
if ! git -C "$dir" diff --cached --quiet; then
164-
git -C "$dir" commit -m "Init $branch"
165-
fi
166-
git -C "$dir" push -u origin "$branch"
167-
echo " Created branch $branch." >&2
157+
return 0
158+
fi
159+
echo " Creating branch $branch in $TRANSLATIONS_REPO from $MASTER_BRANCH..." >&2
160+
git -C "$dir" checkout -B "$MASTER_BRANCH" "origin/$MASTER_BRANCH" || return 1
161+
git -C "$dir" checkout -b "$branch" || return 1
162+
rm -rf "$dir/libs" "$dir/.gitmodules"
163+
git -C "$dir" rm -rf --cached libs .gitmodules 2>/dev/null || true
164+
if ! git -C "$dir" diff --cached --quiet; then
165+
git -C "$dir" commit -m "Init $branch" || return 1
168166
fi
167+
git -C "$dir" push -u origin "$branch" || return 1
168+
echo " Created branch $branch." >&2
169+
return 0
169170
}
170171

171172
ensure_translations_cloned() {

.github/workflows/assets/submodule_ops.sh

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ libs_submodule_names_from_gitmodules_file() {
4747
| sed 's|^libs/||'
4848
}
4949

50+
# Return 1 when .gitmodules has no libs/ submodule paths.
51+
require_libs_submodules_in_gitmodules() {
52+
local gitmodules_file="$1"
53+
local names
54+
names=$(libs_submodule_names_from_gitmodules_file "$gitmodules_file")
55+
if [[ -z "$names" ]]; then
56+
phase_err "No libs/ submodules in .gitmodules. Run add-submodules first."
57+
return 1
58+
fi
59+
}
60+
5061
# Fetch ${BOOST_ORG}/boost .gitmodules at ref; print raw content. Return 1 on failure.
5162
fetch_boost_gitmodules_at_ref() {
5263
local ref="$1"
@@ -100,7 +111,10 @@ process_submodule_list() {
100111
sub="${names[$i]}"
101112
echo "[$(( i + 1 ))/$total] $sub ..." >&2
102113
if "$processor" "$sub"; then
103-
record_submodule_update "$sub" || true
114+
if ! record_submodule_update "$sub"; then
115+
record_submodule_fatal "$sub"
116+
submodule_fatal=$((submodule_fatal + 1))
117+
fi
104118
else
105119
rc=$?
106120
if [[ $rc -eq 2 ]]; then

.github/workflows/start-translation.yml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
# Jobs:
1111
# setup — validate lang_codes and emit matrix JSON.
1212
# sync-mirrors — sync mirror master for all submodules; update super-repo master.
13+
# Zero libs/ entries in .gitmodules is a fatal precondition (run add-submodules first).
1314
# On partial submodule failure: finalize successful pointers, then exit non-zero.
1415
# start-local (matrix per lang) — local-{lang_code} in mirrors + super-repo + Weblate.
15-
# Runs per language from setup when sync-mirrors produced successes (see job if:).
16-
# Processes only submodules that succeeded in sync-mirrors.
16+
# Runs per language when sync-mirrors succeeded (see job if:).
17+
# Requires a non-empty updated_submodules handoff; empty array is a job failure.
1718
#
1819
# client_payload:
1920
# lang_codes: (optional) Comma-separated lang codes (e.g. zh_Hans,ja). Defaults to vars.LANG_CODES.
@@ -129,14 +130,12 @@ jobs:
129130
exit 1
130131
}
131132
132-
mapfile -t submodule_names < <(libs_submodule_names_from_gitmodules_file ".gitmodules")
133-
134-
[[ ${#submodule_names[@]} -eq 0 ]] && {
135-
echo "No libs/ submodules in .gitmodules, nothing to sync." >&2
136-
echo "updated_submodules=[]" >> "$GITHUB_OUTPUT"
133+
require_libs_submodules_in_gitmodules ".gitmodules" || {
137134
end_phase
138-
exit 0
135+
exit 1
139136
}
137+
138+
mapfile -t submodule_names < <(libs_submodule_names_from_gitmodules_file ".gitmodules")
140139
end_phase
141140
142141
begin_phase "$PHASE_PROCESS_SUBMODULES" "Sync mirror master"
@@ -168,8 +167,8 @@ jobs:
168167
fi
169168
170169
start-local:
171-
# always() still evaluates outputs when sync-mirrors fails; updated_submodules is only set on full success.
172-
if: ${{ always() && !cancelled() && needs.setup.result == 'success' && needs.sync-mirrors.outputs.updated_submodules != '' && needs.sync-mirrors.outputs.updated_submodules != '[]' }}
170+
# Run when sync-mirrors succeeded; empty updated_submodules is a fatal error inside the job.
171+
if: ${{ always() && !cancelled() && needs.setup.result == 'success' && needs.sync-mirrors.result == 'success' }}
173172
needs: [setup, sync-mirrors]
174173
runs-on: ubuntu-latest
175174
strategy:
@@ -241,9 +240,9 @@ jobs:
241240
mapfile -t submodule_names < <(parse_submodule_names_json "${SYNC_MIRROR_UPDATES:-[]}")
242241
243242
[[ ${#submodule_names[@]} -eq 0 ]] && {
244-
echo "No submodules succeeded in sync-mirrors, nothing to process." >&2
243+
phase_err "No submodules from sync-mirrors to process (updated_submodules is empty)."
245244
end_phase
246-
exit 0
245+
exit 1
247246
}
248247
end_phase
249248

docs/ARCHITECTURE.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,16 @@ On partial submodule failure, **`sync-mirrors`** still finalizes successful subm
224224
pointers in the super-repo via **`finalize_translations_master`**, then exits non-zero
225225
when **`combine_batch_and_finalize_rc`** reports failure. In that case it does **not**
226226
set the job output **`updated_submodules`** (only written when **`exit_rc == 0`**), so
227-
**`start-local`** is skipped: its **`if:`** requires a non-empty **`updated_submodules`**
228-
JSON array. Operators may expect partial mirror success to still hand off per-language
229-
Weblate work, but the workflow gate requires **full** **`sync-mirrors`** success before
230-
any **`start-local`** matrix job runs.
227+
**`start-local`** is skipped: its **`if:`** requires **`sync-mirrors`** job success.
228+
Operators may expect partial mirror success to still hand off per-language Weblate work,
229+
but the workflow gate requires **full** **`sync-mirrors`** success before any
230+
**`start-local`** matrix job runs.
231+
232+
**Precondition guards.** Zero **`libs/`** entries in this repo’s **`.gitmodules`** is a
233+
fatal error in **`sync-mirrors`** (`Run add-submodules first.`). When **`sync-mirrors`**
234+
succeeds but **`updated_submodules`** is an empty JSON array (all submodules non-fatally
235+
skipped), **`start-local`** runs and exits non-zero with a distinct empty-handoff message
236+
rather than silently succeeding.
231237

232238
### 6.4 Related conventions (not the batch 0/1/2 contract)
233239

tests/test_submodule_ops.bats

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,43 @@ EOF
3636
[ "$output" = "json" ]
3737
}
3838

39+
@test "require_libs_submodules_in_gitmodules: succeeds when libs/ paths exist" {
40+
local gitmodules i name
41+
gitmodules="$(mktemp)"
42+
for i in {1..256}; do
43+
name="lib${i}"
44+
cat >>"$gitmodules" <<EOF
45+
[submodule "libs/${name}"]
46+
path = libs/${name}
47+
EOF
48+
done
49+
run bash -c '
50+
set -o pipefail
51+
# shellcheck source=tests/helpers/common.bash
52+
source "$1/helpers/common.bash"
53+
load_submodule_ops
54+
require_libs_submodules_in_gitmodules "$2"
55+
' _ "$BATS_TEST_DIRNAME" "$gitmodules"
56+
rm -f "$gitmodules"
57+
[ "$status" -eq 0 ]
58+
}
59+
60+
@test "require_libs_submodules_in_gitmodules: fails when no libs/ paths" {
61+
local gitmodules
62+
gitmodules="$(mktemp)"
63+
: >"$gitmodules"
64+
run bash -c '
65+
set -o pipefail
66+
# shellcheck source=tests/helpers/common.bash
67+
source "$1/helpers/common.bash"
68+
load_submodule_ops
69+
require_libs_submodules_in_gitmodules "$2"
70+
' _ "$BATS_TEST_DIRNAME" "$gitmodules"
71+
rm -f "$gitmodules"
72+
[ "$status" -eq 1 ]
73+
[[ "$output" == *"Run add-submodules first."* ]]
74+
}
75+
3976
@test "resolve_add_submodules_names: uses SUBMODULES when set" {
4077
SUBMODULES="algorithm, system"
4178
LIBS_REF="develop"
@@ -95,6 +132,21 @@ EOF
95132
process_submodule_list stub_processor ok ok
96133
}
97134

135+
@test "process_submodule_list: buckets record_submodule_update failures as fatal" {
136+
stub_processor() { return 0; }
137+
record_submodule_update() { return 1; }
138+
139+
set +e
140+
process_submodule_list stub_processor bad_name
141+
status=$?
142+
set -e
143+
[ "$status" -eq 0 ]
144+
[ "$submodule_fatal" -eq 1 ]
145+
[ "${#SUBMODULE_FATAL[@]}" -eq 1 ]
146+
[ "${SUBMODULE_FATAL[0]}" = "bad_name" ]
147+
[ "${#UPDATES[@]}" -eq 0 ]
148+
}
149+
98150
@test "combine_batch_and_finalize_rc: zero when no failures" {
99151
submodule_fatal=0
100152
run combine_batch_and_finalize_rc 0

0 commit comments

Comments
 (0)