-
Notifications
You must be signed in to change notification settings - Fork 145
Add support for baking build-time arguments in container ENTRYPOINTs #2631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add BuildArgs field to TemplateData struct for baking required CLI arguments into container ENTRYPOINT at build time - Update npx.tmpl to insert BuildArgs before "$@" - Update uvx.tmpl to insert BuildArgs before "$@" - Update go.tmpl to insert BuildArgs in JSON array format BuildArgs are for required subcommands (e.g., 'start') while runtime args passed via '-- <args>' append after. This prevents duplication issues that led to MCPArgs removal in PR #2630. Related to: stacklok/dockyard#189
- Add buildArgs []string parameter to BuildFromProtocolSchemeWithName - Update createTemplateData to accept and use buildArgs - Update HandleProtocolScheme to pass nil for buildArgs - Update thv build command callers to pass nil for buildArgs Existing callers pass nil to maintain backward compatibility.
Fixed extra quote in ENTRYPOINT template that was causing test failures. The template now correctly generates: - Empty buildArgs: ENTRYPOINT ["/app/mcp-server"] - With buildArgs: ENTRYPOINT ["/app/mcp-server", "arg1", "arg2"]
Added test cases for all three transport types (NPX, UVX, GO) that exercise the BuildArgs feature: - NPX with single arg (start) - UVX with multiple args (--transport stdio) - GO with multiple args (serve --verbose) All tests verify that BuildArgs are correctly baked into the ENTRYPOINT.
Users can now bake required arguments into containers at build time: $ thv build npx://@launchdarkly/mcp-server -- start $ thv build uvx://package -- --transport stdio This provides: - Consistency with 'thv run -- <args>' syntax - Easy local testing of build args before using in Dockyard - Arguments are baked into ENTRYPOINT, not overridable at runtime Examples: - NPX: exec npx package start "$@" - UVX: exec package "--transport" "stdio" - GO: ENTRYPOINT ["/app/mcp-server", "serve", "--verbose"] Related to: stacklok/dockyard#189
- Updated TestTemplateDataWithLocalPath to include BuildArgs field - Added TestCreateTemplateData with 5 test cases covering: * NPX with single buildArg * UVX with multiple buildArgs * GO with buildArgs * GO local path with buildArgs * NPX without buildArgs (backward compatibility) All tests verify that buildArgs flow correctly through the createTemplateData function and are properly set in TemplateData.
- Broke up long line in build.go (BuildFromProtocolSchemeWithName call) - Broke up long function signature in protocol.go (createTemplateData) Both lines now conform to 130 character limit.
Signed-off-by: Dan Barr <[email protected]>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2631 +/- ##
==========================================
+ Coverage 55.15% 55.26% +0.10%
==========================================
Files 315 315
Lines 30275 30287 +12
==========================================
+ Hits 16699 16738 +39
+ Misses 12118 12088 -30
- Partials 1458 1461 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@claude do a thorough review of this |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
Claude finished @JAORMX's task —— View job Code Review Complete ✅I've completed a thorough review of the buildArgs feature implementation. Overall, this is a well-designed and well-implemented feature with excellent test coverage. Here are my findings: ✅ Strengths1. Excellent Design & Architecture
2. Comprehensive Test Coverage
3. User Experience
🔍 Issues & RecommendationsCritical IssuesNone found. Minor Issues & Suggestions1. Missing Test Coverage Line (Codecov: 75% patch coverage)The codecov report indicates 1 line is missing coverage in Recommendation: Add a test that calls func TestHandleProtocolScheme(t *testing.T) {
t.Parallel()
ctx := context.Background()
// Test that HandleProtocolScheme correctly passes nil for buildArgs
dockerfileContent, err := HandleProtocolScheme(
ctx, nil, "npx://test-package", "", true)
if err != nil {
t.Errorf("HandleProtocolScheme() error = %v", err)
}
// Verify no buildArgs in output
if strings.Contains(dockerfileContent, "test-package ") {
t.Error("Expected no buildArgs in ENTRYPOINT")
}
}2. Template Escaping in UVX (
|
|
I've refactored the NPX and UVX templates to address the complexity concerns while maintaining security. What ChangedInstead of creating complex shell scripts with nested quoting, I've:
Benefits
@claude Could you please re-review? This should address the maintainability concerns while keeping the security improvements. |
This comment was marked as outdated.
This comment was marked as outdated.
Previous approach created complex shell scripts with nested quoting to handle
version stripping and buildArgs. This was error-prone and hard to maintain.
New approach:
- Add MCPPackageClean field to TemplateData, auto-populated by stripVersionSuffix()
- NPX: Use simple JSON array ENTRYPOINT with pre-stripped package name
ENTRYPOINT ["npx", "{{.MCPPackageClean}}"{{range .BuildArgs}}, "{{.}}"{{end}}]
- UVX: Simplified to use MCPPackageClean instead of shell parameter expansion
ENTRYPOINT ["sh", "-c", "exec '{{.MCPPackageClean}}'{{range .BuildArgs}} '{{.}}'{{end}} \"$@\"", "--"]
- Add comprehensive unit tests for version stripping logic
Benefits:
- NPX template reduced from 9 lines of shell script to 2 lines of JSON array
- Version stripping logic centralized, testable, and maintainable
- Properly handles scoped packages (@org/package@version -> @org/package)
- BuildArgs safely passed without shell injection risk
- Prevents NPX from re-pulling packages when @latest is specified
Fixes NPX @latest regression from PR #2630.
265f5cf to
9d190ec
Compare
|
Whoops, missed adding some changes to the last commit, sorry about that - hey @claude my last comment should be more correct now, can you look again? |
|
Claude finished @danbarr's task —— View job Re-review Complete: Excellent Refactoring! ✨I've thoroughly reviewed the refactored code. This is a significant improvement that addresses the previous complexity concerns while maintaining security. Summary of ChangesYou've successfully implemented the
✅ Security AnalysisNPX Template (
|
| Aspect | Previous Implementation | Current Implementation | Assessment |
|---|---|---|---|
| NPX Complexity | 9 lines of shell script | 1 line JSON array | ✅ 89% reduction |
| Version Stripping | Runtime shell manipulation | Centralized Go function | ✅ Much better |
| Security | Shell injection risk | Minimal risk (JSON arrays) | ✅ Significantly improved |
| Maintainability | Hard to understand | Clear and simple | ✅ Excellent |
| Test Coverage | Good | Excellent (with stripVersionSuffix tests) |
✅ Enhanced |
📋 Final Recommendations
High Priority
- Add buildArgs validation to prevent shell metacharacters in UVX template:
This prevents users from accidentally breaking UVX containers with args like
// In pkg/runner/protocol.go, before calling BuildFromProtocolSchemeWithName: if err := validateBuildArgs(buildArgs); err != nil { return "", err }
my'arg.
Nice-to-Have
- Add edge case tests for buildArgs with special characters (spaces, etc.) to document expected behavior
- Document version stripping in the CLI help text or comments (currently well-documented in code comments)
UVX Python Package Note
I don't believe Python/uv packages use @ for scoping like npm does (Python uses - for package names), so the MCPPackageClean approach should work fine for UVX. However, if you encounter any Python packages with @ symbols that break, we'll need to revisit the stripping logic.
🏆 Overall Assessment
Grade: A (excellent work!)
This refactoring is a major improvement:
- ✅ 89% reduction in NPX template complexity
- ✅ Significantly improved security via JSON array ENTRYPOINTs
- ✅ Centralized, testable version stripping logic
- ✅ Excellent test coverage with comprehensive edge cases
- ✅ Clear, maintainable code that's easy to understand
⚠️ Minor security consideration for UVX (easily mitigated with validation)
The implementation correctly addresses all the complexity and security concerns from the previous review. The MCPPackageClean approach is elegant and maintainable.
Recommendation: APPROVE with optional validation enhancement for production hardening.
Specific Line References
pkg/container/templates/templates.go:20-23- MCPPackageClean field definitionpkg/container/templates/templates.go:53-58- stripVersionSuffix implementationpkg/container/templates/templates.go:62-63- Automatic MCPPackageClean populationpkg/container/templates/npx.tmpl:100- Simplified JSON array ENTRYPOINTpkg/container/templates/uvx.tmpl:120- Simplified ENTRYPOINT with single quotespkg/container/templates/templates_test.go:379-432- Comprehensive version stripping tests
Great work on this refactoring! The code is much cleaner and more secure now. 🎉
Summary
Adds support for baking required CLI arguments into container ENTRYPOINTs at build time, addressing Dockyard issue #189.
Some MCP servers (like LaunchDarkly) require specific subcommands that must always be present. This feature allows those arguments to be embedded in the container image, preventing users from accidentally overwriting them.
Note: This PR implements the ToolHive side of the feature. Once released, Dockyard will need to be updated to pass
spec.argstoBuildFromProtocolSchemeWithName(see stacklok/dockyard#189).Changes
buildArgs []stringparameter toBuildFromProtocolSchemeWithNameandcreateTemplateData"$@"thv buildCLI to accept-- <args>syntax for baking argumentsExamples
Implementation Details
nilfor backward compatibility"$@"so runtime args append naturallyTesting
Next Steps
This enables Dockyard to pass
spec.argsto the build system for servers requiring specific subcommands.