PushOutput
Type, in @jr2/orchestrator/src/sandbox-effect.ts. It is an export of @jr2/orchestrator, on the Authoring surface page, in Effects.
ts
type PushOutput =
| Exclude<PushWireOutput, { outcome: "pushed" }>
| (Omit<Extract<PushWireOutput, { outcome: "pushed" }>, "bundle"> & { sync: SyncState; syncReason?: string });The output of a push Effect: what the onDone of its state gets as event.output. Every outcome is output, so route on outcome:
pushed: the remote has the commit.publishedis the commit on the remote, andsourceis the Agent's commit that it comes from. They are the same commit, unless the push was signed or squashed.previousis what the remote had before, andunchangedistruewhen the remote had the commit already.squashedissignedorunsignedwhen the commit on the remote is one squashed commit.viatells how jr2 found the new commits after a signed push:ancestry,pair,cherryorforce.rejected: git or the remote refused the push.stagetells where,reasontells why, anddetailhas the words of git.diverged: the commit on the remote is not in the history of the Agent's branch, and jr2 could not pair it with a commit there. Nothing was pushed.reasontells why, andpublishedandsourcename the two commits.unknown: jr2 cannot tell what the push did.reasontells why.
Beside pushed, sync tells what jr2 did to the Agent's branch after the push:
synced: the commit on the remote is not the Agent's own, and jr2 moved the branch to the published commits.already: the branch has the published commit already.skipped: the remote has the Agent's own commit, so the branch does not move.busy: a rebase, merge, cherry-pick or revert of the branch is in progress. Nothing changed.not-synced: the branch did not move.
With busy or not-synced, syncReason tells why, and jr2 tries again before the next push.
Forms: it is one of these objects.
Form 1:
outcome,stage,reason,detailoutcome:"rejected"stage:stringreason:stringdetail(optional):string
Form 2:
outcome,reason,published,sourceoutcome:"diverged"reason:stringpublished:stringsource:string
Form 3:
outcome,reasonoutcome:"unknown"reason:string
Form 4:
outcome,published,source,previous,via,unchanged,newCommits,squashed,sync,syncReasonoutcome:"pushed"published:stringsource:stringprevious(optional):stringvia(optional):stringunchanged(optional):booleannewCommits(optional):numbersquashed(optional):"signed" | "unsigned"sync:SyncStatesyncReason(optional):string
Example
ts
import { fail, jr2Setup, push, type PushOutput, workspace, type Workspaced } from "@jr2/orchestrator";
type Input = Workspaced<{ sha: string }, "app">;
// A push that did not happen (`rejected` or `diverged`) is a different problem from one jr2
// cannot see (`unknown`).
const notPushed = (output: PushOutput): boolean =>
output.outcome === "rejected" || output.outcome === "diverged";
const body = jr2Setup({
types: {} as { context: Input; input: Input },
actors: { publish: push({ pin: true }), fail: fail() },
}).createMachine({
id: "release",
context: ({ input }) => input,
on: { "workspace.lost": ".lost" },
initial: "publishing",
states: {
publishing: {
invoke: {
src: "publish",
input: ({ context }) => ({ sha: context.sha }),
onDone: [
{ guard: ({ event }) => event.output.outcome === "pushed", target: "published" },
{ guard: ({ event }) => notPushed(event.output), target: "notPushed" },
{ target: "unknown" },
],
},
},
published: { type: "final" },
// Not final states: each one fails the run, with its own reason.
notPushed: { invoke: { src: "fail", input: { reason: "the remote refused the push" } } },
unknown: { invoke: { src: "fail", input: { reason: "jr2 cannot tell if the push happened" } } },
lost: { invoke: { src: "fail", input: { reason: "the Sandbox is gone" } } },
},
});
export const machine = workspace(body, {
repos: { app: { url: "https://github.com/you/app.git" } },
spec: () => ({ branch: "release" }),
});See also