Skip to content

refactor(provider/env): document env var contract and validate reserved names#690

Draft
skevetter wants to merge 3 commits intomainfrom
refactor/provider-env-var-cleanup
Draft

refactor(provider/env): document env var contract and validate reserved names#690
skevetter wants to merge 3 commits intomainfrom
refactor/provider-env-var-cleanup

Conversation

@skevetter
Copy link
Copy Markdown
Owner

@skevetter skevetter commented Apr 5, 2026

Summary

Follow-up to PR #615 which identified architectural confusion around provider environment variables.

  • Add validation that rejects provider option names colliding with reserved system env vars (PROVIDER_ID, WORKSPACE_ID, etc.) — prevents silent overwrites in subprocess environments
  • Document the subprocess environment contract on ToEnvironment and GetBaseEnvironment with comprehensive doc comments
  • Add contract tests verifying PROVIDER_ID is the canonical provider name source and DEVPOD_PROVIDER is not duplicated
  • Clarify that DEVPOD_PROVIDER is reserved by the --provider CLI flag and must not be explicitly set in subprocess environments

Test plan

  • go test ./pkg/provider/ -v passes (5 new tests)
  • go build ./... succeeds
  • Pre-commit hooks pass
  • Existing E2E tests pass (no behavioral changes)

Summary by CodeRabbit

  • Documentation
    • Added and enhanced documentation describing how environment variables are constructed, inherited, and managed during provider subprocess execution.
  • Tests
    • Added comprehensive test coverage for environment variable handling, inheritance verification, and provider configuration option validation.
  • Bug Fixes
    • Enhanced validation to prevent and reject provider options using reserved environment variable names with clear error messages.

Provider options are uppercased and injected as subprocess env vars.
If an option collides with a system-set name like PROVIDER_ID or
WORKSPACE_ID, it silently overwrites the system value. This adds
validation to reject reserved names at provider config parse time.
Adds comprehensive doc comments to ToEnvironment and GetBaseEnvironment
explaining which env vars are set, where they come from, and why
DEVPOD_PROVIDER must not be explicitly added to the subprocess env.
Verifies that PROVIDER_ID and WORKSPACE_PROVIDER carry the provider
name, that DEVPOD_PROVIDER is not duplicated, and that extraEnv vars
are included.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 5, 2026

📝 Walkthrough

Walkthrough

The changes add documentation comments for environment construction functions, implement validation to reject provider options using reserved DevPod environment variable names, and introduce corresponding unit tests to verify the environment handling and validation behavior.

Changes

Cohort / File(s) Summary
Documentation
pkg/provider/env.go
Added doc comments for ToEnvironment and GetBaseEnvironment functions, describing environment variable construction and the reserved DEVPOD_PROVIDER variable behavior.
Environment Tests
pkg/provider/env_test.go
Added three test cases validating ToEnvironment output: includes required provider/workspace variables, avoids duplicating DEVPOD_PROVIDER, and incorporates caller-supplied extra environment variables.
Option Validation
pkg/provider/parse.go
Added reservedOptionNames map and validation logic to reject provider options whose names conflict with reserved DevPod environment variable names.
Validation Tests
pkg/provider/parse_test.go
Added two test cases: one verifying rejection of all reserved option names, another confirming non-reserved names are accepted.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

size/m

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(provider/env): document env var contract and validate reserved names' directly and comprehensively summarizes the main changes: adding documentation for environment variable contracts and validation logic for reserved variable names.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/provider-env-var-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the size/l label Apr 5, 2026
@skevetter skevetter marked this pull request as draft April 5, 2026 07:22
@coderabbitai coderabbitai bot added the size/m label Apr 5, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
pkg/provider/env_test.go (1)

54-59: Add a regression test for extraEnv["DEVPOD_PROVIDER"].

Current tests don’t cover the contract-critical case where callers explicitly pass DEVPOD_PROVIDER through extraEnv. Add that case so this behavior can’t regress silently.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/provider/env_test.go` around lines 54 - 59, Extend the existing test for
ToEnvironment to include the contract-critical key by adding
extraEnv["DEVPOD_PROVIDER"] (e.g., set to a sentinel value) and assert the
produced environ contains that exact key/value; update or add a test (e.g.,
modify TestToEnvironment_IncludesExtraEnv or add
TestToEnvironment_PreservesDevpodProvider) that calls ToEnvironment(nil, nil,
nil, extra) with extra containing DEVPOD_PROVIDER and uses assertEnvContains to
verify it is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/provider/env.go`:
- Around line 38-41: The code currently appends all entries from extraEnv
including DEVPOD_PROVIDER which violates the documented restriction; update the
logic that iterates over extraEnv (the block that appends extraEnv entries in
pkg/provider/env.go) to skip any entry whose key equals "DEVPOD_PROVIDER"
(case-sensitive) so it is never added to the resulting environment for child
devpod processes; include a short comment above the filtering to reference the
DEVPOD_PROVIDER restriction for future readers.

In `@pkg/provider/parse_test.go`:
- Around line 29-39: The test loop in TestValidate_RejectsReservedOptionNames
builds a ProviderConfig without required Exec.Command, so validate(cfg) may fail
on exec.command instead of reserved-name checks; update the cfg in the loop to
include a valid Exec with Command set (e.g., cfg.Exec = &types.Exec{Command:
"true"}) so validation reaches reserved-option-name logic when calling
validate(cfg) for each reserved name.

In `@pkg/provider/parse.go`:
- Around line 25-42: The reservedOptionNames map is missing several
contract-critical env var names, allowing them to be used as provider options;
update the reservedOptionNames declaration to include "DEVPOD_PROVIDER",
"DEVPOD", "DEVPOD_OS", "DEVPOD_ARCH", and "DEVPOD_LOG_LEVEL" (the same missing
keys must also be added where the map is duplicated around the other occurrence
referenced in the comment). Ensure these names match the keys produced by
GetBaseEnvironment so provider options cannot override those system variables.

---

Nitpick comments:
In `@pkg/provider/env_test.go`:
- Around line 54-59: Extend the existing test for ToEnvironment to include the
contract-critical key by adding extraEnv["DEVPOD_PROVIDER"] (e.g., set to a
sentinel value) and assert the produced environ contains that exact key/value;
update or add a test (e.g., modify TestToEnvironment_IncludesExtraEnv or add
TestToEnvironment_PreservesDevpodProvider) that calls ToEnvironment(nil, nil,
nil, extra) with extra containing DEVPOD_PROVIDER and uses assertEnvContains to
verify it is preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2905ad22-124c-480b-be78-524fb5f627fa

📥 Commits

Reviewing files that changed from the base of the PR and between c26fb2d and b5efd73.

📒 Files selected for processing (4)
  • pkg/provider/env.go
  • pkg/provider/env_test.go
  • pkg/provider/parse.go
  • pkg/provider/parse_test.go

Comment on lines +38 to +41
// Note: DEVPOD_PROVIDER is reserved by the --provider CLI flag via inheritFlagsFromEnvironment
// in cmd/root.go. It is inherited from the parent process via os.Environ() but must not be
// explicitly set here, as doing so would override the global provider selection for child
// devpod processes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Documented DEVPOD_PROVIDER restriction is not enforced for extraEnv.

The comment says DEVPOD_PROVIDER must not be explicitly set, but Line 55-Line 57 blindly appends all extraEnv entries, including DEVPOD_PROVIDER.

Suggested patch
 	for k, v := range extraEnv {
+		if k == "DEVPOD_PROVIDER" {
+			continue
+		}
 		osEnviron = append(osEnviron, k+"="+v)
 	}

