Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crowdin CI v2 #12922

Merged
merged 20 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 0 additions & 30 deletions .github/workflows/build-crowdin.yml
Copy link
Member Author

Choose a reason for hiding this comment

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

No longer needed as a dedicated workflow.

This file was deleted.

55 changes: 43 additions & 12 deletions .github/workflows/get-translations.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
name: Import Crowdin translations
name: Crowdin CI

on:
schedule:
- cron: "20 16 1 * *" # Runs at 4:20 PM on the first day of every month
workflow_dispatch:
- cron: "20 4 1 * *" # Runs at 4:20 AM on the first day of every month
workflow_dispatch: # Can be dispatched manually

jobs:
create_pr:
create_approved_language_bucket_prs:
runs-on: ubuntu-latest
env:
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved these up to avoid repeating them during each needed step

steps:
# Set up environment
- name: Check out code
uses: actions/checkout@v3

Expand All @@ -31,17 +37,42 @@ jobs:
- name: Fetch latest dev
run: git fetch origin dev

- name: Get translations
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/getTranslations.ts
env:
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}

- name: Authenticate GitHub CLI
run: |
echo ${{ secrets.GITHUB_TOKEN }} | gh auth login --with-token

# Build translations
- name: Build Crowdin project
id: build-crowdin
run: |
OUTPUT=$(npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/triggerBuild.ts)
echo "$OUTPUT"
shell: bash
Copy link
Member Author

Choose a reason for hiding this comment

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

This syntax echo's the console.log statements made from inside triggerBuild.ts. Console log statements matching a particular pattern of ::set-output name=key_name::value can be extracted in a future step.

In triggerBuild.ts we're console logging ::set-output name=buildId::${id} which allows us to get the buildId variable next.


- name: Await latest build to finish
env:
BUILD_ID: ${{ steps.build-crowdin.outputs.buildId}}
run: |
Copy link
Member Author

Choose a reason for hiding this comment

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

This assigns the buildId output from the previous step (step id: build-crowdin) to an environment variable to be accessed by the next script.

OUTPUT=$(npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/awaitLatestBuild.ts)
echo "$OUTPUT"
shell: bash
Copy link
Member Author

Choose a reason for hiding this comment

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

Similar syntax to above, but this time we're outputting ::set-output name=buildSuccess::true (or false) depending if the build eventually switched to "finished" status or not.


- name: Check build success
run: |
if [ "${{ steps.build-crowdin.outputs.buildSuccess }}" = "false" ]; then
echo "Build timed out, exiting"
exit 1
fi
shell: bash
Copy link
Member Author

Choose a reason for hiding this comment

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

If the buildSuccess from the prior step was "false" the we exit early due to timing out. exit 1 will be treated as a failure, and the workflow will stop here.


# Prepare bucket ids
- name: Get latest translation bucket directory ids
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/getBucketDirectoryIds.ts

Copy link
Member Author

Choose a reason for hiding this comment

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

Run the script to get the up-to-date bucket directory id's

# Import approved translations
- name: Get translations
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/getTranslations.ts

# Post updates as language-specific PRs
- name: Process commits and post PRs by language
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/postLangPRs.ts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 changes: 52 additions & 0 deletions src/scripts/crowdin/translations/awaitLatestBuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import crowdin from "../api-client/crowdinClient"

const FINISHED = "finished"
const TIMEOUT = 2 * 60 * 60 * 1000 // Timeout after 2 hours
const INTERVAL = 10 * 1000 // 10 seconds between checks

async function awaitLatestBuild() {
const projectId = Number(process.env.CROWDIN_PROJECT_ID) || 363359

// BUILD_ID is provided by the triggerBuild script run in the same workflow prior to this script
const buildId = process.env.BUILD_ID

console.log("Build ID provided:", buildId)
const initialResponse = await crowdin.translationsApi.checkBuildStatus(
projectId,
Number(buildId)
)
let data = initialResponse.data

let isFinished = data.status === FINISHED

const timeoutTime = Date.now() + TIMEOUT
let tryAgainTime = Date.now() - 1
while (!isFinished && Date.now() < timeoutTime) {
if (Date.now() < tryAgainTime) continue
tryAgainTime = Date.now() + INTERVAL

const repeatCheck = await crowdin.translationsApi.checkBuildStatus(
projectId,
Number(buildId)
)
data = repeatCheck.data
isFinished = data.status === FINISHED
console.log(
`id: ${buildId}, status: ${data.status}, progress ${data.progress}`
)
}

if (data.status !== FINISHED) {
console.error(`::set-output name=buildSuccess::false`)
Copy link
Member Author

@wackerow wackerow May 9, 2024

Choose a reason for hiding this comment

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

If fetch has timed out and still not finished, export buildSuccess=false to terminate workflow early.

throw new Error(
`Timeout: Build did not finish in ${TIMEOUT / 1000 / 60} minutes`
)
}

console.log("Latest build data:", data)
console.error(`::set-output name=buildSuccess::true`)
}

awaitLatestBuild()

export default awaitLatestBuild
9 changes: 9 additions & 0 deletions src/scripts/crowdin/translations/getBucketDirectoryIds.ts
Copy link
Member Author

Choose a reason for hiding this comment

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

Created this since fetchAndSaveDirectories does not automatically call this function, and it is used elsewhere so I wanted to avoid conflicting.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import getAndSaveDirectories from "../source-files/fetchAndSaveDirectories"

async function main() {
await getAndSaveDirectories()
}

main()

export default main
2 changes: 1 addition & 1 deletion src/scripts/crowdin/translations/postLangPRs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from "fs"

import { LOCALES_CODES } from "../../../lib/constants"
import { BucketsList } from "../import/types"
import type { BucketsList } from "../import/types"

import { BUCKETS_PATH } from "./constants"
import { createLocaleTranslationPR } from "./utils"
Expand Down
13 changes: 8 additions & 5 deletions src/scripts/crowdin/translations/triggerBuild.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import crowdin from "../api-client/crowdinClient"

import "dotenv/config"

Copy link
Member Author

Choose a reason for hiding this comment

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

Turns out this isn't needed when running from an action

async function triggerBuild() {
const projectId = Number(process.env.CROWDIN_PROJECT_ID) || 363359

try {
await crowdin.translationsApi.buildProject(projectId, {
exportApprovedOnly: true,
})
const response = await crowdin.translationsApi.buildProject(projectId)
const { id, status } = response.data
const isAlreadyFinished = status === "finished"
console.log(
`Build ${isAlreadyFinished ? "already finished" : "triggered"} id:`,
id
)
console.log(`::set-output name=buildId::${id}`)
Copy link
Member Author

Choose a reason for hiding this comment

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

output id for use in next workflow step

} catch (error: unknown) {
console.error((error as Error).message)
}
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/crowdin/translations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ yarn markdown-checker

${qaResults}
</details>

@coderabbitai review
`
}

Expand Down