Skip to content

ci: upload translation sources to Weblate after releases - #2097

Merged
jplexer merged 2 commits into
coredevices:mainfrom
jplexer:codex/weblate-release-upload
Sep 18, 2026
Merged

jplexer merged 2 commits into
coredevices:mainfrom
jplexer:codex/weblate-release-upload

Conversation

@jplexer

@jplexer jplexer commented Sep 17, 2026

Copy link
Copy Markdown
Member

No description provided.

@jplexer
jplexer marked this pull request as ready for review September 17, 2026 12:41
@jplexer
jplexer requested a review from gmarull as a code owner September 17, 2026 12:41
Comment thread .github/workflows/build-firmware.yml Outdated
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')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we upload on any push (ie, not only tags)?

Comment thread .github/workflows/build-firmware.yml Outdated
Comment on lines +9 to +13
inputs:
collect_translation_sources:
description: Collect source catalogs for a firmware release
type: boolean
default: false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this needed because on release other conditions would be true?

Comment on lines +27 to +28
- name: Test release source delivery
run: PYTHONPATH=tools python -m unittest discover -s tools/tests -p '*translation_source.py'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is var needed?

Comment thread tools/upload_translation_source.sh Outdated
Comment on lines +27 to +38
# 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.'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things that feel weird:

  • Use single language script (Python?) for all stuff
  • Unit tests for simple tools feel like overkill

@gmarull gmarull left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_sources and the four copies of the push && refs/tags/v guard are redundant: release.yml only runs on tags, and a called workflow inherits the caller's event context. With push-to-main they all go away.
  • WEBLATE_UPLOAD_ENABLED is not needed. Gate on github.repository == 'coredevices/PebbleOS' like nightly.yml and the eng-dash step do. WEBLATE_SOURCE_URL should be hardcoded in the workflow, the way ENGDASH_URL is in .github/actions/publish-engdash.
  • The unittest step in the release path runs tests nothing else runs (nothing in CI executes tools/tests). Both test files should go; test_upload_translation_source.py fakes gh and curl through PATH to 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-body plus jq -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

  1. In build-firmware.yml, add cp build/pebbleos.pot artifacts/ to the existing "Copy artifacts" step under if: 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.
  2. In build-firmware.yml (push-to-main path) or nightly.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.
  3. Drop translation-sources.yml, both Python files, the shell script, the tests, the find exclusion in release.yml and 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.

@gmarull

gmarull commented Sep 17, 2026

Copy link
Copy Markdown
Member

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. src/fw/services/CMakeLists.txt adds each service with pbl_add_subdirectory_ifdef, so a board that disables a CONFIG_SERVICE_* option would silently lose that service's strings from its own POT.

Rather than merging catalogs across boards in CI, I'd make the property hold by construction. src/fw/apps/CMakeLists.txt already does this on purpose: it extracts strings from every app "whether or not this configuration builds the app". Services should follow the same rule, so that any single build produces the complete catalog. That can be a small separate PR or the first commit here. With that in place, the rest of my review stands: upload the POT from one matrix leg, no merge step.

If a CI-side union is still preferred, it is msgcat --use-first over the downloaded POTs, a couple of lines of bash in the upload job; the rest of prepare_translation_source.py is still not needed.

@gmarull
gmarull dismissed their stale review September 17, 2026 13:17

take review comments with a grain of salt, not all may be accurate

@gmarull

gmarull commented Sep 17, 2026

Copy link
Copy Markdown
Member

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?

@jplexer

jplexer commented Sep 17, 2026

Copy link
Copy Markdown
Member Author

we can simplify the POT generation by globbing everything, regardless of what gets configured.

I think this was the case before cmake (lmao), ill revert that behavior

@jplexer
jplexer force-pushed the codex/weblate-release-upload branch from 6cd53f3 to 26327be Compare September 17, 2026 15:04
@jplexer
jplexer requested a review from gmarull September 17, 2026 15:04
Comment thread src/fw/CMakeLists.txt Outdated
Comment on lines +57 to +58
pbl_filter_sources(pot_sources EXCLUDE apps/prf/** applib/vendor/**)
pbl_firmware_pot(${PROJECT_BINARY_DIR}/pebbleos.pot ${pot_sources})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make pbl_firmware_pot allow passing 'EXCLUDE' directly?

Comment thread .github/workflows/build-firmware.yml Outdated
if: >-
github.repository == 'coredevices/PebbleOS' &&
github.event_name == 'push' && github.ref == 'refs/heads/main' &&
vars.WEBLATE_SOURCE_URL != ''

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant?

jplexer and others added 2 commits September 18, 2026 11:29
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>
@jplexer
jplexer force-pushed the codex/weblate-release-upload branch from 26327be to 2179773 Compare September 18, 2026 09:30
@jplexer
jplexer merged commit 0225541 into coredevices:main Sep 18, 2026
49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants