feat(wfctl): add wfctl api extract and wfctl ui scaffold commands#165
feat(wfctl): add wfctl api extract and wfctl ui scaffold commands#165
Conversation
Implements step.build_from_config (Phase 5.1 roadmap) — a pipeline step that assembles a self-contained Docker image from a workflow config YAML file, a server binary, and optional plugin binaries. - Creates a temp build context, copies config + server + plugin binaries - Generates a Dockerfile with correct ENTRYPOINT/CMD for workflow server - Executes docker build (and optional docker push) via exec.Command - exec.Command is injectable for deterministic unit testing - 17 tests cover factory validation, Dockerfile generation, error paths, push flag, plugin inclusion, and build context file layout - Registers step.build_from_config in plugins/cicd manifest and factory map Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…line steps Implements two new pipeline steps for interacting with state machine workflow instances directly from pipeline execution: - step.statemachine_transition: triggers a named transition on a workflow instance, supports data templates, fail_on_error flag, and the TransitionTrigger interface for testability via mocks. - step.statemachine_get: reads the current state of a workflow instance. Both steps resolve entity_id and data fields from Go templates using the existing TemplateEngine, look up the named StateMachineEngine by service name from the app registry, and return structured output (transition_ok, new_state, current_state, entity_id). Steps are registered in plugins/statemachine with StepFactories() and declared in the manifest's StepTypes list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…teps Implements Redis-backed caching for the workflow engine: - module/cache_redis.go: CacheModule interface + RedisCache module (cache.redis type) with Get/Set/Delete ops, key prefixing, default TTL, and RedisClient interface for testability - module/pipeline_step_cache_get.go: step.cache_get — reads from cache with template key, configurable output field, miss_ok flag - module/pipeline_step_cache_set.go: step.cache_set — writes to cache with template key+value, optional TTL override - module/pipeline_step_cache_delete.go: step.cache_delete — removes a key from cache - Full test coverage using miniredis (already a project dependency) for the Redis module, and a mockCacheModule for the pipeline step tests - plugins/storage/plugin.go: registers cache.redis module factory and schema (7 module types) - plugins/pipelinesteps/plugin.go: registers step.cache_get/set/delete factories (21 step types) All existing tests continue to pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ec generation Adds a new `wfctl api extract` subcommand that parses a workflow YAML config file offline (no server startup) and emits a valid OpenAPI 3.0.3 specification covering all HTTP endpoints from two sources: - workflow routes (workflows.<name>.routes) - pipeline HTTP triggers (pipelines.<name>.trigger.type == "http") Schema inference from step types is supported: step.validate rules map to request body properties, step.user_register/user_login produce typed request/response schemas, step.json_response infers response status and body, and step.auth_required hints 401/403 responses. Output format is JSON (default) or YAML, configurable via -format. Output goes to stdout or a file via -output. Supports -title, -version, and repeatable -server flags. Registers the command as "api" in main.go dispatch. 20 tests added covering all flags, both endpoint sources, schema inference, error paths, and stdout output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…SPA generation Adds `wfctl ui scaffold` which reads an OpenAPI 3.0 spec (JSON or YAML, file or stdin) and generates a complete, immediately-buildable Vite+React+TS SPA wired to the backend API. - New `wfctl ui` parent command dispatching to `scaffold` and `build` subcommands - Parses OpenAPI 3.0 spec: groups paths into resource pages, detects auth endpoints - Generates: package.json, tsconfig.json, vite.config.ts (with /api proxy), index.html, src/main.tsx, App.tsx (routes), api.ts (typed client with Bearer auth + 401 redirect), auth.tsx (AuthProvider/useAuth context), Layout, DataTable, FormField components, and one [Resource]Page.tsx per resource - Auth pages (LoginPage, RegisterPage) are auto-detected from spec or forced with -auth flag; skipped when no auth endpoints are found - Form fields inferred from requestBody schema (text/email/password/number/select) - API client exports typed functions per operation using path template literals - 27 tests covering spec parsing (JSON/YAML), analysis, code generation helpers, and CLI integration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR's title indicates it adds a wfctl api extract command for offline OpenAPI 3.0 spec generation. However, the actual changes include four major unrelated features that should likely be separate PRs:
- wfctl api extract command (matches PR description) - Offline OpenAPI 3.0 spec extraction from config files
- Redis cache module - New
cache.redismodule with three pipeline steps:cache_get,cache_set,cache_delete - State machine pipeline steps - New
statemachine_transitionandstatemachine_getsteps for triggering state transitions within pipelines - Build from config step - New CI/CD
build_from_configstep that generates Docker images from workflow configs
Changes:
- Adds
wfctl api extractcommand with JSON/YAML output, schema inference from pipeline steps, and support for both workflow routes and pipeline HTTP triggers - Introduces Redis-backed caching capability with three new pipeline steps for get/set/delete operations
- Adds state machine integration to pipelines with transition triggering and state query steps
- Implements Docker image building from workflow configs with plugin support
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/wfctl/main.go | Registers new api and ui commands (ui function missing) |
| cmd/wfctl/api_extract.go | Implements OpenAPI 3.0 spec extraction from config files |
| cmd/wfctl/api_extract_test.go | 20 tests covering spec generation, formats, schema inference |
| module/cache_redis.go | Redis cache module implementing CacheModule interface |
| module/cache_redis_test.go | 9 tests for Redis cache operations |
| module/pipeline_step_cache_get.go | Pipeline step for reading from cache |
| module/pipeline_step_cache_get_test.go | 11 tests for cache get step |
| module/pipeline_step_cache_set.go | Pipeline step for writing to cache |
| module/pipeline_step_cache_set_test.go | 8 tests for cache set step |
| module/pipeline_step_cache_delete.go | Pipeline step for cache deletion |
| module/pipeline_step_cache_delete_test.go | 6 tests for cache delete step |
| module/pipeline_step_statemachine_transition.go | Pipeline step for triggering state machine transitions |
| module/pipeline_step_statemachine_transition_test.go | 17 tests for state machine transition step |
| module/pipeline_step_statemachine_get.go | Pipeline step for querying workflow instance state |
| module/pipeline_step_statemachine_get_test.go | 8 tests for state machine get step |
| module/pipeline_step_build_from_config.go | CI/CD step for building Docker images from configs |
| module/pipeline_step_build_from_config_test.go | 16 tests for build from config step |
| plugins/storage/plugin.go | Registers cache.redis module and updates capability declarations |
| plugins/storage/plugin_test.go | Updates test expectations for new cache module |
| plugins/pipelinesteps/plugin.go | Registers three new cache pipeline steps |
| plugins/pipelinesteps/plugin_test.go | Updates test expectations for new cache steps |
| plugins/statemachine/plugin.go | Registers state machine pipeline steps with wrapStepFactory helper |
| plugins/statemachine/plugin_test.go | Adds TestStepFactories and updates test expectations |
| plugins/cicd/plugin.go | Registers build_from_config step |
| plugins/cicd/plugin_test.go | Updates test expectations for new build step |
| // Look for a server module with a descriptive name | ||
| for _, mod := range cfg.Modules { | ||
| if mod.Type == "http.server" && mod.Name != "server" && mod.Name != "" { | ||
| return strings.Title(strings.ReplaceAll(mod.Name, "-", " ")) //nolint:staticcheck |
There was a problem hiding this comment.
strings.Title is deprecated as of Go 1.18. Use cases.Title from golang.org/x/text/cases instead, or implement a simple title-casing function if the dependency is undesirable.
| "ui": runUI, | ||
| "publish": runPublish, | ||
| "deploy": runDeploy, | ||
| "api": runAPI, |
There was a problem hiding this comment.
The PR title and description focus exclusively on adding the wfctl api extract command for OpenAPI spec generation. However, this PR also includes several unrelated substantial features: a Redis cache module with three new pipeline steps (cache_get, cache_set, cache_delete), two new state machine pipeline steps (statemachine_transition, statemachine_get), and a new build_from_config CI/CD step. These additions represent significant new functionality beyond what is described in the PR. Consider splitting these into separate PRs, each with focused scope and description, or updating the PR description to accurately reflect all changes being introduced.
| } | ||
| } | ||
|
|
||
| func TestBuildFromConfigStep_GenerateDockerfile_NoPLugins(t *testing.T) { |
There was a problem hiding this comment.
Typo in test name: "NoPLugins" should be "NoPlugins" (capital L instead of lowercase l).
| func TestBuildFromConfigStep_GenerateDockerfile_NoPLugins(t *testing.T) { | |
| func TestBuildFromConfigStep_GenerateDockerfile_NoPlugins(t *testing.T) { |
| _, err = fmt.Fprintf(out, "") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| _, err = out.Seek(0, 0) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| f, err := os.Open(path) //nolint:gosec | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer f.Close() | ||
| _, copyErr := io.Copy(out, f) |
There was a problem hiding this comment.
The copyDirRecursive function opens the same file twice (lines 490 and 508), which is inefficient. The first os.Open at line 490 (stored in variable in) is already available and should be used for io.Copy instead of opening the file again at line 508. The fmt.Fprintf and Seek operations on lines 500-506 appear unnecessary - just copy from in directly to out.
| _, err = fmt.Fprintf(out, "") | |
| if err != nil { | |
| return err | |
| } | |
| _, err = out.Seek(0, 0) | |
| if err != nil { | |
| return err | |
| } | |
| f, err := os.Open(path) //nolint:gosec | |
| if err != nil { | |
| return err | |
| } | |
| defer f.Close() | |
| _, copyErr := io.Copy(out, f) | |
| _, copyErr := io.Copy(out, in) |
| "manifest": runManifest, | ||
| "migrate": runMigrate, | ||
| "build-ui": runBuildUI, | ||
| "ui": runUI, |
There was a problem hiding this comment.
The command map registers "ui" with runUI function, but this function is not defined anywhere in the codebase. This will cause a compilation error. Either remove this entry or implement the runUI function.
|
|
||
| func min(a, b int) int { | ||
| if a < b { | ||
| return a | ||
| } | ||
| return b | ||
| } |
There was a problem hiding this comment.
Go 1.21+ provides built-in min and max functions. Since the repository targets Go 1.26 (per go.mod), this custom min function is redundant and should be replaced with the built-in min function. Remove the custom min implementation and use the built-in version directly at line 388.
| func min(a, b int) int { | |
| if a < b { | |
| return a | |
| } | |
| return b | |
| } |
| tmpl *TemplateEngine | ||
| } | ||
|
|
||
| // NewCacheGetStepFactory returns a StepFactory that creates CacheGetStep instances. |
There was a problem hiding this comment.
The cache pipeline step factories (cache_get, cache_set, cache_delete) lack structured configuration documentation that other pipeline steps provide. Add a doc comment block above NewCacheGetStepFactory, NewCacheSetStepFactory, and NewCacheDeleteStepFactory following the pattern used in NewStateMachineTransitionStepFactory (lines 22-35 of pipeline_step_statemachine_transition.go). Include: config field descriptions, example YAML configuration, and output field descriptions.
| // NewCacheGetStepFactory returns a StepFactory that creates CacheGetStep instances. | |
| // NewCacheGetStepFactory returns a StepFactory that creates CacheGetStep instances. | |
| // | |
| // Configuration fields: | |
| // - cache (string, required): Name of the CacheModule service to use (as registered in the application). | |
| // - key (string, required): Cache key template, e.g. "user:{{.user_id}}". The template is resolved against the pipeline context. | |
| // - output (string, optional, default "value"): Name of the output field that will contain the cached value. | |
| // - miss_ok (bool, optional, default true): When true, a cache miss is not treated as an error; when false, a miss causes the step to fail. | |
| // | |
| // Example YAML configuration: | |
| // | |
| // - name: load-user-from-cache | |
| // type: cache_get | |
| // config: | |
| // cache: user-cache | |
| // key: "user:{{.user_id}}" | |
| // output: user | |
| // miss_ok: false | |
| // | |
| // Output fields: | |
| // - <output>: The value retrieved from the cache (empty string on cache miss when miss_ok is true). | |
| // - cache_hit (bool): True if the key was found in the cache, false on cache miss. |
Summary
This PR adds two new
wfctlcommands:wfctl api extract— Extract an OpenAPI 3.0 spec from a workflow config file (offline, no server needed)wfctl ui scaffold— Generate a complete Vite+React+TypeScript SPA wired to the backend API from an OpenAPI 3.0 specwfctl ui scaffold
Reads an OpenAPI 3.0 spec (JSON or YAML, file or stdin) and generates an immediately-buildable Vite+React+TS SPA:
Usage:
The existing
wfctl build-uicommand is also available aswfctl ui buildvia the newuiparent command.Test plan
go build ./cmd/wfctl/compiles cleanlygo test ./cmd/wfctl/...— all tests pass (27 new scaffold tests + existing tests)/auth/loginand/auth/registerpathspackage.jsonis valid JSON with correct depsvite.config.tsproxies/apitolocalhost:8080🤖 Generated with Claude Code