Skip to content

Merge runtime config override with defaults#4304

Open
lorr1 wants to merge 2 commits intomainfrom
fix/runtime-add-package-merge-defaults
Open

Merge runtime config override with defaults#4304
lorr1 wants to merge 2 commits intomainfrom
fix/runtime-add-package-merge-defaults

Conversation

@lorr1
Copy link

@lorr1 lorr1 commented Mar 21, 2026

Summary

--runtime-add-package without --runtime-image created a RuntimeConfig override with an empty BuilderImage, which loadRuntimeConfig returned as-is — bypassing defaults and producing an invalid Dockerfile (FROM AS builder). Even with --runtime-image, the override's AdditionalPackages replaced the defaults instead of merging with them.

Separately, the npx, go, and uvx Dockerfile templates only installed AdditionalPackages in the builder stage. The runtime stage hardcoded ca-certificates, so runtime dependencies like git added via --runtime-add-package were missing from the final container.

Fixes first half of #4301

Type of change

  • Bug fix

Test plan

  • Unit tests (task test)
  • Manual testing (describe below)

Built thv from the branch and ran thv run --transport stdio --runtime-add-package git --name awesome npx://awesome-agent-skills-mcp. Verified:

  1. Dockerfile renders FROM node:22-alpine AS builder (not empty)
  2. Both builder and runtime stages run apk add --no-cache git ca-certificates
  3. Container starts, clones the skills repo with git, and loads 547 skills

Changes

File Change
pkg/runner/protocol.go Add mergeRuntimeConfig that fills empty BuilderImage from defaults and dedup-merges AdditionalPackages; update loadRuntimeConfig to call it
pkg/runner/protocol_test.go Add TestMergeRuntimeConfig (5 table cases) and TestLoadRuntimeConfigMergesOverrideWithDefaults
pkg/container/templates/npx.tmpl Runtime stage: use AdditionalPackages instead of hardcoded ca-certificates
pkg/container/templates/go.tmpl Same
pkg/container/templates/uvx.tmpl Same (debian, alpine, and default branches)
pkg/container/templates/templates_test.go Add TestRuntimeStageInstallsAdditionalPackages covering all three transports

Does this introduce a user-facing change?

--runtime-add-package now works correctly without requiring --runtime-image, and packages are available at runtime inside the container.

@github-actions github-actions bot added the size/S Small PR: 100-299 lines changed label Mar 21, 2026
@codecov
Copy link

codecov bot commented Mar 21, 2026

Codecov Report

❌ Patch coverage is 61.90476% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.51%. Comparing base (04ae197) to head (b56db14).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
pkg/runner/protocol.go 61.90% 3 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4304      +/-   ##
==========================================
- Coverage   68.77%   68.51%   -0.27%     
==========================================
  Files         473      478       +5     
  Lines       47917    48483     +566     
==========================================
+ Hits        32956    33218     +262     
- Misses      12300    12415     +115     
- Partials     2661     2850     +189     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@aponcedeleonch aponcedeleonch left a comment

Choose a reason for hiding this comment

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

Thanks for the fix — the root cause analysis is spot on and the merge logic is solid. Left two comments on things that could bite us.


# Install runtime dependencies
RUN apk add --no-cache ca-certificates
RUN apk add --no-cache {{range .RuntimeConfig.AdditionalPackages}}{{.}} {{end}}
Copy link
Member

Choose a reason for hiding this comment

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

If AdditionalPackages is ever empty (e.g. user config specifies additional_packages: [], or GetDefaultRuntimeConfig is called with an unknown transport type returning an empty RuntimeConfig{}), this renders as:

RUN apk add --no-cache 

…which fails the Docker build. Same issue in go.tmpl and uvx.tmpl (all three branches).

Consider wrapping with an {{if}} guard:

{{if .RuntimeConfig.AdditionalPackages}}
RUN apk add --no-cache {{range .RuntimeConfig.AdditionalPackages}}{{.}} {{end}}
{{end}}

return nil, fmt.Errorf("invalid runtime config override: %w", err)
}
return override, nil
return merged, nil
Copy link
Member

Choose a reason for hiding this comment

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

The override path now correctly merges with defaults — nice. However, the user config path just below (via provider.GetRuntimeConfig()) still returns the raw config without merging. If a user writes a config.yaml with additional_packages: [git] (omitting ca-certificates), the runtime stage will no longer install it since the templates no longer hardcode ca-certificates.

On main this was safe because the runtime stage always hardcoded ca-certificates regardless of config. With this PR that safety net is gone.

Suggest applying the same merge logic to the user config path as well, e.g.:

if rc := provider.GetRuntimeConfig(string(transportType)); rc != nil {
    merged := mergeRuntimeConfig(transportType, rc)
    if err := merged.Validate(); err != nil {
        return nil, fmt.Errorf("invalid user runtime config: %%w", err)
    }
    return merged, nil
}

@lorr1 lorr1 force-pushed the fix/runtime-add-package-merge-defaults branch from c7001e8 to b56db14 Compare March 24, 2026 03:46
When --runtime-add-package is used without --runtime-image,
loadRuntimeConfig returned the override as-is with an empty
BuilderImage, producing an invalid Dockerfile ("FROM  AS builder").
Even with --runtime-image, AdditionalPackages replaced the defaults
instead of merging.

Additionally, the npx, go, and uvx templates only installed
AdditionalPackages in the builder stage. The runtime stage hardcoded
ca-certificates, so packages needed at runtime (e.g. git) were missing.

Fix both issues:
- Add mergeRuntimeConfig to fill empty BuilderImage from defaults and
  deduplicate-merge AdditionalPackages
- Update all three templates to install AdditionalPackages in the
  runtime stage

Fixes #4301
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 24, 2026
@lorr1 lorr1 force-pushed the fix/runtime-add-package-merge-defaults branch from b56db14 to 21ab354 Compare March 24, 2026 04:38
@lorr1 lorr1 requested a review from rdimitrov as a code owner March 24, 2026 04:38
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Large PR Detected

This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.

How to unblock this PR:

Add a section to your PR description with the following format:

## Large PR Justification

[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformation

Alternative:

Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.

See our Contributing Guidelines for more details.


This review will be automatically dismissed once you add the justification section.

@github-actions github-actions bot added size/XL Extra large PR: 1000+ lines changed and removed size/M Medium PR: 300-599 lines changed labels Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Extra large PR: 1000+ lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants