Skip to content

env and secret commands: build environment and provider-held secrets - #36

Open
Interlap01 wants to merge 7 commits into
mainfrom
env-secrets
Open

Interlap01 wants to merge 7 commits into
mainfrom
env-secrets

Conversation

@Interlap01

Copy link
Copy Markdown
Collaborator

What

  • builder env set|unset|list [--profile] [--json]: plain values in builder.json. New top-level env applies to every build; a profile's env overrides it per key.
  • builder secret set|unset|list [--profile] [--provider] [--json]: the value goes to the CI provider, only the name goes into builder.json (secrets, top level and per profile).
    • GitHub: repository Actions secret, sealed with the repo public key (reuses the existing libsodium code).
    • Codemagic: secure variable in the app's builder variable group (v3 variable-groups API; group created if missing).
    • Bitrise: app secret, created protected with input expansion and PR exposure off; updates send only the value.
    • The value comes from a hidden prompt or --value-stdin. It is never an argument and never printed. Every name check runs before the prompt.
  • Per-profile values use a name suffix: --profile production stores NAME__PRODUCTION. A build with that profile takes it when present, else NAME, and exports it as NAME in both cases. This works the same on all three providers. GitHub Environments are not used.
  • Exposure on the runner:
    • GitHub: the Resolve parameters step (and only that step) receives ${{ toJSON(secrets) }} and exports only the listed names via the existing base64 + random-delimiter $GITHUB_ENV heredoc. Each line of a value gets ::add-mask:: before anything is written.
    • The names travel in the existing profile JSON input. No new dispatch input. On tag builds they are read from builder.json.
    • runner.sh (export_build_secrets) does the same over the provider's variables.
    • A listed name with no value fails the job and names builder secret set.
  • Validation:
    • Secret names must be upper case and must not contain __.
    • The existing reserved env names apply, so secrets cannot shadow IOS_*, MOBAI_API_KEY, GITHUB_*, and so on.
    • A name cannot be both a plain env value and a secret.
    • ios build refuses secrets when the local ios-build.yml predates them, because an older workflow would silently ignore the list.

Why

Roadmap item 8 (environment and secrets profiles), in the style of EAS: build-time config in the repo, secrets kept on the CI provider, with no dashboard steps.

How tested

  • Config: merge, validation and round-trip tests. Build: dispatch input and runner variable mapping, plus the old-workflow refusal.
  • The real Resolve parameters script runs against a fake toJSON(secrets) value. Tests check that it exports exactly the listed secrets, prefers the profile value, masks every line, never prints unlisted values, and fails on missing, invalid or conflicting names.
  • The runner.sh function runs under bash with the same cases.
  • Provider clients are tested against httptest fakes of the GitHub, Codemagic v3 and Bitrise secrets APIs. The GitHub fake decrypts the sealed box to check the stored value.
  • The CLI commands run end to end through the real clients against the three fakes.
  • go test -race ./..., go vet, gofmt and golangci-lint v2.12.2 (the version CI uses) are all clean. No live runs against GitHub, Codemagic or Bitrise.

Left out

  • ios share gets no env or secrets, unchanged from before (its workflow takes no profile).
  • Secrets apply to ios build/ios release only. Organisation-level secrets (Bitrise org, Codemagic team groups) are not managed.
  • builder signing setup still uploads signing sets only to GitHub.

A top-level env applies to every build and a profile's env overrides it
key by key. builder.json also lists the names of provider-held secrets
(top level and per profile) a build exposes; only names, never values.
The names travel in the profile dispatch input (sent now also without a
profile when there is a top-level env or secret) and as BUILDER_SECRETS
for runner.sh. Secret names are upper case, without a double underscore,
and pass the same reserved-name checks as env.
The Resolve parameters step of ios-build.yml receives toJSON(secrets)
and exports only the names builder.json lists, masking every line of each
value first. A profile's build takes NAME__<PROFILE> when it exists, else
NAME. runner.sh does the same over the provider's variables. A listed name
without a value fails the job naming builder secret set.
GitHub: SetSecret seals the value with the repository key, DeleteSecret.
Codemagic: secure variables in the app's builder variable group (v3 API),
creating the group on first use, updating in place. Bitrise: app secrets,
created protected with expansion and pull-request exposure off; an update
sends only the value. All three list names only.
Edits the top-level env or a profile's (--profile) in builder.json with the
same name checks a build applies, and refuses a name that is also a listed
secret. env list --profile shows what that profile's builds get and where
each value is set; --json for scripts.
set reads the value from a hidden prompt or --value-stdin (never argv,
never printed), stores it on the provider (--provider, else the profile's,
else builder.json's, else GitHub) as NAME, or NAME__<PROFILE> with
--profile, and lists the name in builder.json. Name checks run before the
value is asked for. unset deletes the stored value and the name; list
shows each listed name, where it is stored and whether the provider has
it, never a value.
README section on env and secret commands, per-profile values and the
runner exposure; CLAUDE.md commands and patterns; the provider secrets
guide points app secrets at builder secret set.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant