Skip to content

feat(settings): seed settings from OPENC3_SETTING_* env vars - #3699

Draft
mcosgriff wants to merge 12 commits into
mainfrom
3471-create-pattern-to-overwrite-settings-in-init
Draft

feat(settings): seed settings from OPENC3_SETTING_* env vars#3699
mcosgriff wants to merge 12 commits into
mainfrom
3471-create-pattern-to-overwrite-settings-in-init

Conversation

@mcosgriff

@mcosgriff mcosgriff commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #3471. Customers can configure default time zone and time format (and any other Admin Console setting) at deploy time instead of clicking through the Admin Console after every fresh install.

  • Add openc3cli initsettings, run by init.sh after initbuckets, which seeds settings from OPENC3_SETTING_<NAME> environment variables. A prefix scan rather than an enumerated list, so a setting added by a later release needs no code change here.
  • Only write a setting that does not already exist, so a value changed in the Admin Console survives a container restart. OPENC3_SETTINGS_OVERWRITE makes the environment authoritative instead.
  • Reject an unrecognized setting name, suggesting the near match. Nothing reads a misspelled key, so the result of OPENC3_SETTING_TIME_ZONES would otherwise be a dead Redis key plus a setting the operator believes they configured and did not. OPENC3_SETTINGS_ALLOW_UNKNOWN opts out for a setting a newer tool added.
  • Coerce each value by its setting's declared type: booleans reach Redis as real booleans (the string "false" is truthy in the frontend) while every other setting keeps the text given, including astro, classification_banner and context_tag whose components JSON.parse the stored string
  • Add ConfigParser.handle_true_false_strict, accepting 1/TRUE/0/FALSE and raising otherwise. handle_true_false itself is unchanged because table_config.rb:268 feeds item defaults through it, where mapping '1' to true would corrupt a numeric default of 1. This makes OPENC3_SETTINGS_OVERWRITE=0 mean off, unlike the OPENC3_NO_* presence flags where =0 counts as on.
  • Fix LocalMode.sync_settings, which overwrote Redis on every localinit (reverting Admin Console edits) and stored file contents verbatim (so a boolean setting round-tripped to a truthy string). It now uses the same seed guard and JSON coercion.
  • Document the variables in compose.override.yaml. compose.yaml is deliberately unchanged: an override's environment: block adds variables the base file does not list, so no compose.yaml edit is ever needed to add a setting.
  • Enumerate all 14 Admin Console settings in KNOWN_SETTINGS with their allowed values, surfaced by cli initsettings --help so the list can't drift from the code

Example:

openc3-cosmos-init:
  environment:
    - OPENC3_SETTING_TIME_ZONE=UTC
    - OPENC3_SETTING_TIME_FORMAT=24hr
    - OPENC3_SETTING_AI_CHAT=false

Test plan

  • Fresh install with OPENC3_SETTING_TIME_ZONE=UTC in compose.override.yaml under openc3-cosmos-init: Admin Console shows Time Zone as UTC
  • Change Time Zone to local in the Admin Console, restart: the value stays local (seed does not clobber)
  • Repeat with OPENC3_SETTINGS_OVERWRITE=1: the value reverts to UTC on restart
  • OPENC3_SETTINGS_OVERWRITE=0 behaves as off, not on
  • OPENC3_SETTING_AI_CHAT=false hides the AI chat button (boolean stored as a boolean, not the string "false")
  • OPENC3_SETTING_TIME_ZONES=UTC (typo) fails init with an error naming time_zone as the likely intent
  • OPENC3_SETTING_TIME_ZONE=Mars fails init rather than writing an invalid value
  • OPENC3_SETTING_BRAND_NEW=x fails init, then succeeds with OPENC3_SETTINGS_ALLOW_UNKNOWN=1
  • No OPENC3_SETTING_* set: init is a no-op and existing deployments are unaffected
  • Local mode: a plugins/DEFAULT/settings/ai_chat.json containing false syncs as a boolean, and an Admin Console edit is no longer reverted by localinit
  • openc3cli initsettings --help renders correctly and openc3cli help lists the subcommand

- Add `openc3cli initsettings`, run by init.sh, which seeds settings from OPENC3_SETTING_<NAME> variables so time zone, time format and other Admin Console values can be configured at deploy time
- Only write a setting that doesn't already exist so Admin Console edits survive a restart; OPENC3_SETTINGS_OVERWRITE makes the environment authoritative
- Reject an unrecognized setting name (suggesting the near match) rather than writing a dead Redis key a typo would leave behind; OPENC3_SETTINGS_ALLOW_UNKNOWN opts out for settings added by a newer tool
- Coerce values as JSON so boolean settings like ai_chat aren't stored as the string "false", which is truthy in the frontend
- Add ConfigParser.handle_true_false_strict, which accepts 1/TRUE/0/FALSE and raises otherwise, so OPENC3_SETTINGS_OVERWRITE=0 means off rather than the on implied by the OPENC3_NO_* presence flags
- Apply the same seed guard and JSON coercion to LocalMode.sync_settings, which previously overwrote Redis on every localinit and stored files verbatim
- Document the variables in compose.override.yaml; compose.yaml is unchanged because an override's environment block adds variables it doesn't list
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.35%. Comparing base (b05cfc3) to head (f9f3923).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3699      +/-   ##
==========================================
+ Coverage   79.30%   79.35%   +0.05%     
==========================================
  Files         885      885              
  Lines       65367    65494     +127     
  Branches     2585     2585              
