From ca308636c3585ad35d50e1641c9b90da9c7a0e6a Mon Sep 17 00:00:00 2001 From: tooomm Date: Sun, 6 Aug 2023 23:55:50 +0200 Subject: [PATCH] CI: Add automatic PR creation for source string updates (#4544) * wording * add pr creation * Update translations.yml * Update translations.yml * update translation workflow * Update CONTRIBUTING.md * Update CONTRIBUTING.md * Update CONTRIBUTING.md * skip ci update * skip ci update * update conditions * remove empty line * typo * tee test * cleanup * pass data between steps * opt for step output over env variable * remove space * create script * wording * fix fork protection, re-add pr run * updates * Update translations.yml * adjust for new source paths * update comment * wording Co-authored-by: ebbit1q * wording * reorder * reorder * fix deprecation of set-output * fix version --------- Co-authored-by: ebbit1q --- .ci/update_translations.sh | 56 ++++++++++++++++ .ci/update_translations_template.md | 11 ++++ .github/CONTRIBUTING.md | 73 ++++++++++++++------- .github/workflows/desktop-build.yml | 2 + .github/workflows/desktop-lint.yml | 1 + .github/workflows/translations.yml | 99 +++++++++++++++++------------ .github/workflows/web-build.yml | 1 - 7 files changed, 181 insertions(+), 62 deletions(-) create mode 100755 .ci/update_translations.sh create mode 100644 .ci/update_translations_template.md diff --git a/.ci/update_translations.sh b/.ci/update_translations.sh new file mode 100755 index 00000000..7192b8ab --- /dev/null +++ b/.ci/update_translations.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# ci script to update translation files +# usage: +# $0 cockatrice/cockatrice_en@source.ts cockatrice/src common +# or +# FILE="cockatrice/cockatrice_en@source.ts" +# DIRS="cockatrice/src common" +# $0 +# note: directories can't contain spaces + +# check parameters +if [[ ! $FILE ]]; then + FILE="$1" + shift +fi +if [[ ! $FILE ]]; then + echo "no output file selected" >&2 + exit 2; +fi +if [[ ! $DIRS ]]; then + DIRS="$*" +fi +if [[ ! $DIRS ]]; then + echo "no source directories selected to translate" >&2 + exit 2; +fi +if [[ ! -e $FILE ]]; then + echo "output file does not exist at: $FILE" >&2 + exit 3; +fi + +# print version +if ! lupdate -version; then + echo "failed to run lupdate" >&2 + exit 4; +fi + +# run lupdate, duplicating the output in stderr and saving it +# for convenience we ignore that $DIRS will be split on spaces +# shellcheck disable=SC2086 +if ! got="$(lupdate $DIRS -ts "$FILE" | tee /dev/stderr)"; then + echo "failed to update $FILE with $DIRS" >&2 + exit 4; +fi + +# trim output +output="${got##*(}" # trim everything before last ( +output="${output%%)*}" # trim everything after first ) +if [[ $output == $got ]]; then + echo "could not parse generated output" >&2 + exit 4; +fi + +# write output to ci environment file +echo "output=$output" >> $GITHUB_OUTPUT diff --git a/.ci/update_translations_template.md b/.ci/update_translations_template.md new file mode 100644 index 00000000..a2fa690e --- /dev/null +++ b/.ci/update_translations_template.md @@ -0,0 +1,11 @@ +Updated language source strings: +- {{ .cockatrice_output }} (Cockatrice) +- {{ .oracle_output }} (Oracle) + +
+ +Last changes are based on commit {{ .commit }}. + +--- +*This PR is automatically generated and updated by the workflow at `.github/workflows/translations.yml`.*
+*After merging, all changes to the source language are available for translation at [Transifex](https://www.transifex.com/projects/p/cockatrice/) shortly.* diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 57437589..2db04581 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -290,20 +290,21 @@ be included in the next release 👍 Basic workflow for translations: 1. Developer adds a `tr("foo")` string in the code; - 2. Every few days, a maintainer updates the `*_en@source.ts files` with the new strings; - 3. Transifex picks up the new files from GitHub every 24 hours; - 4. Translators translate the new untranslated strings on Transifex; - 5. Before a release, a maintainer fetches the updated translations from Transifex. + 2. CI updates the `*_en@source.ts files` regularly and creates a PR automatically; + 3. Maintainer verifies and merges the change; + 4. Transifex picks up the new files from GitHub automatically; + 5. Translators translate the new untranslated strings on Transifex; + 6. Before a release, a maintainer fetches the updated translations from Transifex. ### Using Translations (for developers) ### -All the user-interface strings inside Cockatrice's source code must be written -in English(US). +All user interface strings inside Cockatrice's source code must be written +in English (US). Translations to other languages are managed using [Transifex]( https://www.transifex.com/projects/p/cockatrice/). Adding a new string to translate is as easy as adding the string in the -'tr("")' function, the string will be picked up as translatable automatically +`tr("")` function, the string will be picked up as translatable automatically and translated as needed. For example, setting the text of a label in the way that the string `"My name is:"` can be translated: @@ -312,7 +313,7 @@ nameLabel.setText(tr("My name is:")); ``` To translate a string that would have plural forms you can add the amount to -the tr call, also you can add an extra string as a hint for translators: +the tr() call, also you can add an extra string as a hint for translators: ```c++ QString message = tr("Everyone draws %n cards", "pop up message", amount); ``` @@ -321,20 +322,46 @@ https://doc.qt.io/qt-5/i18n-source-translation.html#handling-plurals) If you're about to propose a change that adds or modifies any translatable string in the code, you don't need to take care of adding the new strings to -the translation files. -Every few days, or when a lot of new strings have been added, someone from the -development team will take care of extracting all the new strings and adding -them to the english translation files and making them available to translators -on Transifex. +the translation files.
+We have an automated process to update our language source files on a schedule +and provide the translators on Transifex with the new contents.
+Maintainers can also manually trigger this on demand. ### Maintaining Translations (for maintainers) ### -When new translatable strings have been added to the code, a maintainer should -make them available to translators on Transifex. Every few days, or when a lot -of new strings have been added, a maintainer should take care of extracting all -the new strings and add them to the english translation files. +When new translatable strings have been added to the code, a maintainer has to +make them available to translators on Transifex. -To update the english translation files, re-run cmake enabling the appropriate +To help with that, we have an automated CI workflow, that regularly looks at the +code in the master branch, extracts all strings and updates dedicated source string +files with any changes. These updates are not commited right away, the CI creates a +PR for reviewing instead.
+After approval, our translation tool automatically picks the changes up and deploys +them to our translators. Be mindful when merging only a few changes! + +Once a release is planned, or when a lot of strings have been added or changed, a +maintainer can manually trigger a CI run to extract all strings on demand. + +
+Manually trigger CI run (Workflow Dispatch) + +Maintainers can always request the CI to run on demand if it's required. + +Go to the `Actions` tab and select our dedicated translation workflow: +https://github.com/Cockatrice/Cockatrice/actions/workflows/translations.yml + +You see a "This workflow has a workflow_dispatch event trigger." hint at the top of +the list.
+Select `Run workflow` on the right and trigger a run from master branch. + +The CI will now check for changed strings and create a PR if there are any updates. + +
+ +
+Manually update source strings locally + +To update the english source files for translation, re-run cmake enabling the appropriate parameter and then run make: ```sh cd cockatrice/build @@ -357,11 +384,13 @@ It is recommended to disable the parameter afterwards using: ```sh cmake .. -DUPDATE_TRANSLATIONS=OFF ``` -Now you are ready to propose your change. +Now you are ready to commit your changes and open a PR. -Once your change gets merged, Transifex will pick up the modified files -automatically (checked every 24 hours) and update the interface where -translators will be able to translate the new strings. +
+ +Once the changes get merged, Transifex will pick up the modified files +automatically (checked every few hours) and update their online editor where +translators will be able to translate the new strings right in the browser. ### Releasing Translations (for maintainers) ### diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index cf8fbed8..8efe8bfb 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -8,6 +8,7 @@ on: - '**.md' - 'webclient/**' - '.github/workflows/web-*.yml' + - '.github/workflows/translations.yml' tags: - '*' pull_request: @@ -15,6 +16,7 @@ on: - '**.md' - 'webclient/**' - '.github/workflows/web-*.yml' + - '.github/workflows/translations.yml' jobs: configure: diff --git a/.github/workflows/desktop-lint.yml b/.github/workflows/desktop-lint.yml index 95a95a8e..a1dce8a2 100644 --- a/.github/workflows/desktop-lint.yml +++ b/.github/workflows/desktop-lint.yml @@ -6,6 +6,7 @@ on: - '**.md' - 'webclient/**' - '.github/workflows/web-*.yml' + - '.github/workflows/translations.yml' jobs: format: diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml index c9b10d0a..01526a42 100644 --- a/.github/workflows/translations.yml +++ b/.github/workflows/translations.yml @@ -1,66 +1,87 @@ -name: Update translation source +name: Update Translation Source on: workflow_dispatch: schedule: - # runs once per month + # runs at the start of each month (UTC) - cron: '0 0 1 * *' + pull_request: + paths: + - '.github/workflows/translations.yml' jobs: translations: # Do not run the scheduled workflow on forks if: github.event_name != 'schedule' || github.repository_owner == 'Cockatrice' + name: Update translation source runs-on: ubuntu-latest steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Install lupdate shell: bash run: | sudo apt-get update sudo apt-get install -y --no-install-recommends qttools5-dev-tools - - name: Checkout repo - uses: actions/checkout@v3 - - - name: Update cockatrice translations + - name: Update Cockatrice translation source + id: cockatrice shell: bash - run: | - shopt -s globstar # globstar is needed for recursive ** - lupdate -version - echo "reading the following source files:" - # note: there are three strings to translate in common right now - echo {cockatrice,common}/**/*.{cpp,h} - echo "$(echo {cockatrice,common}/**/*.{cpp,h} | wc -w) files total" - lupdate {cockatrice,common}/**/*.{cpp,h} -ts cockatrice/translations/cockatrice_en@source.ts + env: + FILE: 'cockatrice/cockatrice_en@source.ts' + DIRS: 'cockatrice/src common' + run: .ci/update_translations.sh - - name: Update oracle translations + - name: Update Oracle translation source + id: oracle shell: bash - run: | - shopt -s globstar # globstar is needed for recursive ** - lupdate -version - echo "reading the following source files:" - echo oracle/**/*.{cpp,h} - echo "$(echo oracle/**/*.{cpp,h} | wc -w) files total" - lupdate oracle/**/*.{cpp,h} -ts oracle/translations/oracle_en@source.ts + env: + FILE: 'oracle/oracle_en@source.ts' + DIRS: 'oracle/src' + run: .ci/update_translations.sh - - name: Check for updates - id: check - shell: bash - run: | - set +e # do not fail, just save the exit state - git diff --exit-code - echo "deploy=$?" >>"$GITHUB_OUTPUT" + - name: Render template + id: template + uses: chuhlomin/render-template@v1.7 + with: + template: .ci/update_translations_template.md + vars: | + cockatrice_output: ${{ steps.cockatrice.outputs.output }} + oracle_output: ${{ steps.oracle.outputs.output }} + commit: ${{ github.sha }} - - name: Commit changes - if: steps.check.outputs.deploy == '1' + - name: Create pull request + if: github.event_name != 'pull_request' + id: create_pr + uses: peter-evans/create-pull-request@v4 + with: + add-paths: | + cockatrice/cockatrice_en@source.ts + oracle/oracle_en@source.ts + commit-message: Update translation source strings + # author is the owner of the commit + author: github-actions + branch: ci-update_translations + delete-branch: true + title: '[Translations] Update source strings' + body: ${{ steps.template.outputs.result }} + labels: | + CI + Translation + draft: false + + - name: PR Status + if: github.event_name != 'pull_request' shell: bash - working-directory: ${{env.OUTPUT_PATH}} + env: + STATUS: ${{ steps.create_pr.outputs.pull-request-operation }} run: | - git config user.name github-actions - git config user.email github-actions@github.com - git add cockatrice/translations/cockatrice_en@source.ts oracle/translations/oracle_en@source.ts - git commit -m "Automated translation update ( $GITHUB_SHA )" - git push - deploy_commit=$(git rev-parse HEAD) - echo "Created commit: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/commit/$deploy_commit" + if [[ "$STATUS" == "" ]]; then + echo "PR #${{ steps.create_pr.outputs.pull-request-number }} unchanged!" + else + echo "PR #${{ steps.create_pr.outputs.pull-request-number }} $STATUS!" + fi + echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" diff --git a/.github/workflows/web-build.yml b/.github/workflows/web-build.yml index bf1d4a87..a50e90ce 100644 --- a/.github/workflows/web-build.yml +++ b/.github/workflows/web-build.yml @@ -50,4 +50,3 @@ jobs: - name: Test app run: npm run test -