Skip to content

feat(cli): default credentials manage export to a combined .env file#2280

Merged
riderx merged 3 commits into
mainfrom
feat/credentials-export-combined
May 18, 2026
Merged

feat(cli): default credentials manage export to a combined .env file#2280
riderx merged 3 commits into
mainfrom
feat/credentials-export-combined

Conversation

@WcaleNieWolny
Copy link
Copy Markdown
Contributor

@WcaleNieWolny WcaleNieWolny commented May 18, 2026

Summary

  • build credentials manageExport to .env now writes a single .env.capgo.<appId> file containing both iOS and Android secrets when both platforms are configured.
  • iOS and Android env-var names are disjoint, so combining is conflict-free and removes the need to run gh secret set -f (or equivalent) twice in CI setup.
  • Want a single-platform file? Use the existing --platform ios (or --platform android) to narrow the manage scope upstream — the export naturally becomes per-platform.
  • Drift-safe: shared config keys (BUILD_OUTPUT_UPLOAD_ENABLED, BUILD_OUTPUT_RETENTION_SECONDS, SKIP_BUILD_NUMBER_BUMP, CAPGO_IOS_DISTRIBUTION, etc.) can be stored under both platforms and are allowed to diverge. Before writing the combined file, the manager detects any such conflicts, prints them, requires explicit confirmation, and embeds the conflict list as a comment block at the top of the file so future readers can spot it.

Why

The previous behaviour forced users to pick Export to .env twice (once per platform) when configuring CI/CD secrets for an app with both iOS and Android builds. That extra step is invisible from the docs side and confusing in the TUI ("why do I have to do this twice?"). Combining into one file matches the natural gh secret set -f <env-file> workflow and gets users from "I have credentials configured locally" to "GitHub Actions can build" in fewer steps.

File format

# Capgo build credentials — CI/CD environment file
# App: com.example.app
# Platforms: ios, android
# Source: global credentials store
# Generated: 2026-05-18T...
#
# Paste these into your CI/CD provider as secrets, or source the file locally:
#   set -a; . ./this-file; set +a
#
# DO NOT commit this file. Add to .gitignore: .env.capgo.*
#
# ⚠️  CONFLICTING KEYS: the following appear in multiple platform sections      # (only present if drift was detected and the user confirmed the write)
# with different values. Dotenv ingestion (gh secret set -f, shell sourcing)
# keeps only the LAST occurrence per key — re-export per-platform to preserve both.
#   BUILD_OUTPUT_RETENTION_SECONDS (ios & android)

# === IOS ===
BUILD_CERTIFICATE_BASE64=...
P12_PASSWORD=...
APPLE_KEY_ID=...
CAPGO_IOS_PROVISIONING_MAP_BASE64=...

# === ANDROID ===
ANDROID_KEYSTORE_FILE=...
KEYSTORE_KEY_ALIAS=...

Behaviour matrix

Invocation Both platforms configured Only one platform configured
manage Single combined .env.capgo.<appId> (warns + confirms on shared-key drift) Per-platform .env.capgo.<appId>.<platform>
manage --platform ios iOS only, .env.capgo.<appId>.ios iOS only, .env.capgo.<appId>.ios
manage --platform android Android only, .env.capgo.<appId>.android Android only, .env.capgo.<appId>.android

Test plan

  • bun run typecheck clean
  • bun run lint clean
  • bun run build succeeds
  • bun run test:credentials — 17/17 pass
  • bun run test:credentials-validation — 13/13 pass
  • node dist/index.js build credentials manage --help shows expected flags
  • Manual TUI smoke test: two-platform credentials, no drift → combined file written silently
  • Manual TUI smoke test: two-platform credentials with a shared key drifted → warning + confirm prompt, comment block emitted
  • Manual TUI smoke test: --platform ios on a two-platform app → per-platform path
  • Manual TUI smoke test: single-platform credentials → degraded per-platform path

Follow-up

Docs page at website/src/content/docs/docs/cli/cloud-build/github-actions.mdx will be updated to match in a separate PR on Cap-go/website after this ships in @capgo/cli@latest.

Summary by CodeRabbit

  • Improvements
    • Export now produces a single combined .env by default for apps with multiple platforms, avoiding a platform prompt
    • If only one platform has credentials, export falls back to per-platform behavior
    • Detects conflicting keys across platforms and prompts before proceeding
    • Outputs distinct per-platform sections (including provisioning map variants) and enforces secure file permissions
    • Updated help text and export logging with platform context

Review Change Stack

When both iOS and Android are configured, `build credentials manage` now
writes a single `.env.capgo.<appId>` file containing both platforms'
secrets under `# === IOS ===` / `# === ANDROID ===` sections. iOS and
Android env-var names are disjoint, so combining them is conflict-free
and removes the need to run `gh secret set -f` (or equivalent) twice.

Add `--per-platform` to opt back into the old behaviour: one file per
platform, with the TUI prompting for which platform to export.

If only one platform is configured (or only one has any values), the
combined path degrades to the existing per-platform name to avoid
surprising users with a generic file name.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b213195-aafb-49c0-b83a-b39073f7cba6

