Skip to content

feat(sdk): expose terminal agent lifecycle status #51

feat(sdk): expose terminal agent lifecycle status

feat(sdk): expose terminal agent lifecycle status #51

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}"