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
8 changes: 5 additions & 3 deletions .github/workflows/assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/assets/add_submodules.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/assets/create-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
11 changes: 3 additions & 8 deletions .github/workflows/assets/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand Down Expand Up @@ -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"
}
Expand Down Expand Up @@ -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=()
}

Expand Down
17 changes: 10 additions & 7 deletions .github/workflows/assets/submodule_ops.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}

Expand All @@ -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"
Expand Down Expand Up @@ -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"
}

Expand All @@ -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
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/assets/translation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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" \
Expand Down
8 changes: 2 additions & 6 deletions .github/workflows/start-translation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
5 changes: 2 additions & 3 deletions .github/workflows/sync-translation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
25 changes: 16 additions & 9 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.<path>.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.<path>.branch`**, **`submodule update --remote`**, commit, **`push --force-with-lease`**.

---

Expand All @@ -165,16 +165,17 @@ 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
A1 -->|"continue batch"| PSL
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
Comment thread
whisper67265 marked this conversation as resolved.
```

### 6.1 Per-submodule batch processors
Expand Down Expand Up @@ -204,23 +205,29 @@ 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

| Workflow step | Collapse logic |
|---------------|----------------|
| **`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)

Expand Down
6 changes: 3 additions & 3 deletions docs/GETTING-STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
4 changes: 2 additions & 2 deletions docs/endpoint-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand All @@ -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`

Expand Down
2 changes: 1 addition & 1 deletion scripts/install-git-hooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading