Skip to content

Commit 7bce095

Browse files
authored
Merge pull request #12922 from ethereum/crowdin-cd-v2
Crowdin CI v2
2 parents 4c5efcf + 0a50d17 commit 7bce095

File tree

12 files changed

+167
-92
lines changed

12 files changed

+167
-92
lines changed

.github/workflows/build-crowdin.yml

Lines changed: 0 additions & 30 deletions
This file was deleted.

.github/workflows/crowdin-ci.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Crowdin CI
2+
3+
on:
4+
schedule:
5+
- cron: "20 4 1 * *" # Runs at 4:20 AM on the first day of every month
6+
workflow_dispatch: # Can be dispatched manually
7+
8+
jobs:
9+
create_approved_language_bucket_prs:
10+
runs-on: ubuntu-latest
11+
env:
12+
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
13+
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
14+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15+
16+
steps:
17+
# Set up environment
18+
- name: Check out code
19+
uses: actions/checkout@v3
20+
21+
- name: Set up Node.js
22+
uses: actions/setup-node@v3
23+
with:
24+
node-version: 18
25+
26+
- name: Install dependencies
27+
run: yarn install
28+
29+
- name: Install ts-node
30+
run: yarn global add ts-node
31+
32+
- name: Set up git
33+
run: |
34+
git config --global user.email "[email protected]"
35+
git config --global user.name "GitHub Action"
36+
37+
- name: Fetch latest dev
38+
run: git fetch origin dev
39+
40+
# Build translations
41+
- name: Build Crowdin project
42+
id: build-crowdin
43+
run: |
44+
npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/triggerBuild.ts;
45+
grep BUILD_ID output.env >> $GITHUB_ENV;
46+
47+
- name: Await latest build to finish
48+
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/awaitLatestBuild.ts
49+
50+
- name: Check build success
51+
run: |
52+
if [ $(grep BUILD_SUCCESS output.env | cut -d'=' -f2) = false ]; then
53+
echo "Build timed out, exiting"
54+
exit 1
55+
fi
56+
shell: bash
57+
58+
# Prepare bucket ids
59+
- name: Get latest translation bucket directory ids
60+
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/getBucketDirectoryIds.ts
61+
62+
# Import approved translations
63+
- name: Get translations
64+
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/getTranslations.ts
65+
66+
# Post updates as language-specific PRs
67+
- name: Process commits and post PRs by language
68+
run: npx ts-node -O '{"module":"commonjs"}' ./src/scripts/crowdin/translations/postLangPRs.ts

.github/workflows/get-translations.yml

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/scripts/crowdin/import/utils.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "fs"
1+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs"
22
import { join } from "path"
33

