Skip to content

feat(config): one-time config.toml migration to state.toml + keychain#248

Draft
LorrisSaintGenez wants to merge 4 commits into
feat/profile-deprfrom
feat/profile-migration
Draft

feat(config): one-time config.toml migration to state.toml + keychain#248
LorrisSaintGenez wants to merge 4 commits into
feat/profile-deprfrom
feat/profile-migration

Conversation

@LorrisSaintGenez

@LorrisSaintGenez LorrisSaintGenez commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What

GROUT-363: one-time, first-run migration of the legacy config.toml profiles into the new storage model (state.toml + OS keychain). Squashed stack of #245, #246 and #247.

  • Trigger: at startup, when config.toml exists and state.toml doesn't — state.toml is only written on success, so its absence doubles as the "not migrated yet" marker and an aborted run retries on the next command. A failed migration never blocks the command (DEBUG-only log).
  • Per profile: api_key (+ crawler_api_key if set) goes to the OS keychain under the profile's application ID; one state.toml entry per application with alias = <profile name>; current_application_id = the default = true profile's application; api_key_uuid left empty for legacy keys.
  • Atomicity: keychain first, state.toml last (temp + rename). An empty config.toml still writes an empty state.toml so the trigger turns off.
  • Per profile (non-secret): search_hosts and crawler_user_id move to the application's state.toml entry; GetSearchHosts/GetCrawlerUserID read them for the resolved application first, falling back to config.toml while both models coexist
  • Hardening: an undecodable profile (root-level scalar, wrong field type) is skipped with a log instead of hitting ConfiguredProfiles's log.Fatalf (which bricked every command at startup); an unparseable config.toml aborts the migration instead of marking it done with an empty state.toml
  • Skip rules: no application_id or empty api_key → skip + log; profiles sharing the same application_id → the default profile wins, others logged as conflicts; admin_api_key never migrated, one log line pointing to ALGOLIA_ADMIN_API_KEY / --api-key.

config.toml itself is never modified.

Test

# on a machine with legacy config.toml profiles
rm -f ~/.config/algolia/state.toml
go run ./cmd/algolia indices list      # any command triggers the migration once
cat ~/.config/algolia/state.toml       # aliases + current_application_id
# macOS: the migrated key is in the keychain (replace <APP_ID>; go-keyring stores it base64-encoded)
security find-generic-password -s algolia-cli -a "app:<APP_ID>" -w | sed "s/^go-keyring-base64://" | base64 -d
# run it again: no re-migration, no logs
go run ./cmd/algolia indices list

GROUT-363

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Jun 12, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 43 complexity · 12 duplication

Metric Results
Complexity 43
Duplication 12

View in Codacy

TIP This summary will be updated as you push new changes.

LorrisSaintGenez and others added 3 commits June 12, 2026 14:14
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two startup-path regressions found while testing edge cases:

- An undecodable profile (root-level scalar, unconvertible field type)
  hit ConfiguredProfiles' log.Fatalf and bricked every command, --help
  included, forever (state.toml never written). The migration now
  decodes profiles itself and skips undecodable entries with a log.
- An unparseable config.toml was silently read as zero profiles, so an
  empty state.toml marked the migration as done forever. Migrate now
  aborts when config.toml cannot be parsed and retries on the next run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The remaining non-secret profile data moves with the migration so it
survives the eventual config.toml removal. GetSearchHosts and
GetCrawlerUserID gain a new-model branch: the resolved application's
state.toml entry answers first, an empty value falls through to the
legacy config.toml lookup while both models coexist. admin_api_key
stays excluded as decided on the ticket.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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