Skip to content

feat: allow global variables to be substitution-only - #4651

Merged
kmendell merged 4 commits into
getarcaneapp:mainfrom
Dim145:feat/global-variables-inject-flag
Nov 6, 2026
Merged

kmendell merged 4 commits into
getarcaneapp:mainfrom
Dim145:feat/global-variables-inject-flag

Conversation

@Dim145

@Dim145 Dim145 commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Checklist

  • This PR is not opened from my fork’s main branch
  • All new user-facing strings are translated via Paraglide (m.*())

What This PR Implements

Global variables can now be used for Compose ${VAR} substitution only, without being injected into every container. This also keeps secret global variables out of unrelated containers.

Addresses #3407 (discussion #4035). Replaces #4630, which was closed automatically when breaking/v3 was deleted.

Changes Made

  • New per-variable injectIntoContainers flag: DB column, API create/update, CLI --inject, UI switch and "Injected" badge.
  • Migration 00105 (Goose Go migration): existing variables are set to true, so upgrades keep the current behavior; new variables default to false.
  • The effective set (env-scoped overrides included) marks interpolation-only keys with # arcane:noinject KEY lines in the .env.global header, ahead of every value. The env loader reads values and marks from the same read and skips marked keys when injecting.
  • Materialized variables carry an optional per-variable noInject flag (omitted when false) in both the agent PUT and GET, so a re-added agent keeps its interpolation-only keys on import. Local .env.global is replaced atomically.

Testing Done

  • New service/loader tests: flag defaults, scoped overrides, imported legacy variables, local materialization, agent payload, migration 105 up/down.
  • A test loads every prefix of the generated .env.global and checks that an interpolation-only secret is never injected (the file is rewritten in place by truncate + write).
  • golangci-lint (fmt + run) on backend/cli/types, vp fmt --check, eslint, svelte-check and Playwright tsc: all clean.
  • Manual test in the dev environment: a substitution-only variable is resolved through ${VAR} but absent from the container env; an injected variable is present; toggling the flag and redeploying updates the container env; .env.global carries the # arcane:noinject marks ahead of the values.

AI Tool Used (if applicable)

Claude Code (Claude Opus 5.5) was used to implement the backend, CLI, frontend and tests, and to port the change onto v3. I reviewed the changes and tested them manually in the development environment.

Additional Context

  • The materialized list holds non-injected keys: Huma rejects unknown properties, so the agent payload stays identical to the previous one while every key is injected, and an absent list means "inject everything".
  • An agent older than this change will reject the sync (422, visible in the sync status) once a substitution-only variable is in its scope, instead of silently injecting it everywhere.
  • If another PR lands migration 00104 first, I'll renumber this one.

Disclaimer Greptiles Reviews use AI, make sure to check over its work.

To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike

To have Greptile Re-Review the changes, mention greptileai.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; the mounted-file sync issue is fixed.

Summary

Adds a per-variable choice between Compose substitution only and automatic container injection.

  • Global variables can stay out of containers while Compose still uses them.

Reviews (4) · Last reviewed commit: "cr" · Reviewed by Greptile

@Dim145
Dim145 requested a review from a team November 4, 2026 21:13
Comment thread backend/internal/variable/service.go Resolved
Comment thread backend/internal/variable/service.go Resolved
@kmendell kmendell assigned kmendell and unassigned Dim145 Nov 5, 2026
Comment thread backend/internal/variable/service.go Resolved
@Dim145
Dim145 force-pushed the feat/global-variables-inject-flag branch from a15ef66 to 68ae18b Compare November 5, 2026 17:14
Dim145 and others added 2 commits November 5, 2026 17:28
Add a per-variable injectIntoContainers flag. New variables are only available for ${VAR} interpolation; existing ones keep being injected. Interpolation-only keys are marked in the .env.global header so values and marks are read from one snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-import

Overlapping in-place rewrites could leave a value behind without its no-inject mark, so .env.global is now replaced atomically (temp file and rename) instead of rewritten in place. Materialized variables carry a per-variable noInject flag in both the agent PUT and GET, so re-importing an agent's .env.global keeps interpolation-only keys out of containers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kmendell
kmendell force-pushed the feat/global-variables-inject-flag branch from 68ae18b to dbb6155 Compare November 5, 2026 23:29
Comment thread backend/internal/variable/service.go Resolved
@kmendell
kmendell merged commit 71288d9 into getarcaneapp:main Nov 6, 2026
18 of 19 checks passed
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