Branches and Releases
Work collects on dev, and a release pull request moves it to main,
which builds and deploys. Pushes and merges to any other branch run
nothing.
Where work lands
Every merge to main deploys the relay (relay.yml) and, after a
version bump, the lnk release (release.yml, on macOS runners, which
cost the most). So main only moves when a release is ready.
devis where work lands. Branch fromdev, with one branch and one PR per batch of related work, each change its own commit, and the PR againstdev. Don't bump the version in these PRs. Fill in the PR template's Release notes (Breaking, Added or Fixed, one line per user-visible change) and its Semver line, and keep them and the description current as commits are added, because the release PR reads them to pick the version. Say in the description how to try the branch.mainis what's released. It takes release PRs fromdev, and the rare hotfix described below.- Trying unreleased work takes
git fetch && git checkout <branch> && cargo build, or an install oflnkand its plugins from a branch with cargo, as Install lnk says.
Cutting a release
A release is a version bump on dev and a pull request from dev into
main.
- List what
devadds: the PRs merged intodevsince the last release (git log --merges --first-parent origin/main..origin/dev), and their Release notes. - Pick the version from the largest change, against the current
versionin the rootCargo.toml. Before 1.0, Breaking or Added bumps the minor version (0.11.0 -> 0.12.0) and Fixed bumps the patch (0.11.0 -> 0.11.1). From 1.0, Breaking is major, Added minor and Fixed patch. If nothing is user-visible, merge without a bump, and only the relay is rebuilt. - On
dev, commit the bump:versionunder[workspace.package], thencargo buildsoCargo.lockfollows. The release also ships Link Harness, Link's tools and the OpenClaw, Hermes and DeepSeek Harness adapters, whichlnkinstalls only at its own version. So bumpversionin each one'sCargo.toml(link-harness,link-tools's[workspace.package],link-openclaw,link-hermes,link-deepseek) and runcargo buildthere as well, becauserelease.ymlfails if one differs. That commit is the only change the release makes itself. - Open a PR from
devtomaintitledRelease v<version>. Its body is the combined Release notes (Breaking, Added, Fixed), each line with its PR number. - Before merging, run the checks on
dev's head.checks.ymlruns them again after the merge and publishes nothing if they fail. - Merge with a merge commit, never a squash or rebase, or
devandmainstop sharing history. The merge publishesv<version>andrelay-latest.
A hotfix that can't wait for dev branches from main and goes in
with its own PR and a patch bump. Then main is merged back into dev.
What a release publishes
A release publishes lnk with its plugins and the relay, signs both, and
deploys the website.
checks.ymlruns fmt, clippy, tests and shellcheck on release PRs intomainonly. The deploy workflows call it first and publish nothing if it fails.lnk(release.yml) publishes when the workspace version has no release yet. It makesv<version>, the latest release ofwoodpav/link-releases, with one archive per program and platform (lnk-core-<target>andlnk-<plugin>-<target>) for macOS and Linux, each on arm64 and x86_64, with Linux arm64 built on GitHub's arm64 runner. It adds the old all-in-one archive for 0.10 and older, all under one signedSHA256SUMS. The plugins are the core'sPLUGINSinsrc/local/cli/src/main.rs, whichrelease.ymlnames again, andsrc/tests/e2e/tests/release.rschecks the two match. Users getlnkfromcurl -fsSL https://local.link/install.sh | sh, which the relay serves fromsrc/local/cli/install.sh, built into its binary. They get plugins fromlnk upandlnk plugin add, and updates fromlnk upgrade. The relay advertises its own version as the latest client inWelcome, so after a bump connected CLIs printUpdate lnk X is available.- The relay (
relay.yml) publishes on every relevant merge tomain, and needs no version bump. It makes a static x86_64 Linuxlink-relayand the deploy files asrelay-latest, a pre-release and never "latest", here and inwoodpav/link-releases.bootstrap.shand the release key are not in it, because operators run bootstrap from their checkout. Publishing removes any asset the build no longer has. Servers install it within about 5 minutes. - Signing. Both releases sign
SHA256SUMSwith theRELEASE_SIGNING_KEYsecret, using the repository's.github/sign-release.sh. The job runs only frommain, in thereleaseenvironment, which onlymainmay use and a reviewer approves.release.rschecks that every job using a secret does the same. Relay servers,lnk upgrade(link_plugin::release) andinstall.shaccept only the keys insrc/shared/release-signers.install.shandbootstrap.shembed a copy, andsrc/tests/e2e/tests/release.rschecks they match. - The website. The last step of
release.ymlcalls theVERCEL_DEPLOY_HOOKsecret, so the website goes live with the release it describes. Without the secret it warns and skips.
The bare domain's /_link/* and /install.sh belong to the relay. With
LINK_WEBSITE set, which is --website in bootstrap, any other path
there redirects to the website (link-web, on Vercel).