44
import i18Config from "../../../../i18n.config.json"
@@ -45,9 +45,7 @@ export const scrapeDirectory = (
4545
copyFileSync(source, jsonDestinationPath)
4646
// Update .json tracker
4747
trackers.langs[repoLangCode].jsonCopyCount++
48-
} else if (
49-
item.endsWith(".md")
50-
) {
48+
} else if (item.endsWith(".md")) {
5149
const mdDestDirPath: string = join(
5250
TRANSLATIONS_DIR,
5351
repoLangCode,
@@ -60,6 +58,7 @@ export const scrapeDirectory = (
6058
// Update .md tracker
6159
trackers.langs[repoLangCode].mdCopyCount++
6260
} else {
61+
if (!statSync(source).isDirectory()) return
6362
// If another directory, recursively call `scrapeDirectory`
6463
scrapeDirectory(
6564
`${path}/${item}`,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { writeFileSync } from "fs"
2+
import { join } from "path"
3+
4+
import crowdin from "../api-client/crowdinClient"
5+
6+
const FINISHED = "finished"
7+
const TIMEOUT = 2 * 60 * 60 * 1000 // Timeout after 2 hours
8+
const INTERVAL = 10 * 1000 // 10 seconds between checks
9+
10+
const OUTPUT_PATH = join(process.env["GITHUB_WORKSPACE"] || "", "output.env")
11+
12+
async function awaitLatestBuild() {
13+
const projectId = Number(process.env.CROWDIN_PROJECT_ID) || 363359
14+
15+
// BUILD_ID is provided by the triggerBuild script run in the same workflow prior to this script
16+
const buildId = process.env.BUILD_ID
17+
18+
console.log("Build ID provided:", buildId)
19+
const initialResponse = await crowdin.translationsApi.checkBuildStatus(
20+
projectId,
21+
Number(buildId)
22+
)
23+
let data = initialResponse.data
24+
25+
let isFinished = data.status === FINISHED
26+
27+
const timeoutTime = Date.now() + TIMEOUT
28+
let tryAgainTime = Date.now() - 1
29+
while (!isFinished && Date.now() < timeoutTime) {
30+
if (Date.now() < tryAgainTime) continue
31+
tryAgainTime = Date.now() + INTERVAL
32+
33+
const repeatCheck = await crowdin.translationsApi.checkBuildStatus(
34+
projectId,
35+
Number(buildId)
36+
)
37+
data = repeatCheck.data
38+
isFinished = data.status === FINISHED
39+
console.log(
40+
`id: ${buildId}, status: ${data.status}, progress ${data.progress}`
41+
)
42+
}
43+
44+
if (data.status !== FINISHED) {
45+
writeFileSync(OUTPUT_PATH, `BUILD_SUCCESS=false\n`, { flag: "a" })
46+
throw new Error(
47+
`Timeout: Build did not finish in ${TIMEOUT / 1000 / 60} minutes`
48+
)
49+
}
50+
51+
console.log("Latest build data:", data)
52+
writeFileSync(OUTPUT_PATH, `BUILD_SUCCESS=true\n`, { flag: "a" })
53+
}
54+
55+
awaitLatestBuild()
56+
57+
export default awaitLatestBuild
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import { resolve } from "path"
1+
import { join } from "path"
22

33
export const DOT_CROWDIN = ".crowdin"
44

55
export const CROWDIN_DATA_DIR = "src/data/crowdin"
66
export const SAVE_FILE = "download.zip"
7-
export const FILE_PATH = resolve(CROWDIN_DATA_DIR, SAVE_FILE)
7+
export const FILE_PATH = join(CROWDIN_DATA_DIR, SAVE_FILE)
88

99
export const SUMMARY_SAVE_FILE = "import-summary.json"
10-
export const SUMMARY_PATH = resolve(CROWDIN_DATA_DIR, SUMMARY_SAVE_FILE)
10+
export const SUMMARY_PATH = join(CROWDIN_DATA_DIR, SUMMARY_SAVE_FILE)
1111

1212
export const BUCKETS_IMPORTED_FILE = "buckets-imported.json"
13-
export const BUCKETS_PATH = resolve(CROWDIN_DATA_DIR, BUCKETS_IMPORTED_FILE)
13+
export const BUCKETS_PATH = join(CROWDIN_DATA_DIR, BUCKETS_IMPORTED_FILE)
1414

1515
export const APPROVAL_THRESHOLD = 100

src/scripts/crowdin/translations/getApprovedBuckets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import type { BucketsList } from "../import/types"
77
import { APPROVAL_THRESHOLD } from "./constants"
88

99
async function getApprovedBuckets(): Promise<BucketsList> {
10+
console.log("⏳ Getting approved buckets...")
1011
const projectId = Number(process.env.CROWDIN_PROJECT_ID) || 363359
1112

1213
const bucketsList: BucketsList = {}
1314

14-
// TODO: Consider regenerating bucketDirs list on each run for fidelity
1515
for (const bucketDir of bucketDirs) {
1616
const directoryProgress =
1717
await crowdin.translationStatusApi.getDirectoryProgress(
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import getAndSaveDirectories from "../source-files/fetchAndSaveDirectories"
2+
3+
async function main() {
4+
await getAndSaveDirectories()
5+
}
6+
7+
main()
8+
9+
export default main

src/scripts/crowdin/translations/postLangPRs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "fs"
22

33
import { LOCALES_CODES } from "../../../lib/constants"
4-
import { BucketsList } from "../import/types"
4+
import type { BucketsList } from "../import/types"
55

66
import { BUCKETS_PATH } from "./constants"
77
import { createLocaleTranslationPR } from "./utils"

src/scripts/crowdin/translations/triggerBuild.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1+
import { writeFileSync } from "fs"
2+
import { join } from "path"
3+
14
import crowdin from "../api-client/crowdinClient"
25

3-
import "dotenv/config"
6+
const OUTPUT_PATH = join(process.env["GITHUB_WORKSPACE"] || "", "output.env")
47

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

811
try {
9-
await crowdin.translationsApi.buildProject(projectId, {
10-
exportApprovedOnly: true,
11-
})
12+
const response = await crowdin.translationsApi.buildProject(projectId)
13+
const { id, status } = response.data
14+
const isAlreadyFinished = status === "finished"
15+
console.log(
16+
`Build ${isAlreadyFinished ? "already finished" : "triggered"} id:`,
17+
id
18+
)
19+
writeFileSync(OUTPUT_PATH, `BUILD_ID=${id}\n`, { flag: "a" })
1220
} catch (error: unknown) {
1321
console.error((error as Error).message)
1422
}

0 commit comments

Comments
 (0)