Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
42 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

A run that is still going offers Cancel run, and not the re-runs, on every page of it. Which buttons a run's page shows is worked out in one place (runControls, lib/run-controls.ts): the run is going while its latest attempt is, as the attempt list's last entry says, whichever attempt the page shows, and on the latest attempt a job that has not finished (waiting for the jobs it needs, held for an environment's approval, queued for a runner, running) keeps it going whatever the run's own status says. So an earlier attempt's page, and its ?job= links, offer Cancel run while a later attempt runs, where they offered nothing, and keep refreshing until it ends; the re-runs show only on the latest attempt of a finished run. A page left open on a finished run asked for nothing and heard nothing when the run was re-run from somewhere else (the API, the MCP tool, another tab): it kept showing the attempt that had ended, with its Re-run buttons and no Cancel run, until a job of the new attempt started or finished. A re-run now tells pages open on the run, as a job starting or finishing does. The run view's status for its current attempt is read by one function (shown_status), with tests that a job queued for a self-hosted runner leaves its run in progress and a job held at an environment makes it wait. The Actions guide says when Cancel run shows.1/**
2 * Which of a run's buttons its page shows: Cancel run (or Force cancel)
3 * while any of it is still going, the re-runs once it has finished. Pure;
4 * the page is routes/repo/actions-run.tsx.
5 *
6 * A run is going while its latest attempt is: the attempt list's last
7 * entry says how it stands now, whichever attempt the page shows, so an
8 * earlier attempt's page (`?attempt=2`, and its `?job=` links) offers
9 * Cancel run while a later one runs. On the latest attempt a job that has
10 * not finished (waiting, pending approval, queued for a runner, running)
11 * keeps the run going too, whatever the run's own status says.
12 */
13import type { Job, RunAttempt, WorkflowRun } from "@g1t/contracts";
14
15export type RunControls = {
16 /** The attempt shown is the run's latest. */
17 latest: boolean;
18 /** The run, at its latest attempt, has not finished. */
19 going: boolean;
20 /** Cancelled, while its jobs run their cleanup steps: Force cancel in place of Cancel run. */
21 cancelling: boolean;
22 canCancel: boolean;
23 canRerun: boolean;
24};
25
26export function runControls(input: {
27 run: Pick<WorkflowRun, "attempt" | "status" | "conclusion" | "error">;
28 jobs: Pick<Job, "status">[];
29 attempts: Pick<RunAttempt, "attempt" | "status">[];
30 /** The viewer may cancel and re-run (Write). */
31 member: boolean;
32}): RunControls {
33 const { run, jobs, attempts, member } = input;
34 const newest = attempts[attempts.length - 1];
35 const latest = newest == null || run.attempt === newest.attempt;
36 const going = latest ? run.status !== "completed" || jobs.some((job) => job.status !== "completed") : newest.status !== "completed";
37 // Only the latest attempt's own status says it is being cancelled; an
38 // earlier attempt's conclusion is how that attempt ended.
39 const cancelling = going && latest && run.conclusion === "cancelled";
40 const allowed = member && !run.error;
41 return { latest, going, cancelling, canCancel: allowed && going, canRerun: allowed && !going && latest };
42}