go-build/cmd/build/cmd_workflow.go
Snider 4fcf0c4d5f feat(build): cmd migration to core.Command + /release/workflow empty body
- internal/cmdutil/cmdutil.go (new): shared CLI adapter helpers for the
  core.Command registration path
- cmd/build/{cmd_build,cmd_apple,cmd_release,cmd_workflow}.go +
  cmd/ci/ci.go + cmd/sdk/cmd.go: migrated from Cobra-style
  cli.NewCommand/StringFlag to core.Command registration with
  core.Options.String/Int/Bool
- pkg/api/provider.go: POST /release/workflow accepts empty JSON body
  but correctly rejects malformed JSON with 400
- pkg/api/provider_test.go: regression for the new behaviour

Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-14 15:48:31 +01:00

176 lines
8.3 KiB
Go

// cmd_workflow.go implements the release workflow generation command.
package buildcmd
import (
"context"
"dappco.re/go/core"
"dappco.re/go/core/build/internal/ax"
"dappco.re/go/core/build/internal/cmdutil"
"dappco.re/go/core/build/pkg/build"
"dappco.re/go/core/io"
coreerr "dappco.re/go/core/log"
)
// releaseWorkflowRequestInputs keeps the workflow alias inputs grouped by the
// public request fields they represent, rather than by call-site position.
type releaseWorkflowRequestInputs struct {
pathInput string
workflowPathInput string
workflowPathSnakeInput string
workflowPathHyphenInput string
outputPathInput string
outputPathHyphenInput string
outputPathSnakeInput string
legacyOutputInput string
workflowOutputPathInput string
workflowOutputSnakeInput string
workflowOutputHyphenInput string
workflowOutputPathHyphenInput string
workflowOutputPathSnakeInput string
}
// resolveReleaseWorkflowTargetPath merges the workflow path aliases and the
// workflow output aliases into one final target path.
//
// inputs := releaseWorkflowRequestInputs{pathInput: "ci/release.yml", outputPathInput: "ci/release.yml"}
// path, err := inputs.resolveReleaseWorkflowTargetPath("/tmp/project", io.Local)
func (inputs releaseWorkflowRequestInputs) resolveReleaseWorkflowTargetPath(projectDir string, medium io.Medium) (string, error) {
resolvedWorkflowPath, err := resolveReleaseWorkflowInputPathAliases(
projectDir,
inputs.pathInput,
inputs.workflowPathInput,
inputs.workflowPathSnakeInput,
inputs.workflowPathHyphenInput,
)
if err != nil {
return "", err
}
resolvedWorkflowOutputPath, err := resolveReleaseWorkflowOutputPathAliases(
projectDir,
inputs.outputPathInput,
inputs.outputPathHyphenInput,
inputs.outputPathSnakeInput,
inputs.legacyOutputInput,
inputs.workflowOutputPathInput,
inputs.workflowOutputSnakeInput,
inputs.workflowOutputHyphenInput,
inputs.workflowOutputPathSnakeInput,
inputs.workflowOutputPathHyphenInput,
)
if err != nil {
return "", err
}
return build.ResolveReleaseWorkflowInputPathWithMedium(medium, projectDir, resolvedWorkflowPath, resolvedWorkflowOutputPath)
}
// AddWorkflowCommand registers the build/workflow subcommand.
func AddWorkflowCommand(c *core.Core) {
c.Command("build/workflow", core.Command{
Description: "cmd.build.workflow.long",
Action: func(opts core.Options) core.Result {
return cmdutil.ResultFromError(runReleaseWorkflow(cmdutil.ContextOrBackground(), releaseWorkflowRequestInputs{
pathInput: cmdutil.OptionString(opts, "path"),
workflowPathInput: cmdutil.OptionString(opts, "workflowPath"),
workflowPathSnakeInput: cmdutil.OptionString(opts, "workflow_path"),
workflowPathHyphenInput: cmdutil.OptionString(opts, "workflow-path"),
outputPathInput: cmdutil.OptionString(opts, "outputPath"),
outputPathHyphenInput: cmdutil.OptionString(opts, "output-path"),
outputPathSnakeInput: cmdutil.OptionString(opts, "output_path"),
legacyOutputInput: cmdutil.OptionString(opts, "output"),
workflowOutputPathInput: cmdutil.OptionString(opts, "workflowOutputPath"),
workflowOutputSnakeInput: cmdutil.OptionString(opts, "workflow_output"),
workflowOutputHyphenInput: cmdutil.OptionString(opts, "workflow-output"),
workflowOutputPathHyphenInput: cmdutil.OptionString(opts, "workflow-output-path"),
workflowOutputPathSnakeInput: cmdutil.OptionString(opts, "workflow_output_path"),
}))
},
})
}
// runReleaseWorkflow writes the embedded release workflow into the current
// project directory.
//
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{}) // writes .github/workflows/release.yml
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{pathInput: "ci/release.yml"}) // writes ./ci/release.yml under the project root
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowPathInput: "ci/release.yml"}) // uses the workflowPath alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowPathSnakeInput: "ci/release.yml"}) // uses the workflow_path alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowPathHyphenInput: "ci/release.yml"}) // uses the workflow-path alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{outputPathInput: "ci/release.yml"}) // uses the outputPath alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{legacyOutputInput: "ci/release.yml"}) // uses the legacy output alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowOutputPathInput: "ci/release.yml"}) // uses the workflowOutputPath alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowOutputHyphenInput: "ci/release.yml"}) // uses the workflow-output alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowOutputSnakeInput: "ci/release.yml"}) // uses the workflow_output alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowOutputPathSnakeInput: "ci/release.yml"}) // uses the workflow_output_path alias
// runReleaseWorkflow(ctx, releaseWorkflowRequestInputs{workflowOutputPathHyphenInput: "ci/release.yml"}) // uses the workflow-output-path alias
func runReleaseWorkflow(_ context.Context, inputs releaseWorkflowRequestInputs) error {
projectDir, err := ax.Getwd()
if err != nil {
return coreerr.E("build.runReleaseWorkflow", "failed to get working directory", err)
}
resolvedWorkflowPath, err := inputs.resolveReleaseWorkflowTargetPath(projectDir, io.Local)
if err != nil {
return err
}
return build.WriteReleaseWorkflow(io.Local, resolvedWorkflowPath)
}
// resolveReleaseWorkflowInputPathAliases("/tmp/project", "ci/release.yml", "", "", "") // "/tmp/project/ci/release.yml"
// resolveReleaseWorkflowInputPathAliases("/tmp/project", "", "ci/release.yml", "", "") // "/tmp/project/ci/release.yml"
func resolveReleaseWorkflowInputPathAliases(projectDir, pathInput, workflowPathInput, workflowPathSnakeInput, workflowPathHyphenInput string) (string, error) {
resolvedWorkflowPath, err := build.ResolveReleaseWorkflowInputPathAliases(
io.Local,
projectDir,
pathInput,
workflowPathInput,
workflowPathSnakeInput,
workflowPathHyphenInput,
)
if err != nil {
return "", coreerr.E("build.runReleaseWorkflow", "workflow path aliases specify different locations", nil)
}
return resolvedWorkflowPath, nil
}
// resolveReleaseWorkflowOutputPathAliases("/tmp/project", "ci/release.yml", "", "", "", "", "", "", "", "") // "/tmp/project/ci/release.yml"
// resolveReleaseWorkflowOutputPathAliases("/tmp/project", "", "", "", "", "ci/release.yml", "", "", "", "") // "/tmp/project/ci/release.yml"
func resolveReleaseWorkflowOutputPathAliases(projectDir, outputPathInput, outputPathHyphenInput, outputPathSnakeInput, legacyOutputInput, workflowOutputPathInput, workflowOutputSnakeInput, workflowOutputHyphenInput, workflowOutputPathSnakeInput, workflowOutputPathHyphenInput string) (string, error) {
resolvedWorkflowOutputPath, err := build.ResolveReleaseWorkflowOutputPathAliasesInProjectWithMedium(
io.Local,
projectDir,
outputPathInput,
outputPathHyphenInput,
outputPathSnakeInput,
legacyOutputInput,
workflowOutputPathInput,
workflowOutputSnakeInput,
workflowOutputHyphenInput,
workflowOutputPathSnakeInput,
workflowOutputPathHyphenInput,
)
if err != nil {
return "", coreerr.E("build.runReleaseWorkflow", "workflow output aliases specify different locations", nil)
}
return resolvedWorkflowOutputPath, nil
}
// runReleaseWorkflowInDir writes the embedded release workflow into projectDir.
//
// runReleaseWorkflowInDir("/tmp/project", "", "") // /tmp/project/.github/workflows/release.yml
// runReleaseWorkflowInDir("/tmp/project", "ci/release.yml", "") // /tmp/project/ci/release.yml
// runReleaseWorkflowInDir("/tmp/project", ".github/workflows", "") // /tmp/project/.github/workflows/release.yml
func runReleaseWorkflowInDir(projectDir, workflowPathInput, workflowOutputPathInput string) error {
resolvedPath, err := build.ResolveReleaseWorkflowInputPathWithMedium(io.Local, projectDir, workflowPathInput, workflowOutputPathInput)
if err != nil {
return err
}
return build.WriteReleaseWorkflow(io.Local, resolvedPath)
}