Skip to content

Decrypt secrets outside of the repo - #1269

Open
mokagio wants to merge 17 commits into
trunkfrom
ainfra-2731-decrypt-secrets-outside-of-repo-in-simplenote-macos
Open

mokagio wants to merge 17 commits into
trunkfrom
ainfra-2731-decrypt-secrets-outside-of-repo-in-simplenote-macos

Conversation

@mokagio

@mokagio mokagio commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Prep step to then adopt a8c-secrets, which requires secrets outside of the repo. See https://linear.app/a8c/issue/AINFRA-2731.

I modelled the script changes on the recent Gravatar SDK implementation, which benefitted from the back and forth with @AliSoftware.

Same

  • Precedence: internal store first, gitignored external file second
  • Hard failure with a two-audience hint
  • Secrets.template.swift*.external-contributors.swift pair, writing to SCRIPT_OUTPUT_FILE_0 under DERIVED_FILE_DIR
  • README explaining sources generation and why they are red

Different, aside for the differences due to the repo not using a8c-secrets yet

  • Unlike Gravatar, the script in Simplenote has two target consumers:Simplenote and IntentsExtension. The SCRIPT_OUTPUT_FILE_0 indirection is "load-bearing" here, because so one script and one .xcfilelist back two identical phases, each writing into its own target's derived dir
  • Input-list guard. I kept the pre-existing ensure_is_in_input_files_list check (errors if a source isn't declared in the .xcfilelist). Gravatar hasn't one, but I think it's useful and we should eventually adopt it there, too (especially if the endgame is for the script to be generic enough that it can run from a8c-secrets, i.e. without the source being in the repo and therefore the only knobs we can turn being input and ouput paths/lists)
AI-generated Details

Fix

The decrypted SPCredentials.swift no longer lands in the checkout.
Each target that compiles it — Simplenote and IntentsExtension — carries a Copy Secret build phase writing into that target's own $(DERIVED_FILE_DIR), replacing the SimplenoteSecrets aggregate target that wrote into Simplenote/Credentials/.
Internal contributors no longer copy anything into place by hand.

The shared demo credentials are gone with it.
SPCredentials-demo.swift becomes SPCredentials.template.swift, which external contributors copy to the gitignored SPCredentials.external-contributors.swift and fill in with their own Simperium credentials.
With neither that file nor the decrypted secrets present, the build fails in every configuration rather than silently compiling against demo values.

Part of AINFRA-2731, applying the pattern Gravatar-SDK-iOS settled on.
The commit messages carry the per-step rationale.

Test

bundle exec fastlane test

Both consumer targets should compile, each phase writing to its own …/<Target>.build/DerivedSources/SPCredentials.swift.
CI exercises both sources: build-and-test copies the template, so it runs the flow the readme gives external contributors, while verify-app-store-target-builds decrypts and builds against the real secrets.

Review

One developer is enough.

The two SPCredentials.swift entries show in red in Xcode with no resolvable path.
That is expected — DERIVED_FILE_DIR only resolves while a specific target is building — and Simplenote/DerivedSources/README.md exists so the next reader does not "fix" it.

@mokagio mokagio self-assigned this Jul 22, 2026
@mokagio
mokagio force-pushed the ainfra-2731-decrypt-secrets-outside-of-repo-in-simplenote-macos branch from 7e1e0ba to cc054da Compare July 27, 2026 05:23
mokagio and others added 7 commits July 30, 2026 13:42
The decrypted `SPCredentials.swift` no longer lands in the checkout: each
consumer target's build phase declares its own `$(DERIVED_FILE_DIR)` output and
the script writes there, via `SCRIPT_OUTPUT_FILE_0`.

Part of AINFRA-2731, applying the pattern doapple and woocommerce-ios developed
and Gravatar-SDK-iOS solidified.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
Replaces the `SimplenoteSecrets` aggregate target — which wrote the plaintext
into `Simplenote/Credentials/` — with a `Copy Secret` phase on each target that
actually compiles the file, writing into that target's own
`$(DERIVED_FILE_DIR)`.

The two file references are `DERIVED_FILE_DIR`-relative, so Xcode shows them in
red with no resolvable path until a build runs.
`Simplenote/DerivedSources/README.md` exists to stop the next reader "fixing"
that.

`Simplenote/Credentials/` stays gitignored so a stale decrypted copy from before
this change cannot be committed.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
The build phase now falls back to the demo credentials on its own, so copying
them into place by hand is both unnecessary and, since the destination moved out
of the checkout, wrong.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.8 <noreply@anthropic.com>
Matches `Scripts/build-phases/generate-secrets.sh` in Gravatar-SDK-iOS, whose
DerivedData secret generation this branch is modelled on.

Style only: `#!/bin/bash -euo pipefail` does apply all three options on macOS.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
`$(dirname …)` and the `cmp` operands were unquoted, so a `DERIVED_FILE_DIR`
containing a space — an Xcode path the script does not control — split into
multiple words. Verified against a destination under `dir with space/`.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Mirrors the `apply()` helper in Gravatar-SDK-iOS's `generate-secrets.sh`.

The build log now names the source path instead of labelling it "Production" or
"Example"; the following commit drops the `DerivedSources/README.md` pointer to
those old strings.

Keeps `cp -v`, which Gravatar does not need: with two consumer targets writing
to different `DERIVED_FILE_DIR`s, the destination in the log is what tells you
which target's copy you are looking at.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Matches the length and scope of `Demo/Demo/Generated/README.md` in
Gravatar-SDK-iOS: say why the reference is red, say not to delete it, point at
the script.

The dropped material — why `DERIVED_FILE_DIR` cannot resolve at parse time, a
sample Derived Data tree, how to reveal a generated file in Finder — explained
Xcode rather than this project.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
@mokagio
mokagio force-pushed the ainfra-2731-decrypt-secrets-outside-of-repo-in-simplenote-macos branch from cc054da to bee3771 Compare July 30, 2026 04:39
Comment thread Scripts/Build-Phases/copy-secret.sh Outdated
Comment thread .gitignore

# Settings
Simplenote/config.plist
Simplenote/Credentials/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I considered keeping this just to avoid leaking. But given the repo is in maintenance, meaning it doesn't see a lot of active development, the chance of a leak are low and I prioritized keeping things tidy.

Additionally, worth remembering, that even if something from SPCredentials.swift leaked, its content are already de facto public because they are part of the shipped binary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I get it, but at the same time, retaining one line (perhaps adding a comment) is cheap 🙂 anyway not a big deal

Comment on lines +10 to +21
# 1. ${SECRETS_ROOT}, for internal contributors. `bundle exec fastlane run
# configure_apply` decrypts it there, outside the repo; this phase only
# reads it.
# 2. Simplenote/SPCredentials.external-contributors.swift — gitignored, so
# external contributors can keep their own Simperium credentials with
# little-to-no risk of committing them, starting from a copy of the
# committed template.
#
# If neither is present, the build will fail.

SECRETS_ROOT="${HOME}/.configure/simplenote-macos/secrets"
SECRETS_FILE="${SECRETS_ROOT}/SPCredentials.swift"

@mokagio mokagio Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Of course, the paths here will soon change as we adopt a8c-secrects.

mokagio and others added 2 commits July 30, 2026 19:43
The `test` lane never ran `configure_apply`, unlike `test_app_store_build`, so
this job only ever built against the demo credentials the build phase fell back
to. Removing that fallback left it with no credentials at all.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Replaces the `configure_apply` added a commit ago. The unit tests never needed
real credentials — the removed demo fallback was supplying them — so copying the
template keeps this job out of the secret store entirely, and turns it into a
regression test for the flow the readme gives external contributors.

`verify-app-store-target-builds` still decrypts, so the internal path stays
covered.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Comment thread .buildkite/commands/build-and-test.sh Outdated
mokagio and others added 2 commits August 5, 2026 11:03
Xcode already skips the phase when neither the declared inputs nor the outputs
changed, so `cmp` only ever runs in the cases where the copy is warranted.
Same conclusion as Gravatar-SDK-iOS reached for `generate-secrets.sh`.

Part of AINFRA-2731.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR moves secret materialization out of the repository checkout by generating SPCredentials.swift into each target’s $(DERIVED_FILE_DIR) via per-target “Copy Secret” build phases, preparing the project to later adopt a8c-secrets.

Changes:

  • Replace the aggregate “SimplenoteSecrets” flow with per-target “Copy Secret” build phases that write SPCredentials.swift into $(DERIVED_FILE_DIR).
  • Replace demo credentials with a committed SPCredentials.template.swift and a gitignored SPCredentials.external-contributors.swift fallback.
  • Update CI and repo docs/ignore rules to support the new derived-sources workflow.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Simplenote/SPCredentials.template.swift Updates header comment to a credentials template intended for external contributors.
Simplenote/DerivedSources/README.md Documents the intentionally-red derived source references and how they’re generated.
Simplenote.xcodeproj/project.pbxproj Removes aggregate secrets target; adds derived file references and per-target “Copy Secret” phases outputting to $(DERIVED_FILE_DIR).
Scripts/Build-Phases/copy-secret.xcfilelist Updates declared inputs to include the gitignored external-contributor credentials file.
Scripts/Build-Phases/copy-secret.sh Writes secrets to SCRIPT_OUTPUT_FILE_0 (derived dir), adds clearer failure messaging, and updates input/output guarding.
readme.md Updates credential setup narrative to describe generated credentials rather than copying demo values.
.gitignore Ignores derived sources except the explanatory README; ignores external contributor credentials file.
.buildkite/commands/build-and-test.sh Ensures CI unit tests have a credentials source by copying the template into the gitignored file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread readme.md
Comment thread Scripts/Build-Phases/copy-secret.sh Outdated
mokagio and others added 2 commits August 5, 2026 11:37
There are none to distribute since the demo credentials came out: internal
contributors decrypt their own, external ones supply their own.

#1269 (comment)

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
Without it, a run outside Xcode aborts on an unbound variable instead of
printing the error the guard exists to print.

#1269 (comment)

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 5 <noreply@anthropic.com>
@mokagio
mokagio marked this pull request as ready for review August 5, 2026 03:37
@mokagio
mokagio requested a review from a team as a code owner August 5, 2026 03:37
@mokagio mokagio added this to the Future milestone Aug 5, 2026
@mokagio mokagio added the tooling Related to anything that supports the building & maintaining of the project. label Aug 5, 2026
@mokagio
mokagio enabled auto-merge August 5, 2026 03:37
@iangmaia
iangmaia requested a balanced review from Copilot September 10, 2026 16:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Shared demo credentials remain and legacy plaintext credentials can become unignored.

Review details

Suppressed comments (1)

Simplenote/SPCredentials.template.swift:4

  • The template still contains the former demo Simperium app ID and API key, so copying it already supplies the shared credentials without requiring contributors to “fill in” their own. This contradicts the PR’s removal of shared demo credentials and also makes CI continue compiling with them; replace these values with empty placeholders.
  • Files reviewed: 7/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Comment thread readme.md
```

This will allow you to compile and run the app on a device or a simulator.
Credentials live in the `simperium*` properties of the `SPCredentials` type, which is generated at build time from production credentials kept outside the checkout, falling back to user-specified ones.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe good to include the template copy command here and explain what works without supplying your own credentials? 🤔

@iangmaia
iangmaia disabled auto-merge September 10, 2026 18:21
@iangmaia

Copy link
Copy Markdown
Contributor

One developer is enough.

What? 😄

@iangmaia iangmaia left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good, but I'll leave the final merge to you (I left a couple of nits).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tooling Related to anything that supports the building & maintaining of the project.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants