feat(build): expose release workflow generation

This commit is contained in:
Virgil 2026-04-01 19:04:45 +00:00
parent 20f6f8e538
commit eeb87e0444
4 changed files with 93 additions and 2 deletions

View file

@ -105,6 +105,7 @@ func (p *BuildProvider) RegisterRoutes(rg *gin.RouterGroup) {
rg.GET("/release/version", p.getVersion)
rg.GET("/release/changelog", p.getChangelog)
rg.POST("/release", p.triggerRelease)
rg.POST("/release/workflow", p.generateReleaseWorkflow)
// SDK
rg.GET("/sdk/diff", p.getSdkDiff)
@ -165,6 +166,13 @@ func (p *BuildProvider) Describe() []api.RouteDescription {
Description: "Publishes pre-built artifacts from dist/ to configured targets.",
Tags: []string{"release"},
},
{
Method: "POST",
Path: "/release/workflow",
Summary: "Generate release workflow",
Description: "Writes the embedded GitHub Actions release workflow into .github/workflows/release.yml or a custom path.",
Tags: []string{"release", "workflow"},
},
{
Method: "GET",
Path: "/sdk/diff",
@ -504,6 +512,38 @@ func (p *BuildProvider) triggerRelease(c *gin.Context) {
}))
}
type releaseWorkflowRequest struct {
Path string `json:"path"`
}
func (p *BuildProvider) generateReleaseWorkflow(c *gin.Context) {
dir, err := p.resolveDir()
if err != nil {
c.JSON(http.StatusInternalServerError, api.Fail("resolve_failed", err.Error()))
return
}
var req releaseWorkflowRequest
if err := c.ShouldBindJSON(&req); err != nil {
req.Path = ""
}
path := req.Path
if path == "" {
path = build.ReleaseWorkflowPath(dir)
}
if err := build.WriteReleaseWorkflow(p.medium, path); err != nil {
c.JSON(http.StatusInternalServerError, api.Fail("workflow_write_failed", err.Error()))
return
}
c.JSON(http.StatusOK, api.OK(map[string]any{
"generated": true,
"path": path,
}))
}
// -- SDK Handlers -------------------------------------------------------------
func (p *BuildProvider) getSdkDiff(c *gin.Context) {

View file

@ -3,9 +3,14 @@
package api
import (
"bytes"
"io/fs"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"dappco.re/go/core/build/internal/ax"
"dappco.re/go/core/build/pkg/build"
"dappco.re/go/core/io"
@ -45,8 +50,8 @@ func TestProvider_BuildProviderDescribe_Good(t *testing.T) {
p := NewProvider(".", nil)
routes := p.Describe()
// Should have 9 endpoint descriptions
assert.Len(t, routes, 9)
// Should have 10 endpoint descriptions
assert.Len(t, routes, 10)
// Verify key routes exist
paths := make(map[string]string)
@ -61,6 +66,7 @@ func TestProvider_BuildProviderDescribe_Good(t *testing.T) {
assert.Equal(t, "GET", paths["/release/version"])
assert.Equal(t, "GET", paths["/release/changelog"])
assert.Equal(t, "POST", paths["/release"])
assert.Equal(t, "POST", paths["/release/workflow"])
assert.Equal(t, "GET", paths["/sdk/diff"])
assert.Equal(t, "POST", paths["/sdk/generate"])
}
@ -152,3 +158,27 @@ func TestProvider_ResolveProjectType_Good(t *testing.T) {
assert.Equal(t, build.ProjectTypeGo, projectType)
})
}
func TestProvider_GenerateReleaseWorkflow_Good(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(`{}`))
request.Header.Set("Content-Type", "application/json")
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = request
p.generateReleaseWorkflow(ctx)
assert.Equal(t, http.StatusOK, recorder.Code)
path := build.ReleaseWorkflowPath(projectDir)
content, err := io.Local.Read(path)
require.NoError(t, err)
assert.Contains(t, content, "workflow_call:")
assert.Contains(t, content, "workflow_dispatch:")
}

View file

@ -33,6 +33,10 @@ func WriteReleaseWorkflow(fs io_interface.Medium, path string) error {
return coreerr.E("build.WriteReleaseWorkflow", "failed to read embedded workflow template", err)
}
if err := fs.EnsureDir(ax.Dir(path)); err != nil {
return coreerr.E("build.WriteReleaseWorkflow", "failed to create release workflow directory", err)
}
if err := fs.Write(path, string(content)); err != nil {
return coreerr.E("build.WriteReleaseWorkflow", "failed to write release workflow", err)
}

View file

@ -3,6 +3,7 @@ package build
import (
"testing"
"dappco.re/go/core/build/internal/ax"
"dappco.re/go/core/io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -37,6 +38,22 @@ func TestWorkflow_WriteReleaseWorkflow_Good(t *testing.T) {
require.NoError(t, err)
assert.NotEmpty(t, content)
})
t.Run("creates parent directories on a real filesystem", func(t *testing.T) {
projectDir := t.TempDir()
path := ax.Join(projectDir, ".github", "workflows", "release.yml")
err := WriteReleaseWorkflow(io.Local, path)
require.NoError(t, err)
content, err := io.Local.Read(path)
require.NoError(t, err)
template, err := releaseWorkflowTemplate.ReadFile("templates/release.yml")
require.NoError(t, err)
assert.Equal(t, string(template), content)
})
}
func TestWorkflow_ReleaseWorkflowPath_Good(t *testing.T) {