roc-automation provides the pieces every repository needs identically. Reference each one by full commit SHA, and upgrade that reference through a pull request. A shared change never reaches a repository silently.
- uses: lukewilliamboswell/roc-automation/actions/<name>@REVIEWED_FULL_SHA
actions/setup-blueprint
Installs Nix and the blueprint binary that Blueprint.roc names, so that
blueprint run <task> works in the next step. It builds nothing.
- uses: actions/checkout@REVIEWED_FULL_SHA
with:
persist-credentials: false
- uses: lukewilliamboswell/roc-automation/actions/setup-blueprint@REVIEWED_FULL_SHA
- run: blueprint run check
The call takes no inputs. The action reads the roc-blueprint release from the
platform URL in the checked-out Blueprint.roc, and it records the reviewed
SHA-256 of every binary of each release it installs. Blueprint.roc is
therefore the one place a repository names its Blueprint release, and a
workflow cannot disagree with it.
| Input | Meaning |
|---|---|
|
The roc-blueprint release to install. Defaults to the release named by the platform URL in |
|
Reviewed SHA-256 of the binary for x86_64 Linux runners. Overrides the recorded value. |
|
Reviewed SHA-256 of the binary for arm64 Linux runners. Overrides the recorded value. |
|
Reviewed SHA-256 of the binary for Apple Silicon macOS runners. Overrides the recorded value. |
Outputs are blueprint-path, the absolute path of the installed binary,
system, the release system name chosen for the runner, and version, the
release that was installed.
The recorded checksums live in actions/setup-blueprint/releases.json, one
entry for each roc-blueprint release, copied from that release’s
blueprint.sha256 file. A new roc-blueprint release is reviewed once, in the
pull request that adds its entry there, and reaches a repository when that
repository moves its reference to the action. The reference is a full commit
SHA, so the recorded checksums are pinned by the same review as the action’s
code.
A release the referenced revision does not record fails the step, unless the
caller supplies a sha256-<system> input for the runner. Use those inputs to
install a release ahead of the action, such as a release candidate. A system for
which no binary is released also fails.
The action downloads the binary for the runner’s system into a private
temporary file, compares its SHA-256 with the reviewed value, and only then
makes it executable and puts it on PATH. A mismatch fails the step and
installs nothing. It takes no token and needs no cache account.
The blueprint binary states
what the checksum does and does not establish.
Install the nightly compiler
The package compiler is installed by the official
roc-lang/setup-roc action, not by an
action in this repository. Reference it by full commit SHA like any other.
- uses: roc-lang/setup-roc@REVIEWED_FULL_SHA
with:
version: nightly-new-compiler
nightly-tag: ${{ needs.resolve.outputs.nightly-tag }}
It installs the compiler on PATH as roc. With nightly-tag empty it
installs the latest release of roc-lang/nightlies; with a tag it installs that
release.
A workflow with more than one job resolves the latest tag once, in its first
job, and passes it to every job through nightly-tag, so that one run uses one
compiler:
- id: nightly
env:
GH_TOKEN: ${{ github.token }}
run: echo "tag=$(gh api repos/roc-lang/nightlies/releases/latest --jq .tag_name)" >> "$GITHUB_OUTPUT"
Actions that run a Roc script
actions/nightly-report and actions/publish-release are Roc scripts in
roc-automation, scripts/nightly_report.roc and scripts/publish_release.roc,
written as
Write automation as Roc scripts
describes. Each runs from source in the calling job, from the action’s own copy
of roc-automation at the referenced commit. The action:
-
installs Nix, with the install action
actions/setup-blueprintuses, at the same full commit SHA; -
downloads the
blueprintbinary that roc-automation’sBlueprint.rocnames and checks it against its recorded SHA-256, as Theblueprintbinary describes; and -
runs its task,
blueprint run nightly-reportorblueprint run publish-release, in that copy of roc-automation.
roc-automation’s Blueprint.lock at the referenced commit pins what the task
starts: roc-stable, which runs the script, the platform the script is built
on, the gh CLI, which makes every request to GitHub, and coreutils. The full
SHA in the uses: line therefore fixes the script and everything under it.
Nothing comes from the calling repository: its Blueprint.roc is not read, and
the binary is installed in a directory of the action’s own and is not put on
PATH, so a blueprint the job set up earlier is left as it was.
The script is given the action’s inputs and the token in its environment, never
as arguments. gh reads the token from GH_TOKEN and sends requests to the
GitHub host it is configured for, which is GitHub.com unless the job sets
GH_HOST.
These actions need a runner Blueprint supports, which is x86-64 or arm64 Linux
or Apple Silicon macOS, and python3 for the installer, which GitHub’s hosted
runners provide. On a runner with an empty Nix store, installing Nix and
downloading the environment is most of the job’s time: the environment is about
a gigabyte once unpacked, so allow a minute or two. The script itself finishes
in seconds.
actions/nightly-report
Keeps the one tracking issue of a nightly compatibility workflow. It checks out nothing from the calling repository and runs none of its code.
report:
needs: test
if: ${{ always() }}
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: lukewilliamboswell/roc-automation/actions/nightly-report@REVIEWED_FULL_SHA
with:
outcome: ${{ needs.test.outputs.outcome }}
nightly-tag: ${{ needs.test.outputs.nightly }}
release-tag: ${{ needs.test.outputs.release }}
| Input | Default | Meaning |
|---|---|---|
|
Required |
Outcome of the step that ran the examples |
|
Required |
The Roc nightly the examples ran on, named in the issue text |
|
Required |
The release whose examples ran, named in the issue text |
|
|
Exact title of the tracking issue |
|
|
Token used to read and write issues |
Outputs are action, which is opened, commented, closed or none, and
issue-number, the issue that action concerned. issue-number is empty when
action is none.
The tracking issue is the open issue whose title equals title exactly and
whose author is a bot account. The action never comments on, closes or reopens
any other issue. An issue with the same title that a person opened is ignored,
as are a pull request and a closed issue.
outcome |
What the action does |
|---|---|
|
Comments on the tracking issue and closes it. With no tracking issue open it
does nothing, and |
|
Comments on the tracking issue. With no tracking issue open it opens one. |
Anything else |
Fails the step with an error and reads or writes no issue. An empty value,
|
The issue body and every comment name the release and the nightly and link to
the workflow run. Tags are limited to 100 letters, digits and ., _, +,
/, - characters, and a title to 256 characters with no control, format or
private-use characters. A malformed input, or an empty token, fails the step
before any request is made.
The action reads the open issues a page at a time. A listing it cannot read in full, or a record in it that is not an issue record, fails the step: it never opens an issue while it cannot tell whether one exists.
The calling job needs issues: write and no other permission. It has no
checkout step, and it passes the action only the step outcome and the two tags,
never text produced by the examples.
Reporting states what the calling workflow must
provide, and
Actions that run a Roc script
what the action needs of the runner.
actions/build-docs
Renders a repository’s docs/*.adoc sources as an HTML site and a PDF manual
with a shared theme, Roc syntax highlighting and Mermaid diagrams. It can also
place compiler-generated API documentation under site/api.
- id: docs
uses: lukewilliamboswell/roc-automation/actions/build-docs@REVIEWED_FULL_SHA
with:
pdf-filename: my-project.pdf
docs-version: main
api-entrypoint: platform/main.roc
- uses: actions/upload-pages-artifact@REVIEWED_FULL_SHA
with:
path: ${{ steps.docs.outputs.site-directory }}
| Input | Default | Meaning |
|---|---|---|
|
|
Source directory, relative to the repository root |
|
|
The manual’s entrypoint, relative to |
|
|
Generated output, relative to the repository root |
|
|
Filename of the generated PDF |
|
|
Label passed to the sources as the |
|
empty |
Roc package or platform root for the generated API reference |
|
|
Roc executable used when |
|
empty |
A replacement theme directory, relative to the repository root |
Outputs are site-directory and pdf-path, both absolute paths.
The action needs Docker and Python on the runner, which GitHub’s Ubuntu runners
provide. A replacement theme directory must contain docs.css,
docs-theme.yml, mermaid-config.json and fonts/.
Write for the shared renderer
lists what the renderer expects of the sources.
actions/publish-release
Publishes one release from files an earlier job built and tested. It stages them in a draft, reads every asset back and compares it with the tested file, and only then publishes. It checks out nothing from the calling repository and runs none of its code.
publish:
needs: [build, test]
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-24.04
environment: release
permissions:
contents: write
steps:
- uses: actions/download-artifact@REVIEWED_FULL_SHA
with:
name: release-candidate
path: candidate
- uses: lukewilliamboswell/roc-automation/actions/publish-release@REVIEWED_FULL_SHA
with:
version: ${{ needs.build.outputs.release-version }}
prerelease: ${{ needs.build.outputs.is-prerelease }}
notes-file: candidate/release-notes.md
assets: |
candidate/dist/*
candidate/docs/*.pdf
The job names the release environment, holds contents: write and nothing
else, and has no checkout step. Its only inputs are the artifact the build job
uploaded and the test jobs verified. What the assets are, how they are built and
tested, and any attestation of them stay in the repository’s earlier jobs.
| Input | Default | Meaning |
|---|---|---|
|
required |
The release version, such as |
|
required |
Exactly |
|
required |
Path of the rendered release notes, which become the body of the release |
|
required |
Globs of the tested files, one per line, relative to the workspace |
|
|
The tag to create: the version, or |
|
|
Full SHA of the tested commit the tag points at |
|
|
A token with |
Outputs are release-url, the URL of the published release, and digests, the
path of a listing of the published assets in the format sha256sum writes and
sha256sum --check reads.
The tag is the bare version, without a v. A repository that publishes a
second, independently versioned package gives that package’s releases a name
with the tag input, such as kit-0.1.0. The tag must end with the version,
so the two cannot disagree.
The action does the following, in this order, and stops at the first failure.
-
Check the inputs. The version, tag, target and
prereleasevalue must be well formed and agree with each other. The notes file must be a non-empty regular file of UTF-8 text. -
Collect the assets. Every glob must match at least one file. Every match must be a regular, non-empty file and not a symbolic link, and no two may share a filename. Filenames are limited to letters, digits,
.,_and-, because GitHub rewrites other characters and the published name would then differ from the tested one. The SHA-256 of each file is recorded now. The token and the repository name are checked last, so every check that needs no request has passed before the first request is made. -
Require a new version. The target must be a commit of the repository. The tag must not exist, and no release for it may exist, published or draft.
-
Create a draft at the target commit, with the notes file as its body.
-
Upload every asset to the draft.
-
Verify the draft. It must hold exactly the collected filenames. Each asset is downloaded into a new, empty directory and must equal the tested file in size and SHA-256. A download is cut off one byte past the tested size.
-
Publish. A prerelease version is published as a prerelease and is not made the latest release. Any other version is published as the latest release.
-
Confirm. The published release must carry the requested tag and be immutable. The digest listing is written to the job summary.
Nothing is public before the publish step. With immutable releases enabled, publishing a draft is the moment its tag and assets become permanent, which is why every check comes before it. A failure after the draft exists leaves the draft unpublished and the tag uncreated; the error names the draft. Recover an interrupted publication says what to do with it.
The action does not adopt an existing draft, replace an asset, or add to a published release. Once it has created the draft, every request names that draft by its numeric identifier, never by tag. It publishes the notes file as written and does not ask GitHub to generate notes: release notes are authored, as Write the release notes describes.
A glob in assets is a path relative to the workspace. Within one path segment
matches any run of characters and ? any one character, and neither
matches the dot at the start of a name. . segments are ignored. Nothing else
is a pattern: *, [, a backslash, a .. segment and an absolute path are
each an error, so a glob names only paths under the workspace.
Every request is a gh api call. gh adds the token to requests for the
GitHub host and does not repeat it when GitHub redirects an asset download to
the storage host that serves the bytes. A repository without immutable releases
fails the last step, after the release is published;
Releases lists the setting.
Actions that run a Roc script
states what the action needs of the runner.
actions/publish-build-inputs
Publishes native linker inputs built on a same-repository pull request as an immutable, content-addressed release, and commits only the resulting lock to that pull request. The job that holds release credentials never checks out or runs the pull request’s code.
| Input | Meaning |
|---|---|
|
Number of the same-repository pull request that holds the producer source |
|
Filename of the producer workflow to dispatch on that branch |
|
Optional: an existing successful producer run for the current head, used for recovery |
|
Name of the artifact the producer uploads |
|
The only repository path the action may commit |
|
Prefix of the content-derived release tag |
Publish linker inputs from a pull request specifies the producer, the caller workflow and its permissions, recovery, and review.