kargo icon indicating copy to clipboard operation
kargo copied to clipboard

Add "Create Path If Not Exists" Flag to Copy Step

Open tjlet opened this issue 9 months ago • 2 comments

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.

tjlet avatar Mar 21 '25 12:03 tjlet

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.

krancour avatar Mar 24 '25 22:03 krancour

@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.

fuskovic avatar Apr 16 '25 00:04 fuskovic

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.

krancour avatar Apr 28 '25 14:04 krancour