Skip to content

Commit 62d4d57

Browse files
JAORMXclaude
andauthored
Add CLI commands and template integration for build environment (#2742)
Add CLI commands and Dockerfile template integration for the build environment configuration added in the previous PR. CLI Commands: - thv config set-build-env KEY value: Set a validated env variable - thv config get-build-env [KEY]: Show one or all configured variables - thv config unset-build-env KEY: Remove a specific variable - thv config unset-build-env --all: Remove all variables Template Integration: - Add BuildEnv field to TemplateData struct - Update npx.tmpl, uvx.tmpl, go.tmpl to inject ENV directives - Modify protocol handler to load build env from config Example usage: thv config set-build-env NPM_CONFIG_REGISTRY https://npm.corp.example.com thv config set-build-env GOPROXY https://goproxy.corp.example.com thv run npx://@modelcontextprotocol/server-github 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Juan Antonio Osorio <[email protected]> Co-authored-by: Claude <[email protected]>
1 parent 7d89e0f commit 62d4d57

File tree

11 files changed

+420
-0
lines changed

11 files changed

+420
-0
lines changed

cmd/thv/app/config_buildenv.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/stacklok/toolhive/pkg/config"
10+
)
11+
12+
var (
13+
unsetBuildEnvAll bool
14+
)
15+
16+
var setBuildEnvCmd = &cobra.Command{
17+
Use: "set-build-env <KEY> <value>",
18+
Short: "Set a build environment variable for protocol builds",
19+
Long: `Set a build environment variable that will be injected into Dockerfiles
20+
during protocol builds (npx://, uvx://, go://). This is useful for configuring
21+
custom package mirrors in corporate environments.
22+
23+
Environment variable names must:
24+
- Start with an uppercase letter
25+
- Contain only uppercase letters, numbers, and underscores
26+
- Not be a reserved system variable (PATH, HOME, etc.)
27+
28+
Common use cases:
29+
- NPM_CONFIG_REGISTRY: Custom npm registry URL
30+
- PIP_INDEX_URL: Custom PyPI index URL
31+
- UV_DEFAULT_INDEX: Custom uv package index URL
32+
- GOPROXY: Custom Go module proxy URL
33+
- GOPRIVATE: Private Go module paths
34+
35+
Examples:
36+
thv config set-build-env NPM_CONFIG_REGISTRY https://npm.corp.example.com
37+
thv config set-build-env PIP_INDEX_URL https://pypi.corp.example.com/simple
38+
thv config set-build-env GOPROXY https://goproxy.corp.example.com
39+
thv config set-build-env GOPRIVATE "github.com/myorg/*"`,
40+
Args: cobra.ExactArgs(2),
41+
RunE: setBuildEnvCmdFunc,
42+
}
43+
44+
var getBuildEnvCmd = &cobra.Command{
45+
Use: "get-build-env [KEY]",
46+
Short: "Get build environment variables",
47+
Long: `Display configured build environment variables.
48+
If a KEY is provided, shows only that specific variable.
49+
If no KEY is provided, shows all configured variables.
50+
51+
Examples:
52+
thv config get-build-env # Show all variables
53+
thv config get-build-env NPM_CONFIG_REGISTRY # Show specific variable`,
54+
Args: cobra.MaximumNArgs(1),
55+
RunE: getBuildEnvCmdFunc,
56+
}
57+
58+
var unsetBuildEnvCmd = &cobra.Command{
59+
Use: "unset-build-env [KEY]",
60+
Short: "Remove build environment variable(s)",
61+
Long: `Remove a specific build environment variable or all variables.
62+
63+
Examples:
64+
thv config unset-build-env NPM_CONFIG_REGISTRY # Remove specific variable
65+
thv config unset-build-env --all # Remove all variables`,
66+
Args: cobra.MaximumNArgs(1),
67+
RunE: unsetBuildEnvCmdFunc,
68+
}
69+
70+
func init() {
71+
// Add build-env subcommands to config command
72+
configCmd.AddCommand(setBuildEnvCmd)
73+
configCmd.AddCommand(getBuildEnvCmd)
74+
configCmd.AddCommand(unsetBuildEnvCmd)
75+
76+
// Add --all flag to unset command
77+
unsetBuildEnvCmd.Flags().BoolVar(
78+
&unsetBuildEnvAll,
79+
"all",
80+
false,
81+
"Remove all build environment variables",
82+
)
83+
}
84+
85+
func setBuildEnvCmdFunc(_ *cobra.Command, args []string) error {
86+
key := args[0]
87+
value := args[1]
88+
89+
provider := config.NewDefaultProvider()
90+
if err := provider.SetBuildEnv(key, value); err != nil {
91+
return fmt.Errorf("failed to set build environment variable: %w", err)
92+
}
93+
94+
fmt.Printf("Successfully set build environment variable: %s\n", key)
95+
return nil
96+
}
97+
98+
func getBuildEnvCmdFunc(_ *cobra.Command, args []string) error {
99+
provider := config.NewDefaultProvider()
100+
101+
if len(args) == 1 {
102+
// Get specific variable
103+
key := args[0]
104+
value, exists := provider.GetBuildEnv(key)
105+
if !exists {
106+
fmt.Printf("Build environment variable %s is not configured.\n", key)
107+
return nil
108+
}
109+
fmt.Printf("%s=%s\n", key, value)
110+
return nil
111+
}
112+
113+
// Get all variables
114+
envVars := provider.GetAllBuildEnv()
115+
if len(envVars) == 0 {
116+
fmt.Println("No build environment variables are configured.")
117+
return nil
118+
}
119+
120+
// Sort keys for consistent output
121+
keys := make([]string, 0, len(envVars))
122+
for k := range envVars {
123+
keys = append(keys, k)
124+
}
125+
sort.Strings(keys)
126+
127+
fmt.Println("Configured build environment variables:")
128+
for _, k := range keys {
129+
fmt.Printf(" %s=%s\n", k, envVars[k])
130+
}
131+
return nil
132+
}
133+
134+
func unsetBuildEnvCmdFunc(_ *cobra.Command, args []string) error {
135+
provider := config.NewDefaultProvider()
136+
137+
if unsetBuildEnvAll {
138+
envVars := provider.GetAllBuildEnv()
139+
if len(envVars) == 0 {
140+
fmt.Println("No build environment variables are configured.")
141+
return nil
142+
}
143+
144+
if err := provider.UnsetAllBuildEnv(); err != nil {
145+
return fmt.Errorf("failed to remove build environment variables: %w", err)
146+
}
147+
148+
fmt.Printf("Successfully removed %d build environment variable(s).\n", len(envVars))
149+
return nil
150+
}
151+
152+
if len(args) == 0 {
153+
return fmt.Errorf("please specify a KEY to remove or use --all to remove all variables")
154+
}
155+
156+
key := args[0]
157+
_, exists := provider.GetBuildEnv(key)
158+
if !exists {
159+
fmt.Printf("Build environment variable %s is not configured.\n", key)
160+
return nil
161+
}
162+
163+
if err := provider.UnsetBuildEnv(key); err != nil {
164+
return fmt.Errorf("failed to remove build environment variable: %w", err)
165+
}
166+
167+
fmt.Printf("Successfully removed build environment variable: %s\n", key)
168+
return nil
169+
}

docs/cli/thv_config.md

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_config_get-build-env.md

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_config_set-build-env.md

Lines changed: 59 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/thv_config_unset-build-env.md

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/container/templates/go.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
FROM golang:1.25-alpine AS builder
22

3+
{{if .BuildEnv}}
4+
# Custom build environment variables
5+
{{range $key, $value := .BuildEnv}}ENV {{$key}}="{{$value}}"
6+
{{end}}
7+
{{end}}
38
{{if .CACertContent}}
49
# Add custom CA certificate BEFORE any network operations
510
# This ensures that package managers can verify TLS certificates in corporate networks

pkg/container/templates/npx.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
FROM node:22-alpine AS builder
22

3+
{{if .BuildEnv}}
4+
# Custom build environment variables
5+
{{range $key, $value := .BuildEnv}}ENV {{$key}}="{{$value}}"
6+
{{end}}
7+
{{end}}
38
{{if .CACertContent}}
49
# Add custom CA certificate BEFORE any network operations
510
# This ensures that package managers can verify TLS certificates in corporate networks

pkg/container/templates/templates.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type TemplateData struct {
2929
// These are typically required subcommands (e.g., "start") that must always be present.
3030
// Runtime arguments passed via "-- <args>" will be appended after these build args.
3131
BuildArgs []string
32+
// BuildEnv contains environment variables to inject into the Dockerfile builder stage.
33+
// These are used for configuring package managers (e.g., custom registry URLs).
34+
// Keys must be uppercase with underscores, values are validated for safety.
35+
BuildEnv map[string]string
3236
}
3337

3438
// TransportType represents the type of transport to use.

0 commit comments

Comments
 (0)