📥 Commits

Reviewing files that changed from the base of the PR and between 2c63e74 and 0f7636c.

📒 Files selected for processing (1)
  • cli/src/build/credentials-manage.ts

📝 Walkthrough

Walkthrough

Export flow auto-detects multi-platform credential entries and writes a single combined .env by default (skipping platform selection). Help text updated; new combined exporter assembles per-platform sections, detects conflicts, serializes provisioning maps, prompts if needed, and writes the file with 0o600 permissions.

Changes

Combined Multi-Platform Export

Layer / File(s) Summary
Help text and multi-platform export routing
cli/src/build/credentials-manage.ts
Export action help text describes combined multi-platform output by default; exportToEnvFile detects multiple configured platforms and routes to exportCombinedEnvFile without prompting.
Combined export implementation and rendering
cli/src/build/credentials-manage.ts
Adds exportCombinedEnvFile with per-platform section assembly, conflict detection and confirmation, fallback to single-platform export, secure write + explicit chmod(0o600), and renderEnvFileCombined producing per-platform sections (including CAPGO_IOS_PROVISIONING_MAP_BASE64 and escaped raw map). resolveExportTarget now accepts `ios

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Cap-go/capgo#2052: Related prior changes to .env export flow, platform handling, and file permission enforcement that this combined-export extension builds on.
  • Cap-go/capgo#2025: Related work on CAPGO_IOS_PROVISIONING_MAP_BASE64 handling; the combined renderer writes both base64 and escaped raw provisioning-map values.

Suggested reviewers

  • zinc-builds

Poem

🐰 I stitched iOS and Android into one tidy file,
With headers, maps, and secrets kept close and agile.
Conflicts get a nudge, you confirm with a grin,
Then 0o600 locks the treasure within.
Hoppity export — one .env, two-platform style.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: making the credentials export default to a combined .env file instead of requiring per-platform selection.
Description check ✅ Passed The description is comprehensive and well-structured, covering summary, rationale, file format, behavior matrix, test plan with clear status indicators, and follow-up plans.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/credentials-export-combined

Comment @coderabbitai help to get the list of available commands and usage tips.

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 18, 2026

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing feat/credentials-export-combined (0f7636c) with main (58c8448)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cli/src/build/credentials-manage.ts (1)

1232-1248: 💤 Low value

Consider extracting shared export-and-log logic.

The single-platform fallback path (lines 1233–1247) duplicates the write-file, chmod, log pattern from exportToEnvFile (lines 1195–1214). A small helper could reduce this, but the current code is correct and self-contained.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/build/credentials-manage.ts` around lines 1232 - 1248, The
single-platform branch duplicates the write/chmod/log sequence found in
exportToEnvFile; extract a small helper (e.g., exportEnvFile or
performEnvExport) that accepts the resolved target (from resolveExportTarget),
the rendered content (from renderEnvFile), and the creds (to compute
fieldCount), then performs writeFile(..., {mode: 0o600}), chmod(..., 0o600), and
the pLog.success/pLog.info messages; replace the duplicated block in this branch
with a call to that helper and reuse it from exportToEnvFile to remove
duplication while keeping behavior identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cli/src/build/credentials-manage.ts`:
- Around line 1232-1248: The single-platform branch duplicates the
write/chmod/log sequence found in exportToEnvFile; extract a small helper (e.g.,
exportEnvFile or performEnvExport) that accepts the resolved target (from
resolveExportTarget), the rendered content (from renderEnvFile), and the creds
(to compute fieldCount), then performs writeFile(..., {mode: 0o600}), chmod(...,
0o600), and the pLog.success/pLog.info messages; replace the duplicated block in
this branch with a call to that helper and reuse it from exportToEnvFile to
remove duplication while keeping behavior identical.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a32a081-309f-4de4-af05-b79887dd7ab9

📥 Commits

Reviewing files that changed from the base of the PR and between 58c8448 and 2d10614.

📒 Files selected for processing (2)
  • cli/src/build/credentials-manage.ts
  • cli/src/index.ts

The new flag was redundant with the existing --platform option:
  - Want both platforms in one file? → manage (no flag)
  - Want only one platform? → manage --platform ios
  - Want both as separate files? → manage --platform ios, then --platform android

Combined export is now the only behaviour when both platforms are in
scope; --platform narrows scope upstream so the export naturally
becomes single-platform.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d10614d3a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1393 to +1398
for (const [key, value] of Object.entries(creds)) {
if (typeof value !== 'string' || value.length === 0)
continue
if (key === 'CAPGO_IOS_PROVISIONING_MAP')
continue
lines.push(`${key}=${escapeDotenvValue(value)}`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Deduplicate shared keys in combined env export

When both platforms are exported together, this loop writes every key from each platform verbatim, so shared settings like BUILD_OUTPUT_UPLOAD_ENABLED, BUILD_OUTPUT_RETENTION_SECONDS, or SKIP_BUILD_NUMBER_BUMP can appear twice in the same dotenv file. The manager explicitly allows shared keys to exist (and even drift) per platform, so combined export can silently emit conflicting assignments where one value overwrites the other during dotenv ingestion (e.g., gh secret set -f/shell sourcing), producing incorrect CI secrets for at least one platform.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
cli/src/build/credentials-manage.ts (2)

1369-1409: ⚡ Quick win

Consider extracting the provisioning-map rendering logic into a helper.

The provisioning-map handling (lines 1390-1404) duplicates the logic from renderEnvFile (lines 1348-1363): extract the raw value, skip it in the main loop, then append base64 and commented raw forms. Extracting this into a helper function would eliminate the duplication and keep both renderers DRY.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/build/credentials-manage.ts` around lines 1369 - 1409, The
provisioning-map handling is duplicated between renderEnvFileCombined and
renderEnvFile: both extract creds.CAPGO_IOS_PROVISIONING_MAP, skip it in the
main loop, then append a base64 entry and a commented raw entry. Extract that
logic into a small helper (e.g., renderProvisioningMapEntries or
formatProvisioningMapLines) that accepts the raw provisioningMap string and
escapeDotenvValue and returns the array/string lines for the base64 and
commented raw forms (including the comment header). Replace the duplicated block
in renderEnvFileCombined and the block in renderEnvFile to call this helper and
append its returned lines, keeping existing symbols CAPGO_IOS_PROVISIONING_MAP,
CAPGO_IOS_PROVISIONING_MAP_BASE64, and escapeDotenvValue unchanged.

