feat(build): validate release workflow path alias
This commit is contained in:
parent
ed7b1ef083
commit
a470c4054f
6 changed files with 119 additions and 18 deletions
|
|
@ -14,13 +14,14 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
releaseWorkflowPath string
|
||||
releaseWorkflowOutputPath string
|
||||
)
|
||||
|
||||
var releaseWorkflowCmd = &cli.Command{
|
||||
Use: "workflow",
|
||||
RunE: func(cmd *cli.Command, args []string) error {
|
||||
return runReleaseWorkflow(cmd.Context(), releaseWorkflowOutputPath)
|
||||
return runReleaseWorkflow(cmd.Context(), releaseWorkflowPath, releaseWorkflowOutputPath)
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +31,7 @@ func setWorkflowI18n() {
|
|||
}
|
||||
|
||||
func initWorkflowFlags() {
|
||||
releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowOutputPath, "path", "", i18n.T("cmd.build.workflow.flag.path"))
|
||||
releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowPath, "path", "", i18n.T("cmd.build.workflow.flag.path"))
|
||||
releaseWorkflowCmd.Flags().StringVar(&releaseWorkflowOutputPath, "output", "", i18n.T("cmd.build.workflow.flag.path"))
|
||||
}
|
||||
|
||||
|
|
@ -44,9 +45,10 @@ func AddWorkflowCommand(buildCmd *cli.Command) {
|
|||
// runReleaseWorkflow writes the embedded release workflow into the project.
|
||||
//
|
||||
// buildcmd.AddWorkflowCommand(buildCmd)
|
||||
// runReleaseWorkflow(ctx, "") // writes to .github/workflows/release.yml
|
||||
// runReleaseWorkflow(ctx, "ci/release.yml") // writes to ./ci/release.yml under the project root
|
||||
func runReleaseWorkflow(ctx context.Context, path string) error {
|
||||
// runReleaseWorkflow(ctx, "", "") // writes to .github/workflows/release.yml
|
||||
// runReleaseWorkflow(ctx, "ci/release.yml", "") // writes to ./ci/release.yml under the project root
|
||||
// runReleaseWorkflow(ctx, "", "ci/release.yml") // output is an alias for path
|
||||
func runReleaseWorkflow(ctx context.Context, path, output string) error {
|
||||
_ = ctx
|
||||
|
||||
projectDir, err := ax.Getwd()
|
||||
|
|
@ -54,19 +56,22 @@ func runReleaseWorkflow(ctx context.Context, path string) error {
|
|||
return coreerr.E("build.runReleaseWorkflow", "failed to get working directory", err)
|
||||
}
|
||||
|
||||
return runReleaseWorkflowInDir(projectDir, path)
|
||||
return runReleaseWorkflowInDir(projectDir, path, output)
|
||||
}
|
||||
|
||||
// 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
|
||||
func runReleaseWorkflowInDir(projectDir, path string) error {
|
||||
path = build.ResolveReleaseWorkflowPath(projectDir, path)
|
||||
// runReleaseWorkflowInDir("/tmp/project", "", "") // /tmp/project/.github/workflows/release.yml
|
||||
// runReleaseWorkflowInDir("/tmp/project", "ci/release.yml", "") // /tmp/project/ci/release.yml
|
||||
func runReleaseWorkflowInDir(projectDir, path, output string) error {
|
||||
resolvedPath, err := build.ResolveReleaseWorkflowInputPath(projectDir, path, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := io.Local.EnsureDir(ax.Dir(path)); err != nil {
|
||||
if err := io.Local.EnsureDir(ax.Dir(resolvedPath)); err != nil {
|
||||
return coreerr.E("build.runReleaseWorkflowInDir", "failed to create release workflow directory", err)
|
||||
}
|
||||
|
||||
return build.WriteReleaseWorkflow(io.Local, path)
|
||||
return build.WriteReleaseWorkflow(io.Local, resolvedPath)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) {
|
|||
projectDir := t.TempDir()
|
||||
|
||||
t.Run("writes to the conventional workflow path by default", func(t *testing.T) {
|
||||
err := runReleaseWorkflowInDir(projectDir, "")
|
||||
err := runReleaseWorkflowInDir(projectDir, "", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
path := build.ReleaseWorkflowPath(projectDir)
|
||||
|
|
@ -39,7 +39,7 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) {
|
|||
|
||||
t.Run("writes to a custom relative path", func(t *testing.T) {
|
||||
customPath := "ci/release.yml"
|
||||
err := runReleaseWorkflowInDir(projectDir, customPath)
|
||||
err := runReleaseWorkflowInDir(projectDir, customPath, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
content, err := io.Local.Read(ax.Join(projectDir, customPath))
|
||||
|
|
@ -50,4 +50,15 @@ func TestBuildCmd_RunReleaseWorkflow_Good(t *testing.T) {
|
|||
assert.Contains(t, content, "actions/download-artifact@v4")
|
||||
assert.Contains(t, content, "command: ci")
|
||||
})
|
||||
|
||||
t.Run("writes to the output alias", func(t *testing.T) {
|
||||
customPath := "ci/alias-release.yml"
|
||||
err := runReleaseWorkflowInDir(projectDir, "", customPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
content, err := io.Local.Read(ax.Join(projectDir, customPath))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, content, "workflow_call:")
|
||||
assert.Contains(t, content, "workflow_dispatch:")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -554,11 +554,11 @@ func (p *BuildProvider) generateReleaseWorkflow(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
path := req.Path
|
||||
if path == "" {
|
||||
path = req.Output
|
||||
path, err := build.ResolveReleaseWorkflowInputPath(dir, req.Path, req.Output)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, api.Fail("invalid_request", err.Error()))
|
||||
return
|
||||
}
|
||||
path = build.ResolveReleaseWorkflowPath(dir, path)
|
||||
|
||||
if err := build.WriteReleaseWorkflow(p.medium, path); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Fail("workflow_write_failed", err.Error()))
|
||||
|
|
|
|||
|
|
@ -257,6 +257,28 @@ func TestProvider_GenerateReleaseWorkflow_OutputAlias_Good(t *testing.T) {
|
|||
assert.Contains(t, content, "workflow_dispatch:")
|
||||
}
|
||||
|
||||
func TestProvider_GenerateReleaseWorkflow_ConflictingPathAndOutput_Bad(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
projectDir := t.TempDir()
|
||||
p := NewProvider(projectDir, nil)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodPost, "/release/workflow", bytes.NewBufferString(`{"path":"ci/release.yml","output":"ops/release.yml"}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = request
|
||||
|
||||
p.generateReleaseWorkflow(ctx)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, recorder.Code)
|
||||
|
||||
path := build.ReleaseWorkflowPath(projectDir)
|
||||
_, err := io.Local.Read(path)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestProvider_GenerateReleaseWorkflow_InvalidJSON_Bad(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
|
|
|
|||
|
|
@ -65,3 +65,31 @@ func ResolveReleaseWorkflowPath(projectDir, path string) string {
|
|||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// ResolveReleaseWorkflowInputPath resolves the workflow path from the CLI/API
|
||||
// `path` field and its `output` alias.
|
||||
//
|
||||
// build.ResolveReleaseWorkflowInputPath("/tmp/project", "", "") // /tmp/project/.github/workflows/release.yml
|
||||
// build.ResolveReleaseWorkflowInputPath("/tmp/project", "ci/release.yml", "") // /tmp/project/ci/release.yml
|
||||
// build.ResolveReleaseWorkflowInputPath("/tmp/project", "", "ci/release.yml") // /tmp/project/ci/release.yml
|
||||
// build.ResolveReleaseWorkflowInputPath("/tmp/project", "ci/release.yml", "ci.yml") // error
|
||||
func ResolveReleaseWorkflowInputPath(projectDir, path, output string) (string, error) {
|
||||
if path != "" && output != "" {
|
||||
resolvedPath := ResolveReleaseWorkflowPath(projectDir, path)
|
||||
resolvedOutput := ResolveReleaseWorkflowPath(projectDir, output)
|
||||
if resolvedPath != resolvedOutput {
|
||||
return "", coreerr.E("build.ResolveReleaseWorkflowInputPath", "path and output specify different locations", nil)
|
||||
}
|
||||
return resolvedPath, nil
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
return ResolveReleaseWorkflowPath(projectDir, path), nil
|
||||
}
|
||||
|
||||
if output != "" {
|
||||
return ResolveReleaseWorkflowPath(projectDir, output), nil
|
||||
}
|
||||
|
||||
return ReleaseWorkflowPath(projectDir), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,3 +77,38 @@ func TestWorkflow_ResolveReleaseWorkflowPath_Good(t *testing.T) {
|
|||
assert.Equal(t, "/tmp/release.yml", ResolveReleaseWorkflowPath("/tmp/project", "/tmp/release.yml"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkflow_ResolveReleaseWorkflowInputPath_Good(t *testing.T) {
|
||||
t.Run("uses the conventional path when both inputs are empty", func(t *testing.T) {
|
||||
path, err := ResolveReleaseWorkflowInputPath("/tmp/project", "", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/tmp/project/.github/workflows/release.yml", path)
|
||||
})
|
||||
|
||||
t.Run("accepts path as the primary input", func(t *testing.T) {
|
||||
path, err := ResolveReleaseWorkflowInputPath("/tmp/project", "ci/release.yml", "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/tmp/project/ci/release.yml", path)
|
||||
})
|
||||
|
||||
t.Run("accepts output as an alias for path", func(t *testing.T) {
|
||||
path, err := ResolveReleaseWorkflowInputPath("/tmp/project", "", "ci/release.yml")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/tmp/project/ci/release.yml", path)
|
||||
})
|
||||
|
||||
t.Run("accepts matching path and output values", func(t *testing.T) {
|
||||
path, err := ResolveReleaseWorkflowInputPath("/tmp/project", "ci/release.yml", "ci/release.yml")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/tmp/project/ci/release.yml", path)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkflow_ResolveReleaseWorkflowInputPath_Bad(t *testing.T) {
|
||||
t.Run("rejects conflicting path and output values", func(t *testing.T) {
|
||||
path, err := ResolveReleaseWorkflowInputPath("/tmp/project", "ci/release.yml", "ops/release.yml")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, path)
|
||||
assert.Contains(t, err.Error(), "path and output specify different locations")
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue