From 597e7a678a42eccd8836d7f9c3fc45a2998ba6ac Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 02:15:59 +0000 Subject: [PATCH] feat(build): add outputPath workflow alias Co-Authored-By: Virgil --- cmd/build/cmd_workflow.go | 9 ++++++++- cmd/build/cmd_workflow_test.go | 22 +++++++++++++++++----- pkg/api/provider.go | 10 ++-------- pkg/build/workflow.go | 21 +++++++++++++++------ pkg/build/workflow_test.go | 22 ++++++++++++++-------- 5 files changed, 56 insertions(+), 28 deletions(-) diff --git a/cmd/build/cmd_workflow.go b/cmd/build/cmd_workflow.go index 7a3eb2d..d1943ca 100644 --- a/cmd/build/cmd_workflow.go +++ b/cmd/build/cmd_workflow.go @@ -20,6 +20,7 @@ var ( releaseWorkflowWorkflowPathSnakeInput string releaseWorkflowOutputPathHyphenInput string releaseWorkflowOutputPathSnakeInput string + releaseWorkflowOutputPathInput string releaseWorkflowOutputLegacyInput string releaseWorkflowWorkflowOutputPathInput string releaseWorkflowWorkflowOutputPathHyphenInput string @@ -33,6 +34,7 @@ type releaseWorkflowInputs struct { workflowPathInput string workflowPathHyphenInput string workflowPathSnakeInput string + outputPathInput string outputPathHyphenInput string outputPathSnakeInput string outputLegacyInput string @@ -49,6 +51,7 @@ var releaseWorkflowCmd = &cli.Command{ workflowPathInput: releaseWorkflowWorkflowPathInput, workflowPathHyphenInput: releaseWorkflowWorkflowPathHyphenInput, workflowPathSnakeInput: releaseWorkflowWorkflowPathSnakeInput, + outputPathInput: releaseWorkflowOutputPathInput, outputPathHyphenInput: releaseWorkflowOutputPathHyphenInput, outputPathSnakeInput: releaseWorkflowOutputPathSnakeInput, outputLegacyInput: releaseWorkflowOutputLegacyInput, @@ -69,6 +72,7 @@ func initWorkflowFlags() { releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowWorkflowPathInput, "workflowPath", "", i18n.T("cmd.build.workflow.flag.path")) releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowWorkflowPathHyphenInput, "workflow-path", "", i18n.T("cmd.build.workflow.flag.path")) releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowWorkflowPathSnakeInput, "workflow_path", "", i18n.T("cmd.build.workflow.flag.path")) + releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowOutputPathInput, "outputPath", "", i18n.T("cmd.build.workflow.flag.output_path")) releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowOutputPathHyphenInput, "output-path", "", i18n.T("cmd.build.workflow.flag.output_path")) releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowOutputPathSnakeInput, "output_path", "", i18n.T("cmd.build.workflow.flag.output_path")) releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowOutputLegacyInput, "output", "", i18n.T("cmd.build.workflow.flag.output")) @@ -93,6 +97,7 @@ func AddWorkflowCommand(buildCmd *cli.Command) { // runReleaseWorkflow(ctx, releaseWorkflowInputs{workflowPathInput: "ci/release.yml"}) // uses the workflowPath alias // runReleaseWorkflow(ctx, releaseWorkflowInputs{workflowPathHyphenInput: "ci/release.yml"}) // uses the workflow-path alias // runReleaseWorkflow(ctx, releaseWorkflowInputs{workflowPathSnakeInput: "ci/release.yml"}) // uses the workflow_path alias +// runReleaseWorkflow(ctx, releaseWorkflowInputs{outputPathInput: "ci/release.yml"}) // uses the outputPath alias // runReleaseWorkflow(ctx, releaseWorkflowInputs{workflowOutputPathInput: "ci/release.yml"}) // uses the workflowOutputPath alias func runReleaseWorkflow(_ context.Context, inputs releaseWorkflowInputs) error { projectDir, err := ax.Getwd() @@ -113,6 +118,7 @@ func runReleaseWorkflow(_ context.Context, inputs releaseWorkflowInputs) error { resolvedWorkflowOutputPath, err := resolveReleaseWorkflowOutputPathAliases( projectDir, + inputs.outputPathInput, inputs.outputPathHyphenInput, inputs.outputPathSnakeInput, inputs.outputLegacyInput, @@ -147,9 +153,10 @@ func resolveReleaseWorkflowInputPathAliases(projectDir, pathInput, workflowPathI // resolveReleaseWorkflowOutputPathAliases keeps the CLI error wording stable while // delegating the conflict detection to the shared build helper. -func resolveReleaseWorkflowOutputPathAliases(projectDir, outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, workflowOutputPathHyphenInput, workflowOutputPathSnakeInput string) (string, error) { +func resolveReleaseWorkflowOutputPathAliases(projectDir, outputPathInput, outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, workflowOutputPathHyphenInput, workflowOutputPathSnakeInput string) (string, error) { resolvedWorkflowOutputPath, err := build.ResolveReleaseWorkflowOutputPathAliasesInProject( projectDir, + outputPathInput, outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, diff --git a/cmd/build/cmd_workflow_test.go b/cmd/build/cmd_workflow_test.go index 5f616d0..a231c0c 100644 --- a/cmd/build/cmd_workflow_test.go +++ b/cmd/build/cmd_workflow_test.go @@ -47,7 +47,7 @@ func TestBuildCmd_resolveReleaseWorkflowOutputPathInput_Bad(t *testing.T) { func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_Good(t *testing.T) { projectDir := t.TempDir() - path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "", "./ci/release.yml", "ci/release.yml") + path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "", "", "./ci/release.yml", "ci/release.yml") require.NoError(t, err) assert.Equal(t, ax.Join(projectDir, "ci", "release.yml"), path) } @@ -55,7 +55,15 @@ func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_Good(t *testing.T) { func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_CamelCaseGood(t *testing.T) { projectDir := t.TempDir() - path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "", "", "", "ci/release.yml", "", "") + path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "", "", "", "") + require.NoError(t, err) + assert.Equal(t, ax.Join(projectDir, "ci", "release.yml"), path) +} + +func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_WorkflowCamelCaseGood(t *testing.T) { + projectDir := t.TempDir() + + path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "", "", "", "", "ci/release.yml", "", "") require.NoError(t, err) assert.Equal(t, ax.Join(projectDir, "ci", "release.yml"), path) } @@ -63,7 +71,7 @@ func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_CamelCaseGood(t *testi func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_Bad(t *testing.T) { projectDir := t.TempDir() - _, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "ops/release.yml", "", "") + _, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "", "ops/release.yml", "", "") require.Error(t, err) assert.Contains(t, err.Error(), "workflow output aliases specify different locations") } @@ -71,7 +79,7 @@ func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_Bad(t *testing.T) { func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_HyphenatedGood(t *testing.T) { projectDir := t.TempDir() - path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "", "", "", "", "ci/release.yml", "") + path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "", "ci/release.yml", "", "", "", "", "") require.NoError(t, err) assert.Equal(t, ax.Join(projectDir, "ci", "release.yml"), path) } @@ -80,7 +88,7 @@ func TestBuildCmd_resolveReleaseWorkflowOutputPathAliases_AbsoluteEquivalent_Goo projectDir := t.TempDir() absolutePath := ax.Join(projectDir, "ci", "release.yml") - path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "", "", absolutePath) + path, err := resolveReleaseWorkflowOutputPathAliases(projectDir, "ci/release.yml", "", "", "", "", "", absolutePath) require.NoError(t, err) assert.Equal(t, absolutePath, path) } @@ -134,6 +142,7 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) { workflowPathCamelFlag := releaseWorkflowCmd.Flags().Lookup("workflowPath") workflowPathFlag := releaseWorkflowCmd.Flags().Lookup("workflow-path") workflowPathSnakeFlag := releaseWorkflowCmd.Flags().Lookup("workflow_path") + outputPathCamelFlag := releaseWorkflowCmd.Flags().Lookup("outputPath") outputPathFlag := releaseWorkflowCmd.Flags().Lookup("output-path") outputPathSnakeFlag := releaseWorkflowCmd.Flags().Lookup("output_path") outputFlag := releaseWorkflowCmd.Flags().Lookup("output") @@ -145,6 +154,7 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) { assert.NotNil(t, workflowPathCamelFlag) assert.NotNil(t, workflowPathFlag) assert.NotNil(t, workflowPathSnakeFlag) + assert.NotNil(t, outputPathCamelFlag) assert.NotNil(t, outputPathFlag) assert.NotNil(t, outputPathSnakeFlag) assert.NotNil(t, outputFlag) @@ -154,6 +164,7 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) { assert.NotEmpty(t, workflowPathCamelFlag.Usage) assert.NotEmpty(t, workflowPathFlag.Usage) assert.NotEmpty(t, workflowPathSnakeFlag.Usage) + assert.NotEmpty(t, outputPathCamelFlag.Usage) assert.NotEmpty(t, outputPathFlag.Usage) assert.NotEmpty(t, outputPathSnakeFlag.Usage) assert.NotEmpty(t, outputFlag.Usage) @@ -164,6 +175,7 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) { assert.Equal(t, pathFlag.Usage, workflowPathCamelFlag.Usage) assert.Equal(t, pathFlag.Usage, workflowPathFlag.Usage) assert.Equal(t, workflowPathFlag.Usage, workflowPathSnakeFlag.Usage) + assert.Equal(t, outputPathFlag.Usage, outputPathCamelFlag.Usage) assert.NotEqual(t, outputPathFlag.Usage, outputFlag.Usage) assert.Equal(t, outputPathFlag.Usage, outputPathSnakeFlag.Usage) assert.Equal(t, workflowOutputPathFlag.Usage, workflowOutputPathCamelFlag.Usage) diff --git a/pkg/api/provider.go b/pkg/api/provider.go index 2f1ff0c..17e02bc 100644 --- a/pkg/api/provider.go +++ b/pkg/api/provider.go @@ -608,16 +608,10 @@ func (r ReleaseWorkflowRequest) resolvedWorkflowPath(dir string, medium io.Mediu // resolvedOutputPath resolves the workflow output aliases with the same // conflict rules as the CLI. func (r ReleaseWorkflowRequest) resolvedOutputPath(dir string) (string, error) { - outputPath := r.OutputPath - if outputPath == "" { - outputPath = r.OutputPathHyphen - } else if r.OutputPathHyphen != "" && outputPath != r.OutputPathHyphen { - return "", coreerr.E("api.ReleaseWorkflowRequest", "workflow output aliases specify different locations", nil) - } - resolvedOutputPath, err := build.ResolveReleaseWorkflowOutputPathAliasesInProject( dir, - outputPath, + r.OutputPath, + r.OutputPathHyphen, r.OutputPathSnake, r.LegacyOutputPath, r.WorkflowOutputPath, diff --git a/pkg/build/workflow.go b/pkg/build/workflow.go index 7bda1cf..8d094d0 100644 --- a/pkg/build/workflow.go +++ b/pkg/build/workflow.go @@ -160,6 +160,7 @@ func ResolveReleaseWorkflowInputPathAliases(filesystem io_interface.Medium, proj func ResolveReleaseWorkflowOutputPath(outputPathInput, outputPathSnakeInput, outputLegacyInput string) (string, error) { return ResolveReleaseWorkflowOutputPathAliases( outputPathInput, + "", outputPathSnakeInput, outputLegacyInput, "", @@ -171,12 +172,13 @@ func ResolveReleaseWorkflowOutputPath(outputPathInput, outputPathSnakeInput, out // ResolveReleaseWorkflowOutputPathAliases resolves every public workflow output // alias across the CLI, API, and UI layers. // -// build.ResolveReleaseWorkflowOutputPathAliases("ci/release.yml", "", "", "", "", "") // "ci/release.yml" -// build.ResolveReleaseWorkflowOutputPathAliases("", "", "", "ci/release.yml", "", "") // "ci/release.yml" -// build.ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "", "ci/release.yml") // "ci/release.yml" -// build.ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "ci/release.yml", "") // "ci/release.yml" +// build.ResolveReleaseWorkflowOutputPathAliases("ci/release.yml", "", "", "", "", "", "") // "ci/release.yml" +// build.ResolveReleaseWorkflowOutputPathAliases("", "ci/release.yml", "", "", "", "", "") // "ci/release.yml" +// build.ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "ci/release.yml", "", "") // "ci/release.yml" +// build.ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "", "", "ci/release.yml") // "ci/release.yml" func ResolveReleaseWorkflowOutputPathAliases( outputPathInput, + outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, @@ -185,6 +187,7 @@ func ResolveReleaseWorkflowOutputPathAliases( ) (string, error) { return resolveReleaseWorkflowOutputAliasSet( outputPathInput, + outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, @@ -197,11 +200,12 @@ func ResolveReleaseWorkflowOutputPathAliases( // ResolveReleaseWorkflowOutputPathAliasesInProject resolves the workflow output // aliases relative to a project directory before checking for conflicts. // -// build.ResolveReleaseWorkflowOutputPathAliasesInProject("/tmp/project", "ci/release.yml", "", "", "", "", "") // "/tmp/project/ci/release.yml" -// build.ResolveReleaseWorkflowOutputPathAliasesInProject("/tmp/project", "", "", "", "/tmp/project/ci/release.yml", "", "") // "/tmp/project/ci/release.yml" +// build.ResolveReleaseWorkflowOutputPathAliasesInProject("/tmp/project", "ci/release.yml", "", "", "", "", "", "") // "/tmp/project/ci/release.yml" +// build.ResolveReleaseWorkflowOutputPathAliasesInProject("/tmp/project", "", "", "", "", "/tmp/project/ci/release.yml", "", "") // "/tmp/project/ci/release.yml" func ResolveReleaseWorkflowOutputPathAliasesInProject( projectDir, outputPathInput, + outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, @@ -211,6 +215,7 @@ func ResolveReleaseWorkflowOutputPathAliasesInProject( return resolveReleaseWorkflowOutputAliasSetInProject( projectDir, outputPathInput, + outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, @@ -252,6 +257,7 @@ func resolveReleaseWorkflowInputPathPair(pathInput, outputPathInput string, reso // value when aliases agree. func resolveReleaseWorkflowOutputAliasSet( outputPathInput, + outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, @@ -261,6 +267,7 @@ func resolveReleaseWorkflowOutputAliasSet( ) (string, error) { values := []string{ normalizeWorkflowOutputAlias(outputPathInput), + normalizeWorkflowOutputAlias(outputPathHyphenInput), normalizeWorkflowOutputAlias(outputPathSnakeInput), normalizeWorkflowOutputAlias(outputLegacyInput), normalizeWorkflowOutputAlias(workflowOutputPathInput), @@ -290,6 +297,7 @@ func resolveReleaseWorkflowOutputAliasSet( func resolveReleaseWorkflowOutputAliasSetInProject( projectDir, outputPathInput, + outputPathHyphenInput, outputPathSnakeInput, outputLegacyInput, workflowOutputPathInput, @@ -299,6 +307,7 @@ func resolveReleaseWorkflowOutputAliasSetInProject( ) (string, error) { values := []string{ cleanWorkflowInput(outputPathInput), + cleanWorkflowInput(outputPathHyphenInput), cleanWorkflowInput(outputPathSnakeInput), cleanWorkflowInput(outputLegacyInput), cleanWorkflowInput(workflowOutputPathInput), diff --git a/pkg/build/workflow_test.go b/pkg/build/workflow_test.go index 267db30..1022f11 100644 --- a/pkg/build/workflow_test.go +++ b/pkg/build/workflow_test.go @@ -385,6 +385,12 @@ func TestWorkflow_ResolveReleaseWorkflowOutputPath_Good(t *testing.T) { assert.Equal(t, "ci/release.yml", path) }) + t.Run("accepts the hyphenated output path alias", func(t *testing.T) { + path, err := ResolveReleaseWorkflowOutputPathAliases("", "ci/release.yml", "", "", "", "", "") + require.NoError(t, err) + assert.Equal(t, "ci/release.yml", path) + }) + t.Run("accepts the legacy output alias", func(t *testing.T) { path, err := ResolveReleaseWorkflowOutputPath("", "", "ci/release.yml") require.NoError(t, err) @@ -419,19 +425,19 @@ func TestWorkflow_ResolveReleaseWorkflowOutputPath_Bad(t *testing.T) { func TestWorkflow_ResolveReleaseWorkflowOutputPathAliases_Good(t *testing.T) { t.Run("accepts workflowOutputPath aliases", func(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliases("", "", "", "ci/release.yml", "", "") + path, err := ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "ci/release.yml", "", "") require.NoError(t, err) assert.Equal(t, "ci/release.yml", path) }) t.Run("accepts the hyphenated workflowOutputPath alias", func(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "", "ci/release.yml") + path, err := ResolveReleaseWorkflowOutputPathAliases("", "", "", "", "", "", "ci/release.yml") require.NoError(t, err) assert.Equal(t, "ci/release.yml", path) }) t.Run("normalises matching workflow output aliases", func(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliases("ci/release.yml", "", "", "./ci/release.yml", "ci/release.yml", "") + path, err := ResolveReleaseWorkflowOutputPathAliases("ci/release.yml", "", "", "./ci/release.yml", "ci/release.yml", "", "") require.NoError(t, err) assert.Equal(t, "ci/release.yml", path) }) @@ -442,19 +448,19 @@ func TestWorkflow_ResolveReleaseWorkflowOutputPathAliasesInProject_Good(t *testi absolutePath := ax.Join(projectDir, "ci", "release.yml") t.Run("accepts the preferred output path", func(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "ci/release.yml", "", "", "", "", "") + path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "ci/release.yml", "", "", "", "", "", "") require.NoError(t, err) assert.Equal(t, absolutePath, path) }) t.Run("accepts an absolute workflow output alias equivalent to the project path", func(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "", "", "", absolutePath, "", "") + path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "", "", "", "", absolutePath, "", "") require.NoError(t, err) assert.Equal(t, absolutePath, path) }) t.Run("accepts matching relative and absolute aliases", func(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "ci/release.yml", "", "", "", "", absolutePath) + path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "ci/release.yml", "", "", "", "", "", absolutePath) require.NoError(t, err) assert.Equal(t, absolutePath, path) }) @@ -463,14 +469,14 @@ func TestWorkflow_ResolveReleaseWorkflowOutputPathAliasesInProject_Good(t *testi func TestWorkflow_ResolveReleaseWorkflowOutputPathAliasesInProject_Bad(t *testing.T) { projectDir := t.TempDir() - path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "ci/release.yml", "", "", "", "", ax.Join(projectDir, "ops", "release.yml")) + path, err := ResolveReleaseWorkflowOutputPathAliasesInProject(projectDir, "ci/release.yml", "", "", "", "", "", ax.Join(projectDir, "ops", "release.yml")) assert.Error(t, err) assert.Empty(t, path) assert.Contains(t, err.Error(), "output aliases specify different locations") } func TestWorkflow_ResolveReleaseWorkflowOutputPathAliases_Bad(t *testing.T) { - path, err := ResolveReleaseWorkflowOutputPathAliases("ci/release.yml", "", "", "ops/release.yml", "", "") + path, err := ResolveReleaseWorkflowOutputPathAliases("ci/release.yml", "", "", "", "ops/release.yml", "", "") assert.Error(t, err) assert.Empty(t, path) assert.Contains(t, err.Error(), "output aliases specify different locations")