CI/CD

CI pipelines

Any CI that can run a shell can push the source language on merge and pull every language before a release build.

What you get

Source in on merge, every language out before the build.

  • Scriptable from the CLI

    Push, pull and sync from a terminal, a build phase or a coding agent's shell, with a token in the environment.

    The CLI
  • Everything is an HTTP call

    Project tokens carry read, push and manage scopes, so a CI token that pushes files cannot delete keys.

    The HTTP API
  • Pulls on every build

    A Run Script phase above Compile Sources keeps the target on the latest verified text without anyone remembering to sync.

    Xcode build phase
  • Updates over the air

    Publish the verified strings and let the running app fetch them, so a fixed translation reaches users without a store release.

    Over-the-air delivery

Set it up

From nothing to every language.

  1. Step 1: Store the token as a secret

    Two tokens keep the rights apart: one with push for the job that writes source files, one with read for the job that pulls.

  2. Step 2: Push the source on merge to main

    A GitHub Actions job; the same two lines work on GitLab, Bitbucket or Jenkins.

    .github/workflows/localization.yml
    on:
      push:
        branches: [main]
    jobs:
      push-source:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: |
              curl -fsS -X POST "https://anylocale.com/api/loc/push?project=my-app&format=xcstrings" \
                -H "Authorization: Bearer $ANYLOCALE_TOKEN" \
                --data-binary @Localizable.xcstrings
            env:
              ANYLOCALE_TOKEN: ${{ secrets.ANYLOCALE_PUSH_TOKEN }}
  3. Step 3: Pull before the release build

    A second job with the read token. The installer lands in ~/.local/bin on a runner without write access to /usr/local/bin, so PATH is extended in the same step; the build that follows picks the files up.

    .github/workflows/release.yml
    jobs:
      build:
        runs-on: macos-latest
        steps:
          - uses: actions/checkout@v4
          - run: |
              curl -fsSL https://anylocale.com/install.sh | sh
              export PATH="$HOME/.local/bin:$PATH"
              anylocale sync --project my-app --platform ios --out-dir Sources/Resources
            env:
              ANYLOCALE_TOKEN: ${{ secrets.ANYLOCALE_READ_TOKEN }}
          - run: xcodebuild -scheme MyApp -configuration Release

Good to know

  • The installer writes to ~/.local/bin on a runner without write access to /usr/local/bin; add it to PATH in the same step.
  • Exit code 2 means a request or download failed; let it fail the job.

Questions

Why two tokens?
So the rights stay apart: the job that pushes source files gets push, the job that pulls before a build gets read. Neither can delete a key.
What happens when a download fails?
The CLI exits with code 2 and no half-written file is left behind. Let it fail the job; a build on stale text is the worse outcome.
Which CI systems work?
Any that can run a shell. The examples are GitHub Actions; the same two lines work on GitLab, Bitbucket, Jenkins or a Mac mini under a desk.

Start with the file your app already ships.

After checkout, import one strings file and read the first drafts within minutes.

Nothing to connect and nothing to install. The CLI is optional.