dwarves — CI/CD wizard live seams (task #69)
The CI/CD quick-setup wizard (wizard pane +
dwarves pipelineCLI + generator + stage DAG) shipped at bf328f6 with four run-time seams left stubbed. Task #69 turns the SOFTWARE-wiring seams into working code and records the two INFRA-heavy ones as explicit, honest deferrals. Everything reuses shared leaves (L5) and keeps the SSH-only law (L6) and zero-shell law (L9).
What landed (the software-wiring seams)
1. clone_source → dwarves pipeline run --git
The GitSource a wizard config carries never entered the bare *.runa.ron, so a run against a file used DWARVES_REPO_ROOT/local only. pipeline run now takes --git <ssh-url> [--git-ref <rev>] [--ssh-key <vault-name>] [--secret NAME[=ENV_KEY]]: it CLONES the SSH source (reusing nornir_git::gitio::clone_or_fetch, never the git binary) into <warehouse>/sources/<project> and runs the pipeline against that checkout. pipeline_wizard::clone_source was relaxed to permit SSH remotes and local filesystem paths while still rejecting http(s):///git:// network remotes (is_blocked_remote) — the SSH-only law holds for network remotes, and a local bare/ working repo (the test + on-box case) passes through the local transport. The clone needs a git-source-enabled build (--features git-source/server); a non-git-source build fails --git with a clear message (never a silent wrong-repo run). Red-when-broken test (git_source_tests): a run clones a local repo then runs green.
2. CredentialStore → vault
vault::VaultKeyCreds is a CredentialStore backed by the encrypted Vault — it replaces the ambient EnvFallbackCreds for the CI/CD clone: the deploy SSH key is fetched BY NAME from the sealed vault and materialized to a 0600 tmpfs file (the clone gets a key PATH exactly as it did from ~/.ssh). vault::inject_secret_env pulls named secrets from the (unlocked) vault into the runner env that RunaStepRunner::with_env merges into every dispatched job payload (ProcessSpec/ContainerRun env) — so credentials + the fast-runner tuning actually reach the work, never baked into an image (inject_into still handles tmpfs FILE mounts for binary secrets). Fails closed: a locked vault errors, so a run refuses rather than dispatching a blank credential, and the clone falls back to ~/.ssh only for its key (never leaking an unrelated secret). Test proves unlocked→present, locked→error.
3. Service-container sidecars
TestChoice.services were collected but never attached at exec. RunaStepRunner:: with_services + plan_service_sidecars now turn each declared service image into its OWN jera Container job (reusing jera's container policy — WorkPayload::Container → JobPolicy::Container) dispatched alongside the test stage (the stage that runs Nextest/CargoTest); the count surfaces as RunaRunReport.sidecars. Services are a RUNNER concern (not a dwarves_runa::Step), so they ride the runner + the config-aware run_preview, never the *.runa.ron. Test: a declared service enqueues one sidecar container job.
4. dwarves-gui Profile + Settings → REAL vault
The demo vault feature was an empty no-op; it now enables dwarves/vault so the DwarvesVault seam actually links the landed dwarves::vault. Profile lists credential NAMES through an unlocked Session key (never a value); Settings' Unlock/Lock drive the real dwarves::vault::Session (the same session the OIDC VaultTokenStore reads). The honest SampleVault + StubLogin stay the default when the feature is off. Robot tests (feature-gated) cover both.
Deferred (INFRA-heavy — recorded here, NOT built)
(a) Baked runner image + live embedded-holger cache
DEFAULT_RUNNER_IMAGE = ghcr.io/nordisk/dwarves-runner:latest and RunnerSpec.holger_cache_index are wired end-to-end in the runner env, but the actual image (mold + sccache + toolchain preinstalled) and the live embedded-holger /cache mirror it points cargo at are not built here — they need image-registry infrastructure (a registry to push the image, a running SparringRegistry/holger dev-pair to serve the mirror). Until then the env keys are emitted and inert on a bare host; the moment the image + mirror exist, the existing RunnerSpec::env wiring lights them up with no code change.
(b) Real Terraform/draupnir IaC apply/destroy for ProvisionTestEnv
Step::ProvisionTestEnv { backend, teardown } maps to a descriptive dwarves-provision process argv (provision draupnir|terraform <artifact> [--teardown]). A REAL apply/destroy is deferred: the zero-shell law (L9) forbids shelling terraform, and a draupnir/Terraform apply needs live IaC infrastructure + credentials that are not present in this environment. It degrades to a no-op with a warning — the stub argv is dispatched and historised, so the DAG shape (provision → tests, torn down after) is exercised, but nothing is actually provisioned. Wiring the real apply/destroy (a draupnir BootSpec submitted as a jera job for the KVM/OCI path; a pure-Rust Terraform driver, never a shell, for the cloud path) is the follow-up when that infra lands.