Project

Releasing

Cut an lla release: changelog notes, the Prepare Release workflow, the checks that run, and how to resume a failed release.

↓ on this page

A release ships the CLI, the four plugin crates, the bundled plugins and the themes under one version and one Git tag. This page is for maintainers. Everything runs from GitHub Actions, and the workflows live in .github/workflows.

Cut a release

  1. Add your notes under ## [Unreleased] in CHANGELOG.md, in Keep a Changelog form.
  2. Run the Prepare Release workflow with version set, such as 0.6.7. The optional changelog input is a fallback for when CHANGELOG.md has no ## [Unreleased] section.
  3. Review and merge the pull request it opens, chore: prepare release v0.6.7, from the branch prepare-release-v0.6.7.

Merging is the trigger. release.yml runs when a pull request labeled release is merged, and it creates the v0.6.7 tag itself from the workspace version.

Prepare locally

The workflow's script also runs on its own:

RELEASE_VERSION=0.6.7 .github/scripts/prepare_release.sh

Set RELEASE_CHANGELOG to supply notes when there is no ## [Unreleased] section. The script fails if it has no notes, or if CHANGELOG.md already has a section for that version. It updates:

  • the workspace version in the root Cargo.toml
  • internal dependency versions in lla, utils, sdk, macros and the root manifest
  • Cargo.toml and plugin.toml in every directory under plugins/
  • CHANGELOG.md, moving the unreleased notes to the new version

It then runs validate_release_versions and validate_changelog_section. The workflow refreshes Cargo.lock with cargo metadata, and the script doesn't, so do that yourself when you run it by hand.

Check before merging

These are the commands CI runs on every pull request:

cargo fmt --all -- --check
cargo clippy -p lla --all-targets -- -D warnings
cargo clippy -p lla --no-default-features --all-targets -- -D warnings
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace
cargo test -p lla --features wasm-plugins
cargo check --manifest-path sdk/tests/fixtures/minimal_native/Cargo.toml
cargo check --manifest-path sdk/tests/fixtures/custom_batch/Cargo.toml
cargo check --manifest-path sdk/tests/fixtures/wasm_component/Cargo.toml
cargo build --manifest-path sdk/tests/fixtures/wasm_component/Cargo.toml --target wasm32-wasip2

To verify the plugin bundle for your own platform:

./scripts/build_plugins.sh --target "$(rustc -vV | sed -n 's/^host: //p')"
./scripts/verify_plugins_v3.sh dist/plugins-<os>-<arch>

build_plugins.sh writes dist/plugins-<os>-<arch>/ with .tar.gz and .zip archives. verify_plugins_v3.sh needs rustup and Python 3 with tomllib. It compares the plugin count and manifests, builds the WASM fixture, builds lla with --features wasm-plugins, and runs lla plugin doctor, a file_hash action and a WASM listing.

To check versions and notes without building:

bash -c 'source .github/scripts/release_helpers.sh; validate_release_versions 0.6.7; validate_changelog_section 0.6.7'

What the release workflow does

release.yml runs these jobs in dependency order:

JobWhat it does
validateChecks the tag is SemVer and on main, the versions agree, the changelog section exists, and crates.io is in a consistent state.
qualityRuns formatting, clippy and tests.
build_coreBuilds the CLI for every target with --features wasm-plugins.
build_netbsdBuilds lla-netbsd-amd64 natively inside NetBSD, without Wasmtime.
build_pluginsBuilds and verifies the plugin archives.
test_muslSmoke tests the static Linux binaries in Alpine.
build_packagesBuilds .deb, .rpm, .apk and .pkg.tar.zst packages.
package_themesBuilds themes.zip.
collect_assetsGathers artifacts, checks the expected list, and writes SHA256SUMS.
publish_cratesPublishes to crates.io.
publish_releaseCreates a draft GitHub release, uploads and verifies assets, then publishes it.

Crates publish in this order, because each depends on the ones before it:

  1. lla_plugin_interface
  2. lla_plugin_sdk_macros
  3. lla_plugin_sdk
  4. lla_plugin_utils
  5. lla

Each step runs cargo publish --dry-run, publishes, and waits for the version to appear on crates.io, up to 30 tries 10 seconds apart. Versions already published are skipped.

Release assets

KindFiles
Linux binarieslla-linux-amd64, lla-linux-amd64-musl, lla-linux-arm64, lla-linux-arm64-musl, lla-linux-i686
macOS binarieslla-macos-amd64, lla-macos-arm64
NetBSD binarylla-netbsd-amd64
Windows binarieslla-windows-amd64.exe, lla-windows-arm64.exe
Plugin bundlesplugins-linux-amd64, plugins-linux-arm64, plugins-linux-i686, plugins-macos-amd64 and plugins-macos-arm64, each as .tar.gz and .zip
Windows plugin bundlesplugins-windows-amd64.zip, plugins-windows-arm64.zip
Linux packages.deb, .rpm, .apk and .pkg.tar.zst for amd64, arm64 and i386
Otherthemes.zip, SHA256SUMS

write_expected_assets in release_helpers.sh is the source of this list, and check_expected_files fails the release if anything is missing.

Recover from a failure

Rerun Release through workflow_dispatch with release_tag set to the existing tag, such as v0.6.7. Crates already on crates.io are skipped, and assets already uploaded stay in place. A push of a v*.*.* tag also starts the workflow.

To replace only the plugin archives of a published release, run rebuild-release-plugins.yml with release_tag. It rebuilds and verifies the archives, replaces them on the release, and regenerates SHA256SUMS and the release notes.

The workflows need GITHUB_TOKEN and CRATES_IO_TOKEN.

Helper functions

.github/scripts/release_helpers.sh holds the shared shell functions. It sets -euo pipefail, so source it in a bash -c subshell to run one by hand.

FunctionPurpose
workspace_versionPrints the workspace version from the root Cargo.toml.
validate_semver_tagChecks a tag has the form vX.Y.Z.
validate_tag_on_mainChecks the tag's commit is on main.
validate_changelog_sectionChecks CHANGELOG.md has a section for the version.
validate_release_versionsChecks all manifests agree on the version.
crate_version_publishedChecks whether a crate version is on crates.io.
validate_crates_io_stateRejects an inconsistent partial publication.
write_expected_assetsWrites the expected asset list.
check_expected_filesCompares files on disk with that list.
generate_checksumsWrites SHA256SUMS.
create_release_notesBuilds release notes from the changelog and checksums.
ensure_release_existsCreates a draft release when there is none.
asset_exists, upload_asset_if_missingUpload assets idempotently.
publish_releasePublishes the draft.
dispatch_next_stageTriggers the next workflow stage.