Skip to content

OPENC3_LOCAL_ONLY_TARGETS - #3942

Open
ryanmelt wants to merge 5 commits into
mainfrom
target_local_only_mode
Open

ryanmelt wants to merge 5 commits into
mainfrom
target_local_only_mode

Conversation

@ryanmelt

@ryanmelt ryanmelt commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

What changed

Adds an environment variable that makes certain targets only read/write from local mode for all files.

Why it changed

Allows for local git control of the files COSMOS uses for a target (scripts, screens, tables, notebooks)

Testing strategy

Unit Tests

@ryanmelt

Copy link
Copy Markdown
Member Author

@0lionelzhang0 please review

@codecov

codecov Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.13924% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.22%. Comparing base (688f928) to head (c17e363).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
openc3/lib/openc3/utilities/target_file.rb 80.00% 6 Missing ⚠️
...-cmd-tlm-api/app/controllers/storage_controller.rb 94.91% 3 Missing ⚠️
openc3/lib/openc3/script/storage.rb 93.33% 3 Missing ⚠️
openc3/lib/openc3/models/target_model.rb 81.81% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3942      +/-   ##
==========================================
+ Coverage   80.08%   80.22%   +0.14%     
==========================================
  Files         901      901              
  Lines       68356    68573     +217     
  Branches     2645     2699      +54     
==========================================
+ Hits        54743    55014     +271     
+ Misses      12946    12893      -53     
+ Partials      667      666       -1     
Flag Coverage Δ
frontend 66.95% <ø> (-0.01%) ⬇️
python 80.19% <ø> (+0.06%) ⬆️
ruby-api 82.62% <94.91%> (+0.61%) ⬆️
ruby-backend 85.82% <88.88%> (+0.16%) ⬆️

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.

@0lionelzhang0

Copy link
Copy Markdown

Thanks! It'd be nice to get docs on usage in these:
docs.openc3.com/docs/configuration/environment.md
guides/local-mode.md

@github-actions

Copy link
Copy Markdown

AI adversarial review

✅ Claude and Codex converged after 3 turn(s) with 1 fix commit(s).

Reviewed commit: dd8f3b5856f9a766878b9380c868a72ec2d3c76b

Open concerns for a human

  • Minor: delete_bucket_directory takes the scope from the first segment of the requested path and calls build_local_catalog, which runs mkdir_p on <local_mode>//targets_modified, so it can create an empty directory for any scope name. The path is sanitized and only admins or allowed overlay writes reach this code, so it's harmless.
  • Stale bucket copies of local-only target files (made before the target was added to OPENC3_LOCAL_ONLY_TARGETS, or written under /targets/ by plugin installs) are hidden from reads but never cleaned up. A person should confirm this is intended.
Turn-by-turn log

Turn 1: Claude (no changes)

The OPENC3_LOCAL_ONLY_TARGETS feature is implemented consistently across the storage controller, TargetFile, TargetModel, LocalMode sync and the Ruby/Python script storage helpers, and path safety is preserved through safe_key?/key_path. I found no defects confident enough to change; the test suites were not run because dependencies aren't installed.

  • Concern: Stale bucket copies: files for a local-only target that are already in the bucket (from before the target was made local-only, or from plugin installs writing /targets/) are hidden from reads but never cleaned up. This matches the .env comment, but a person should confirm it is the intended behavior.
  • Concern: storage_controller#download_file: for a local-only key that resolves to a directory, File.exist? passes and File.read raises EISDIR, so the endpoint returns a 500 instead of a 404. Minor.

Turn 2: Codex (commit ac993a3ed)

Fixed three storage API gaps for local-only targets and added regression coverage. Diff whitespace checks passed; tests and Ruby syntax checks could not run because dependencies and Ruby are unavailable.

  • Fixed: openc3-cosmos-cmd-tlm-api/app/controllers/storage_controller.rb:348 - Directory downloads returned 500; require a regular file and return 404 otherwise.
  • Fixed: openc3-cosmos-cmd-tlm-api/app/controllers/storage_controller.rb:785 - Directory deletion skipped local-only files; include local files and exclude their stale bucket copies from deletion.
  • Fixed: openc3-cosmos-cmd-tlm-api/app/controllers/storage_controller.rb:889 - Bulk downloads read local-only files from the bucket; use local contents without bucket fallback.

Turn 3: Claude (no changes)

I checked Codex's three turn-2 fixes (directory downloads return 404, directory deletes include local-only files and skip their stale bucket copies, bulk downloads read local-only files from the local volume). They are correct, stay inside the local mode volume and keep the authorization checks. I found no further defects. CI passed; I did not run the tests locally because dependencies aren't installed.

  • Concern: Stale bucket copies of local-only target files (made before the target was added to OPENC3_LOCAL_ONLY_TARGETS, or written under /targets/ by plugin installs) are hidden from reads but never cleaned up. A person should confirm this is intended.
  • Concern: Minor: delete_bucket_directory takes the scope from the first segment of the requested path and calls build_local_catalog, which runs mkdir_p on <local_mode>//targets_modified, so it can create an empty directory for any scope name. The path is sanitized and only admins or allowed overlay writes reach this code, so it's harmless.

Warning

The fix commits above could not be pushed (the branch probably moved); they were discarded.

- openc3-cosmos-cmd-tlm-api/app/controllers/storage_controller.rb:348 - Directory downloads returned 500; require a regular file and return 404 otherwise.
- openc3-cosmos-cmd-tlm-api/app/controllers/storage_controller.rb:785 - Directory deletion skipped local-only files; include local files and exclude their stale bucket copies from deletion.
- openc3-cosmos-cmd-tlm-api/app/controllers/storage_controller.rb:889 - Bulk downloads read local-only files from the bucket; use local contents without bucket fallback.

AI-Review-Bot: true
AI-Review-Run: 36501940046
ryanmelt and others added 2 commits September 28, 2026 20:38
delete_bucket_directory scanned the whole scope with build_local_catalog,
which mkdir_p's <scope>/targets_modified for any requested scope. Only
glob the requested directory, and only if it already exists. Also fix the
bulk download spec, which could not read the zip back from a buffer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merge the nested RBAC check in upload_file, and patch openc3.script
globals with patch.object in the local only target tests instead of
assigning them directly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# existing directory so this never creates directories for arbitrary paths.
local_dir = OpenC3::LocalMode.key_path(path.chomp('/'))
if local_dir && File.directory?(local_dir)
Dir.glob("#{local_dir}/**/*").each do |filename|
Add the variable to the environment reference and a Local Only Targets
section to the Local Mode guide covering where files live, the missing
plugin fallback, script access and stale bucket copies.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
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.

3 participants