Workspace version goes from 1.8.1 → 1.9.0. Internal crate deps that
carry an explicit version (wfe-buildkit-protos, wfe-containerd-protos,
wfe in wfe-deno) are bumped to match.
CHANGELOG.md documents the release under `## [1.9.0] - 2026-04-07`:
* wfectl CLI with 17 subcommands
* wfectl validate (local YAML compile, no round-trip)
* Human-friendly workflow names (instance sequencing + definition
display name)
* wfe-server full feature set (kubernetes + deno + buildkit +
containerd + rustlang) on a debian base
* wfe-ci builder Dockerfile
* /bin/bash for run scripts
* ensure_store_exists called on host start
* SubWorkflowStep parent data inheritance
* workflows.yaml restructured for YAML 1.1 shallow-merge semantics
Two scope changes that together get the self-hosted wfe CI pipeline
passing against builds.sunbeam.pt.
1. Add `name:` display names to all 12 workflow definitions
(Continuous Integration, Unit Tests, Build Image, etc.) so the
new wfectl tables and UIs have human-friendly labels alongside
the slug ids.
2. Restructure step references from the old `<<: *ci_step` / `<<:
*ci_long` anchors to inner-config merges of the form:
- name: foo
type: kubernetes
config:
<<: *ci_config
run: |
...
YAML 1.1 merge keys are *shallow*. The old anchors put `config:`
on the top-level step, then the step's own `config:` block
replaced it wholesale — image, memory, cpu, env all vanished.
The new pattern merges at the `config:` level so step-specific
fields (`run:`, `outputs:`, etc.) sit alongside the inherited
`image:`, `memory:`, `cpu:`, `env:`.
3. Secret env vars (GITEA_TOKEN, TEA_TOKEN, CARGO_REGISTRIES_*,
BUILDKIT_*) moved into the shared `ci_env` anchor. Individual
steps used to declare their own `env:` blocks which — again due
to shallow merge — would replace the whole inherited env map.
wfectl is a command-line client for wfe-server with 17 subcommands
covering the full workflow lifecycle:
* Auth: login (OAuth2 PKCE via Ory Hydra), logout, whoami
* Definitions: register (YAML → gRPC), validate (local compile),
definitions list
* Instances: run, get, list, cancel, suspend, resume
* Events: publish
* Streaming: watch (lifecycle), logs, search-logs (full-text)
Key design points:
* `validate` compiles YAML locally via `wfe-yaml::load_workflow_from_str`
with the full executor feature set enabled — instant feedback, no
server round-trip, no auth required. Uses the same compile path as
the server's `register` RPC so what passes validation is guaranteed
to register.
* Lookup commands accept either UUID or human name; the server
resolves the identifier for us. Display tables show both columns.
* `run --name <N>` lets users override the auto-generated
`{def_id}-{N}` instance name when they want a sticky reference.
* Table and JSON output formats, shared bearer-token or cached-login
auth path, direct token injection via `WFECTL_TOKEN`.
* 5 new unit tests for the validate command cover happy path, unknown
step type rejection, and missing file handling.
Dockerfile.ci ships the prebuilt image used as the `image:` for
kubernetes CI steps: rust stable, cargo-nextest, cargo-llvm-cov,
sccache (configured via WFE_SCCACHE_* env), buildctl for in-cluster
buildkitd, kubectl, tea for Gitea releases, and git. Published to
`src.sunbeam.pt/studio/wfe-ci:latest`.
Proto changes:
* Add `name` to `WorkflowInstance`, `WorkflowSearchResult`,
`RegisteredDefinition`, and `DefinitionSummary` messages.
* Add optional `name` override to `StartWorkflowRequest` and echo the
assigned name back in `StartWorkflowResponse`.
* Document that `GetWorkflowRequest.workflow_id` accepts UUID or
human name.
gRPC handler changes:
* `start_workflow` honors the optional name override and reads the
instance back to return the assigned name to clients.
* `get_workflow` flows through `WorkflowHost::get_workflow`, which
already falls back from UUID to name lookup.
* `stream_logs`, `watch_lifecycle`, and `search_logs` resolve
name-or-UUID up front so the LogStore/lifecycle bus (keyed by
UUID) subscribe to the right instance.
* `register_workflow` propagates the definition's display name into
`RegisteredDefinition.name`.
Crate build changes:
* Enable the full executor feature set on wfe-yaml —
`rustlang,buildkit,containerd,kubernetes,deno` — so the shipped
binary recognizes every step type users can write.
* Dockerfile switched from `rust:alpine` to `rust:1-bookworm` +
`debian:bookworm-slim` runtime. `deno_core` bundles a v8 binary
that only ships glibc; alpine/musl can't link it without building
v8 from source.
Add an optional `name` field to `WorkflowSpec` so YAML authors can
declare a human-friendly display name alongside the existing slug
`id`. The compiler copies it through to `WorkflowDefinition.name`,
which surfaces in definitions listings, run tables, and JSON output.
Slug `id` remains the primary lookup key.
Also adds a small smoke test for the schema generators to catch
regressions in `generate_json_schema` / `generate_yaml_schema`.
Three related host.rs changes that together make the 1.9 name support
end-to-end functional.
1. `WorkflowHost::start()` now calls `persistence.ensure_store_exists()`.
The method existed on the trait and was implemented by every
provider but nothing ever invoked it, so the Postgres/SQLite schema
was never auto-created on startup — deployments failed on first
persist with `relation "wfc.workflows" does not exist`.
2. New `start_workflow_with_name` entry point accepting an optional
caller-supplied name override. The normal `start_workflow` is now a
thin wrapper that passes `None` (auto-assign). The default path
calls `next_definition_sequence(definition_id)` and formats the
result as `{definition_id}-{N}` before persisting. Sub-workflow
children also get auto-assigned names via HostContextImpl.
3. `get_workflow`/`suspend_workflow`/`resume_workflow`/
`terminate_workflow` now accept either a UUID or a human-friendly
name. `get_workflow` tries the UUID index first, then falls back to
name lookup. A new `resolve_workflow_id` helper returns the
canonical UUID so the gRPC log/lifecycle streams (which are keyed
by UUID internally) can translate before subscribing.
Land the `name` field and `next_definition_sequence` counter in the
two real persistence backends. Both providers:
* Add `name TEXT NOT NULL UNIQUE` to the `workflows` table.
* Add a `definition_sequences` table (`definition_id, next_num`) with
an atomic UPSERT + RETURNING to give the host a race-free monotonic
counter for `{def_id}-{N}` name generation.
* INSERT/UPDATE queries now include `name`; SELECT row parsers hydrate
it back onto `WorkflowInstance`.
* New `get_workflow_instance_by_name` method for name-based lookups
used by grpc handlers.
Postgres includes a DO-block migration that back-fills `name` from
`id` on pre-existing deployments so the NOT NULL + UNIQUE invariant
holds retroactively; callers can overwrite with a real name on the
next persist.
Add a `name` field to both `WorkflowDefinition` (optional display name
declared in YAML, e.g. "Continuous Integration") and `WorkflowInstance`
(required, unique alongside the UUID primary key). Instance names are
auto-assigned as `{definition_id}-{N}` via a per-definition monotonic
counter so the 42nd run of `ci` becomes `ci-42`.
Persistence trait gains two methods:
* `get_workflow_instance_by_name` — name-based lookup for Get/Cancel/
Suspend/Resume/Watch/Logs RPCs so callers can address instances
interchangeably as either UUID or human name.
* `next_definition_sequence` — atomic per-definition counter used by
the host at start time to allocate the next N.
This commit wires the in-memory test provider and touches the deno
bridge test helper; the real postgres/sqlite impls follow in the next
commit. UUIDs remain the primary key throughout — names are a second
unique index, never a replacement.
Kubernetes step jobs with a `run:` block were invoked via
`/bin/sh -c <script>`. On debian-family base images that resolves to
dash, which rejects `set -o pipefail` ("Illegal option") and other
bashisms (arrays, process substitution, `{1..10}`). The first line of
nearly every real CI script relies on `set -euo pipefail`, so the
steps were failing with exit code 2 before running a single command.
Switch to `/bin/bash -c` so `run:` scripts can rely on the bash
feature set. Containers that lack bash should use the explicit
`command:` form instead.
Pure formatting pass from `cargo fmt --all`. No logic changes. Separating
this out so the 1.9 release feature commits that follow show only their
intentional edits.
SubWorkflowStep was hard-coding `inputs: serde_json::Value::Null` from
the YAML compiler, so every `type: workflow` step kicked off a child
instance with an empty data object. Scripts in child workflows then
saw empty `$REPO_URL`, `$COMMIT_SHA`, etc. and failed immediately.
Now: when no explicit inputs are set, the child inherits the parent
workflow's data (when it's an object). Scripts in child workflows can
reference the same top-level inputs the parent was started with without
every `type: workflow` step needing to re-declare them.
Local dev runs with the SQLite backend leave `wfe.db{,-shm,-wal}` files
in the repo root, and `workflows.schema.yaml` is a generated artifact
we prefer to fetch from the running server's `/schema/workflow.yaml`
endpoint rather than checking in.
- tonic-reflection for gRPC service discovery
- /schema/workflow.json (JSON Schema from schemars derives)
- /schema/workflow.yaml (same schema in YAML)
- /schema/workflow.proto (raw proto file)
- Multi-stage alpine Dockerfile with all executor features
- Comprehensive configuration reference (wfe-server/README.md)
- Release script (scripts/release.sh)
- Bumped to 1.8.1
Sets step_name on execution pointers when advancing to next steps,
compensation steps, and parallel branch children so that runtime
consumers can identify steps by name without lookup.
Adds WorkflowBuilder::add_step_typed<S>() for adding named, configured
steps directly — needed for parallel branch closures in the CLI.
Makes wire_outcome() public so callers can wire custom step graphs.
Adds StepBuilder::config() to attach arbitrary JSON configuration to
individual steps, readable at runtime via context.step.step_config.
Bumps version to 1.6.1.
Add wfe-server-protos and wfe-server to workspace members.
Update StepExecutionContext constructions with log_sink: None
in buildkit and containerd test files.
Shell step streaming: when LogSink is present, uses cmd.spawn() with
tokio::select! to interleave stdout/stderr line-by-line. Respects
timeout_ms with child.kill() on timeout. Falls back to buffered mode
when no LogSink.
Security: block sensitive env var overrides (PATH, LD_PRELOAD, etc.)
from workflow data injection. Proper error handling for pipe capture.
4 LogSink regression tests + 2 env var security regression tests.