resolve hands apply a deployment record, and settle finds its own
Decision record 0035
Amended by 0056:
resolvealso runs on a dispatch of the workflow, where it starts the queued stacks that are ready, andsettlestarts the workflow again when one is.applynever deploys a queued record.Amended by 0054:
scansetsmatrixtoo, for the record it opens after a merge from the dashboard. It is[]on every other scan.
Records 0003 and 0019 say that resolve creates the deployment record and passes it on, and that apply deploys only on an open record. No record named the inputs and outputs that carry this through a workflow. The brief’s names (stack, expected-hash, a matrix of { stack, environment, expectedHash }) no longer fit: the approved hash lives on the record (0003), so handing it over a second time would give two sources for one fact. This record fixes the names.
resolve has one output, matrix: a JSON array with one entry per deploy it started.
[{ "stack": "apps/grafana:prod", "environment": "sluiceway", "deployment": 1234567890 }]apply has one input of its own, deployment-id. It reads the record and takes everything from it: the stack id from the task, the approved hash and the ticker from the payload. stack and environment are in the matrix entry for the workflow, not for the action: the per stack concurrency group needs the stack id (0006), and the optional job level environment: key needs a name (0003, 0020).
A stack input on apply next to deployment-id was rejected. apply would have to check that the two agree, and a mismatch could only ever be a mistake in the workflow file. An expected-hash input was rejected for the reason above.
settle takes no input. It reads the open deployment records whose payload carries the run id of its own workflow run and gives each the result error (0003). Handing it the matrix was rejected: a resolve that failed after it created a record has no output to hand over, and that is one of the cases settle exists for.
The two names the bootstrap chose stay: the github-token input, which is also what pulumi/actions and most other actions call it, and preview-timeout in whole minutes.
Consequences
action.ymlin v1 has five inputs:mode,concurrency,preview-timeout,github-token,deployment-id. It has one output,matrix, which onlyresolvesets.resolvealways setsmatrix, to[]when it started nothing. It sets the output directly after it created the records and before it writes the body, so a failed body write does not lose the hand-off.deployment-idis required inapplymode and an error in every other mode. A record that is not Sluiceway’s (itstaskdoes not start withsluiceway:), or whose stack discovery does not know, deploys nothing and the job goes red.- The
environmentin a matrix entry is the label on the record: the stack’s configured environment, else the fixed namesluiceway(0003). - A workflow run has at most 256 matrix jobs.
resolvestarts at most 256 deploys in one run, in stack id order. Ticks beyond that are cleared with the note that asks for a fresh tick (0025). - The example workflow’s groups are
sluiceway-scan,sluiceway-resolveandsluiceway-apply-<stack id>. Only the last one usesqueue: max(0025). - The
applyjob is green only when the stack deployed. It goes red when the tool failed, when the change moved since the tick (0008), and when the record had already ended (0019). This is the rule of 0012 seen from the other side: a person who opens a greenapplyjob must be able to read it as “this went out”. - The
settlejob in the example runs withif: always()and only whenresolvestarted at least one deploy, so a refused tick or a rescan costs no third runner. - The per stack time limit of 0012 is the config key
previewTimeouton a stack’s entry, in whole minutes like the input. - A later
stackinput for a deploy without a tick (docs/later.md) stays free, because nothing uses that name now.
Settled while building (slice 2.4)
- The order of one
resolverun: the cheap check on the payload, the body and the history in one read, the open deployments of the ticked stacks, the permission lookups, the deployment records asqueuedin stack id order, thematrixoutput, the dispatch for the rescan box, the body write, the comment for refused ticks, and only then a red job. resolvesetsmatrixon every path, also when it fails before it created a record. The output is then[].- A failure after the first record does not stop the run. What was started is handed on and shown on the dashboard, and the job goes red at the end with every reason. Creating records is the exception: it stops at the first record that cannot be written, so a missing
deployments: writecosts one request and not 256. - A red
resolvejob would make GitHub skipapply, because a job whoseif:has no status check is skipped when a job itneedsfailed. Thensettlewould give every record of the runerror, and the hand-off this record protects would be lost after all. So theif:of the example’sapplyjob starts with!cancelled(). Every entry inmatrixis a record thatresolvecreated after it checked the ticker, so a redresolvelets nothing else through. - A record whose
queuedstatus could not be written is still handed on. A record without a status is an open deployment (0003), soapplytakes it. - The
shaon a record isGITHUB_SHAof theresolvejob. Anissuesevent always runs on the head of the default branch. - The cheap check reads the payload in two halves. Open, authored by the bot and a root marker on the first line need no config. Only then is
sluiceway.yamlread for the label. A broken config file therefore never turns an edit of an ordinary issue red. resolveacts on the issue of its event and lists no issues. A duplicate dashboard can take a tick until the next scan closes it, which 0017 accepts.- 258 pending rows as a scan writes them do not fit in one issue: the smallest shortened row is about 280 characters and the hard limit is 65,536. The cap of 256 is kept, because a body can also be edited by hand, and it costs nothing.
- The dispatch for the rescan box, and for a body of another version (0009), is
POST /repos/{owner}/{repo}/actions/workflows/{file}/dispatcheswith the file and the ref ofGITHUB_WORKFLOW_REF: this same workflow, on the ref this job runs on. The example’s scan job runs for every event that is notissues, so the dispatched run is a full scan. Withoutactions: writeGitHub answers 403, and the job goes red with a message that names the permission.
Settled while building (slice 2.6)
settlereads the page of the newest records of every environment name its stacks use, and giveserrorto every record of Sluiceway’s whose payload names its own run and that has no result yet. Each record is judged on its own, so it does not have to be the newest of its stack. It never asks whether a run is over: its own run is not, and theapplyjobs itneedsare. Every other record is left alone, an open record of another run whose run is over too. That one is ended by the next render, as 0003 says.- The reason is
the run ended without a resultfrom the fixed list (0022), and thelog_urlis the run, which is both the run of the deploy and the run ofsettle. - The REST fall back of 0003 runs only when an environment holds more than its page, which happens when a hundred deploys went out while one of this run waited on a reviewer. Only then is the live dashboard read, the issue of the run’s event, and the fall back asks for the stacks whose row says deploying or is still ticked (a
resolvewhose body write failed). A run with nothing open costs one request per environment name and writes nothing. settlewrites no row. It runs no tool (0014), so it has no diff, and none of the row forms of 0027 fits a stack whose change is unknown. When it ended at least one record it starts a full scan by dispatching this same workflow on this same ref, the wayresolvedoes for the rescan box. That scan meets a deploying row with no open deployment, previews the stack and writes its row with the failure line (0004, slice 2.1). This amends 0004 and 0009, which listsettleamong the writers that swap rows. Thesettlejob needsactions: write, which the example gives the whole workflow.- It stops at the first status it cannot write, as
resolvestops at the first record, and the job goes red namingdeployments: write. A scan it cannot start turns the job red after the records are ended, namingactions: write. Otherwise the job is green, also when it ended records: the red job of that deploy isapply’s, or the run was cancelled.
Settled while building (slice 2.5)
- The order of one
applyrun: the latest status of the record (one request, so a record that already ended costs exactly that and no tool call, 0019), the record itself,in_progress, config and discovery, the version check, the fresh preview, the hash check, the deploying row, the deploy, the result, the summary, and the row swap. Only then a red job. applydeploys a record only in the run whoseresolvecreated it: therunin the payload must be the run of the job. A record lives as long as its run (0003), so a deploy from another run could be ended under it by a scan. A record of another run, one that is not Sluiceway’s and one whose payload this version cannot read are left alone, with no status.- Every other way out after
in_progressgives the record a result, also an error nobody planned for, so a row never waits forsettlewhenapplyknows how it ended. A moved change ends aserror, everything else that did not deploy asfailure(0003). - The result is written before the dashboard. The record is the truth and the body a view (0004): a body that cannot be written leaves a success a success and turns the job red. A result that cannot be written leaves the body alone, because an in sync row next to an open deployment would be wrong, and
settleends the record. deployment-idis checked for every mode before the mode does anything. GitHub readsrequiredinaction.ymlfor no mode in particular, so the input is declared not required andapplyrequires it itself.