==========================================
+ Hits        51839    51975     +136     
+ Misses      12857    12847      -10     
- Partials      671      672       +1     
Flag Coverage Δ
frontend 63.57% <ø> (-0.03%) ⬇️
python 81.53% <ø> (-0.02%) ⬇️
ruby-api 82.49% <ø> (+0.31%) ⬆️
ruby-backend 84.15% <100.00%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- Coerce each value by its setting's declared type rather than by attempting JSON.parse: booleans reach Redis as real booleans (the string "false" is truthy in the frontend) while every other setting keeps the text given
- Fix astro, classification_banner and context_tag being stored as parsed objects. Those components JSON.parse the stored value, so a Hash makes them throw. Type-driven coercion also stops a subtitle of "2024" becoming the number 2024
- Enumerate all 14 Admin Console settings in KNOWN_SETTINGS with their types and allowed values. The table previously held 4, so OPENC3_SETTING_THEME failed as an unknown name
- Add SettingModel.describe_settings, used by `cli initsettings --help`, so the documented list is generated from the table and can't drift
- Document how to add a setting above KNOWN_SETTINGS, including how to tell a JSON-text setting from an object one
- Point compose.override.yaml at `cli initsettings --help` instead of repeating the list, and show the boolean, free text and JSON text forms
- Add .claude/commands/commit-message.md following Conventional Commits v1.0.0, citing the numbered rules and separating them from git convention like the 72 character first line, so the same file works in any repo
- Scope the command to git diff --cached only, so committed-but-unpushed work, unstaged changes and untracked files stay out of the generated message
- Point CLAUDE.md at the command instead of restating the format, dropping the duplicated Angular rules and the 2-4 line body cap that contradicted it
- Remove the 🤖 Generated with Claude Code footer, which is not a valid token: value footer under rule 9
- Delete generate-commit-message.md, fully superseded by the new command
- Compare KNOWN_SETTINGS against the setting names and types extracted from the Vue components so the table cannot silently fall behind. The previous "seeds every setting" test looped over the table itself, so dropping a row kept it green
- Assert the declared type matches what each component passes to saveSetting, catching a JSON text setting declared as a boolean. Both directions verified by mutation
- Require the heuristics to resolve every setting rather than only agreeing on what they resolved, so a component shape infer_type does not handle fails instead of quietly shrinking the check
- Fix tool_config_model_spec "deletes", which depended on ambient container state: delete_tool_config returns nil early unless OPENC3_LOCAL_MODE is set and the local mode path exists, so the test only passed in Docker. Set both up against a temp dir and restore them after
- Assert the config file is actually removed, not just that rm_f echoed the path it was asked to remove, and cover the early-return branch with local mode off
- Wrap body lines at 72 characters, cap the body at 5 bullets, and
  allow one sentence each so git log stays readable
- Drop the "note a non-obvious consequence" guidance, which invited a
  second sentence on nearly every bullet
- Read OPENC3_LOCAL_MODE via ENV.fetch with an explicit nil default,
  since nil records that the variable was unset
- Add else clauses to the two case statements in setting_model_spec,
  making the existing implicit nil fall-through explicit
- Add initsettings to the cli command list and to the branch offering
  only --help, since it takes no positional arguments
- Add reingest, which was missing entirely, with its own branch
  prompting JOB_ID then SCOPE as both are required
- Indent every commented service block so uncommenting yields valid
  YAML, since a service at column 0 fails with "additional properties
  'openc3-cosmos-init' not allowed"
- Document that services: must be uncommented too, quoting that error
  so the message maps to the cause
- Stop the log to stderr section redeclaring openc3-operator, which
  collided with the block above and discarded its ports and volumes
- Record each value initsettings writes to a companion Redis hash, so a
  later init can tell an untouched setting from one an operator changed
- Apply a changed env value while the setting still matches what was
  seeded, which makes editing the override and restarting work
- Leave any setting whose value differs from the seeded one, including
  settings with no record from before this tracking existed
- Name the destroyed value when OPENC3_SETTINGS_OVERWRITE clobbers an
  edit, which previously logged the same line as a first-time seed
- Add --dry-run, which reports the action planned for each setting,
  writes nothing, and exits non-zero if any would fail
- Skip an invalid setting instead of aborting init, which crash looped
  COSMOS under restart: on-failure over a cosmetic value
- Run cli initsettings in the init container, the only one that
  receives OPENC3_SETTING_* variables
- Extract plan_setting so a dry run cannot report one thing and the
  real run do another
- Fall back to name and value checks when Redis is unreachable, so the
  check is usable before starting COSMOS
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
CRITICAL Code Smells Severity on New Code (required < MAJOR)

See analysis details on SonarQube Cloud

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.

Support configuring default time zone and time format via environment variables

1 participant