Add "Create Path If Not Exists" Flag to Copy Step
Checklist
- [x] I've searched the issue queue to verify this is not a duplicate feature request.
- [ ] I've pasted the output of
kargo version, if applicable. - [ ] I've pasted logs, if applicable.
Proposed Feature
It would be great to add a flag to the copy step that allows the creation of the destination path if it doesn't already exist. Currently, when copying to a folder like /foo/bar, the path must exist beforehand, which can be cumbersome—especially when commands like touch cannot be used to create the directory.
Motivation
This feature would significantly improve user experience by reducing setup friction and avoiding manual path creation, while still providing flexibility for those who prefer to manage paths manually.
Suggested Implementation
Introduce a flag (e.g., create_path_if_missing) to the copy step. This flag would default to false but can be set to true to automatically create the required path if it doesn’t exist.
- copy: src: /source/path dest: /foo/bar create_path_if_missing: true
If there's a UI component for this step, consider adding a checkbox for users to opt in or out of this behavior, as some users may not want the path to be auto-created.
I might prefer making applicable steps create new paths as needed.
Using the copy step as an example, this suggestion may seem strange if compared to the cp command, which does not do this... but a Docker COPY directive does do this.
I think this would be a major convenience and there's enough precedent that it's not too odd or surprising a behavior.
@tjlet Took a look at this.
I added a test in file_copier_test.go, :
{
name: "create path if not exists",
setupFiles: func(t *testing.T) string {
tmpDir := t.TempDir()
inPath := filepath.Join(tmpDir, "input.txt")
require.NoError(t, os.WriteFile(inPath, []byte("test content"), 0o600))
return tmpDir
},
cfg: builtin.CopyConfig{
InPath: "input.txt",
OutPath: "doesntexist/output.txt",
},
assertions: func(t *testing.T, workDir string, result promotion.StepResult, err error) {
require.NoError(t, err)
require.Equal(t, promotion.StepResult{Status: kargoapi.PromotionPhaseSucceeded}, result)
outPath := filepath.Join(workDir, "doesntexist", "output.txt")
b, err := os.ReadFile(outPath)
require.NoError(t, err)
require.Equal(t, "test content", string(b))
},
},
Test passes:
--- PASS: Test_fileCopier_run/create_path_if_not_exists (0.00s)
PASS
ok github.com/akuity/kargo/internal/promotion/runner/builtin 0.641s
So it seems like this is already the existing behavior.
If you can add some repro steps and logs I'll take another look.
Since we cannot reproduce this and haven't heard back from @tjlet, I'm closing this and @tjlet is invited to re-open it if the issues persists and details of how to reproduce can be shared.