Also applies to: 55-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/provider/env.go` around lines 38 - 41, The code currently appends all
entries from extraEnv including DEVPOD_PROVIDER which violates the documented
restriction; update the logic that iterates over extraEnv (the block that
appends extraEnv entries in pkg/provider/env.go) to skip any entry whose key
equals "DEVPOD_PROVIDER" (case-sensitive) so it is never added to the resulting
environment for child devpod processes; include a short comment above the
filtering to reference the DEVPOD_PROVIDER restriction for future readers.

Comment on lines +29 to +39
for _, name := range reserved {
cfg := &ProviderConfig{
Name: "test-provider",
Options: map[string]*types.Option{
name: {Description: "test"},
},
}
err := validate(cfg)
if err == nil {
t.Errorf("expected error for reserved option name %q, got nil", name)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reserved-name test can pass for unrelated validation errors.

TestValidate_RejectsReservedOptionNames does not set Exec.Command, so validate(cfg) can fail with exec.command is required even if reserved-name validation is broken. This makes the test a false positive risk.

Suggested patch
 import (
+	"strings"
 	"testing"

 	"github.com/skevetter/devpod/pkg/types"
 )
@@
 	for _, name := range reserved {
 		cfg := &ProviderConfig{
 			Name: "test-provider",
 			Options: map[string]*types.Option{
 				name: {Description: "test"},
 			},
+			Exec: ProviderCommands{
+				Command: []string{"echo hello"},
+			},
 		}
 		err := validate(cfg)
-		if err == nil {
-			t.Errorf("expected error for reserved option name %q, got nil", name)
+		if err == nil || !strings.Contains(err.Error(), "reserved environment variable name") {
+			t.Errorf("expected reserved-name error for option %q, got: %v", name, err)
 		}
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/provider/parse_test.go` around lines 29 - 39, The test loop in
TestValidate_RejectsReservedOptionNames builds a ProviderConfig without required
Exec.Command, so validate(cfg) may fail on exec.command instead of reserved-name
checks; update the cfg in the loop to include a valid Exec with Command set
(e.g., cfg.Exec = &types.Exec{Command: "true"}) so validation reaches
reserved-option-name logic when calling validate(cfg) for each reserved name.

Comment on lines +25 to +42
var reservedOptionNames = map[string]bool{
"PROVIDER_ID": true,
"PROVIDER_CONTEXT": true,
"PROVIDER_FOLDER": true,
"WORKSPACE_ID": true,
"WORKSPACE_UID": true,
"WORKSPACE_PROVIDER": true,
"WORKSPACE_CONTEXT": true,
"WORKSPACE_FOLDER": true,
"WORKSPACE_SOURCE": true,
"WORKSPACE_ORIGIN": true,
"WORKSPACE_PICTURE": true,
"MACHINE_ID": true,
"MACHINE_CONTEXT": true,
"MACHINE_FOLDER": true,
"MACHINE_PROVIDER": true,
"LOFT_PROJECT": true,
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reserved-name validation is incomplete and misses contract-critical env vars.

reservedOptionNames currently omits DEVPOD_PROVIDER (explicitly reserved in this PR’s contract) and several system vars set by GetBaseEnvironment (DEVPOD, DEVPOD_OS, DEVPOD_ARCH, DEVPOD_LOG_LEVEL). Those names can still be used as provider options and bypass the new protection.

Suggested patch
 var reservedOptionNames = map[string]bool{
+	"DEVPOD":            true,
+	"DEVPOD_OS":         true,
+	"DEVPOD_ARCH":       true,
+	"DEVPOD_LOG_LEVEL":  true,
+	"DEVPOD_PROVIDER":   true,
 	"PROVIDER_ID":        true,
 	"PROVIDER_CONTEXT":   true,
 	"PROVIDER_FOLDER":    true,
 	"WORKSPACE_ID":       true,
 	"WORKSPACE_UID":      true,
 	"WORKSPACE_PROVIDER": true,
 	"WORKSPACE_CONTEXT":  true,
 	"WORKSPACE_FOLDER":   true,
 	"WORKSPACE_SOURCE":   true,
 	"WORKSPACE_ORIGIN":   true,
 	"WORKSPACE_PICTURE":  true,
 	"MACHINE_ID":         true,
 	"MACHINE_CONTEXT":    true,
 	"MACHINE_FOLDER":     true,
 	"MACHINE_PROVIDER":   true,
 	"LOFT_PROJECT":       true,
 }

Also applies to: 101-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/provider/parse.go` around lines 25 - 42, The reservedOptionNames map is
missing several contract-critical env var names, allowing them to be used as
provider options; update the reservedOptionNames declaration to include
"DEVPOD_PROVIDER", "DEVPOD", "DEVPOD_OS", "DEVPOD_ARCH", and "DEVPOD_LOG_LEVEL"
(the same missing keys must also be added where the map is duplicated around the
other occurrence referenced in the comment). Ensure these names match the keys
produced by GetBaseEnvironment so provider options cannot override those system
variables.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant