This document describes why the system is shaped the way it is, how the main pieces relate, and which concerns cut across workflows. For triggers, secrets, and copy-paste examples, see README.md. For a zero-to-operational walkthrough, see GETTING-STARTED.md.
Context. Boost ships documentation inside many separate boostorg repositories.
Translation work needs stable, narrow remotes (doc-only trees), language-specific
branches, and automation that does not clobber in-progress translator submissions.
Goals.
- Mirror each relevant library’s documentation into a dedicated GitHub repo under
a configurable org (
MODULE_ORG, fromSUBMODULES_ORG). - Super-repo (this repository): one checkout that pins exact commits of every
mirrored lib via
libs/<name>submodules onMASTER_BRANCHand on${LOCAL_BRANCH_PREFIX}{lang_code}branches. - Upstream sync: refresh mirror
MASTER_BRANCHfromboostorgat a chosen ref, then fold changes into${LOCAL_BRANCH_PREFIX}*when safe. - Weblate handoff: after successful git updates, tell the translation server which org, version, languages, and components changed so it can add or refresh projects asynchronously.
- Consumer branches: keep every
${LOCAL_BRANCH_PREFIX}*branch in the super-repo pointing at the latest${LOCAL_BRANCH_PREFIX}*tip of each submodule (scheduledsync-translation).
Non-goals: building Boost, serving rendered HTML from this repo, or replacing
Weblate’s internal project configuration beyond the add-or-update contract.
flowchart LR
subgraph upstream [Upstream]
BO[boostorg repos]
BB[boostorg/boost .gitmodules]
end
subgraph ci [GitHub Actions]
AS[add-submodules]
ST[start-translation]
SY[sync-translation]
end
subgraph mirrors [Library mirrors MODULE_ORG]
M1["repo: algorithm"]
M2["repo: json"]
end
subgraph super [Translations super-repo]
TR["this repo MASTER_BRANCH and LOCAL_BRANCH_PREFIX*"]
end
subgraph tw [Weblate]
EP["POST .../${WEBLATE_ENDPOINT_PATH}"]
end
BB --> AS
BO --> AS
BO --> ST
AS --> M1
AS --> M2
ST --> M1
ST --> M2
AS --> TR
ST --> TR
ST --> EP
M1 --> TR
M2 --> TR
SY --> TR
Legend. add-submodules may discover names from boostorg/boost;
start-translation discovers names from this repo’s .gitmodules. Both
mutate mirror repos and then update the super-repo. sync-translation only
moves submodule SHAs on existing ${LOCAL_BRANCH_PREFIX}* branches.
| Path | Role |
|---|---|
.gitmodules |
Declares libs/<lib> → https://github.com/{MODULE_ORG}/{lib}.git, branch master for default checkout metadata. |
.github/workflows/add-submodules.yml |
On dispatch: create missing mirrors, push master / local-*, install create-tag.yml, record submodules in the super-repo. |
.github/workflows/start-translation.yml |
On dispatch: setup → sync-mirrors → start-local matrix (per lang); sync mirrors, merge policy on local-*, update super-repo pointers, call Weblate. |
.github/workflows/sync-translation.yml |
On dispatch or schedule: discover → sync-local matrix (per lang); submodule update --remote and force-push per local-*. |
.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; both source shared 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
lib.sh. add-submodules and start-translation call
parse_and_validate_lang_codes once per run; sync-translation uses
validate_lang_codes in discover only; submodule pointer updates remain inline git.
| Branch | Where | Meaning |
|---|---|---|
MASTER_BRANCH (master) |
Mirror and super-repo | Doc-only content aligned with upstream English sources for the synced ref. |
${LOCAL_BRANCH_PREFIX}{lang_code} |
Mirror and super-repo | Translation line for lang_code; Weblate opens PRs from ${TRANSLATION_BRANCH_PREFIX}{lang_code}-* into this branch on mirrors. |
${TRANSLATION_BRANCH_PREFIX}{lang_code}-{version} |
Mirror (head of PR) | Weblate working branch naming assumed by start-translation (open PR guard) and create-tag.yml (tag extraction). |
Constants MASTER_BRANCH, LOCAL_BRANCH_PREFIX, and TRANSLATION_BRANCH_PREFIX are defined in .github/workflows/assets/env.sh.
Super-repo local-* vs mirror local-*. The super-repo’s local-* branch
commits submodule pointers that track each mirror’s local-* (after
finalize_translations_repo / sync-translation). The mirror’s local-*
holds actual file merges from master plus translator edits.
- Resolve library names:
client_payload.submodulesorboostorg/boost.gitmodulesatLIBS_REF. - For each name:
get_doc_paths(meta/libraries.json) → cloneboostorg/{name}→prune_to_doc_only. - If mirror missing:
gh repo create, init from pruned tree, pushmaster, branchlocal-*withcreate-tag.ymlcommitted on each. - Clone super-repo to a temp dir;
ensure_local_branch_in_translationsper language; for each updated lib,update_translations_submoduleonmasterand eachlocal-*; push (local-*force-pushed infinalize_translations_repo).
setup: validate language codes; emit JSON for the matrix.sync-mirrors(serialized viatranslations-mirror-master): submodule names from this repo.gitmodules(libs/only); per lib, clone upstream and mirror, replace mirrormastertree with pruned upstream snapshot, push;finalize_translations_masteron the super-repo.start-local(matrix perlang_code, concurrencylocal-branch-{lang_code}): per lib, create or merge${LOCAL_BRANCH_PREFIX}*in mirrors when no open${TRANSLATION_BRANCH_PREFIX}{lang_code}-*PR;finalize_translations_localfor that language; buildadd_or_updatefor that lang; POST to${WEBLATE_ENDPOINT_PATH}; tolerate 202 (async) or 200.
discover: listrefs/heads/local-*on the super-repo; emit JSON for the matrix.sync-local(matrix perlang_code, concurrencylocal-branch-{lang_code}): checkout onelocal-*branch,git submodule update --init, setsubmodule.<path>.branch,submodule update --remote, commit,push --force-with-lease.
Per-submodule processors in .github/workflows/assets/ share a three-code return
convention. Workflow steps collapse these into a job exit status (0 or non-zero;
2 is never propagated to the GitHub Actions step exit code).
flowchart TD
subgraph processors [Per-submodule processors]
A0["0 success"]
A1["1 non-fatal skip"]
A2["2 fatal error"]
end
PSL[process_submodule_list]
CB[combine_batch_and_finalize_rc]
FTL[finalize_translations_local]
TW[trigger_weblate]
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
FTL -->|"finalize_rc"| CB
FTL -->|"rc == 0"| TW
TW -->|"weblate_rc"| CB
CB --> JobExit
Functions passed to process_submodule_list in submodule_ops.sh:
| Code | Meaning | Recorded in | Examples |
|---|---|---|---|
| 0 | Success | UPDATES |
Mirror synced; repo created; local branch ready for Weblate |
| 1 | Non-fatal skip (batch continues) | Summary buckets (NO_DOC_PATHS, REPO_EXISTS_SKIP, OPEN_PR_SKIP) |
No doc paths; mirror repo already exists; open translation PR |
| 2 | Fatal submodule error | SUBMODULE_FATAL / submodule_fatal |
Git clone/push/commit failure; missing org mirror; libraries.json fetch failure; invalid START_PHASE; has_open_translation_pr API failure |
Processors that follow this contract:
add_one_submodule(add_submodules.sh) —return 1when the mirror repo already exists or metadata has no doc paths;return 2for git/gh/metadata failures.sync_one_submodule(translation.sh) —return 1when metadata has no doc paths or no language was added for the submodule;return 2for git/clone/sync failures.process_local_branch(translation.sh) —0= eligible for Weblateadd_or_update;1= skipped due to open translation PR;2= git/API failure.
process_submodule_list (always returns 0):
rc == 0: callsrecord_submodule_update.rc == 1: ignored for exit purposes; batch continues.rc == 2: callsrecord_submodule_fatal; incrementssubmodule_fatal.
combine_batch_and_finalize_rc finalize_rc [weblate_rc]:
- Start
exit_rc=0. - If
submodule_fatal > 0→exit_rc=1(fatals are collapsed, not propagated as2). - If
finalize_rc != 0→exit_rc=$finalize_rc(finalize wins over batch fatal). - If
weblate_rc != 0(optional second arg, default0) →exit_rc=$weblate_rc(weblate wins over finalize and batch fatal).
| 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 |
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 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 sync-mirrors job success.
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.
Precondition guards. Zero libs/ entries in this repo’s .gitmodules is a
fatal error in sync-mirrors (Run add-submodules first.). When sync-mirrors
succeeds but updated_submodules is an empty JSON array (all submodules non-fatally
skipped), start-local runs and exits non-zero with a distinct empty-handoff message
rather than silently succeeding.
| Helper | Convention |
|---|---|
has_open_translation_pr (lib.sh) |
Separate tri-state: 0 = open PR exists, 1 = none, 2 = GitHub API failure |
get_doc_paths |
Returns 1 on API failure; callers upgrade to processor return 2 after recording META_MISSING |
Setup-phase helpers (resolve_add_submodules_names, ensure_all_translation_lang_branches, ensure_translations_cloned failures) |
Return non-zero (fail) and cause immediate job exit before batch processing; resolve_add_submodules_names hard-exits 1, while ensure_translations_cloned / ensure_all_translation_lang_branches propagate the failing command's exit status (e.g. git 128) via rc=$? capture and exit $rc |
trigger_weblate (translation.sh) |
0 success or empty skip; 1 validation or HTTP/curl failure |
validate_secrets / parse_and_validate_lang_codes |
exit 1 (fatal to entire step) |
sync-translation.yml |
Does not use this convention |
scripts/trigger-*.sh |
exit 0 success (including --help); exit 1 for any client/dispatch error — does not participate in the 0/1/2 batch contract |
Authentication. SYNC_TOKEN is passed to actions/checkout and
GITHUB_TOKEN for gh and gh auth setup-git, so pushes and API calls
share one credential. WEBLATE_TOKEN is only used for the outbound HTTP call.
Idempotency and safety.
add-submodulesskips if the mirror repo already exists.start-translationskips mirrormastermerge intolocal-*when a Weblate PR is open, reducing race risk with translators.create-tagskips if the derived tag already exists.
Workflow concurrency.
- Per-language group:
local-branch-{lang_code}— shared bystart-translation(start-localmatrix) andsync-translation(sync-localmatrix). Jobs targeting the same language queue;cancel-in-progress: falseso in-flight work completes rather than being cancelled. - Mirror-master group:
translations-mirror-master— serializes mirrormastersync insidestart-translation(sync-mirrorsjob) so parallel per-language jobs do not race on shared mirror repos. - Example: a manual
start-translationdispatch forjawaits ifsync-translationalready has async-localjob running forlocal-ja.zh_Hansandjajobs from the same workflow run can still execute in parallel.
Observability. Shell steps echo progress to Actions logs; Weblate response bodies
are printed for start-translation on success or failure paths where implemented.
Configuration surface. env.sh centralizes org and branch constants
(MASTER_BRANCH, LOCAL_BRANCH_PREFIX, TRANSLATION_BRANCH_PREFIX,
WEBLATE_ENDPOINT_PATH); workflow YAML supplies LIBS_REF, LANG_CODES,
EXTENSIONS, and secrets so behavior is traceable without reading the entire inline script.
Update this file when branch naming, Weblate contract, ownership model
(MODULE_ORG), or repository layout changes. Routine secret rotation or
version bumps in actions/checkout alone do not require edits here.
Consumer-facing semver for the orchestration contract is tracked in CHANGELOG.md and endpoint-contract.md § Versioning and stability.