diff --git a/.github/workflows/assets/README.md b/.github/workflows/assets/README.md index 3a822e7..22c4bc5 100644 --- a/.github/workflows/assets/README.md +++ b/.github/workflows/assets/README.md @@ -13,9 +13,11 @@ PR is merged into a `local-{lang_code}` branch. **Condition:** PR must be merged (`github.event.pull_request.merged == true`) and the head branch must start with `translation-` (Weblate-created branches). -**Bot identity:** The “Create and push tag” step sets -`user.email` to `Boost-Translation-CI-Bot@cppalliance.local`, matching the -orchestration bot pattern in [`env.sh`](env.sh) for the mirror’s GitHub org. +**Bot identity:** The “Create and push tag” step sources [`env.sh`](env.sh) and +[`lib.sh`](lib.sh), then calls **`set_git_bot_config "$GITHUB_WORKSPACE"`** so +`user.name` / `user.email` match the orchestration bot pattern: +**`BOT_EMAIL="Boost-Translation-CI-Bot@$ORG.local"`** (with **`ORG`** derived from +**`GITHUB_REPOSITORY`** in the mirror repo). **How it works:** diff --git a/.github/workflows/assets/add_submodules.sh b/.github/workflows/assets/add_submodules.sh index 733c412..27c4623 100644 --- a/.github/workflows/assets/add_submodules.sh +++ b/.github/workflows/assets/add_submodules.sh @@ -30,7 +30,6 @@ create_new_repo_and_push() { git -C "$sub_clone" remote remove origin 2>/dev/null || true git -C "$sub_clone" remote add origin "$repo_url" || return 2 git -C "$sub_clone" push -u origin "$MASTER_BRANCH" || return 2 - git -C "$sub_clone" push origin "$MASTER_BRANCH" || return 2 for lang_code in "${lang_codes_arr[@]}"; do local local_br="${LOCAL_BRANCH_PREFIX}${lang_code}" git -C "$sub_clone" checkout -B "$local_br" "$MASTER_BRANCH" || return 2 diff --git a/.github/workflows/assets/create-tag.yml b/.github/workflows/assets/create-tag.yml index 2367c73..5f46e63 100644 --- a/.github/workflows/assets/create-tag.yml +++ b/.github/workflows/assets/create-tag.yml @@ -61,7 +61,10 @@ jobs: echo "Tag $TAGNAME already exists, skipping." exit 0 fi - git config user.name "Boost-Translation-CI-Bot" - git config user.email "Boost-Translation-CI-Bot@cppalliance.local" + # shellcheck source=assets/env.sh + source "$GITHUB_WORKSPACE/.github/workflows/assets/env.sh" + # shellcheck source=assets/lib.sh + source "$GITHUB_WORKSPACE/.github/workflows/assets/lib.sh" + set_git_bot_config "$GITHUB_WORKSPACE" git tag "$TAGNAME" git push origin "$TAGNAME" diff --git a/.github/workflows/assets/lib.sh b/.github/workflows/assets/lib.sh index 3a52072..da24cbc 100644 --- a/.github/workflows/assets/lib.sh +++ b/.github/workflows/assets/lib.sh @@ -72,7 +72,7 @@ has_open_translation_pr() { # Clone repo at branch/tag into $3. Pass "keep" as $4 to preserve .git. clone_repo() { mkdir -p "$3" - git clone --branch "$2" "$1" "$3" + git clone --branch "$2" "$1" "$3" || return 2 [[ "${4:-}" == "keep" ]] || rm -rf "$3/.git" } @@ -236,9 +236,10 @@ commit_and_push_translations_branch() { # Update one branch of the translations super-repo (checkout → update pointers → push). sync_translations_branch() { local dir="$1" branch="$2" libs_ref="$3" force="${4:-false}" + local sub git -C "$dir" checkout -B "$branch" "origin/$branch" for sub in "${UPDATES[@]}"; do - update_translations_submodule "$dir" "$MODULE_ORG" "$sub" "$branch" + update_translations_submodule "$dir" "$MODULE_ORG" "$sub" "$branch" || return 2 done commit_and_push_translations_branch "$dir" "$branch" "$libs_ref" "$force" } @@ -350,16 +351,10 @@ record_submodule_fatal() { } # Summary bucket globals; filled by sync_one_submodule before print_submodule_processing_summary. -# add-submodules uses init_add_submodule_summary_buckets for REPO_EXISTS_SKIP. init_submodule_summary_buckets() { META_MISSING=() NO_DOC_PATHS=() ORG_REPO_MISSING=() -} - -init_add_submodule_summary_buckets() { - META_MISSING=() - NO_DOC_PATHS=() REPO_EXISTS_SKIP=() } diff --git a/.github/workflows/assets/submodule_ops.sh b/.github/workflows/assets/submodule_ops.sh index ab57711..7a8aead 100644 --- a/.github/workflows/assets/submodule_ops.sh +++ b/.github/workflows/assets/submodule_ops.sh @@ -10,8 +10,9 @@ # Per-submodule batch return convention (see docs/ARCHITECTURE.md §6): # 0 = success, 1 = non-fatal skip, 2 = fatal error. # process_submodule_list collapses 2 → submodule_fatal; combine_batch_and_finalize_rc -# maps submodule_fatal to job exit 1 (finalize_rc wins when non-zero). -# shellcheck disable=SC2034,SC2154 +# maps submodule_fatal to job exit 1 (finalize_rc wins when non-zero; optional weblate_rc +# overrides both when non-zero). +# shellcheck disable=SC2034,SC2154,SC2153 # Create temp workspace dirs. Pass "with_org_work" to also set ORG_WORK. init_translation_work_dirs() { @@ -46,11 +47,11 @@ libs_submodule_names_from_gitmodules_file() { | sed 's|^libs/||' } -# Fetch boostorg/boost .gitmodules at ref; print raw content. Return 1 on failure. +# Fetch ${BOOST_ORG}/boost .gitmodules at ref; print raw content. Return 1 on failure. fetch_boost_gitmodules_at_ref() { local ref="$1" gh api \ - "repos/boostorg/boost/contents/.gitmodules?ref=$ref" \ + "repos/${BOOST_ORG}/boost/contents/.gitmodules?ref=$ref" \ -H "Accept: application/vnd.github.v3.raw" 2>/dev/null } @@ -64,7 +65,7 @@ resolve_add_submodules_names() { echo "Using ${#submodule_names[@]} submodules from input." >&2 return 0 fi - echo "Fetching .gitmodules from boostorg/boost at ${libs_ref_for_fetch}..." >&2 + echo "Fetching .gitmodules from ${BOOST_ORG}/boost at ${libs_ref_for_fetch}..." >&2 local gitmodules_content gitmodules_content=$(fetch_boost_gitmodules_at_ref "$libs_ref_for_fetch") || { phase_err "Failed to fetch .gitmodules" @@ -115,12 +116,14 @@ process_submodule_list() { return 0 } -# Combine submodule_fatal count with finalize_rc; return combined exit code. +# Combine submodule_fatal count with finalize_rc and optional weblate_rc; return combined exit code. combine_batch_and_finalize_rc() { local finalize_rc="${1:-0}" + local weblate_rc="${2:-0}" local exit_rc=0 [[ "${submodule_fatal:-0}" -gt 0 ]] && exit_rc=1 [[ "$finalize_rc" -ne 0 ]] && exit_rc=$finalize_rc + [[ "$weblate_rc" -ne 0 ]] && exit_rc=$weblate_rc return "$exit_rc" } @@ -129,7 +132,7 @@ add_submodules_main() { local rc finalize_rc exit_rc init_translation_state - init_add_submodule_summary_buckets + init_submodule_summary_buckets begin_phase "$PHASE_SETUP" "Validate inputs and prepare workspace" validate_secrets diff --git a/.github/workflows/assets/translation.sh b/.github/workflows/assets/translation.sh index 83a9178..b0c5be7 100644 --- a/.github/workflows/assets/translation.sh +++ b/.github/workflows/assets/translation.sh @@ -219,10 +219,6 @@ trigger_weblate() { add_or_update_json=$(jq -n --arg lc "$lang_code" --argjson s "$subs_json" \ '{($lc): $s}') - [[ "$add_or_update_json" == "{}" ]] && { - echo "Weblate skipped: no translations to update." >&2; return - } - local payload payload=$(jq -n \ --arg org "$MODULE_ORG" --arg ver "$libs_ref" \ diff --git a/.github/workflows/start-translation.yml b/.github/workflows/start-translation.yml index f21b860..6fa5490 100644 --- a/.github/workflows/start-translation.yml +++ b/.github/workflows/start-translation.yml @@ -59,7 +59,7 @@ jobs: source "$GITHUB_WORKSPACE/.github/workflows/assets/lib.sh" begin_phase "$PHASE_SETUP" "Validate language matrix" parse_and_validate_lang_codes - langs_json=$(printf '%s\n' "${lang_codes_arr[@]}" | jq -R . | jq -s -c .) + langs_json=$(submodule_names_to_json "${lang_codes_arr[@]}") echo "json=$langs_json" >> "$GITHUB_OUTPUT" echo "Lang codes: ${lang_codes_arr[*]}" >&2 end_phase @@ -282,10 +282,6 @@ jobs: echo "Skipping Weblate add-or-update: finalize_translations_local failed (rc=$rc)." >&2 fi - exit_rc=0 - [[ $submodule_fatal -gt 0 ]] && exit_rc=1 - [[ $rc -ne 0 ]] && exit_rc=$rc - [[ $weblate_rc -ne 0 ]] && exit_rc=$weblate_rc - [[ $exit_rc -ne 0 ]] && exit $exit_rc + combine_batch_and_finalize_rc "$rc" "$weblate_rc" || exit $? echo "Done." >&2 diff --git a/.github/workflows/sync-translation.yml b/.github/workflows/sync-translation.yml index 50fd417..bf66bc0 100644 --- a/.github/workflows/sync-translation.yml +++ b/.github/workflows/sync-translation.yml @@ -74,7 +74,7 @@ jobs: done validate_lang_codes "${lang_codes_arr[@]}" - langs_json=$(printf '%s\n' "${lang_codes_arr[@]}" | jq -R . | jq -s -c .) + langs_json=$(submodule_names_to_json "${lang_codes_arr[@]}") echo "json=$langs_json" >> "$GITHUB_OUTPUT" end_phase @@ -111,8 +111,7 @@ jobs: # shellcheck source=assets/lib.sh source "$GITHUB_WORKSPACE/.github/workflows/assets/lib.sh" - git config user.name "$BOT_NAME" - git config user.email "$BOT_EMAIL" + set_git_bot_config "$GITHUB_WORKSPACE" branch="${LOCAL_BRANCH_PREFIX}${SYNC_LANG}" begin_phase "$PHASE_SYNC_POINTERS" "Sync pointers on $branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 297c4b8..2fdcefd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,12 +25,28 @@ are a separate namespace — see [README](README.md#releases) and ### Changed +- Extracted shared dispatch helpers into + [`scripts/trigger-dispatch-common.sh`](scripts/trigger-dispatch-common.sh) + (`DEFAULT_REPO`, `DEFAULT_VERSION`, JSON build, `POST …/dispatches`); both + `trigger-*.sh` wrappers source it. +- Mirror **`create-tag.yml`** bot identity now uses **`set_git_bot_config`** from + **`env.sh`** / **`lib.sh`** instead of a hardcoded org email. - Renamed and expanded operator quick reference to [GETTING-STARTED.md](docs/GETTING-STARTED.md): end-to-end walkthrough with per-step verification and create-tag coverage. - [endpoint-contract.md](docs/endpoint-contract.md) Outbound Weblate section: request schema is now the source of truth for payload fields. +### Fixed + +- Clone and finalize steps now fail fast: **`clone_repo`**, **`sync_translations_branch`**, + and **`finalize_translations_*`** propagate non-zero status instead of continuing with + incomplete state. +- **`combine_batch_and_finalize_rc`** collapses all three exit sources—batch + **`submodule_fatal`**, **`finalize_rc`**, and optional **`weblate_rc`**—with documented + last-wins priority; **`start-translation.yml`** **`start-local`** delegates to it instead + of inline collapse logic. + ## [1.0.0] - 2026-07-07 Initial semver baseline for the orchestration repo. Summarizes the operator and diff --git a/README.md b/README.md index 5734bea..3613167 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,8 @@ From a clone of this repo: - **`scripts/trigger-add-submodules.sh`** — fires **`add-submodules`**. - **`scripts/trigger-start-translation.sh`** — fires **`start-translation`** (optional **`--version`**, **`--lang-codes`**, **`--extensions`**). +- **`scripts/trigger-dispatch-common.sh`** — shared by both triggers: **`DEFAULT_REPO`**, + **`DEFAULT_VERSION`**, JSON payload build, and **`POST …/dispatches`**. These wrappers use only **`exit 0`** (success, including **`--help`**) and **`exit 1`** (all errors); they do not implement the asset-script 0/1/2 batch return convention @@ -207,7 +209,7 @@ documented below. | Variable | Used by | Description | | ---------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `LANG_CODES` | `add-submodules`, `start-translation` | Default language codes when **`client_payload.lang_codes`** is omitted (comma- or bracket-list, e.g. `zh_Hans,ja`). Must be set here or passed in the dispatch payload. | -| `SUBMODULES_ORG` | `add-submodules`, `start-translation` | Optional. GitHub org for **`boostorg`** mirror repos (e.g. `CppDigest`). If unset, the org is the same as this repository’s owner. **`sync-translation`** relies on **`.gitmodules`** URLs already pointing at the correct hosts. | +| `SUBMODULES_ORG` | `add-submodules`, `start-translation` | Optional. GitHub org for per-library mirror repos under **`MODULE_ORG`** (e.g. `CppDigest`). If unset, **`MODULE_ORG`** defaults to this repository’s owner. Upstream Boost libraries are cloned from **`boostorg`**. **`sync-translation`** relies on **`.gitmodules`** URLs already pointing at the correct hosts. | ## Development diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c61f4f8..e31953b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -94,7 +94,7 @@ moves submodule SHAs on existing **`${LOCAL_BRANCH_PREFIX}*`** branches. | **`.github/workflows/assets/env.sh`** | Derives **`ORG`**, **`TRANSLATIONS_REPO`**, **`MODULE_ORG`**, **`BOOST_ORG`**, **`MASTER_BRANCH`**, **`LOCAL_BRANCH_PREFIX`**, **`TRANSLATION_BRANCH_PREFIX`**, **`WEBLATE_ENDPOINT_PATH`**, bot identity. | | **`.github/workflows/assets/lib.sh`** | Shared implementation: GitHub **`gh`** helpers, clone/prune, **`meta/libraries.json`** parsing, translations-repo branch and submodule updates. | | **`.github/workflows/assets/create-tag.yml`** | Template copied into each mirror; tags merged Weblate PRs (see **assets/README.md**). | -| **`scripts/trigger-*.sh`** | Optional local wrappers around **`repository_dispatch`**; no server-side logic. | +| **`scripts/trigger-*.sh`** | Optional local wrappers around **`repository_dispatch`**; both source shared **[`scripts/trigger-dispatch-common.sh`](../scripts/trigger-dispatch-common.sh)** (defaults, JSON build, POST). No server-side logic. | **Dependency direction.** Inline bash in **`add-submodules.yml`**, **`start-translation.yml`**, and **`sync-translation.yml`** sources **`env.sh`** then @@ -144,7 +144,7 @@ holds **actual file** merges from **`master`** plus translator edits. ### 5.3 Pointer roll-up (**`sync-translation`**) 1. **`discover`**: list **`refs/heads/local-*`** on the super-repo; emit JSON for the matrix. -2. **`sync-local`** (matrix per **`lang_code`**, concurrency **`local-branch-{lang_code}`**): checkout one **`local-*`** branch, **`git submodule update --init`**, set **`submodule..branch`**, **`submodule update --remote`**, commit, **`push ----force-with-lease`**. +2. **`sync-local`** (matrix per **`lang_code`**, concurrency **`local-branch-{lang_code}`**): checkout one **`local-*`** branch, **`git submodule update --init`**, set **`submodule..branch`**, **`submodule update --remote`**, commit, **`push --force-with-lease`**. --- @@ -165,7 +165,6 @@ flowchart TD CB[combine_batch_and_finalize_rc] FTL[finalize_translations_local] TW[trigger_weblate] - Compose["Inline: submodule_fatal to 1, then finalize, then weblate last wins"] JobExit["Workflow step exit 0 or non-zero"] A0 -->|"record_submodule_update"| PSL @@ -173,8 +172,10 @@ flowchart TD A2 -->|"record_submodule_fatal"| PSL PSL --> CB PSL --> FTL - CB -->|"add-submodules, sync-mirrors"| JobExit - FTL --> TW --> Compose --> JobExit + FTL -->|"finalize_rc"| CB + FTL -->|"rc == 0"| TW + TW -->|"weblate_rc"| CB + CB --> JobExit ``` ### 6.1 Per-submodule batch processors @@ -204,11 +205,12 @@ Processors that follow this contract: - **`rc == 1`**: ignored for exit purposes; batch continues. - **`rc == 2`**: calls **`record_submodule_fatal`**; increments **`submodule_fatal`**. -**`combine_batch_and_finalize_rc finalize_rc`**: +**`combine_batch_and_finalize_rc finalize_rc [weblate_rc]`**: 1. Start **`exit_rc=0`**. 2. If **`submodule_fatal > 0`** → **`exit_rc=1`** (fatals are collapsed, not propagated as **`2`**). 3. If **`finalize_rc != 0`** → **`exit_rc=$finalize_rc`** (**finalize wins** over batch fatal). +4. If **`weblate_rc != 0`** (optional second arg, default **`0`**) → **`exit_rc=$weblate_rc`** (**weblate wins** over finalize and batch fatal). ### 6.3 Workflow job exit @@ -216,11 +218,16 @@ Processors that follow this contract: |---------------|----------------| | **`add-submodules.yml`** | **`add_submodules_main`** → **`combine_batch_and_finalize_rc`** | | **`start-translation.yml`** **`sync-mirrors`** | **`combine_batch_and_finalize_rc "$rc"`** after **`finalize_translations_master`** | -| **`start-translation.yml`** **`start-local`** | Inline duplicate: **`submodule_fatal > 0` → 1**, then **`finalize_translations_local`** rc, then **`trigger_weblate`** rc (last non-zero assignment wins) | +| **`start-translation.yml`** **`start-local`** | **`combine_batch_and_finalize_rc "$rc" "$weblate_rc"`** after **`finalize_translations_local`** and **`trigger_weblate`** (Weblate runs only when finalize succeeded; collapse still considers all three sources) | On partial submodule failure, **`sync-mirrors`** still finalizes successful submodule -pointers in the super-repo, then exits non-zero so downstream jobs can distinguish full -success from partial failure. +pointers in the super-repo via **`finalize_translations_master`**, then exits non-zero +when **`combine_batch_and_finalize_rc`** reports failure. In that case it does **not** +set the job output **`updated_submodules`** (only written when **`exit_rc == 0`**), so +**`start-local`** is skipped: its **`if:`** requires a non-empty **`updated_submodules`** +JSON array. Operators may expect partial mirror success to still hand off per-language +Weblate work, but the workflow gate requires **full** **`sync-mirrors`** success before +any **`start-local`** matrix job runs. ### 6.4 Related conventions (not the batch 0/1/2 contract) diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index 4484e09..1e4fd2c 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -118,11 +118,11 @@ scripts/trigger-add-submodules.sh \ - Omit **`--lang-codes`** to use repository variable **`LANG_CODES`**. - Omit **`--submodules`** to use the script default **`DEFAULT_SUBMODULES`** - (`unordered, json` at - [trigger-add-submodules.sh:40](../scripts/trigger-add-submodules.sh)) — **not** + (`unordered, json`; see + [trigger-add-submodules.sh](../scripts/trigger-add-submodules.sh#L40)) — **not** auto-discovery from **`boostorg/boost`**. The script substitutes that default before building the payload - ([line 160](../scripts/trigger-add-submodules.sh)), so `client_payload.submodules` + ([`SUBMODULES` assignment](../scripts/trigger-add-submodules.sh#L99)), so `client_payload.submodules` is never omitted. Full discovery runs only when the workflow receives a dispatch **without** a `submodules` field (raw API / GitHub UI). diff --git a/docs/endpoint-contract.md b/docs/endpoint-contract.md index 9b1177e..5829796 100644 --- a/docs/endpoint-contract.md +++ b/docs/endpoint-contract.md @@ -101,7 +101,7 @@ submodule pointer updates, and Boost release refs in `client_payload.version`. | Workflow | `.github/workflows/add-submodules.yml` | | Body shape | `{"event_type":"add-submodules","client_payload":{...}}` | | `client_payload` | All optional: `version`, `submodules` (list-like string), `lang_codes` (comma-separated). See [README](../README.md). | -| Script | `scripts/trigger-add-submodules.sh` builds JSON with `jq` or Python; omits empty optional fields. | +| Script | `scripts/trigger-add-submodules.sh` builds JSON with `jq` or Python; omits empty optional fields. Both trigger scripts source shared [`scripts/trigger-dispatch-common.sh`](../scripts/trigger-dispatch-common.sh). | ### `event_type: start-translation` @@ -110,7 +110,7 @@ submodule pointer updates, and Boost release refs in `client_payload.version`. | Workflow | `.github/workflows/start-translation.yml` | | Body shape | `{"event_type":"start-translation","client_payload":{...}}` | | `client_payload` | Optional: `version`, `lang_codes`, `extensions`. See [README](../README.md). | -| Script | `scripts/trigger-start-translation.sh` | +| Script | `scripts/trigger-start-translation.sh`; sources [`scripts/trigger-dispatch-common.sh`](../scripts/trigger-dispatch-common.sh) with `trigger-add-submodules.sh`. | ### `event_type: sync-translation` diff --git a/scripts/install-git-hooks.sh b/scripts/install-git-hooks.sh index 910fca0..75ba631 100755 --- a/scripts/install-git-hooks.sh +++ b/scripts/install-git-hooks.sh @@ -9,4 +9,4 @@ chmod +x .githooks/pre-commit scripts/lint.sh scripts/test.sh scripts/pre-commit git config core.hooksPath .githooks echo "Installed git hooks from .githooks/ (core.hooksPath=.githooks)." >&2 -echo "Pre-commit runs: scripts/pre-commit.sh (lint + make test)." >&2 +echo "Pre-commit runs: scripts/pre-commit.sh (lint + test)." >&2 diff --git a/scripts/lint.sh b/scripts/lint.sh index f9d0eb3..bf86e24 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -10,19 +10,62 @@ cd "$ROOT" SHELLCHECK_VERSION="v0.11.0" ACTIONLINT_VERSION="1.7.7" -ensure_shellcheck() { - local version="$SHELLCHECK_VERSION" - local cache_dir="$ROOT/.cache/shellcheck" - local extract_dir="$cache_dir/shellcheck-${version}" - local bin="$cache_dir/shellcheck-bin-${version}" +# Download, verify, extract, and cache a release binary under .cache/. +# Args: name version dest_var tarball expected_sha256 url inner_bin [format] +# format: xz (default) or gz +ensure_cached_binary() { + local name="$1" version="$2" dest_var="$3" tarball="$4" expected_sha256="$5" url="$6" inner_bin="$7" + local format="${8:-xz}" + local cache_dir="$ROOT/.cache/$name" + local extract_dir="$cache_dir/${name}-${version}" + local bin="$cache_dir/${name}-bin-${version}" mkdir -p "$cache_dir" if [[ -x "$bin" ]]; then - SHELLCHECK_BIN="$bin" - return + printf -v "$dest_var" '%s' "$bin" + return 0 fi - local os arch tarball url expected_sha256 + local os inner_path="$extract_dir/$inner_bin" + os="$(uname -s)" + if [[ ! -f "$cache_dir/$tarball" ]]; then + echo "lint: downloading ${name} ${version}..." >&2 + curl -fsSL -o "$cache_dir/$tarball" "$url" + fi + if [[ ! -f "$inner_path" ]]; then + if [[ "$os" == "Linux" ]]; then + echo "${expected_sha256} $cache_dir/$tarball" | sha256sum -c - + else + echo "${expected_sha256} $cache_dir/$tarball" | shasum -a 256 -c - + fi + rm -rf "$extract_dir" + if [[ "$format" == xz ]]; then + if ! tar -xJf "$cache_dir/$tarball" -C "$cache_dir"; then + rm -rf "$extract_dir" + echo "lint: failed to extract ${name} (is xz installed? apt install xz-utils)." >&2 + exit 1 + fi + else + mkdir -p "$extract_dir" + if ! tar -xzf "$cache_dir/$tarball" -C "$extract_dir"; then + rm -rf "$extract_dir" + echo "lint: failed to extract ${name}." >&2 + exit 1 + fi + fi + if [[ ! -f "$inner_path" ]]; then + rm -rf "$extract_dir" + echo "lint: expected binary missing after extracting ${name}: $inner_path" >&2 + exit 1 + fi + fi + cp "$inner_path" "$bin" + chmod +x "$bin" + printf -v "$dest_var" '%s' "$bin" +} + +ensure_shellcheck() { + local version="$SHELLCHECK_VERSION" os arch tarball expected_sha256 url os="$(uname -s)" arch="$(uname -m)" case "$os" in @@ -63,41 +106,12 @@ ensure_shellcheck() { exit 1 ;; esac - url="https://github.com/koalaman/shellcheck/releases/download/${version}/${tarball}" - if [[ ! -f "$cache_dir/$tarball" ]]; then - echo "lint: downloading shellcheck ${version}..." >&2 - curl -fsSL -o "$cache_dir/$tarball" "$url" - fi - if [[ ! -d "$extract_dir" ]]; then - if [[ "$os" == "Linux" ]]; then - echo "${expected_sha256} $cache_dir/$tarball" | sha256sum -c - - else - echo "${expected_sha256} $cache_dir/$tarball" | shasum -a 256 -c - - fi - if ! tar -xJf "$cache_dir/$tarball" -C "$cache_dir"; then - echo "lint: failed to extract shellcheck (is xz installed? apt install xz-utils)." >&2 - exit 1 - fi - fi - cp "$extract_dir/shellcheck" "$bin" - chmod +x "$bin" - SHELLCHECK_BIN="$bin" + ensure_cached_binary shellcheck "$version" SHELLCHECK_BIN "$tarball" "$expected_sha256" "$url" shellcheck xz } ensure_actionlint() { - local version="$ACTIONLINT_VERSION" - local cache_dir="$ROOT/.cache/actionlint" - local extract_dir="$cache_dir/actionlint-${version}" - local bin="$cache_dir/actionlint-bin-${version}" - mkdir -p "$cache_dir" - - if [[ -x "$bin" ]]; then - ACTIONLINT_BIN="$bin" - return - fi - - local os arch tarball expected_sha256 + local version="$ACTIONLINT_VERSION" os arch tarball expected_sha256 url os="$(uname -s)" arch="$(uname -m)" case "$os" in @@ -138,23 +152,8 @@ ensure_actionlint() { exit 1 ;; esac - if [[ ! -f "$cache_dir/$tarball" ]]; then - echo "lint: downloading actionlint ${version}..." >&2 - curl -fsSL -o "$cache_dir/$tarball" \ - "https://github.com/rhysd/actionlint/releases/download/v${version}/${tarball}" - fi - if [[ ! -d "$extract_dir" ]]; then - if [[ "$os" == "Linux" ]]; then - echo "${expected_sha256} $cache_dir/$tarball" | sha256sum -c - - else - echo "${expected_sha256} $cache_dir/$tarball" | shasum -a 256 -c - - fi - mkdir -p "$extract_dir" - tar -xzf "$cache_dir/$tarball" -C "$extract_dir" - fi - cp "$extract_dir/actionlint" "$bin" - chmod +x "$bin" - ACTIONLINT_BIN="$bin" + url="https://github.com/rhysd/actionlint/releases/download/v${version}/${tarball}" + ensure_cached_binary actionlint "$version" ACTIONLINT_BIN "$tarball" "$expected_sha256" "$url" actionlint gz } ensure_shellcheck diff --git a/scripts/trigger-add-submodules.sh b/scripts/trigger-add-submodules.sh index 480ee10..346ccf9 100644 --- a/scripts/trigger-add-submodules.sh +++ b/scripts/trigger-add-submodules.sh @@ -14,7 +14,7 @@ # scripts/trigger-add-submodules.sh [--repo OWNER/NAME] [--token PAT] \ # [--version REF] [--submodules 'a, b'] [--lang-codes zh_Hans,ja] # -# If --repo is omitted: GITHUB_REPOSITORY, then DEFAULT_REPO below, then git origin. +# If --repo is omitted: GITHUB_REPOSITORY, then git origin, then DEFAULT_REPO below. set -euo pipefail @@ -30,13 +30,13 @@ _ASSETS_DIR="$_REPO_ROOT/.github/workflows/assets" source "$_ASSETS_DIR/env.sh" # shellcheck source=/dev/null source "$_ASSETS_DIR/lib.sh" +# shellcheck source=/dev/null +source "$_REPO_ROOT/scripts/trigger-dispatch-common.sh" unset _REPO_ROOT _ASSETS_DIR # --------------------------------------------------------------------------- # Typical run — edit these. CLI flags override (except --token uses env/PAT). # --------------------------------------------------------------------------- -DEFAULT_REPO="cppalliance/boost-docs-translation" -DEFAULT_VERSION="boost-1.90.0" DEFAULT_SUBMODULES="unordered, json" usage() { @@ -51,7 +51,7 @@ Requires: curl; jq or Python 3 (python3 / python) Auth: .env (GH_TOKEN), GH_TOKEN / GITHUB_TOKEN in env, or --token (needs repo scope on the target). Options: - --repo OWNER/REPO Target repository (default: GITHUB_REPOSITORY, then DEFAULT_REPO, then origin) + --repo OWNER/REPO Target repository (default: GITHUB_REPOSITORY, then origin, then DEFAULT_REPO) --token PAT GitHub token --version REF Boost ref; default DEFAULT_VERSION in script --submodules LIST default DEFAULT_SUBMODULES in script @@ -86,108 +86,24 @@ while [[ $# -gt 0 ]]; do esac done -if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" >&2 - exit 1 -fi +require_curl || exit 1 -# Emit repository_dispatch JSON on stdout (jq preferred, else Python). -dispatch_json() { - local version="$1" submodules="$2" lang_codes="$3" - if command -v jq >/dev/null 2>&1; then - jq -n \ - --arg event_type "$EVENT_ADD_SUBMODULES" \ - --arg version "$version" \ - --arg submodules "$submodules" \ - --arg lang_codes "$lang_codes" \ - '{ - event_type: $event_type, - client_payload: ( - {} - | if ($version | length) > 0 then . + {version: $version} else . end - | if ($submodules | length) > 0 then . + {submodules: $submodules} else . end - | if ($lang_codes | length) > 0 then . + {lang_codes: $lang_codes} else . end - ) - }' - return 0 - fi - local py="" - command -v python3 >/dev/null 2>&1 && py="python3" - [[ -z "$py" ]] && command -v python >/dev/null 2>&1 && py="python" - if [[ -n "$py" ]]; then - "$py" -c "import json,sys; et,v,s,lc=sys.argv[1:5]; d={k:x for k,x in (('version',v),('submodules',s),('lang_codes',lc)) if x}; print(json.dumps({'event_type':et,'client_payload':d}))" \ - "$EVENT_ADD_SUBMODULES" "$version" "$submodules" "$lang_codes" - return 0 - fi - return 1 -} +TOKEN="$(resolve_trigger_token "$TOKEN")" || exit 1 -TOKEN="${TOKEN:-${GH_TOKEN:-${GITHUB_TOKEN:-}}}" -if [[ -z "$TOKEN" ]]; then - echo "error: set GH_TOKEN (e.g. in repo-root .env), GITHUB_TOKEN, or pass --token" >&2 +REPO="$(resolve_trigger_repo "$REPO")" || { + echo "error: could not determine repo; set DEFAULT_REPO, GITHUB_REPOSITORY, or --repo OWNER/REPO" >&2 exit 1 -fi - -infer_repo_from_git() { - local url root o r - root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1 - url="$(git -C "$root" remote get-url origin 2>/dev/null)" || return 1 - if [[ "$url" =~ github\.com[:/]([^/]+)/([^[:space:]]+) ]]; then - o="${BASH_REMATCH[1]}" - r="${BASH_REMATCH[2]}" - r="${r%.git}" - r="${r%/}" - echo "${o}/${r}" - return 0 - fi - return 1 } -if [[ -z "$REPO" ]]; then - REPO="${GITHUB_REPOSITORY:-}" -fi -if [[ -z "$REPO" ]]; then - REPO="${DEFAULT_REPO:-}" -fi -if [[ -z "$REPO" ]]; then - REPO="$(infer_repo_from_git)" || { - echo "error: could not determine repo; set DEFAULT_REPO, GITHUB_REPOSITORY, or --repo OWNER/REPO" >&2 - exit 1 - } -fi - VERSION="${VERSION:-$DEFAULT_VERSION}" SUBMODULES="${SUBMODULES:-$DEFAULT_SUBMODULES}" validate_event_type "$EVENT_ADD_SUBMODULES" -body="$(dispatch_json "$VERSION" "$SUBMODULES" "$LANG_CODES")" || { +body="$(build_dispatch_json "$EVENT_ADD_SUBMODULES" \ + version "$VERSION" submodules "$SUBMODULES" lang_codes "$LANG_CODES")" || { echo "error: install jq, or Python 3 (python3 or python on PATH), to build the request JSON" >&2 exit 1 } -resp="$(mktemp)" -trap 'rm -f "$resp"' EXIT - -url="https://api.github.com/repos/${REPO}/dispatches" -code="$( - curl -sS -o "$resp" -w '%{http_code}' \ - -X POST \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - -H "Content-Type: application/json" \ - -d "$body" \ - "$url" -)" - -if [[ "$code" == "204" ]]; then - echo "Dispatched add-submodules to ${REPO} (HTTP ${code})." - exit 0 -fi - -echo "GitHub API error: HTTP ${code}" >&2 -if [[ -s "$resp" ]]; then - cat "$resp" >&2 -fi -exit 1 +post_repository_dispatch "$REPO" "$TOKEN" "$body" "add-submodules" diff --git a/scripts/trigger-dispatch-common.sh b/scripts/trigger-dispatch-common.sh new file mode 100644 index 0000000..9579b5a --- /dev/null +++ b/scripts/trigger-dispatch-common.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# Shared helpers for repository_dispatch trigger scripts. +# Safe to source: does not enable set -euo (callers own shell options). +# shellcheck shell=bash +# shellcheck disable=SC2034 + +DEFAULT_REPO="cppalliance/boost-docs-translation" +DEFAULT_VERSION="boost-1.90.0" + +infer_repo_from_git() { + local url root o r + root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1 + url="$(git -C "$root" remote get-url origin 2>/dev/null)" || return 1 + if [[ "$url" =~ github\.com[:/]([^/]+)/([^[:space:]]+) ]]; then + o="${BASH_REMATCH[1]}" + r="${BASH_REMATCH[2]}" + r="${r%.git}" + r="${r%/}" + echo "${o}/${r}" + return 0 + fi + return 1 +} + +resolve_trigger_repo() { + local explicit="${1:-}" + if [[ -n "$explicit" ]]; then + echo "$explicit" + return 0 + fi + if [[ -n "${GITHUB_REPOSITORY:-}" ]]; then + echo "$GITHUB_REPOSITORY" + return 0 + fi + local inferred + inferred="$(infer_repo_from_git)" && { + echo "$inferred" + return 0 + } + if [[ -n "${DEFAULT_REPO:-}" ]]; then + echo "$DEFAULT_REPO" + return 0 + fi + return 1 +} + +resolve_trigger_token() { + local explicit="${1:-}" + local token="${explicit:-${GH_TOKEN:-${GITHUB_TOKEN:-}}}" + if [[ -z "$token" ]]; then + echo "error: set GH_TOKEN (e.g. in repo-root .env), GITHUB_TOKEN, or pass --token" >&2 + return 1 + fi + echo "$token" +} + +require_curl() { + if ! command -v curl >/dev/null 2>&1; then + echo "error: curl is required" >&2 + return 1 + fi +} + +# Build repository_dispatch JSON. Remaining args are alternating KEY VALUE pairs; +# keys with empty values are omitted from client_payload. +build_dispatch_json() { + local event_type="$1" + shift + if command -v jq >/dev/null 2>&1; then + local json_pairs='[]' key val + while [[ $# -gt 0 ]]; do + key="$1" + val="$2" + shift 2 + json_pairs="$(jq -n --argjson arr "$json_pairs" --arg k "$key" --arg v "$val" \ + '$arr + [{key: $k, value: $v}]')" + done + jq -n --arg event_type "$event_type" --argjson pairs "$json_pairs" \ + '{ + event_type: $event_type, + client_payload: ( + {} + | reduce pairs[] as $p ( + .; + if ($p.value | length) > 0 then . + {($p.key): $p.value} else . end + ) + ) + }' + return 0 + fi + local py="" + command -v python3 >/dev/null 2>&1 && py="python3" + [[ -z "$py" ]] && command -v python >/dev/null 2>&1 && py="python" + if [[ -n "$py" ]]; then + "$py" -c 'import json,sys +et=sys.argv[1] +pairs=sys.argv[2:] +d={} +for i in range(0,len(pairs),2): + k,v=pairs[i],pairs[i+1] + if v: + d[k]=v +print(json.dumps({"event_type":et,"client_payload":d}))' \ + "$event_type" "$@" + return 0 + fi + return 1 +} + +post_repository_dispatch() { + local repo="$1" token="$2" body="$3" label="$4" + local resp url code + resp="$(mktemp)" + # shellcheck disable=SC2064 + trap 'rm -f "$resp"' RETURN + url="https://api.github.com/repos/${repo}/dispatches" + code="$( + curl -sS -o "$resp" -w '%{http_code}' \ + -X POST \ + -H "Authorization: Bearer ${token}" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + -H "Content-Type: application/json" \ + -d "$body" \ + "$url" + )" + if [[ "$code" == "204" ]]; then + echo "Dispatched ${label} to ${repo} (HTTP ${code})." + return 0 + fi + echo "GitHub API error: HTTP ${code}" >&2 + if [[ -s "$resp" ]]; then + cat "$resp" >&2 + fi + return 1 +} diff --git a/scripts/trigger-start-translation.sh b/scripts/trigger-start-translation.sh index b9492d8..8b023ca 100755 --- a/scripts/trigger-start-translation.sh +++ b/scripts/trigger-start-translation.sh @@ -14,7 +14,7 @@ # scripts/trigger-start-translation.sh [--repo OWNER/NAME] [--token PAT] \ # [--version REF] [--lang-codes zh_Hans,ja] [--extensions '.adoc, .qbk'] # -# If --repo is omitted: GITHUB_REPOSITORY, then DEFAULT_REPO below, then git origin. +# If --repo is omitted: GITHUB_REPOSITORY, then git origin, then DEFAULT_REPO below. # # The workflow still needs repo secrets SYNC_TOKEN, WEBLATE_URL, WEBLATE_TOKEN (and # vars.LANG_CODES or lang_codes in the payload). @@ -33,6 +33,8 @@ _ASSETS_DIR="$_REPO_ROOT/.github/workflows/assets" source "$_ASSETS_DIR/env.sh" # shellcheck source=/dev/null source "$_ASSETS_DIR/lib.sh" +# shellcheck source=/dev/null +source "$_REPO_ROOT/scripts/trigger-dispatch-common.sh" unset _REPO_ROOT _ASSETS_DIR # --------------------------------------------------------------------------- @@ -41,8 +43,6 @@ unset _REPO_ROOT _ASSETS_DIR # Set DEFAULT_VERSION="" to omit version from payload → workflow uses develop. # Extensions default to .adoc and .qbk; set DEFAULT_EXTENSIONS="" to omit from payload. # --------------------------------------------------------------------------- -DEFAULT_REPO="cppalliance/boost-docs-translation" -DEFAULT_VERSION="boost-1.90.0" DEFAULT_EXTENSIONS=".adoc, .qbk" usage() { @@ -57,7 +57,7 @@ Requires: curl; jq or Python 3 (python3 / python) Auth: .env (GH_TOKEN), GH_TOKEN / GITHUB_TOKEN in env, or --token (needs repo scope on the target). Options: - --repo OWNER/REPO Target repository (default: GITHUB_REPOSITORY, then DEFAULT_REPO, then origin) + --repo OWNER/REPO Target repository (default: GITHUB_REPOSITORY, then origin, then DEFAULT_REPO) --token PAT GitHub token --version REF Boost ref; default DEFAULT_VERSION in script (clear default to omit → develop) --lang-codes CSV optional; omit → workflow uses repo vars.LANG_CODES @@ -92,108 +92,24 @@ while [[ $# -gt 0 ]]; do esac done -if ! command -v curl >/dev/null 2>&1; then - echo "error: curl is required" >&2 - exit 1 -fi +require_curl || exit 1 -# Emit repository_dispatch JSON on stdout (jq preferred, else Python). -dispatch_json() { - local version="$1" lang_codes="$2" extensions="$3" - if command -v jq >/dev/null 2>&1; then - jq -n \ - --arg event_type "$EVENT_START_TRANSLATION" \ - --arg version "$version" \ - --arg lang_codes "$lang_codes" \ - --arg extensions "$extensions" \ - '{ - event_type: $event_type, - client_payload: ( - {} - | if ($version | length) > 0 then . + {version: $version} else . end - | if ($lang_codes | length) > 0 then . + {lang_codes: $lang_codes} else . end - | if ($extensions | length) > 0 then . + {extensions: $extensions} else . end - ) - }' - return 0 - fi - local py="" - command -v python3 >/dev/null 2>&1 && py="python3" - [[ -z "$py" ]] && command -v python >/dev/null 2>&1 && py="python" - if [[ -n "$py" ]]; then - "$py" -c "import json,sys; et,v,lc,ex=sys.argv[1:5]; d={k:x for k,x in (('version',v),('lang_codes',lc),('extensions',ex)) if x}; print(json.dumps({'event_type':et,'client_payload':d}))" \ - "$EVENT_START_TRANSLATION" "$version" "$lang_codes" "$extensions" - return 0 - fi - return 1 -} +TOKEN="$(resolve_trigger_token "$TOKEN")" || exit 1 -TOKEN="${TOKEN:-${GH_TOKEN:-${GITHUB_TOKEN:-}}}" -if [[ -z "$TOKEN" ]]; then - echo "error: set GH_TOKEN (e.g. in repo-root .env), GITHUB_TOKEN, or pass --token" >&2 +REPO="$(resolve_trigger_repo "$REPO")" || { + echo "error: could not determine repo; set DEFAULT_REPO, GITHUB_REPOSITORY, or --repo OWNER/REPO" >&2 exit 1 -fi - -infer_repo_from_git() { - local url root o r - root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1 - url="$(git -C "$root" remote get-url origin 2>/dev/null)" || return 1 - if [[ "$url" =~ github\.com[:/]([^/]+)/([^[:space:]]+) ]]; then - o="${BASH_REMATCH[1]}" - r="${BASH_REMATCH[2]}" - r="${r%.git}" - r="${r%/}" - echo "${o}/${r}" - return 0 - fi - return 1 } -if [[ -z "$REPO" ]]; then - REPO="${GITHUB_REPOSITORY:-}" -fi -if [[ -z "$REPO" ]]; then - REPO="${DEFAULT_REPO:-}" -fi -if [[ -z "$REPO" ]]; then - REPO="$(infer_repo_from_git)" || { - echo "error: could not determine repo; set DEFAULT_REPO, GITHUB_REPOSITORY, or --repo OWNER/REPO" >&2 - exit 1 - } -fi - VERSION="${VERSION:-$DEFAULT_VERSION}" EXTENSIONS="${EXTENSIONS:-$DEFAULT_EXTENSIONS}" validate_event_type "$EVENT_START_TRANSLATION" -body="$(dispatch_json "$VERSION" "$LANG_CODES" "$EXTENSIONS")" || { +body="$(build_dispatch_json "$EVENT_START_TRANSLATION" \ + version "$VERSION" lang_codes "$LANG_CODES" extensions "$EXTENSIONS")" || { echo "error: install jq, or Python 3 (python3 or python on PATH), to build the request JSON" >&2 exit 1 } -resp="$(mktemp)" -trap 'rm -f "$resp"' EXIT - -url="https://api.github.com/repos/${REPO}/dispatches" -code="$( - curl -sS -o "$resp" -w '%{http_code}' \ - -X POST \ - -H "Authorization: Bearer ${TOKEN}" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - -H "Content-Type: application/json" \ - -d "$body" \ - "$url" -)" - -if [[ "$code" == "204" ]]; then - echo "Dispatched start-translation to ${REPO} (HTTP ${code})." - exit 0 -fi - -echo "GitHub API error: HTTP ${code}" >&2 -if [[ -s "$resp" ]]; then - cat "$resp" >&2 -fi -exit 1 +post_repository_dispatch "$REPO" "$TOKEN" "$body" "start-translation" diff --git a/tests/helpers/common.bash b/tests/helpers/common.bash index dad96c2..7289db3 100644 --- a/tests/helpers/common.bash +++ b/tests/helpers/common.bash @@ -78,5 +78,5 @@ init_process_globals() { init_add_submodule_globals() { init_process_globals - init_add_submodule_summary_buckets + init_submodule_summary_buckets } diff --git a/tests/test_lib.bats b/tests/test_lib.bats index a04afa3..97d9799 100644 --- a/tests/test_lib.bats +++ b/tests/test_lib.bats @@ -197,6 +197,20 @@ teardown() { cleanup_git_fixture_root } +@test "clone_repo: failed git clone yields status 2" { + # shellcheck source=tests/helpers/git_fixtures.bash + source "$BATS_TEST_DIRNAME/helpers/git_fixtures.bash" + init_git_fixture_root + dest="$GIT_FIXTURE_ROOT/clone-dest" + + set +e + clone_repo "file://$GIT_FIXTURE_ROOT/does-not-exist.git" "master" "$dest" keep + status=$? + set -e + + [ "$status" -eq 2 ] +} + @test "finalize_translations_master: no-op when UPDATES empty" { # shellcheck source=tests/helpers/git_fixtures.bash source "$BATS_TEST_DIRNAME/helpers/git_fixtures.bash" diff --git a/tests/test_submodule_ops.bats b/tests/test_submodule_ops.bats index 6865bd5..f750c40 100644 --- a/tests/test_submodule_ops.bats +++ b/tests/test_submodule_ops.bats @@ -125,6 +125,42 @@ EOF [ "$status" -eq 1 ] } +@test "combine_batch_and_finalize_rc: weblate_rc only yields non-zero" { + submodule_fatal=0 + run combine_batch_and_finalize_rc 0 1 + [ "$status" -eq 1 ] +} + +@test "combine_batch_and_finalize_rc: weblate_rc wins over submodule_fatal" { + submodule_fatal=2 + run combine_batch_and_finalize_rc 0 5 + [ "$status" -eq 5 ] +} + +@test "combine_batch_and_finalize_rc: weblate_rc wins over finalize_rc" { + submodule_fatal=0 + run combine_batch_and_finalize_rc 3 5 + [ "$status" -eq 5 ] +} + +@test "combine_batch_and_finalize_rc: weblate_rc wins over fatal and finalize" { + submodule_fatal=2 + run combine_batch_and_finalize_rc 3 5 + [ "$status" -eq 5 ] +} + +@test "combine_batch_and_finalize_rc: fatal plus finalize non-zero yields finalize" { + submodule_fatal=2 + run combine_batch_and_finalize_rc 3 0 + [ "$status" -eq 3 ] +} + +@test "combine_batch_and_finalize_rc: fatal plus finalize_rc 2 yields 2" { + submodule_fatal=2 + run combine_batch_and_finalize_rc 2 0 + [ "$status" -eq 2 ] +} + @test "init_translation_work_dirs: creates BOOST_WORK and optional ORG_WORK" { run bash -c ' set -euo pipefail diff --git a/tests/test_translations_submodule.bats b/tests/test_translations_submodule.bats index f8e99ad..97edb4d 100644 --- a/tests/test_translations_submodule.bats +++ b/tests/test_translations_submodule.bats @@ -99,3 +99,45 @@ install_translations_submodule_fixtures() { [ "$status" -ne 0 ] } + +@test "sync_translations_branch: failed update_translations_submodule yields 2" { + install_translations_submodule_fixtures + + UPDATES=("algorithm" "system") + UPDATE_CALLS=() + update_translations_submodule() { + UPDATE_CALLS+=("$3") + [[ "$3" == "algorithm" ]] && return 1 + return 0 + } + + set +e + sync_translations_branch "$trans_dir" "$MASTER_BRANCH" "$libs_ref" false + status=$? + set -e + + [ "$status" -eq 2 ] + [ "${#UPDATE_CALLS[@]}" -eq 1 ] + [ "${UPDATE_CALLS[0]}" = "algorithm" ] +} + +@test "finalize_translations_master: failed update_translations_submodule yields 2" { + install_translations_submodule_fixtures + + UPDATES=("algorithm" "system") + UPDATE_CALLS=() + update_translations_submodule() { + UPDATE_CALLS+=("$3") + [[ "$3" == "algorithm" ]] && return 1 + return 0 + } + + set +e + finalize_translations_master "$trans_dir" "$libs_ref" + status=$? + set -e + + [ "$status" -eq 2 ] + [ "${#UPDATE_CALLS[@]}" -eq 1 ] + [ "${UPDATE_CALLS[0]}" = "algorithm" ] +}