Skip to content

Allow configuring app directory/path binaries are placed into#1403

Open
UnseenWizzard wants to merge 4 commits intoko-build:mainfrom
UnseenWizzard:feat/allow-overwriting-app-directory
Open

Allow configuring app directory/path binaries are placed into#1403
UnseenWizzard wants to merge 4 commits intoko-build:mainfrom
UnseenWizzard:feat/allow-overwriting-app-directory

Conversation

@UnseenWizzard
Copy link
Copy Markdown

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!

entryPointBasePath string
}

func validateImage(t *testing.T, img oci.SignedImage, baseLayers int64, creationTime v1.Time, opts validationOptions) {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown

@den-vasyliev den-vasyliev Feb 21, 2026

Choose a reason for hiding this comment

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

Thanks, just use your PR locally. Appreciate for the project, but not sure why we need branded entrypoints (Makefile, workflows already has references)...

@github-actions
Copy link
Copy Markdown

This Pull Request is stale because it has been open for 90 days with
no activity. It will automatically close after 30 more days of
inactivity. Keep fresh with the 'lifecycle/frozen' label.

@maboehm
Copy link
Copy Markdown
Contributor

maboehm commented Apr 11, 2025

@UnseenWizzard @imjasonh
Really a bummer to see this PR get stale and closed. We would really find this feature helpful and that change (while adding some new functionality to maintain) is non-intrusive and non-breaking.

Could we re-open this PR?

@UnseenWizzard
Copy link
Copy Markdown
Author

UnseenWizzard commented Apr 11, 2025

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.

@cpanato cpanato reopened this Apr 11, 2025
@cpanato
Copy link
Copy Markdown
Member

cpanato commented Apr 11, 2025

reopened, i also agree with that would be a nice feature

@UnseenWizzard UnseenWizzard force-pushed the feat/allow-overwriting-app-directory branch from 6fa8faf to 2342b17 Compare April 15, 2025 06:25
@Laubi
Copy link
Copy Markdown

Laubi commented Apr 15, 2025

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.
@UnseenWizzard UnseenWizzard force-pushed the feat/allow-overwriting-app-directory branch from 2342b17 to 0795ff9 Compare April 18, 2025 18:35
@UnseenWizzard
Copy link
Copy Markdown
Author

@cpanato change is rebased and conflicts (in docs/ only) resolved :)
CI ran successfully on my fork - note the comment about linting above, lmk if and how you want that addressed with this PR.

I'd also like to add an E2E test for this change - should that simply be added as another part of the Build and run [...] step in the e2e.yaml action? Or would it make sense to split that action into more than one step?

(Btw I can't view why the license compliance check fails here but it seems to do the same on main?)

@rfranzke
Copy link
Copy Markdown

rfranzke commented May 7, 2025

We would also very much like to have this feature :) Can we do something to move this PR forward?

@hakman
Copy link
Copy Markdown

hakman commented Jun 28, 2025

This would be very useful to lots of projects.
@justinsb had a similar PR, but I think he missed the feedback from @imjasonh. #1128 (review)
@cpanato @imjasonh could you take another look? Thanks!

Copilot AI review requested due to automatic review settings March 23, 2026 19:48
@imjasonh
Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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-dir flag and .ko.yaml config key defaultAppDirectory, 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.

Comment on lines +1184 to +1193
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)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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:\".

Copilot uses AI. Check for mistakes.
Comment on lines 602 to 613
// 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
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +214 to +219
func WithAppDir(dir string) Option {
return func(gbo *gobuildOpener) error {
gbo.appDir = dir
return nil
}
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
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.
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
// Empty string means the default directly /ko-app.
// Empty string means the default directory: /ko-app.

Copilot uses AI. Check for mistakes.
Comment on lines +183 to +187
if bo.AppDirectory == "" {
if appDir := v.GetString("defaultAppDirectory"); appDir != "" {
bo.AppDirectory = appDir
}
}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +817 to 824
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)
}
})
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
t.Fatalf("Build() not an Image: %T", result)
}

// Check that the entrypoint of the image is not overwritten
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

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").

Suggested change
// Check that the entrypoint of the image is not overwritten
// Check that the entrypoint includes the debugger and uses the configured app dir

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants