Skip to content
Go to console
Go to console

A full scan lists deploys made outside the dashboard from the tool's own history

Decision record 0073

Amended by 0076: an outside deploy that ended after a stack’s failed deploy clears that row’s failure line. The trail keeps both lines.

Record 0016 made deploys outside Sluiceway legal and left them undetected, and rejected reading pulumi stack history because all it would improve was the list of commits on a row. Its note of 2026-09-21 names a second benefit the owner asked for: a trail that lists every deploy, not only the dashboard’s own. The research on Pulumi’s update history (branch research/pulumi-update-history) found that a history entry says what went out, when, and from which commit, that it names the GitHub Actions run it ran in, and that it does not say who ran it on a self-managed backend. Build plan slice 5.6 brings it in.

This amends 0016 (outside deploys are now listed, and still legal and never stopped), 0029 and 0062 (the trail holds more than deployment records).

Decision

  • An optional adapter method, deployHistory. It reads the tool’s own history of one stack and hands over its deploys, newest first: deploy or destroy, when it ended, the commit that was checked out and whether the tree held changes in no commit, and the GitHub Actions run it ran in. A deploy is an entry that went out (succeeded), is an update or a destroy, and changed something (a count other than same). A failed run, a refresh, a preview and a deploy that changed nothing are left out, as a failed deploy was left out of the first trail (0029). The shape has no field that can hold a value, a message or a person.
  • Pulumi only. pulumi stack history --json --page-size <n> --stack <name>, from the stack’s directory, with the job environment minus INPUT_* (0013). --json, not --output json, which v3.229.0 does not know. Never --show-secrets, so no passphrase is needed. It takes no stack lock and adds no entry. OpenTofu, Helm and kubectl keep no history of their deploys (OpenTofu’s state serial says that something wrote the state, not what or when), so their adapters leave the method out, and the scan says in its job log which stacks it could not read and why.
  • The schema drops what is not needed, whole. Of an entry only kind, result, endTime, resourceChanges and four environment keys are read: git.head, git.dirty, ci.system and ci.build.id. config holds every plain config value, so it is never parsed (0021). message is anybody’s words, git.author and git.committer name the people behind the commit and not the person who deployed, and their e-mail addresses do not belong on an issue. A commit that is not a whole commit id and a run id that is not digits are left out, not passed on. Stdout never reaches the job log: only stderr is the tool’s words (0022).
  • Full scans only. It is one more backend call per stack, so a scan reads the histories once every stack is previewed, through the same pool, with each stack’s preview time limit. A narrowed scan reads none. A scan after a merge that narrows reads none. A read that fails is a warning on the run, the tool’s words in a log group of their own, and the stack keeps the lines the dashboard had. The scan stays green: the trail only explains.
  • As many entries as the trail lists. --page-size is dashboard.recentlyDeployed. With 0 nothing is read. The page counts every entry, so a stack with many refreshes since its last outside deploy may not reach it. A longer page reads more small objects from the backend for lines the trail would cut anyway.
  • Sluiceway’s own deploys are told apart by the run. A deploy whose run id is the run on a deployment record of the same stack is Sluiceway’s own, and the record already makes its line. Every other deploy is an outside deploy: one from a laptop or a script has no run id, one from another workflow has another run id. The run on the record is the run id of the apply job, which the tool writes into its history because Sluiceway hands it the job environment. Every record of the stack that this version can read counts, whatever its result.
  • The line says when and from which commit, never who. - network:dev · deployed outside the dashboard, from commit 59ff6e7 · 2026-09-21 18:11 UTC, the commit linked to its page in the repo, with uncommitted changes after it when the tree was dirty, destroyed for a destroy, and no commit when the tool recorded none. Under a header it has the green result dot of a deploy that went out. No run link: the run page names the person who started it, and the line does not point at people. The time is the other machine’s clock, shown as every trail time is (0029). It sits in the one list by time and counts toward dashboard.recentlyDeployed.
  • Every writer carries the lines in a marker. Only a full scan can read the history, and resolve, apply and a narrowed scan hold no credentials or no reason to. So each line ends in <!-- sluiceway:outside stack="..." kind="deploy" at="<ISO>" commit="<sha>" dirty="true" -->, every writer reads it back with parseDashboard and draws the line again, and a full scan replaces the lines of every stack whose history it read. A stack the repo no longer has loses its lines. A marker a person broke is not read. Of two lines for one deploy the first stays.

Consequences

  • A full scan costs one call of pulumi stack history per Pulumi stack: about 0.1 s on a file backend (research), more on a bucket, where the tool lists the whole history directory of the stack.
  • An own deploy whose deployment record is no longer on the page of the newest 100 records per environment (0003) would be listed as outside. By then a hundred newer records of its environment exist, and the trail’s length cuts the line in nearly every repo.
  • A workflow that runs its own pulumi up on the same stack in the same run as Sluiceway’s apply has its deploy taken for Sluiceway’s. That is an unusual workflow.
  • The commit link points at this repo. A commit that was never pushed has no page there.
  • Attribution still starts at the last successful deployment record (0026). An outside deploy does not move it, so a pending row may still list commits that are already live, as 0016 says.
  • State surgery (pulumi stack import, pulumi state edits) adds no history entry and stays invisible, as before.
  • A body written before this version has no outside lines. The next full scan adds them.

Rejected

  • Naming who deployed. A self-managed backend does not record it. git.author is the author of the last commit, often not the person who ran the tool. Pulumi Cloud’s requestedBy is only in its REST API, which would be a network call outside the tool (0001, build plan section 1).
  • A run link for a deploy from another workflow. It points at the person who started the run, and Pulumi builds the link with github.com even on a GitHub Enterprise server.
  • Change counts on the line. The dashboard’s own lines have none, and the history counts resources by the tool’s ops, which are not Sluiceway’s ops (0007).
  • Reading the history on every scan, or only for stacks whose lastUpdate moved. The first costs a backend call per stack on every push. The second needs a value stored per stack and a second command, for a saving the research could not measure on a real bucket.
  • Matching by time. One second of precision, a clock Sluiceway does not control, and two deploys in one window cannot be told apart.
  • An adapter method on OpenTofu that reports the state serial. It says that the state changed, not that something was deployed, when, or from which commit.

Settled while building (slice 5.10)

  • The line drops the word commit and takes the short time of the trail (0029): - network:dev · deployed outside the dashboard, from 59ff6e7 · 09-21 18:11. With a stack id of 40 characters that fits on one line at GitHub’s issue width, and with commit it did not. A line with with uncommitted changes after the commit still takes two lines at that length; no shorter words said it as plainly.