From fb9557fe8fdafdab793976fe2bb5f8c255d36f34 Mon Sep 17 00:00:00 2001 From: Blackbaud Sky Build User Date: Wed, 12 Feb 2025 16:22:18 -0500 Subject: [PATCH] ci: workflow to keep automated-translations in sync (#3145) (#3148) :cherries: Cherry picked from #3145 [ci: workflow to keep automated-translations in sync](https://github.com/blackbaud/skyux/pull/3145) [AB#3256198](https://dev.azure.com/blackbaud/f565481a-7bc9-4083-95d5-4f953da6d499/_workitems/edit/3256198) Co-authored-by: John White <750350+johnhwhite@users.noreply.github.com> --- .github/workflows/automated-translations.yml | 86 ++++++++++ scripts/automated-translations.ps1 | 172 +++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 .github/workflows/automated-translations.yml create mode 100755 scripts/automated-translations.ps1 diff --git a/.github/workflows/automated-translations.yml b/.github/workflows/automated-translations.yml new file mode 100644 index 0000000000..0f599d6280 --- /dev/null +++ b/.github/workflows/automated-translations.yml @@ -0,0 +1,86 @@ +# Developers commit changes to the LTS branch. This workflow keeps the `automated-translations` branch up to date +# with the LTS branch. +# +# Lingoport will watch the automated-translations branch and automatically translate the resources_en_US.json +# files to the target languages. The translated files will be committed back to the automated-translations branch. +# +# When the `automated-translations` branch has changes to merge back, this workflow creates a pull request. +# +# When the `automated-translations` branch is merged and deleted, this workflow recreates the branch. + +name: Automated Translations 11.x.x +on: + push: + branches: + - 11.x.x + - automated-translations + workflow_dispatch: +env: + LTS_BRANCH: '11.x.x' +jobs: + automated-translations: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + token: '${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}' + - uses: actions/setup-node@v4 + id: setup-node + with: + node-version-file: '.nvmrc' + - name: Ensure cache directory exists + run: mkdir -p /home/runner/.npm + continue-on-error: true + - name: Sync Translation Branch + id: sync + run: | + ./scripts/automated-translations.ps1 \ + -LtsBranchName $LTS_BRANCH \ + -TempPath ${{ runner.temp }} + env: + GH_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} + + - name: Notify Slack when a PR is created + if: ${{ steps.sync.outputs.prCreated == 'true' && steps.sync.outputs.prTitle && steps.sync.outputs.prUrl }} + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_TITLE: ':writing_hand: `automated-translations` PR created: ${{ steps.sync.outputs.prTitle }}' + SLACK_MESSAGE: '${{ steps.sync.outputs.prUrl }}' + SLACK_ICON_EMOJI: ':github:' + SLACK_USERNAME: GitHub + #cor-skyux-notifications + SLACK_CHANNEL: C01GY7ZP4HM + SLACK_COLOR: ${{ job.status }} + SLACK_FOOTER: 'Blackbaud Sky Build User' + MSG_MINIMAL: 'true' + + - name: Notify Slack when a PR is updated + if: ${{ steps.sync.outputs.prCreated == 'false' && steps.sync.outputs.prTitle && steps.sync.outputs.prUrl }} + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_TITLE: ':writing_hand: `automated-translations` PR updated: ${{ steps.sync.outputs.prTitle }}' + SLACK_MESSAGE: '${{ steps.sync.outputs.prUrl }}' + SLACK_ICON_EMOJI: ':github:' + SLACK_USERNAME: GitHub + #cor-skyux-notifications + SLACK_CHANNEL: C01GY7ZP4HM + SLACK_COLOR: ${{ job.status }} + SLACK_FOOTER: 'Blackbaud Sky Build User' + MSG_MINIMAL: 'true' + + - name: Notify Slack for fails + if: ${{ failure() }} + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + SLACK_TITLE: ':writing_hand: :x: `automated-translations` sync failed' + SLACK_MESSAGE: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' + SLACK_ICON_EMOJI: ':github:' + SLACK_USERNAME: GitHub + #cor-skyux-notifications + SLACK_CHANNEL: C01GY7ZP4HM + SLACK_COLOR: 'fail' + SLACK_FOOTER: 'Blackbaud Sky Build User' + MSG_MINIMAL: 'true' diff --git a/scripts/automated-translations.ps1 b/scripts/automated-translations.ps1 new file mode 100755 index 0000000000..1eeeed5316 --- /dev/null +++ b/scripts/automated-translations.ps1 @@ -0,0 +1,172 @@ +#!/usr/bin/env pwsh + +[CmdletBinding()] +param ( + [string]$LtsBranchName, + [string]$TempPath, + [string]$IsDryRun="false" +) + +if (-not $LtsBranchName) +{ + Write-Output "`n::error::The LTS branch name is required.`n" + exit 1 +} + +if (-not $TempPath -or -not (Test-Path -Path $TempPath -PathType Container)) +{ + Write-Output "`n::error::The temp path is required.`n" + exit 1 +} + +$IsDryRunBool = [System.Convert]::ToBoolean("$IsDryRun") + +$CommitMessage = "chore: update library resources" +$GitUser = "user.name=blackbaud-sky-build-user" +$GitEmail = "user.email=sky-build-user@blackbaud.com" +$GitRepo = "blackbaud/skyux" +$TranslationBranchName = "automated-translations" +$WorkingCopy = "$TempPath/$TranslationBranchName" + +if (Test-Path -Path $WorkingCopy -PathType Container) +{ + Write-Output "`n::error::The path $WorkingCopy already exists.`n" + exit 1 +} + +# Sync the translation branch with the LTS branch +Write-Output "`n::group::Clone $LtsBranchName branch`n" +Write-Output "`n# gh repo clone $GitRepo $WorkingCopy --upstream-remote-name origin -- --branch $LtsBranchName" +gh repo clone $GitRepo $WorkingCopy --upstream-remote-name origin -- --branch $LtsBranchName +Write-Output "`n::endgroup::`n" + +Set-Location -Path $WorkingCopy +$remoteBranchExists = git ls-remote -b origin $TranslationBranchName + +if (-not $remoteBranchExists) +{ + Write-Output "`n::group::Create new $TranslationBranchName branch`n" + Write-Output "`n# git checkout -B $TranslationBranchName $LtsBranchName" + git checkout -B $TranslationBranchName $LtsBranchName + + if (-not $IsDryRunBool) + { + Write-Output "`n➡︎ The $TranslationBranchName branch does not exist. Creating the branch.`n" + Write-Output "`n# git push origin $TranslationBranchName" + git push origin $TranslationBranchName + } + Write-Output "`n::endgroup::`n" +} +else +{ + Write-Output "`n::group::Update $TranslationBranchName branch from $LtsBranchName branch`n" + Write-Output "`n# git checkout -B $TranslationBranchName origin/$TranslationBranchName" + git checkout -B $TranslationBranchName origin/$TranslationBranchName + Write-Output "`n# git pull" + git pull --set-upstream origin $TranslationBranchName + + if ($IsDryRunBool) + { + Write-Output "`n# git merge -X theirs --no-commit $LtsBranchName" + git merge -X theirs --no-commit $LtsBranchName + } + else + { + Write-Output "`n# git merge -X theirs $LtsBranchName" + git -c "$GitUser" -c "$GitEmail" merge -X theirs $LtsBranchName + } + Write-Output "`n::endgroup::`n" + + Write-Output "`n::group::NPM Install`n" + Write-Output "`n# npm ci" + npm ci --no-audit --no-progress --no-fund + Write-Output "`n::endgroup::`n" + + Write-Output "`n::group::Update library resources`n" + Write-Output "`n# npm run dev:create-library-resources" + npm run dev:create-library-resources + Write-Output "`n::endgroup::`n" + + Write-Output "`n::group::Prettier`n" + Write-Output "`n# npx nx format:write" + npx nx format:write + Write-Output "`n::endgroup::`n" + + Write-Output "`n::group::Check for changes`n" + Write-Output "`n# git status" + git status + Write-Output "#" + + $changes = git status --porcelain + + if ($changes) + { + if ($IsDryRunBool) + { + Write-Output "`nChanges detected. Run the script without the -IsDryRun flag to commit the changes.`n" + } + else + { + Write-Output "`n::endgroup::`n" + + Write-Output "`n::group::Push changes to $TranslationBranchName branch`n" + Write-Output "`n# git commit -am '${CommitMessage}'" + git add -A + git -c "$GitUser" -c "$GitEmail" commit -m "${CommitMessage}" + } + } + else + { + Write-Output "`n➡︎ No changes detected.`n" + } + + if (-not $IsDryRunBool) + { + Write-Output "`n# git push origin $TranslationBranchName" + git push origin $TranslationBranchName + } + else + { + Write-Output "`nDry run complete. Run the script without the -IsDryRun flag to push the changes.`n" + } + Write-Output "`n::endgroup::`n" + + $changesFromLts = git diff $LtsBranchName --name-only + if ($changesFromLts) + { + Write-Output "`n::group::Pull request`n" + $prForChanges = gh pr list --json title,url,headRefName --jq ".[] | select(.headRefName == `"${LtsBranchName}`")" + if ($prForChanges) + { + if ($env:GITHUB_OUTPUT) + { + Write-Output "prCreated=false" >> $env:GITHUB_OUTPUT + } + } + else + { + Write-Output "`n➡︎ Creating a pull request for changes" + Write-Output "`n# gh pr create --base $TranslationBranchName --head $LtsBranchName --title '${CommitMessage}'" + gh pr create --base $TranslationBranchName --head $LtsBranchName --title "${CommitMessage}" + $prForChanges = gh pr list --json title,url,headRefName --jq ".[] | select(.headRefName == `"${LtsBranchName}`")" + if ($env:GITHUB_OUTPUT) + { + Write-Output "prCreated=true" >> $env:GITHUB_OUTPUT + } + } + $pr = $prForChanges | ConvertFrom-Json + Write-Output "`n➡︎ Pull request for changes:`n $($pr.title)`n $($pr.url)`n" + if ($env:GITHUB_OUTPUT) + { + Write-Output "prTitle=$($pr.title)" >> $env:GITHUB_OUTPUT + Write-Output "prUrl=$($pr.url)" >> $env:GITHUB_OUTPUT + } + Write-Output "`n::endgroup::`n" + } + else + { + Write-Output "`n::group::No pull request`n" + Write-Output "`n➡︎ No changes to merge to $LtsBranchName branch from $TranslationBranchName branch.`n" + Write-Output "`n::endgroup::`n" + } +}