Allow configuring app directory/path binaries are placed into#1403
Allow configuring app directory/path binaries are placed into#1403UnseenWizzard wants to merge 4 commits intoko-build:mainfrom
Conversation
| entryPointBasePath string | ||
| } | ||
|
|
||
| func validateImage(t *testing.T, img oci.SignedImage, baseLayers int64, creationTime v1.Time, opts validationOptions) { |
There was a problem hiding this comment.
note that baseLayers being 3 in all test uses triggers the linter - as it's unrelated to the change, I didn't want to change the function to appease the linter. lmk if I should
There was a problem hiding this comment.
Thanks, just use your PR locally. Appreciate for the project, but not sure why we need branded entrypoints (Makefile, workflows already has references)...
|
This Pull Request is stale because it has been open for 90 days with |
|
@UnseenWizzard @imjasonh Could we re-open this PR? |
|
From my perspective I'm very happy to reopen this PR and make any changes needed to get in! Just let me know I just honestly got the impression from the issue discussions, that a feature like this, while relevant to a part of the user base, is not of interest to maintainers. Which is a pity. |
|
reopened, i also agree with that would be a nice feature |
6fa8faf to
2342b17
Compare
|
This feature would also be really nice for us, as we don't want to introduce breaking changes into existing images. What is needed to merge this feature? |
The currently hardcoded 'ko-app' app directory was used as a hardcoded string in several places in gobuild. To simplify making it overwritable, all occurances are extraced into a constant for the default value.
An option is added to overwrite the default /ko-app folder to place app binaries in. This can be set via cmd line flag or as a default in the config file.
2342b17 to
0795ff9
Compare
|
@cpanato change is rebased and conflicts (in docs/ only) resolved :) I'd also like to add an E2E test for this change - should that simply be added as another part of the (Btw I can't view why the license compliance check fails here but it seems to do the same on main?) |
|
We would also very much like to have this feature :) Can we do something to move this PR forward? |
|
This would be very useful to lots of projects. |
|
It sounds like there's sufficient interest in this feature that we should move forward on it. I've resolved a merge conflict and will review now. |
There was a problem hiding this comment.
Pull request overview
This PR adds a configurable “app directory” so ko can place built binaries somewhere other than the default /ko-app inside the resulting image, addressing issue #944 for users who need stable absolute binary paths.
Changes:
- Add
--app-dirflag and.ko.yamlconfig keydefaultAppDirectory, and plumb them into the build options. - Thread the configured app directory through the Go builder so entrypoint/PATH (and debugger paths) use the configured directory.
- Add/adjust tests and update reference/configuration docs to document the new flag and config.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/commands/resolver.go | Passes BuildOptions.AppDirectory into the build layer via build.WithAppDir. |
| pkg/commands/options/build.go | Introduces AppDirectory, wires --app-dir, and loads defaultAppDirectory from config. |
| pkg/build/options.go | Adds build.WithAppDir functional option. |
| pkg/build/gobuild.go | Applies configurable app dir to entrypoint/PATH and layer creation logic. |
| pkg/build/gobuild_test.go | Updates validation harness and adds tests for custom app dir + debugger interaction. |
| docs/reference/ko_{run,resolve,create,build,apply}.md | Documents the new --app-dir flag in CLI reference output. |
| docs/configuration.md | Documents .ko.yaml config key defaultAppDirectory and behavior overview. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| appPath := strings.Replace(path.Join("C:", g.getAppDir(), appFileName), "/", `\`, -1) | ||
| if g.debug { | ||
| cfg.Config.Entrypoint = append([]string{"C:\\" + delvePath}, delveArgs...) | ||
| cfg.Config.Entrypoint = append(cfg.Config.Entrypoint, appPath) | ||
| } else { | ||
| cfg.Config.Entrypoint = []string{appPath} | ||
| } | ||
|
|
||
| updatePath(cfg, `C:\ko-app`) | ||
| addDirPath := strings.Replace(path.Join("C:", g.getAppDir()), "/", `\`, -1) | ||
| updatePath(cfg, addDirPath) |
There was a problem hiding this comment.
Windows path construction is likely broken when appDir starts with "/" (default and most user values). In Go, path.Join("C:", "/ko-app", ...) discards the "C:" prefix, producing a path like "\ko-app\..." after replacement. This will yield an invalid entrypoint and PATH entry on Windows. Consider handling Windows separately (e.g., trim a leading slash from appDir before joining, or build the path using a Windows-aware join) so the result is always rooted at "C:\".
| // Write the parent directories to the tarball archive. | ||
| // For Windows, the layer must contain a Hives/ directory, and the root | ||
| // of the actual filesystem goes in a Files/ directory. | ||
| // For Linux, the binary goes into /ko-app/ | ||
| dirs := []string{"ko-app"} | ||
| dirs := []string{binaryDir} | ||
| if platform.OS == "windows" { | ||
| dirs = []string{ | ||
| "Hives", | ||
| "Files", | ||
| "Files/ko-app", | ||
| path.Join("Files", binaryDir), | ||
| } | ||
| name = "Files" + name |
There was a problem hiding this comment.
tarBinary now writes directory headers using binaryDir directly. If binaryDir is an absolute path like "/ko-app" or "/usr/local/bin", the Windows branch's path.Join("Files", binaryDir) will ignore the "Files" prefix (because the second argument is absolute), and the layer will not contain the expected "Files/..." directory structure. It also only writes a single directory header (not all parent segments) despite the comment saying it writes parent directories. Consider normalizing binaryDir to a relative path for tar entry names (e.g., trim leading "/" and split into segments to write each parent), and for Windows always prefix with "Files/" after normalization.
| func WithAppDir(dir string) Option { | ||
| return func(gbo *gobuildOpener) error { | ||
| gbo.appDir = dir | ||
| return nil | ||
| } | ||
| } |
There was a problem hiding this comment.
WithAppDir accepts any string and passes it through unmodified. Because this value influences tar entry names, the image entrypoint, and PATH, it’s worth normalizing and validating it (e.g., path.Clean, reject empty/"."/".." segments, and require an absolute POSIX path for non-Windows). This avoids surprising runtime failures when users pass relative paths or paths with trailing slashes.
| WorkingDirectory string | ||
|
|
||
| // AppDirectory allows for setting where the Go application binaries will be placed in the resulting container. | ||
| // Empty string means the default directly /ko-app. |
There was a problem hiding this comment.
The comment says "Empty string means the default directly /ko-app." which reads like a grammar mistake and is a bit unclear. Suggest rewording to something like "Empty string means the default directory: /ko-app." to avoid ambiguity.
| // Empty string means the default directly /ko-app. | |
| // Empty string means the default directory: /ko-app. |
| if bo.AppDirectory == "" { | ||
| if appDir := v.GetString("defaultAppDirectory"); appDir != "" { | ||
| bo.AppDirectory = appDir | ||
| } | ||
| } |
There was a problem hiding this comment.
LoadConfig now supports a new config key (defaultAppDirectory), but there’s no accompanying unit test verifying: (1) the key is read from .ko.yaml, and (2) the CLI flag (--app-dir) properly overrides the config value. Adding a small test in pkg/commands/options/build_test.go (and a testdata .ko.yaml) would prevent regressions.
| want := "/ko-app/test" | ||
| if opts.entryPointBasePath != "" { | ||
| want = path.Join(opts.entryPointBasePath, "test") | ||
| } | ||
| if got := entrypoint[0]; got != want { | ||
| t.Errorf("entrypoint = %v, want %v", got, want) | ||
| } | ||
| }) |
There was a problem hiding this comment.
The new app-dir feature is only asserted via config (entrypoint/PATH), but the tests don’t verify that the binary actually exists in the layer at that directory. Since validateImage doesn’t currently inspect the binary layer tar headers, a regression in tarBinary/buildLayer path handling could slip through. Consider extending these assertions to read the binary layer tar and confirm the expected entry (e.g., /test) is present when entryPointBasePath is set.
| t.Fatalf("Build() not an Image: %T", result) | ||
| } | ||
|
|
||
| // Check that the entrypoint of the image is not overwritten |
There was a problem hiding this comment.
This comment seems incorrect: the test is asserting that the entrypoint is overwritten to include dlv and then the app path. Consider rewording to avoid confusion (e.g., "Check that the entrypoint includes the debugger and uses the configured app dir").
| // Check that the entrypoint of the image is not overwritten | |
| // Check that the entrypoint includes the debugger and uses the configured app dir |
This PR proposes a way to allow optionally modifying under which path binaries are placed inside containers built with ko - as raised in issue #944
Without deeper insight into the code base, this seems like a straightforward addition with limited impact on future maintainability to me - while allowing users migrating to ko to still place binaries in the same location as their previous Docker containers.
Looking forward to your input and any suggestions!