This manual is end-to-end guidance for managing Roc packages, platforms and the infrastructure that supports them: the tooling environment, the repository layout, continuous integration, releases, documentation, and the routine upkeep in between.

Keeping a Roc project in good shape means juggling several things that move at different speeds. The compiler is still changing, and breaking changes are routine. A bug can sit in the compiler, in a platform, in a dependency, or in the project itself. Releases rarely line up: a package may need a platform release that has not happened, or a fix may be merged long before users can download it. Each repository that solves these problems alone solves them slightly differently, and each difference is something a maintainer has to remember.

A common baseline is the answer this manual offers. It records the practices that have worked across these repositories and the reasons behind them, so that a lesson learnt in one place is applied everywhere. The practices are opinionated, and they are not finished: when experience shows a better way, the baseline changes here first and the repositories follow.

One decision shapes most of the rest. The package or platform always meets the newest compiler, because that is what its users will install. The repository’s own tooling stays on a compiler the maintainer chose and moves only when the maintainer moves it, so that scripts, checks and release steps keep working on the day a new compiler breaks something.

The model in one page

Concern Practice

Package compiler

The latest published Roc nightly. The package, platform and examples carry no pin.

Tooling compiler

roc-stable: one release named in Blueprint.roc and repeated in each script’s roc header field, changed by a reviewed pull request.

Tools and toolchains

Declared in Blueprint.roc, pinned by Blueprint.lock, identical for contributors and CI.

Automation

Roc scripts under scripts/, run by roc-stable.

Repository examples

Depend on the working tree by relative path.

Pull requests

Scripts pass on roc-stable; the working tree passes on the latest nightly; a platform’s committed host bindings match what that nightly generates.

A release

One immutable release holding the bundle, an examples archive pinned to that bundle, a documentation archive, and the manual where there is one. Every asset is tested as the exact bytes that are published.

Nightly

Runs the latest release’s own examples archive on the latest nightly. A failure means the package needs a patch release.

Upkeep

Blueprint, the lock, the tooling compiler, the scripts and a platform’s host bindings are each changed by hand, in a pull request.

Three compilers, three jobs

A repository involves three Roc compilers. Each has one job, and they are never interchangeable.

Compiler Selected by Used for

Blueprint’s own; it has no command name

The blueprint binary

Evaluating Blueprint.roc

roc-stable

The release tag named in Blueprint.roc

Running the repository’s automation scripts

roc, the latest nightly

Nothing: it is whatever roc-lang/nightlies published most recently

Checking, testing, bundling and running the package, the platform and their examples

A maintainer chooses the second and observes the third. The first is hidden: blueprint fetches it when needed, as described in Get blueprint.

Blueprint has a compiler of its own because Blueprint is itself written in Roc. It has to evaluate Blueprint.roc before any environment exists, so it cannot take its compiler from the environment that file describes: that would be a bootstrap problem. Each blueprint release therefore fixes the one compiler it was built with.

Three compilers is more than anyone wants, and the arrangement is accepted as temporary. It follows from Roc having only nightly builds. Once Roc 0.1 is released, Blueprint and most repository tooling are expected to settle on that release, which leaves two compilers to think about: the released one that tooling runs on, and the nightly that packages are tested against.

roc-stable exists so that repository tooling keeps working when a nightly breaks something. The package deliberately floats, because the question a maintainer needs answered is whether the package works with the compiler users will install today. "Stable" means only that the repository does not move the tag without review. There is no shared registry of stable versions, and repositories need not agree.

"Latest nightly" means the most recent release published by roc-lang/nightlies. This model treats that as the last good build and does not check upstream CI separately.

CI installs that compiler as roc with the official roc-lang/setup-roc action, which is also how it is named on a contributor’s machine. The Blueprint environment provides roc-stable and no roc, so the two names never collide.

What each workflow answers

Diagram
Figure 1. The three workflows and the question each one answers

ci.yml tests the working tree. release.yml tests and publishes artifacts. nightly.yml tests what users already have. Each can fail without the others being wrong: a new nightly can break ci.yml on an untouched pull request, and nightly.yml can fail while the default branch is green because the fix has not been released. The three workflows describes each in full.

A pure Roc package

Most repositories are the simplest kind, and the model is at its smallest for them. A repository is a pure Roc package when all of these hold:

  • it publishes a package and has no host or platform code;

  • it fetches no native build inputs;

  • none of its code behaves differently from one target to another.

Such a package is expected to work wherever Roc does. A difference between targets is a compiler defect to report upstream, not something the package’s own CI is there to find. It therefore tests on one runner.

Concern A pure Roc package

Runners

One. ci.yml, the test job of release.yml and nightly.yml each run on ubuntu-latest only.

Required status checks

The job names themselves. With no matrix there is no aggregate job.

Examples

Run on a published platform, such as basic-cli, and are tested on the one runner.

Chapters that do not apply

Maintain a platform and Publish linker inputs from a pull request: host bindings, native build inputs, rebuild selection and actions/publish-build-inputs.

Everything else applies unchanged: the two compilers, the Blueprint environment, the Roc scripts, the three workflows, and what a release contains.

The cost is stated here so that it is a decision and not an accident: a compiler defect that appears on one target only is not detected by this package’s CI.

A package leaves this profile as soon as it gains behaviour that can differ by target, for example when it starts a process, interprets file paths, or depends on the width of a pointer. It then adds a runner for each target it claims, and takes on the matrix and the aggregate job described in Continuous integration.

What is shared and what a repository owns

Anything two repositories need identically belongs in roc-automation, referenced by full commit SHA. A repository keeps only what knows its own package.

Lives in roc-automation Lives in the repository

actions/setup-blueprint: install Nix and the verified blueprint binary

Blueprint.roc and Blueprint.lock

actions/nightly-report: keep the nightly tracking issue

The test job of nightly.yml, its matrix and its schedule

actions/build-docs: render an AsciiDoc manual as a site and PDF

docs/*.adoc

actions/publish-release: stage a draft release, verify every asset, then publish

What a release contains, and the jobs that build and test it

actions/publish-build-inputs: publish linker inputs from a pull request

The producer workflow and its build recipe

The check-settings task: compare a repository’s GitHub settings with this manual

The settings themselves, applied by a maintainer

This manual

Thin caller workflows, scripts/, tests and expected output

Shared actions is the reference for each action.

What the model does not do

  • It does not make package builds reproducible. The package compiler floats by design; only the tooling environment is pinned.

  • It does not verify upstream compiler quality beyond using a published nightly.

  • It does not support holding a package on an older compiler line. There are no compiler-compatibility branches: a release works with the compilers named in its notes, and a patch release follows when a newer compiler breaks it.

  • It does not describe application repositories that publish no package. They can use the environment and script conventions, and have no release or nightly workflow.