Build and release
A change reaches users through a pull request, a label and a merge. This page explains each automated step and how to reproduce it locally, so a red check is something you can fix rather than something you wait on.
Layout
Section titled “Layout”| Path | Contents |
|---|---|
Source/arc |
The cratis_arc Hex package: the only thing that is published |
Samples |
The plug, Phoenix and React samples |
ContractTests |
The proxy contract gate: generated TypeScript, compiled and run against the real client |
Documentation |
These pages, and the excerpt verifier |
decisions |
Accepted design decisions |
Reproduce the checks locally
Section titled “Reproduce the checks locally”You need Elixir 1.18 or later, and Node.js 22 for the contract gate and the React sample. CI uses Elixir 1.19.5 on Erlang/OTP 28.5.
cd Source/arcmix deps.getmix format --check-formattedMIX_ENV=test mix compile --warnings-as-errorsmix test
cd ../..Samples/run-tests.sh # the samplesContractTests/run.sh # the proxy contractpython3 Documentation/verify-snippets.pyThe pull request pipeline
Section titled “The pull request pipeline”Every pull request runs Build and Verify Pull Request Policy.
| Workflow | Job | What it proves |
|---|---|---|
build.yml |
Build and test | Formatting, no compiler warnings, and the specs |
build.yml |
Documentation | Every # From: excerpt in these pages still matches its source file |
build.yml |
Samples | The plug and Phoenix samples format, compile and test, and the React sample type-checks and builds against the proxies the Phoenix sample generated |
build.yml |
Proxy contract | Proxies are deterministic, compile under strict TypeScript against the published @cratis/arc packages, and the real client works against an Arc server |
verify-pull-request-policy.yml |
policy | The description is a valid release note and exactly one release label is set |
Verification that runs only after a merge cannot inform the merge, so everything Publish runs also runs here first.
The proxy contract
Section titled “The proxy contract”ContractTests/run.sh is the guard on the most coupled part of the project, the generated TypeScript:
- Generate proxies for the test fixtures twice and compare hashes: output must be deterministic.
npm ciandtsc --noEmitwithstrictandverbatimModuleSyntaxagainst pinned versions of@cratis/arc,@cratis/arc.reactand@cratis/fundamentals.- Start an Arc server on Bandit and run the real client against it with
node --test.
The npm versions are pinned in ContractTests/TypeScript/package.json so the gate means something. Dependabot proposes bumps as no-release pull requests; a bump that breaks the gate shows you the incompatibility before users meet it.
Documentation excerpts
Section titled “Documentation excerpts”A fenced block whose first line is # From: path/to/file is an excerpt of a real file. Documentation/verify-snippets.py fails when an excerpt no longer appears, line for line, in its source. Change the sample, then the page.
Labels and versions
Section titled “Labels and versions”Exactly one label decides what a merge releases:
| Label | Use for |
|---|---|
major |
A breaking change to a public module, option or generated-proxy shape |
minor |
A new capability that breaks nothing |
patch |
A bug fix or an internal change with identical behavior |
no-release |
Documentation, samples, tests, CI, and other changes nothing published depends on |
A pull request that touches nothing a user can observe is no-release: an unnecessary release burns a version number and buries the ones that matter. Group small related changes into one pull request, because the pull request is the release boundary.
The pull request description is the release note
Section titled “The pull request description is the release note”The description is published verbatim as the GitHub release. Follow .github/pull_request_template.md: an optional short summary, then only the Added, Changed, Fixed, Removed, Security and Deprecated sections that have bullets, each bullet short and about what a user compiles against or observes. End a bullet that fully delivers an issue with (#123). Put test plans, verification and review notes in a pull request comment, not the description.
Publishing
Section titled “Publishing”publish.yml runs on every push to main that changes Source/**, and can be started by hand with an explicit version and release notes.
- release uses
cratis/release-actionto find the merged pull request, read its label, choose the version, and create the GitHub release from the description. - test compiles and runs the specs once more.
- publish runs when a release was cut. It copies the README and LICENSE into the package, writes the version into
Source/arc/VERSION, compiles, and runsmix hex.publish --yeswith theHEX_API_KEYsecret. - verify-published fails the run when a merge published nothing for a reason that means something went wrong (no label, or an error), so a missing release cannot be mistaken for a success. A commit pushed straight to
mainor ano-releasemerge publishes nothing and is fine.
Releases are serialized: a new merge queues behind a release in flight instead of cancelling it.
VERSION in the repository is a placeholder. The real version exists only at publish time, from the release label.
Keeping dependencies current
Section titled “Keeping dependencies current”- Update Packages runs nightly. It runs
mix deps.update --allin the package and each Elixir sample and opens ano-releasepull request, becausemix.lockis not part of the published package. - Dependabot updates the pinned npm packages and the workflow actions, also as
no-release.
Documentation site
Section titled “Documentation site”When Documentation/** changes on main, documentation.yml asks the Cratis documentation site to rebuild. A merged page is live only after that build; confirm it before saying so.
When a check is red
Section titled “When a check is red”- Formatting: run
mix format. If a declaration macro such asfieldis rewritten with parentheses, checkimport_deps: [:cratis_arc]in.formatter.exs. - Warnings: the build compiles with
--warnings-as-errors; fix the warning, do not suppress it. - Proxy contract, step 1: something nondeterministic entered the generator output, for example an unordered map. Sort it.
- Proxy contract, step 2: the generated TypeScript no longer compiles. Read the first error; it usually names a template line in
Arc.ProxyGenerator.Render. - Policy: read the failure; it names the rule and the line.