feat(sdk): expose terminal agent lifecycle status #51
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Translate Catalogs | |
| # Every enabled locale is gated by `lingui compile --strict`, so a PR that | |
| # adds or changes an English string owes a translation in all of them. This | |
| # workflow closes that gap on the PR itself: when the extracted catalogs have | |
| # untranslated entries, it fills them with Claude, verifies the strict gate, | |
| # and pushes the fills back to the PR branch. | |
| # | |
| # The push uses a GitHub App token rather than GITHUB_TOKEN because pushes | |
| # made with GITHUB_TOKEN do not trigger workflows — the PR would be stuck | |
| # with no checks on its newest commit. | |
| on: | |
| pull_request: | |
| types: [opened, synchronize] | |
| concurrency: | |
| group: translate-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| translate: | |
| name: Fill missing translations | |
| # Fork PRs have no secrets; their catalog gap surfaces in the Lint job's | |
| # catalog audit instead, for a maintainer to fill. | |
| if: github.event.pull_request.head.repo.full_name == github.repository | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Mint app token | |
| id: app-token | |
| uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 | |
| with: | |
| app-id: ${{ secrets.GH_APP_ID }} | |
| private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} | |
| # Least privilege: pushing the fills needs contents only. | |
| permission-contents: write | |
| - name: Checkout PR head | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| ref: ${{ github.event.pull_request.head.ref }} | |
| # The stale-translation check resolves a merge base. | |
| fetch-depth: 0 | |
| # Extract and the translator execute repo-controlled code (lingui | |
| # config, scripts), so the token must not sit in git config where a | |
| # malicious PR could read it. It is passed only to the push step. | |
| persist-credentials: false | |
| - name: Setup Bun | |
| id: setup-bun | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version-file: .bun-version | |
| - name: Cache dependencies | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| with: | |
| path: ~/.bun/install/cache | |
| key: ${{ runner.os }}-bun-${{ steps.setup-bun.outputs.bun-revision }}-${{ hashFiles('bun.lock') }} | |
| - name: Install dependencies | |
| run: bun install --frozen --ignore-scripts | |
| - name: Extract catalogs and count missing translations | |
| id: missing | |
| working-directory: packages/i18n | |
| run: | | |
| bunx lingui extract --clean --overwrite > /dev/null | |
| bun scripts/sort-po-references.ts > /dev/null | |
| # Entry-aware count: the PO header is msgid "" / msgstr "" and must | |
| # not be mistaken for an untranslated entry. | |
| COUNT=$(bun -e ' | |
| import { readFileSync, readdirSync } from "node:fs"; | |
| let sum = 0; | |
| for (const loc of readdirSync("locales", { withFileTypes: true }) | |
| .filter((e) => e.isDirectory() && e.name !== "en") | |
| .map((e) => e.name)) { | |
| const src = readFileSync(`locales/${loc}/messages.po`, "utf8"); | |
| for (const m of src.matchAll(/msgid "((?:[^"\\]|\\.)+)"\nmsgstr ""/g)) sum++; | |
| } | |
| console.log(sum);') | |
| echo "count=$COUNT" >> "$GITHUB_OUTPUT" | |
| echo "Missing translations: $COUNT" | |
| - name: Translate missing entries | |
| if: steps.missing.outputs.count != '0' | |
| working-directory: packages/i18n | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: bun scripts/translate-missing.ts | |
| - name: Verify the strict gate passes with the fills | |
| if: steps.missing.outputs.count != '0' | |
| working-directory: packages/i18n | |
| run: | | |
| bun scripts/sort-po-references.ts > /dev/null | |
| bunx lingui compile --strict | |
| bun scripts/check-stale-translations.ts | |
| # Commit carries no token: earlier PR-controlled steps can write | |
| # .git/hooks, and a hook run by git commit inherits the step env. | |
| - name: Commit fills | |
| if: steps.missing.outputs.count != '0' | |
| run: | | |
| git config user.name "superset-i18n[bot]" | |
| git config user.email "i18n-bot@superset.sh" | |
| git add packages/i18n/locales | |
| if git diff --cached --quiet; then | |
| echo "Translator produced no accepted fills; leaving the PR red for a human." | |
| exit 1 | |
| fi | |
| git -c core.hooksPath=/dev/null commit -m "chore(i18n): translate new strings across enabled locales" | |
| - name: Push fills | |
| if: steps.missing.outputs.count != '0' | |
| env: | |
| APP_TOKEN: ${{ steps.app-token.outputs.token }} | |
| # The head ref is attacker-named text; it reaches the shell only as | |
| # a quoted variable, never by template interpolation. | |
| HEAD_REF: ${{ github.event.pull_request.head.ref }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| git -c core.hooksPath=/dev/null push \ | |
| "https://x-access-token:${APP_TOKEN}@github.com/${REPO}.git" \ | |
| "HEAD:refs/heads/${HEAD_REF}" |