Building a release
A COLLAB release is one archive, dist/collab-<version>.tar.gz, with a checksum file beside it and a SHA256SUMS inside. tools/build_release.py builds it from the working tree in one run: it checks and tests everything, stages a copy, versions and precompresses the site in the copy, packs a reproducible archive, and verifies that archive from a clean extraction. This page goes through the steps, for maintainers who cut a release and for reviewers who check one. Operators install the result as Installing COLLAB on a server describes.
Before you build
- Python 3.11.4 or later (the deploy-bundle export reads
foundry.tomlwithtomllib, new in 3.11, and the verification extracts the archive withtarfile'sdatafilter, new in 3.11.4), Foundry, Pillow, and pycryptodome or OpenSSL 3.2 or later. No Node.js. - Merge any CSS parts first:
python3 tools/merge_css.py. The build stops whileweb/assets/css/app.cssstill importsparts/*.css. - The contracts carry their own version string:
DAOFactory.VERSIONand the recordscript/Deploy.s.solwrites,"COLLAB DAO v2.2.0". A server's admin service accepts only records of its release's major and minor version.
python3 tools/merge_css.py
python3 tools/build_release.py --version 2.2.0
FORGE_FLAGS="--offline --use /path/to/solc-0.8.28" python3 tools/build_release.py --version 2.2.0 # without network| Setting | Effect |
|---|---|
--version X.Y.Z | the release's version (default 2.2.0): the archive's name, BUILD.json, the bundle's release |
--skip-tests | skips forge test only; every other check runs |
FORGE, FORGE_FLAGS | the forge binary, and flags added to every forge call |
FORGE_BATCH | how many script and test files each forge build compiles (default 4) |
SOURCE_DATE_EPOCH | the timestamp of every archive entry (default 1793577600, 2026-11-02 00:00 UTC) |
In the working tree
- Refresh the generated blocks.
tools/shell_html.py --writewrites the portal shell and the preload block of every entry.tools/build_docs.py --writewrites the documentation pages. The docs renderer's unit tests run (python3 -m unittest discover -s tools/tests -p "test_*.py"), thentools/build_docs.py --check --strictfails on any problem or placeholder page or a sitemap that lists other docs pages, andtools/docs_figures.py --checkon a missing figure. - Check the site.
tools/make_assets.py --check(the icons are renders offavicon.svg),tools/lint_web.py(0 errors),tools/prerender.py --static-pages --check(the /pools and /locks copies). - Test the server kit.
python3 -m unittest discover -s server/tests -p "test_*.py": the admin service, the site settings, the records and config.js. - Compile from scratch.
forge build --force --skip test --skip scriptbuilds the contracts; then the files ofscript/andtest/are builtFORGE_BATCHat a time. One via-IR solc run over everything can need more than 5 GB of memory; in batches it stays below 2 GB, with the same artifacts. The build always starts from scratch, because Foundry's cache does not notice every settings change and a stale artifact must never reach the deploy bundle. - Run the suite.
forge test(331 tests in 92 suites for 2.2.0). - Export.
tools/export_deploy_artifacts.py --release <version>writesweb/assets/deploy/collab-protocol.json, the bundle the admin page's Deploy tab sends;tools/export_abis.pywritesabi/andweb/js/core/abi.js;tools/abi_summary.pywritesdocs/CONTRACT_API.md.
Steps 1 and 6 rewrite generated files in the working tree itself; when they change anything, commit those files with the release.
In the stage
- Copy the tree to
dist/collab-<version>/:README.md,LICENSE,THIRD_PARTY_NOTICES.md,foundry.toml,remappings.txt,.gitignore, andcontracts/,test/,script/,externals/,abi/,docs/,tools/,web/,deployments/,deploy/andserver/. The libraries are trimmed to what COLLAB compiles and their licences: OpenZeppelin'scontracts/folders, Solady'ssrc/utils/SSTORE2.sol, forge-std'ssrc/. Left out:__pycache__,config.local.js, the local chain's files indeployments/(local.json,demo.json,anvil.pid,anvil.log,integrations.local.json),web/assets/css/parts, and the per-deploymentweb/dao/andweb/og/. - Version the assets.
tools/version_assets.py web --version <version> --no-gzipappends one release-wide?v=<12 hex>to every module import (static and dynamic), every/js/string, the stylesheets, fonts, the icon sprite and CSSurl()s, and writesweb/BUILD.json(the build stamp). It fails when a reference is left unversioned or a module would be reached under two URLs, which would load it twice. - Write the static pages again from the versioned tree:
tools/prerender.py --static-pages(/pools and /locks) andtools/build_docs.py --write(the docs pages, which now carry the release's?v=). - Precompress. Every text file of 1 KB or more (
.js,.css,.html,.svg,.json,.webmanifest,.txt,.xml,.map,.md), exceptconfig.js, gets a.gzsibling for nginx'sgzip_static, at level 9 and without a timestamp, so the same file always compresses to the same bytes. - Checksum.
SHA256SUMSlists every file of the stage as<sha256> <path>.
The archive
- Pack. A PAX tar of the stage under the folder
collab-<version>/: entries sorted, every modification time set toSOURCE_DATE_EPOCH, owner and group 0 with no names, mode 0755 for folders and executables and 0644 for the rest. It is gzipped at level 9 with no file name or time in the header, asdist/collab-<version>.tar.gz, and its SHA-256 goes todist/collab-<version>.tar.gz.sha256. The build prints the size, the number of files and the digest.
Nothing in the archive depends on when or where it was built beyond the tools' versions: the same tree with the same Foundry, solc, Python and zlib gives the same archive.
Verification from a clean extraction
- The archive is extracted into a temporary folder, and every line of
SHA256SUMSmust verify. - In the extracted tree:
tools/lint_web.py,tools/version_assets.py web --check(every asset URL carries the version),tools/shell_html.py --check,tools/prerender.py --static-pages --checkandtools/build_docs.py --check --strict. - The contracts are compiled from the archive alone (
forge build --force --skip test --skip script), andtools/export_deploy_artifacts.py --release <version> --checkconfirms the deploy bundle matches them byte for byte, so the archive builds the exact code the admin page deploys.
The build ends with "release verified: checksums match, lint passes, the docs pages are current, contracts compile from the archive and the deploy page's bundle matches them". Any failing step stops the build with that step's error.
Checking a release yourself
sha256sum -c collab-2.2.0.tar.gz.sha256
tar xzf collab-2.2.0.tar.gz && cd collab-2.2.0
sha256sum -c --quiet SHA256SUMS
python3 tools/lint_web.py && python3 tools/version_assets.py web --check && python3 tools/build_docs.py --check --strict
forge build --force --skip test --skip script && python3 tools/export_deploy_artifacts.py --release 2.2.0 --checkTo run the whole suite on the release, build the test files in batches and run forge test in the extracted folder; the end-to-end suites of a release run against its built tree (Testing).
Related
- Testing: every check the build runs.
- Data formats:
BUILD.json, the deploy bundle and config.js. - The web app: the docs build.
- Upgrades, rollbacks and backups: installing a new release on a server.
- Release notes: what changed in 2.2.0.