1215-1268: ⚡ Quick win

Consider extracting the single-platform export logic into a shared helper.

The fallback path (lines 1230-1246) duplicates the main single-platform export logic from exportToEnvFile (lines 1193-1212). Both resolve the target, render content, write the file, chmod, and log the same success messages. Extracting this into a helper like exportSinglePlatformEnvFile would reduce duplication and make future changes easier to maintain.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/build/credentials-manage.ts` around lines 1215 - 1268, The
single-platform export block in exportCombinedEnvFile duplicates the same steps
found in exportToEnvFile (resolveExportTarget, renderEnvFile, writeFile, chmod
and pLog messages); extract that repeated logic into a helper function (e.g.
exportSinglePlatformEnvFile) that accepts (entry, platform, creds, defaultName)
and performs resolveExportTarget, renders via renderEnvFile, writes/chmods the
file and emits the same pLog.info/pLog.success messages, then replace both the
fallback block in exportCombinedEnvFile and the code in exportToEnvFile to call
this new helper to remove duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cli/src/build/credentials-manage.ts`:
- Around line 1369-1409: The provisioning-map handling is duplicated between
renderEnvFileCombined and renderEnvFile: both extract
creds.CAPGO_IOS_PROVISIONING_MAP, skip it in the main loop, then append a base64
entry and a commented raw entry. Extract that logic into a small helper (e.g.,
renderProvisioningMapEntries or formatProvisioningMapLines) that accepts the raw
provisioningMap string and escapeDotenvValue and returns the array/string lines
for the base64 and commented raw forms (including the comment header). Replace
the duplicated block in renderEnvFileCombined and the block in renderEnvFile to
call this helper and append its returned lines, keeping existing symbols
CAPGO_IOS_PROVISIONING_MAP, CAPGO_IOS_PROVISIONING_MAP_BASE64, and
escapeDotenvValue unchanged.
- Around line 1215-1268: The single-platform export block in
exportCombinedEnvFile duplicates the same steps found in exportToEnvFile
(resolveExportTarget, renderEnvFile, writeFile, chmod and pLog messages);
extract that repeated logic into a helper function (e.g.
exportSinglePlatformEnvFile) that accepts (entry, platform, creds, defaultName)
and performs resolveExportTarget, renders via renderEnvFile, writes/chmods the
file and emits the same pLog.info/pLog.success messages, then replace both the
fallback block in exportCombinedEnvFile and the code in exportToEnvFile to call
this new helper to remove duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6adb3e9-3029-46da-a811-aaba9b4e883c

📥 Commits

Reviewing files that changed from the base of the PR and between 2d10614 and 2c63e74.

📒 Files selected for processing (1)
  • cli/src/build/credentials-manage.ts

The combined export emits every key from each platform verbatim, so a
shared config toggle stored under both platforms (e.g.
BUILD_OUTPUT_UPLOAD_ENABLED, BUILD_OUTPUT_RETENTION_SECONDS,
SKIP_BUILD_NUMBER_BUMP, CAPGO_IOS_DISTRIBUTION) that has drifted out of
sync would silently appear twice in the file. Dotenv ingestion (gh
secret set -f, shell sourcing) keeps only the LAST occurrence per key,
so one platform would get the wrong value with no signal.

Detect conflicts before writing:
  - print each conflicting key with both values (long values masked as
    "(N chars)" to avoid leaking accidental shared secrets);
  - require explicit confirmation before writing the file;
  - embed the conflict list as a comment block at the top of the file
    so future readers and audit tooling can spot it without rerunning
    the manager.

Same-value duplicates remain harmless and pass through silently.
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit eaedaab into main May 18, 2026
42 checks passed
@riderx riderx deleted the feat/credentials-export-combined branch May 18, 2026 08:09
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.

2 participants