Skip to content

Commit 339d854

Browse files
authored
Auto-trust mise config files in workspace (#328) (#331)
* Auto-trust mise config files in workspace (#328) Set MISE_TRUSTED_CONFIG_PATHS to the container workspace path via /etc/profile.d/coi-mise-trust.sh so mise doesn't prompt or error when the workspace contains mise.toml / .tool-versions. * Fix mise trust for non-login shells + add to coi run + integration tests The /etc/profile.d/ approach only works for login shells, but the bash session inside containers (via tmux) is a non-login shell that only sources ~/.bashrc. mise activate in ~/.bashrc runs before the env var is set, causing trust errors. Fix: also prepend MISE_TRUSTED_CONFIG_PATHS to /etc/bash.bashrc which is sourced before ~/.bashrc for all interactive shells. Extract SetupMiseTrust into a shared function and call it from both coi shell (setup.go) and coi run (run.go). Add integration tests verifying the env var is set in both login and non-login shell contexts.
1 parent 3b07229 commit 339d854

5 files changed

Lines changed: 130 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
- [Feature] **Minimum nftables version check (>= 0.9.0)**`coi health` now validates the installed nftables version and fails if it is below the minimum required 0.9.0 for network monitoring features. The nft version is also displayed in the health check output. Includes reusable version parsing utilities and unit/integration tests.
117117
- [Feature] **Split version checks between health (warn) and CLI commands (fail)**`coi health` now reports old Incus/nftables versions as warnings (degraded, not unhealthy), while `coi shell`, `coi run`, and `coi build` fail with a clear error if Incus < 6.1. Added `container.CheckMinimumVersion()` helper. (#214)
118118
- [Feature] **Improved firewalld setup UX with masquerade checks and auto-setup**`coi health` now reports granular firewall state (installed/running/masquerade) with actionable fix commands. `install.sh` offers interactive auto-setup for firewalld (install, start, enable masquerade, configure passwordless sudo). (#216)
119+
- [Feature] **Auto-trust mise config files in workspace** — Workspaces containing `mise.toml` or `.tool-versions` no longer require manual `mise trust` inside the container. COI now sets `MISE_TRUSTED_CONFIG_PATHS` to the container workspace path via `/etc/profile.d/coi-mise-trust.sh`, which is sourced alongside the existing mise activation script. This covers all mise config file types and subdirectories. Non-fatal — logs a warning if the profile.d file cannot be created. (#328)
119120

120121
### Bug Fixes
121122

internal/cli/run.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ func runCommand(cmd *cobra.Command, args []string) error {
279279
// Configure timezone in container filesystem
280280
tz := applyContainerTimezone(mgr)
281281

282+
// Auto-trust mise config files in the workspace
283+
session.SetupMiseTrust(mgr, containerWorkspacePath, func(msg string) {
284+
fmt.Fprintf(os.Stderr, "%s\n", msg)
285+
})
286+
282287
// Execute command directly (args are already the full command to run)
283288
fmt.Fprintf(os.Stderr, "Executing: %s\n", strings.Join(args, " "))
284289

internal/session/setup.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,10 @@ func Setup(opts SetupOptions) (*SetupResult, error) {
526526
opts.Logger("Reusing existing workspace and mount configurations")
527527
}
528528

529+
// 10.2 Auto-trust mise config files in the workspace so mise doesn't
530+
// prompt or error when the workspace contains mise.toml / .tool-versions.
531+
SetupMiseTrust(result.Manager, result.ContainerWorkspacePath, opts.Logger)
532+
529533
// 10.5 Set auto-context path for config-based tools (must happen before setupCLIConfig
530534
// so the path is included in GetSandboxSettings output)
531535
if opts.Tool != nil && config.BoolVal(opts.AutoContext) {

internal/session/setup_helpers.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/mensfeld/code-on-incus/internal/config"
10+
"github.com/mensfeld/code-on-incus/internal/container"
1011
)
1112

1213
// isColimaOrLimaEnvironment detects if we're running inside a Colima or Lima VM
@@ -44,6 +45,24 @@ func buildJSONFromSettings(settings map[string]interface{}) (string, error) {
4445
return string(jsonBytes), nil
4546
}
4647

48+
// SetupMiseTrust configures MISE_TRUSTED_CONFIG_PATHS so mise automatically
49+
// trusts config files (mise.toml, .tool-versions, etc.) in the workspace.
50+
// The env var is written to both /etc/profile.d/ (login shells) and prepended
51+
// to /etc/bash.bashrc (non-login interactive shells, sourced before ~/.bashrc
52+
// where mise activates). Non-fatal: logs a warning on failure.
53+
func SetupMiseTrust(mgr *container.Manager, containerWorkspacePath string, logger func(string)) {
54+
exportLine := fmt.Sprintf(`export MISE_TRUSTED_CONFIG_PATHS="%s"`, containerWorkspacePath)
55+
trustCmd := fmt.Sprintf(
56+
`printf '%%s\n' '%s' > /etc/profile.d/coi-mise-trust.sh && `+
57+
`sed -i '/MISE_TRUSTED_CONFIG_PATHS/d' /etc/bash.bashrc && `+
58+
`sed -i '1i %s' /etc/bash.bashrc`,
59+
exportLine, exportLine,
60+
)
61+
if _, err := mgr.ExecCommand(trustCmd, container.ExecCommandOptions{Capture: true}); err != nil {
62+
logger(fmt.Sprintf("Warning: Failed to configure mise workspace trust: %v", err))
63+
}
64+
}
65+
4766
// hasLimits checks if any limits are configured
4867
func hasLimits(cfg *config.LimitsConfig) bool {
4968
if cfg == nil {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""
2+
Test that MISE_TRUSTED_CONFIG_PATHS is set to the workspace path in container shells.
3+
4+
The env var is written to both /etc/profile.d/coi-mise-trust.sh (login shells)
5+
and /etc/bash.bashrc (non-login interactive shells) during session setup,
6+
so mise automatically trusts config files in the workspace.
7+
"""
8+
9+
import subprocess
10+
11+
12+
def test_mise_trust_env_login_shell(coi_binary, cleanup_containers, workspace_dir):
13+
"""
14+
Test MISE_TRUSTED_CONFIG_PATHS is set in a login shell.
15+
16+
Login shells source /etc/profile.d/*.sh, where coi-mise-trust.sh sets the var.
17+
"""
18+
result = subprocess.run(
19+
[
20+
coi_binary,
21+
"run",
22+
"--workspace",
23+
workspace_dir,
24+
"--",
25+
"bash",
26+
"-l",
27+
"-c",
28+
"echo TRUST_PATH=$MISE_TRUSTED_CONFIG_PATHS",
29+
],
30+
capture_output=True,
31+
text=True,
32+
timeout=180,
33+
)
34+
35+
assert result.returncode == 0, f"Run should succeed. stderr: {result.stderr}"
36+
37+
combined = result.stdout + result.stderr
38+
assert "TRUST_PATH=/workspace" in combined, (
39+
f"MISE_TRUSTED_CONFIG_PATHS should be /workspace in login shell. Got:\n{combined}"
40+
)
41+
42+
43+
def test_mise_trust_env_nonlogin_shell(coi_binary, cleanup_containers, workspace_dir):
44+
"""
45+
Test MISE_TRUSTED_CONFIG_PATHS is set in a non-login interactive shell.
46+
47+
Non-login shells source /etc/bash.bashrc (but NOT /etc/profile.d/),
48+
where coi also injects the var so it's set before ~/.bashrc activates mise.
49+
"""
50+
result = subprocess.run(
51+
[
52+
coi_binary,
53+
"run",
54+
"--workspace",
55+
workspace_dir,
56+
"--",
57+
"bash",
58+
"-c",
59+
# Source /etc/bash.bashrc explicitly since bash -c is non-interactive
60+
# and won't source it automatically. This mirrors what happens in
61+
# interactive non-login shells (like tmux bash).
62+
". /etc/bash.bashrc 2>/dev/null; echo TRUST_PATH=$MISE_TRUSTED_CONFIG_PATHS",
63+
],
64+
capture_output=True,
65+
text=True,
66+
timeout=180,
67+
)
68+
69+
assert result.returncode == 0, f"Run should succeed. stderr: {result.stderr}"
70+
71+
combined = result.stdout + result.stderr
72+
assert "TRUST_PATH=/workspace" in combined, (
73+
f"MISE_TRUSTED_CONFIG_PATHS should be /workspace via bash.bashrc. Got:\n{combined}"
74+
)
75+
76+
77+
def test_mise_trust_profile_d_file_exists(coi_binary, cleanup_containers, workspace_dir):
78+
"""
79+
Test that /etc/profile.d/coi-mise-trust.sh is created with correct content.
80+
"""
81+
result = subprocess.run(
82+
[
83+
coi_binary,
84+
"run",
85+
"--workspace",
86+
workspace_dir,
87+
"--",
88+
"cat",
89+
"/etc/profile.d/coi-mise-trust.sh",
90+
],
91+
capture_output=True,
92+
text=True,
93+
timeout=180,
94+
)
95+
96+
assert result.returncode == 0, f"File should exist. stderr: {result.stderr}"
97+
98+
combined = result.stdout + result.stderr
99+
assert 'MISE_TRUSTED_CONFIG_PATHS="/workspace"' in combined, (
100+
f"Profile.d file should contain the workspace path. Got:\n{combined}"
101+
)

0 commit comments

Comments
 (0)