fj workflow run returns no handle on the triggered run: no run id, --json, or --watch #129

Open
opened 2026-06-11 00:51:52 +00:00 by stephen · 1 comment
Owner

What

fj workflow run (workflow_dispatch) fires the trigger and prints a one-line
confirmation, but gives the caller no handle on the run it just started: no
run number, no --json, and no --watch to block until it finishes. After
dispatching, a script has no reliable way to find "the run my dispatch created"
to then fj run watch / fj run view it.

Evidence

dispatch returns only a success string (src/cli/workflow.rs:83):

async fn dispatch(args: DispatchArgs, host: Option<&str>) -> Result<()> {
    ...
    api::workflow::dispatch(&ctx.client, &ctx.owner, &ctx.name, &args.workflow, &ref_, inputs).await?;
    println!("✓ Dispatched {} on {}", args.workflow, ref_);
    Ok(())
}

The underlying API call discards the response entirely; Forgejo answers the
dispatch with a 204 and no body (src/api/workflow_run.rs:85-119), so there
is genuinely no run id handed back. DispatchArgs has no --watch / --json
(src/cli/workflow.rs:44-56). The result: the most important automation verb
(trigger a workflow) is a dead end for scripting.

Why it matters for CI/automation buyers

The point of workflow_dispatch from a CLI is "trigger from outside and wait for
the result" (deploy gates, cross-repo orchestration, release pipelines):

fj workflow run deploy.yml --ref "$TAG" -f env=prod --watch --exit-status \
  || rollback

Today that is impossible. Even the manual two-step fails, because there is no
returned id and (until run-list filtering lands, rasterstate/fj#128) no way to
narrow fj run list to the run you just kicked. So fj workflow run can start
work but never confirm or gate on it, the exact thing CI needs.

Proposed shape

Since Forgejo's dispatch returns no id, resolve the run by recency (this is what
gh does for the analogous case):

  • After dispatch, poll actions/runs filtered to the same workflow + ref
    and pick the newest run whose created_at is at/after the dispatch time.
  • --json: print that resolved run (number, status, url) so scripts get a
    handle.
  • --watch [--exit-status]: hand the resolved run straight to the existing
    run watch path so "trigger + stream + gate" is one command.

Naturally composes with run-list filtering (rasterstate/fj#128) and
run watch --exit-status (rasterstate/fj#125).

Scope

Builds on endpoints fj already calls (actions/runs for resolution, the watch
path for streaming). The recency match is best-effort and should be documented
as such; a --no-follow default preserves today's fire-and-forget behavior.

## What `fj workflow run` (workflow_dispatch) fires the trigger and prints a one-line confirmation, but gives the caller **no handle on the run it just started**: no run number, no `--json`, and no `--watch` to block until it finishes. After dispatching, a script has no reliable way to find "the run my dispatch created" to then `fj run watch` / `fj run view` it. ## Evidence `dispatch` returns only a success string (`src/cli/workflow.rs:83`): ```rust async fn dispatch(args: DispatchArgs, host: Option<&str>) -> Result<()> { ... api::workflow::dispatch(&ctx.client, &ctx.owner, &ctx.name, &args.workflow, &ref_, inputs).await?; println!("✓ Dispatched {} on {}", args.workflow, ref_); Ok(()) } ``` The underlying API call discards the response entirely; Forgejo answers the dispatch with a `204` and no body (`src/api/workflow_run.rs:85-119`), so there is genuinely no run id handed back. `DispatchArgs` has no `--watch` / `--json` (`src/cli/workflow.rs:44-56`). The result: the most important automation verb (trigger a workflow) is a dead end for scripting. ## Why it matters for CI/automation buyers The point of `workflow_dispatch` from a CLI is "trigger from outside and wait for the result" (deploy gates, cross-repo orchestration, release pipelines): ```sh fj workflow run deploy.yml --ref "$TAG" -f env=prod --watch --exit-status \ || rollback ``` Today that is impossible. Even the manual two-step fails, because there is no returned id and (until run-list filtering lands, rasterstate/fj#128) no way to narrow `fj run list` to the run you just kicked. So `fj workflow run` can start work but never confirm or gate on it, the exact thing CI needs. ## Proposed shape Since Forgejo's dispatch returns no id, resolve the run by recency (this is what `gh` does for the analogous case): - After dispatch, poll `actions/runs` filtered to the same `workflow` + `ref` and pick the newest run whose `created_at` is at/after the dispatch time. - `--json`: print that resolved run (number, status, url) so scripts get a handle. - `--watch [--exit-status]`: hand the resolved run straight to the existing `run watch` path so "trigger + stream + gate" is one command. Naturally composes with run-list filtering (rasterstate/fj#128) and `run watch --exit-status` (rasterstate/fj#125). ## Scope Builds on endpoints fj already calls (`actions/runs` for resolution, the watch path for streaming). The recency match is best-effort and should be documented as such; a `--no-follow` default preserves today's fire-and-forget behavior.
Author
Owner

Converted to backlog item rasterstate/fj#139 (p2, size M).

Converted to backlog item rasterstate/fj#139 (p2, size M).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rasterstate/fj#129
No description provided.