From a049ddece7abdd8df0bd7f4181c32778a453f12b Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Thu, 8 May 2025 17:12:28 +0100 Subject: [PATCH] Chore: Clean up completed tasks in CrowdIn (#105118) * clean up tasks as part of action * split into separate files/steps * remove unnecessary comment * update CODEOWNERS --- .github/CODEOWNERS | 1 + .../workflows/i18n-crowdin-create-tasks.yml | 8 ++- .../scripts/crowdin/cleanup-tasks.ts | 71 +++++++++++++++++++ .../workflows/scripts/crowdin/create-tasks.ts | 8 +-- 4 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/scripts/crowdin/cleanup-tasks.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b75bb8ff3e2..0b7e156aebf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -815,6 +815,7 @@ embed.go @grafana/grafana-as-code /.github/workflows/i18n-crowdin-download.yml @grafana/grafana-frontend-platform /.github/workflows/i18n-crowdin-create-tasks.yml @grafana/grafana-frontend-platform /.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform +/.github/workflows/scripts/crowdin/cleanup-tasks.ts @grafana/grafana-frontend-platform /.github/workflows/pr-go-workspace-check.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-dependabot-update-go-workspace.yml @grafana/grafana-app-platform-squad /.github/workflows/pr-k8s-codegen-check.yml @grafana/grafana-app-platform-squad diff --git a/.github/workflows/i18n-crowdin-create-tasks.yml b/.github/workflows/i18n-crowdin-create-tasks.yml index f17552ea362..fcd58d96f18 100644 --- a/.github/workflows/i18n-crowdin-create-tasks.yml +++ b/.github/workflows/i18n-crowdin-create-tasks.yml @@ -1,4 +1,4 @@ -name: Crowdin Create Tasks +name: Crowdin automatic task management on: workflow_dispatch: @@ -37,6 +37,12 @@ jobs: - run: yarn install --immutable --check-cache + - name: Clean up completed tasks + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + run: node --experimental-strip-types ./.github/workflows/scripts/crowdin/cleanup-tasks.ts + - name: Create tasks env: CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} diff --git a/.github/workflows/scripts/crowdin/cleanup-tasks.ts b/.github/workflows/scripts/crowdin/cleanup-tasks.ts new file mode 100644 index 00000000000..893d62aefd7 --- /dev/null +++ b/.github/workflows/scripts/crowdin/cleanup-tasks.ts @@ -0,0 +1,71 @@ +import crowdinImport from '@crowdin/crowdin-api-client'; + +// TODO Remove this type assertion when https://github.com/crowdin/crowdin-api-client-js/issues/508 is fixed +// @ts-expect-error +const crowdin = crowdinImport.default as typeof crowdinImport; + +const API_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN; +if (!API_TOKEN) { + console.error('Error: CROWDIN_PERSONAL_TOKEN environment variable is not set'); + process.exit(1); +} + +const PROJECT_ID = process.env.CROWDIN_PROJECT_ID ? parseInt(process.env.CROWDIN_PROJECT_ID, 10) : undefined; +if (!PROJECT_ID) { + console.error('Error: CROWDIN_PROJECT_ID environment variable is not set'); + process.exit(1); +} + +const credentials = { + token: API_TOKEN, + organization: 'grafana' +}; + +const { tasksApi } = new crowdin(credentials); + +const tasks = await listTasks(PROJECT_ID); +for (const task of tasks) { + const { id, status, progress } = task.data; + if (status === 'todo' && progress.done === progress.total) { + console.log(`Marking task ${id} as done`); + await markTaskAsDone(PROJECT_ID, id); + } else { + console.log(`Task ${id} is not done, skipping`); + } +} + +async function listTasks(projectId: number) { + try { + const listTasksParams = { + limit: 500, + } + const response = await tasksApi.listTasks(projectId, listTasksParams); + const tasks = response.data; + console.log('Fetched tasks successfully!'); + return tasks; + } catch (error) { + console.error('Failed to fetch tasks: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function markTaskAsDone(projectId: number, taskId: number) { + try { + const response = await tasksApi.editTask(projectId, taskId, [{ + op: 'replace', + path: '/status', + value: 'done', + }]); + console.log(`Task ${taskId} marked as done successfully!`); + return response.data; + } catch (error) { + console.error('Failed to mark task as done: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} diff --git a/.github/workflows/scripts/crowdin/create-tasks.ts b/.github/workflows/scripts/crowdin/create-tasks.ts index b4f76e699bd..3a2535640c8 100644 --- a/.github/workflows/scripts/crowdin/create-tasks.ts +++ b/.github/workflows/scripts/crowdin/create-tasks.ts @@ -34,7 +34,7 @@ for (const language of languages) { await createTask(PROJECT_ID, `Translate to ${name}`, id, fileIds, workflowStepId); } -async function getLanguages(projectId) { +async function getLanguages(projectId: number) { try { const project = await projectsGroupsApi.getProject(projectId); const languages = project.data.targetLanguages; @@ -49,7 +49,7 @@ async function getLanguages(projectId) { } } -async function getFileIds(projectId) { +async function getFileIds(projectId: number) { try { const response = await sourceFilesApi.listProjectFiles(projectId); const files = response.data; @@ -65,7 +65,7 @@ async function getFileIds(projectId) { } } -async function getWorkflowStepId(projectId) { +async function getWorkflowStepId(projectId: number) { try { const response = await workflowsApi.listWorkflowSteps(projectId); const workflowSteps = response.data; @@ -84,7 +84,7 @@ async function getWorkflowStepId(projectId) { } } -async function createTask(projectId, title, languageId, fileIds, workflowStepId) { +async function createTask(projectId: number, title: string, languageId: string, fileIds: number[], workflowStepId: number) { try { const taskParams = { title,