ci: upload translation sources to Weblate after releases - #2097
Conversation
| run: pbl build | ||
|
|
||
| - name: Prepare translation source artifact | ||
| if: inputs.collect_translation_sources && matrix.slot == 0 && github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') |
There was a problem hiding this comment.
should we upload on any push (ie, not only tags)?
| inputs: | ||
| collect_translation_sources: | ||
| description: Collect source catalogs for a firmware release | ||
| type: boolean | ||
| default: false |
There was a problem hiding this comment.
is this needed because on release other conditions would be true?
| - name: Test release source delivery | ||
| run: PYTHONPATH=tools python -m unittest discover -s tools/tests -p '*translation_source.py' |
There was a problem hiding this comment.
hmm unittest here feels weird, what's this doing?
| if: >- | ||
| github.repository == 'coredevices/PebbleOS' && | ||
| github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && | ||
| vars.WEBLATE_UPLOAD_ENABLED == 'true' |
| # Use Weblate's native PO source merge; do not follow redirects with credentials. | ||
| curl --silent --show-error --fail-with-body --proto '=https' \ | ||
| --connect-timeout 15 --max-time 180 --retry 3 --retry-max-time 600 \ | ||
| --header "Authorization: Token $WEBLATE_TOKEN" \ | ||
| --header 'Accept: application/json' \ | ||
| --form-string 'method=source' --form "file=@$1;type=text/plain" \ | ||
| --output weblate-response.json --write-out '%{http_code}' \ | ||
| "$WEBLATE_SOURCE_URL" > weblate-status.txt | ||
| # Redirects and unexpected successful responses must not masquerade as imports. | ||
| test "$(cat weblate-status.txt)" = 200 | ||
| jq -e '.result == true' weblate-response.json > /dev/null | ||
| echo 'Released source catalog uploaded to Weblate.' |
There was a problem hiding this comment.
Things that feel weird:
- Use single language script (Python?) for all stuff
- Unit tests for simple tools feel like overkill
gmarull
left a comment
There was a problem hiding this comment.
Thanks for putting this together. Following up on my inline comments with the full picture: the general direction I'd like is simplicity and consistency with the workflows we already have. Once a couple of assumptions are removed, this feature is roughly fifteen lines of YAML with no scripts and no tests.
The merge job is not needed
The POT is identical for every board. Every service that owns translatable strings is enabled in src/fw/prj_normal.conf (not per board), and src/fw/apps/CMakeLists.txt globs every app into apps.pot regardless of what a given board builds. So the multi-board msgcat union, the plural-conflict check, the per-board revision cross-check and the normalisation in tools/prepare_translation_source.py only reconcile catalogs that differ in their POT-Creation-Date header. Uploading the POT from one matrix leg is enough.
The release coupling is wrong, not just heavy
upload_translation_source.sh picks the newest release by published_at and skips otherwise. Maintenance branches break that: on 2026-09-15 both v4.30.3 and v4.27.3 were released, and v4.27.3 came out later. With this PR the 4.27 strings would be pushed to Weblate after the 4.30 ones.
Uploading on push to main removes the bug together with the gh api pagination, the concurrency group and the whole translation-sources.yml workflow. If per-push uploads feel too chatty, nightly.yml already builds main once a day and skips unchanged SHAs, so attaching the upload to its publish job is the other consistent option.
Answers to the inline questions
collect_translation_sourcesand the four copies of thepush && refs/tags/vguard are redundant:release.ymlonly runs on tags, and a called workflow inherits the caller's event context. With push-to-main they all go away.WEBLATE_UPLOAD_ENABLEDis not needed. Gate ongithub.repository == 'coredevices/PebbleOS'likenightly.ymland the eng-dash step do.WEBLATE_SOURCE_URLshould be hardcoded in the workflow, the wayENGDASH_URLis in.github/actions/publish-engdash.- The
unitteststep in the release path runs tests nothing else runs (nothing in CI executestools/tests). Both test files should go;test_upload_translation_source.pyfakesghandcurlthroughPATHto exercise a bash script, which is far beyond what one curl call warrants. - Language mix: with the merge gone there is nothing left for Python to do. The upload is one
curl --fail-with-bodyplusjq -e .result, inline in the workflow, matching the style of the eng-dash action.
Secrets
The call from release.yml passes no secrets: inherit; it only works because environment: translations on the job pulls environment secrets directly. A plain repository secret plus secrets: inherit, like ENGDASH_OTA_TOKEN, is simpler and matches the other workflows.
The one thing worth keeping
Weblate's method=source upload runs msgmerge over every catalog and commits whatever changed, and a fresh POT-Creation-Date changes every PO header. Stripping that line before upload avoids a churn commit in pebbleos-translations on every upload. That is a single sed line, not a script.
Proposed shape
- In
build-firmware.yml, addcp build/pebbleos.pot artifacts/to the existing "Copy artifacts" step underif: matrix.board == 'asterix'. The POT then also lands on releases and nightlies as a normal asset, which is useful on its own since the pebbleos-translations tooling takes a POT as input. - In
build-firmware.yml(push-to-main path) ornightly.yml, one job gated on the repository name that downloads the asterix artifact, drops the date header and does one curl to the hardcoded Weblate URL with a repository secret. - Drop
translation-sources.yml, both Python files, the shell script, the tests, thefindexclusion inrelease.ymland the paths-filter addition.
Housekeeping
The commit carries Signed-off-by: GPT-6 <noreply@openai.com>. The DCO sign-off is a human attestation, so the AI belongs only in Co-authored-by. The PR body is also empty; please describe the intended flow there.
|
One correction to my review: I claimed the POT is identical for every board. That happens to be true today, but it is not guaranteed. Rather than merging catalogs across boards in CI, I'd make the property hold by construction. If a CI-side union is still preferred, it is |
take review comments with a grain of salt, not all may be accurate
|
In general, if we are going to make universal language packs (ie, not per watch model), we can simplify the POT generation by globbing everything, regardless of what gets configured. This would remove the need for merge, we could even just invoke gettext directly? |
I think this was the case before cmake (lmao), ill revert that behavior |
6cd53f3 to
26327be
Compare
| pbl_filter_sources(pot_sources EXCLUDE apps/prf/** applib/vendor/**) | ||
| pbl_firmware_pot(${PROJECT_BINARY_DIR}/pebbleos.pot ${pot_sources}) |
There was a problem hiding this comment.
maybe make pbl_firmware_pot allow passing 'EXCLUDE' directly?
| if: >- | ||
| github.repository == 'coredevices/PebbleOS' && | ||
| github.event_name == 'push' && github.ref == 'refs/heads/main' && | ||
| vars.WEBLATE_SOURCE_URL != '' |
Extract strings from all normal firmware sources regardless of board or configured services. Include headers and definitions, while excluding recovery-only apps and applib vendor sources. Replace the per-area catalogs and merge machinery with one gettext pass so every normal firmware build produces the complete source catalog. Co-authored-by: GPT-6 <noreply@openai.com> Signed-off-by: Joshua Jun <lets@throw.rocks>
Include the POT in the asterix artifact and upload it to Weblate after successful main builds. Strip POT-Creation-Date to avoid catalog churn. Use WEBLATE_SOURCE_URL as a repository variable and WEBLATE_TOKEN as a repository secret. Release and maintenance builds do not upload sources. Co-authored-by: GPT-6 <noreply@openai.com> Signed-off-by: Joshua Jun <lets@throw.rocks>
26327be to
2179773
Compare
No description provided.