verzly/toolchain is the single source repository and release surface for Verzly release automation, Rust executable packaging, Tauri artifact builds, repository standards, cache routing, and mobile signing preflight checks.
It provides a compact toolchain surface with built-in support for:
- one executable:
verzly - one repository-hosted action surface:
verzly/toolchainandverzly/toolchain/actions/* - GitHub release orchestration through
github-release - Rust executable packaging through
cargo-release - Tauri desktop and mobile artifact builds through
tauri-release - project-local build/cache routing through
rust-cache - Android and iOS signing preflight checks through
android-signingandios-signing - repository structure and workflow checks through
repository
Use it from CI as a GitHub Action, or locally as a Rust workspace executable. Consuming repositories should depend on verzly/toolchain directly instead of wiring multiple verzly/<tool> distribution repositories together.
Read on to learn how the toolchain is structured and how each tool is intended to be used. Or jump straight to Get started for GitHub Actions usage, or to Release management if you are maintaining verzly/toolchain itself.
verzly/toolchain keeps the source, GitHub Actions, release workflow, and published binary in one repository. The public contract is intentionally small: install verzly, then call the tool you need as a subcommand.
The toolchain no longer uses a .verzly/distributions/* model. There are no separate public distribution repositories to sync, release, or configure in downstream projects.
The repository itself is the release surface:
verzly/toolchain
├── action.yml
├── actions/*/action.yml
├── crates/verzly
├── crates/*
└── .github/workflows/release.yml
Downstream projects such as Tauri apps should consume this repository directly:
- uses: verzly/toolchain@v1or use a focused tool action:
- uses: verzly/toolchain/actions/tauri-release@v1
with:
args: build --config .github/release/app.tauri-release.toml --platform linuxAll public tools are available through one executable:
verzly github-release --help
verzly cargo-release --help
verzly tauri-release --help
verzly rust-cache --help
verzly android-signing --help
verzly ios-signing --help
verzly repository --helpCommon aliases are available for shorter local usage:
verzly repo check
verzly cache env
verzly tauri build --platform linux
verzly android check-env
verzly ios check-envThe root action installs verzly and can optionally run a command. Tool-specific actions install the same binary and run one tool with a cleaner workflow surface.
action.yml
actions/github-release/action.yml
actions/cargo-release/action.yml
actions/tauri-release/action.yml
actions/rust-cache/action.yml
actions/android-signing/action.yml
actions/ios-signing/action.yml
actions/repository/action.yml
The actions download the matching verzly release asset, add it to PATH, and optionally create compatibility shims such as github-release, tauri-release, and ios-signing.
Standalone command names remain available for migration compatibility:
github-release --help
cargo-release --help
tauri-release --help
rust-cache --help
android-signing --help
ios-signing --help
repository --helpThese commands are compatibility entrypoints. Internally they delegate to the same Rust command logic that powers verzly <tool>.
The toolchain is designed to keep build output outside the source tree. In this repository, Cargo output is routed to:
.cache/rust/packages/toolchain/target
Downstream projects can use rust-cache to route Cargo, Gradle, package manager caches, and configured generated output under .cache.
verzly rust-cache env
verzly rust-cache run -- cargo build --workspace
verzly rust-cache clean-generated --dry-runUse verzly/toolchain from GitHub Actions for released toolchain usage. Build from source only when developing this repository.
Install the latest released verzly binary:
- uses: verzly/toolchain@v1This adds verzly to PATH. By default, the action also creates compatibility shims for the previous standalone command names.
Use the root action when a job only needs one command:
- uses: verzly/toolchain@v1
with:
command: verzly repository checkUse the root action once, then call verzly normally:
- uses: verzly/toolchain@v1
- name: Export cache environment
run: verzly rust-cache env >> "$GITHUB_ENV"
- name: Check repository configuration
run: verzly repository check
- name: Preview release
run: verzly github-release plan --config datarose.toml --release-target app --version 1.2.3Use a fixed release version for reproducible release workflows:
- uses: verzly/toolchain@v1
with:
version: v1.2.3Accepted version values:
latest
v1.2.3
1.2.3
Build the unified executable from the workspace:
cargo build --release -p verzly
.cache/rust/packages/toolchain/target/release/verzly --helpRun from source without installing:
cargo run -p verzly -- repository check
cargo run -p verzly -- github-release plan --config datarose.toml --release-target verzly --version 1.2.3Each tool is available through the unified executable and through a matching GitHub Action. Prefer the verzly <tool> form for new scripts.
verzly repository check
verzly github-release prepare --config datarose.toml --release-target app --version 1.2.3
verzly cargo-release build --config datarose.toml --release-target verzly --version 1.2.3 --target linux-x64
verzly tauri-release build --config .github/release/app.tauri-release.toml --platform linux
verzly rust-cache env
verzly android-signing check-env
verzly ios-signing check-envUse the root action when a workflow wants to install the toolchain or run arbitrary verzly commands.
- uses: verzly/toolchain@v1
with:
command: verzly repository checkInputs:
| Input | Default | Description |
|---|---|---|
version |
latest |
Verzly release version to install. |
repository |
verzly/toolchain |
Repository that publishes the verzly release assets. |
github-token |
github.token |
Token used to download release assets. |
command |
empty | Optional command to run after installing Verzly. |
working-directory |
. |
Directory used when running the optional command. |
install-dir |
runner temp | Directory where the executable and shims are installed. |
create-shims |
true |
Create compatibility command names next to verzly. |
Outputs:
| Output | Description |
|---|---|
path |
Absolute path to the installed verzly executable. |
install-dir |
Directory added to PATH. |
version |
Installed Verzly version. |
target |
Resolved release asset target for the current runner. |
Use tool actions when the workflow should read naturally and only needs one tool.
- uses: verzly/toolchain/actions/repository@v1
with:
args: check
- uses: verzly/toolchain/actions/github-release@v1
with:
args: plan --config datarose.toml --release-target app --version 1.2.3
- uses: verzly/toolchain/actions/tauri-release@v1
with:
args: build --config .github/release/app.tauri-release.toml --platform linuxCommon inputs for tool actions:
| Input | Default | Description |
|---|---|---|
version |
latest |
Verzly release version to install. |
repository |
verzly/toolchain |
Repository that publishes the release assets. |
github-token |
github.token |
Token used to download assets and exposed as GH_TOKEN for GitHub-aware commands. |
install-only |
false |
Install Verzly without running the selected tool. |
args |
--help |
Arguments passed after verzly <tool>. |
working-directory |
. |
Directory where the command runs. |
install-dir |
runner temp | Directory where the executable and shims are installed. |
create-shims |
true |
Create compatibility command names next to verzly. |
Signing actions have additional inputs and outputs documented in android-signing and ios-signing.
repository manages repository standards: datarose.toml, hk.pkl, Cargo.toml lint policy, formatter/linter configuration, quality workflows, release target metadata, project inventory, and expected action surfaces.
verzly repository init
verzly repository init --language rust --language js --js-runner pnpm
verzly repository update
verzly repository plan
verzly repository projects
verzly repository check
verzly repository doctor
verzly repository tuiManage release targets:
verzly repository release list
verzly repository release show app
verzly repository release set \
--name app \
--path . \
--repository owner/app \
--strategy same-repo \
--workflow managed \
--source-kind tauri-app
verzly repository release remove app --yesGitHub Action:
- uses: verzly/toolchain/actions/repository@v1
with:
args: checkRust quality defaults are repository-managed instead of hand-copied per project. repository init and repository update can maintain the shared Rust baseline in the configured quality workspace:
Cargo.toml workspace lint policy
config/.clippy.toml Clippy threshold/config values
config/rustfmt.toml Rust formatting defaults
config/.oxfmtrc.json Oxlint/Oxfmt formatter defaults
config/.oxlintrc.json Oxlint linter defaults
config/vitest.config.ts Vitest defaults
config/rector.php Rector defaults
config/phpunit.xml.dist Pest/PHPUnit defaults
hk.pkl quality command orchestration
test.yml CI quality workflow
The default Cargo lint policy is intentionally small and portable across many repositories. It forbids unsafe code, denies unused must_use results, denies the normal Clippy lint set, and blocks obvious development leftovers such as dbg!, todo!, and unimplemented!. Stricter project rules can be added through datarose.toml and then applied with verzly repository update.
[quality.rust]
manage_cargo_lints = true
manage_clippy_config = true
lint_profile = "strict"
[quality.rust.lints.rust]
unsafe_code = "forbid"
unused_must_use = "deny"
[quality.rust.lints.clippy]
all = "deny"
dbg_macro = "deny"
todo = "deny"
unimplemented = "deny"For Cargo workspaces, repository writes the defaults into [workspace.lints.*] and opts member crates into the shared policy with [lints] workspace = true. For single-crate projects, it writes [lints.*] directly to the project Cargo.toml. Existing project-specific lint levels are preserved unless --force is used.
.clippy.toml is kept focused on Clippy configuration values such as complexity thresholds. The actual lint policy belongs in Cargo lint tables so it can be shared, reviewed, and extended consistently across repositories.
Repository-managed tool configs can live either in the workspace root or in a dedicated config directory. The default shared model is config-directory first, so thousands of repositories can carry the same baseline without scattering formatter and linter files across the root.
[quality.configs]
directory = "config"
placement = "directory"
update_mode = "preserve"Set placement = "root" and run verzly repository update to move supported configs back to the workspace root. Set directory to another relative folder, such as tools/config or .config/verzly, to relocate them again. During update, repository moves existing known config files to the configured location when the destination does not already exist, so project-local edits are preserved.
The generated hk.pkl commands pass explicit config paths where the underlying tool supports them. Rustfmt receives --config-path, Clippy receives CLIPPY_CONF_DIR, Oxfmt/Oxlint/Vitest receive --config, Rector receives --config, and Pest receives --configuration. This lets the files move without requiring package scripts or project-specific command glue.
update_mode = "preserve" keeps existing project config files in place. New projects receive Verzly defaults, while existing projects can edit, delete, or add local config settings. Use --force only when intentionally reapplying the shared baseline.
datarose.toml stays TOML. There is no datarose.json runtime configuration.
verzly validates an existing datarose.toml before dispatching any subcommand. This validation is built into the executable, runs offline, and does not depend on VS Code, Taplo, .taplo.toml, network access, or the public schema URL.
The preflight validation runs for commands such as:
verzly github-release prepare --config datarose.toml --release-target app --version 1.2.3
verzly cargo-release build --config datarose.toml --release-target app --target linux-x64
verzly tauri-release build --config datarose.toml --release-target app --platform android
verzly rust-cache env --config datarose.toml
verzly repository checkIf no datarose.toml exists, verzly allows the command to run. This keeps the executable usable outside Verzly-managed repositories. If a datarose.toml exists, it must pass the embedded schema validator before the selected tool runs.
The validator catches TOML syntax errors, unknown sections, unknown keys, wrong value types, unsupported enum values, invalid arrays, and required release fields that are missing. This prevents typos from being silently ignored in workflow or release configuration.
Examples of errors the validator catches:
[quality]
langauges = ["rust"] # typo: should be languages
[release]
manage_workflowz = true # typo: should be manage_workflows
[[release.targets]]
nam = "app" # typo: should be namedatarose.toml may include a leading schema directive for humans and optional editor tooling, but this directive is not required by runtime validation:
#:schema https://raw.githubusercontent.com/verzly/toolchain/v0.4.0/schemas/datarose.toml.schema.json
version = 1The toolchain repository itself may use the local schema file while developing the schema:
#:schema ./schemas/datarose.toml.schema.json
version = 1Runtime validation is implemented in Rust and parses the actual TOML document. It does not fetch the schema URL and does not require the directive to exist. The public schemas/datarose.toml.schema.json file is a mirrored public contract for review and optional editor integrations. When a supported config key changes, update the embedded Rust validator and the public schema together.
Use repository first in downstream projects. It should describe the project layout, release targets, quality rules, cache conventions, and generated workflow expectations before release tooling is wired in.
github-release prepares, finalizes, publishes, deletes, and aborts GitHub releases. It can update configured version files, create release branches, merge prepared source, tag releases, upload assets, and update floating tags.
verzly github-release init
verzly github-release plan --version 1.2.3 --config datarose.toml --release-target app
verzly github-release prepare --version 1.2.3 --config datarose.toml --release-target app
verzly github-release finalize --version 1.2.3 --config datarose.toml --release-target app --assets dist/release
verzly github-release publish --version 1.2.3 --config datarose.toml --release-target app --assets dist/release
verzly github-release delete --version 1.2.3 --config datarose.toml --release-target app
verzly github-release abort --version 1.2.3 --config datarose.toml --release-target appConfigured floating tags are updated as part of finalization:
verzly github-release finalize \
--version 1.2.3 \
--config datarose.toml \
--release-target app \
--assets dist/releaseThe explicit --update-floating-tags, --update-latest-tag, and --update-next-tag flags are escape hatches for one-off overrides. Normal workflows should let datarose.toml decide which tag families are managed.
Manage floating tags directly when needed:
verzly github-release update-floating-tags --config datarose.toml --release-target app --all --pruneGitHub Action:
- uses: verzly/toolchain/actions/github-release@v1
with:
args: prepare --version 1.2.3 --config datarose.toml --release-target appgithub-release expects GH_TOKEN or GITHUB_TOKEN in CI for GitHub CLI operations. The toolchain release workflow uses github.token; no separate distribution repository token is required.
cargo-release builds Rust executable release assets for configured targets and writes checksums/manifests next to the generated artifacts.
verzly cargo-release init
verzly cargo-release plan --config datarose.toml --release-target verzly
verzly cargo-release build --config datarose.toml --release-target verzly --version 1.2.3
verzly cargo-release build --config datarose.toml --release-target verzly --version 1.2.3 --target linux-x64
verzly cargo-release build --config datarose.toml --release-target verzly --version 1.2.3 --target windows-x64
verzly cargo-release clean --config datarose.toml --release-target verzly
verzly cargo-release doctor --config datarose.toml --release-target verzlyGitHub Action:
- uses: verzly/toolchain/actions/cargo-release@v1
with:
args: build --config datarose.toml --release-target verzly --version 1.2.3 --target linux-x64Use this for toolchain-style Rust binaries. Use tauri-release for Tauri desktop/mobile app artifacts.
tauri-release builds Tauri desktop and mobile release artifacts from declarative platform configuration.
verzly tauri-release init
verzly tauri-release plan --config .github/release/app.tauri-release.toml
verzly tauri-release build --config .github/release/app.tauri-release.toml
verzly tauri-release build --config .github/release/app.tauri-release.toml --platform linux
verzly tauri-release build --config .github/release/app.tauri-release.toml --platform macos
verzly tauri-release build --config .github/release/app.tauri-release.toml --platform windows
verzly tauri-release build --config .github/release/app.tauri-release.toml --platform android
verzly tauri-release build --config .github/release/app.tauri-release.toml --platform ios
verzly tauri-release clean --config .github/release/app.tauri-release.toml
verzly tauri-release doctor --config .github/release/app.tauri-release.tomlGitHub Action:
- uses: verzly/toolchain/actions/tauri-release@v1
with:
args: build --config .github/release/app.tauri-release.toml --platform linuxMobile release workflows should gate Android and iOS builds behind signing-action outputs so missing signing secrets skip mobile artifacts instead of failing the whole release.
rust-cache routes generated and build output into a project-local .cache tree. It covers Cargo target output, optional Cargo home routing, Gradle cache/build routing, package-manager caches, and configured generated paths.
verzly rust-cache init
verzly rust-cache env
verzly rust-cache run -- cargo test --workspace
verzly rust-cache run -- pnpm install
verzly rust-cache clean
verzly rust-cache clean-generated
verzly rust-cache clean-generated --dry-run
verzly rust-cache doctorGitHub Action:
- uses: verzly/toolchain/actions/rust-cache@v1
with:
args: envExport cache variables in workflows:
- name: Export Verzly cache environment
run: verzly rust-cache env >> "$GITHUB_ENV"Example config shape:
[rust_cache.cache]
dir = ".cache"
package = "app"
redirect_cargo_home = false
redirect_gradle = true
[rust_cache.cargo]
target_dir = "rust/packages/{package}/target"
[rust_cache.env]
GRADLE_USER_HOME = "android/gradle"
PNPM_STORE_PATH = "js/pnpm-store"android-signing helps create, encode, inspect, and validate Android release signing material. CI can use it to detect whether Android signing is configured and skip Android artifacts when secrets are intentionally missing.
verzly android-signing doctor
verzly android-signing generate --output android-release.jks --alias release-key --generate-passwords
verzly android-signing base64 android-release.jks
verzly android-signing fingerprint android-release.jks --alias release-key
verzly android-signing verify-fingerprint android-release.jks --alias release-key --expected-sha256 AA:BB:CC
verzly android-signing print-secrets android-release.jks --alias release-key
verzly android-signing write-github-env android-release.jks --alias release-key
verzly android-signing check-env
verzly android-signing check-env --require-fingerprint --require ANDROID_KEYSTORE_PATHStandard CI variables:
ANDROID_KEYSTORE_BASE64
ANDROID_KEYSTORE_PASSWORD
ANDROID_KEY_ALIAS
ANDROID_KEY_PASSWORD
Optional fingerprint variable:
ANDROID_SIGNING_CERT_SHA256
GitHub Action:
- id: android-signing
uses: verzly/toolchain/actions/android-signing@v1
with:
optional: "true"
run-doctor: "true"
check-env: "true"
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
- name: Build Android artifact
if: ${{ steps.android-signing.outputs.signing-ready == 'true' }}
run: verzly tauri-release build --config .github/release/app.tauri-release.toml --platform androidAction-specific inputs:
| Input | Default | Description |
|---|---|---|
optional |
true |
Missing signing variables produce outputs and notices instead of failing. |
run-doctor |
true |
Run verzly android-signing doctor before checking secrets. |
check-env |
true |
Validate required signing environment variables. |
require-fingerprint |
false |
Also require ANDROID_SIGNING_CERT_SHA256. |
extra-required-env |
empty | Additional env names to require; supports newline, comma, or space separated values. |
args |
empty | Optional additional android-signing command to run after preflight. |
Action-specific outputs:
| Output | Description |
|---|---|
signing-ready |
true, false, or unknown. |
missing-secrets |
Comma-separated missing variable names. |
ios-signing validates iOS signing environments and helps encode existing Apple signing files for GitHub Actions secrets. It supports optional preflight behavior so projects can skip iOS artifacts when signing is not configured.
verzly ios-signing doctor
verzly ios-signing base64 ios-release.p12
verzly ios-signing print-secrets --certificate ios-release.p12 --provisioning-profile app.mobileprovision
verzly ios-signing write-github-env --certificate ios-release.p12 --provisioning-profile app.mobileprovision
verzly ios-signing check-env
verzly ios-signing check-env --skip-apple-team-id
verzly ios-signing check-env --require APPLE_IDStandard CI variables:
IOS_SIGNING_CERTIFICATE_BASE64
IOS_SIGNING_CERTIFICATE_PASSWORD
IOS_SIGNING_PROVISIONING_PROFILE_BASE64
IOS_SIGNING_KEYCHAIN_PASSWORD
APPLE_TEAM_ID
GitHub Action:
- id: ios-signing
uses: verzly/toolchain/actions/ios-signing@v1
with:
optional: "true"
run-doctor: "true"
check-env: "true"
env:
IOS_SIGNING_CERTIFICATE_BASE64: ${{ secrets.IOS_SIGNING_CERTIFICATE_BASE64 }}
IOS_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.IOS_SIGNING_CERTIFICATE_PASSWORD }}
IOS_SIGNING_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_SIGNING_PROVISIONING_PROFILE_BASE64 }}
IOS_SIGNING_KEYCHAIN_PASSWORD: ${{ secrets.IOS_SIGNING_KEYCHAIN_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Build iOS artifact
if: ${{ steps.ios-signing.outputs.signing-ready == 'true' }}
run: verzly tauri-release build --config .github/release/app.tauri-release.toml --platform iosAction-specific inputs:
| Input | Default | Description |
|---|---|---|
optional |
true |
Missing signing variables produce outputs and notices instead of failing. |
run-doctor |
true |
Run verzly ios-signing doctor before checking secrets. |
check-env |
true |
Validate required signing environment variables. |
skip-apple-team-id |
false |
Do not require APPLE_TEAM_ID. |
extra-required-env |
empty | Additional env names to require; supports newline, comma, or space separated values. |
args |
empty | Optional additional ios-signing command to run after preflight. |
command |
empty | Deprecated alias for args. |
Action-specific outputs:
| Output | Description |
|---|---|
signing-ready |
true, false, or unknown. |
missing-secrets |
Comma-separated missing variable names. |
This is the intended downstream shape for a Tauri app that publishes desktop artifacts and only publishes mobile artifacts when signing is configured.
name: Release
on:
workflow_dispatch:
inputs:
version:
description: Version to release
required: true
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: verzly/toolchain@v1
- name: Export cache environment
run: verzly rust-cache env >> "$GITHUB_ENV"
- name: Check repository configuration
run: verzly repository check
- name: Prepare release
run: verzly github-release prepare --version "${{ inputs.version }}" --config datarose.toml --release-target app
- id: android-signing
uses: verzly/toolchain/actions/android-signing@v1
with:
optional: "true"
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
- id: ios-signing
uses: verzly/toolchain/actions/ios-signing@v1
with:
optional: "true"
env:
IOS_SIGNING_CERTIFICATE_BASE64: ${{ secrets.IOS_SIGNING_CERTIFICATE_BASE64 }}
IOS_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.IOS_SIGNING_CERTIFICATE_PASSWORD }}
IOS_SIGNING_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_SIGNING_PROVISIONING_PROFILE_BASE64 }}
IOS_SIGNING_KEYCHAIN_PASSWORD: ${{ secrets.IOS_SIGNING_KEYCHAIN_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- name: Build desktop artifacts
run: verzly tauri-release build --config .github/release/app.tauri-release.toml --platform linux
- name: Build Android artifacts
if: ${{ steps.android-signing.outputs.signing-ready == 'true' }}
run: verzly tauri-release build --config .github/release/app.tauri-release.toml --platform android
- name: Build iOS artifacts
if: ${{ steps.ios-signing.outputs.signing-ready == 'true' }}
run: verzly tauri-release build --config .github/release/app.tauri-release.toml --platform ios
- name: Finalize release
run: verzly github-release finalize --version "${{ inputs.version }}" --config datarose.toml --release-target app --assets dist/releaseThis section describes how verzly/toolchain releases itself. Consuming projects normally only need the usage examples above.
The visible workflow surface is intentionally small:
.github/workflows/release.yml Publishes the Verzly toolchain release.
.github/workflows/delete-release.yml Deletes a published release and its tags after confirmation.
.github/workflows/update-floating-tags.yml Reconciles moving tags after manual tag edits.
.github/workflows/test.yml Runs pull request quality checks.
The release workflow:
- validates that the workflow is running from
master, - checks that the target release/tag does not already exist,
- prepares a release branch through
verzly github-release prepare, - updates workspace crate versions and matching
Cargo.lockpackage entries, - runs formatting, Clippy, and tests,
- builds release assets through
verzly cargo-release build, - finalizes the release in
verzly/toolchain, uploads assets, and updates floating tags throughverzly github-release finalize.
The toolchain publishes one executable, verzly, for these targets:
linux-x64
macos-x64
macos-arm64
windows-x64
The configured release target is verzly in datarose.toml:
[[release.targets]]
name = "verzly"
repository = "verzly/toolchain"
cargo_binary = "verzly"
cargo_package = "verzly"
cargo_targets = ["linux-x64", "macos-x64", "macos-arm64", "windows-x64"]
floating_tags = true
latest_tag = true
next_tag = trueThe main release path writes only to verzly/toolchain through github.token.
It must not require:
DISTRIBUTION_REPO_TOKEN
separate distribution repository tokens
separate PATs for the normal release path
Floating tags are handled by github-release finalize during normal releases according to the release target settings in datarose.toml:
verzly github-release finalize \
--config datarose.toml \
--release-target verzly \
--version 1.2.3 \
--assets dist/verzlyManual maintenance is handled by github-release update-floating-tags:
verzly github-release update-floating-tags \
--config datarose.toml \
--release-target verzly \
--all \
--pruneTag maintenance follows the configured release target. If next_tag = false, the next tag is not created, updated, deleted, or pruned by finalize, delete, or maintenance runs. The same rule applies to latest_tag = false and floating_tags = false. To manage a disabled tag family, enable it in datarose.toml first.
Use the Delete Release workflow when a release must be removed after confirmation. The underlying command is:
verzly github-release delete \
--config datarose.toml \
--release-target verzly \
--version 1.2.3The delete command removes the configured release/tag surface and repairs configured floating tags from the remaining release tags.
github-release owns moving tag reconciliation. Normal releases update floating tags from finalize, and Delete Release repairs them after deleting a version.
Use Update Floating Tags when tags were changed outside the normal release workflow, for example after manually deleting v1.2.3 from GitHub or pushing a historical tag. The workflow runs the same command locally:
verzly github-release update-floating-tags \
--config datarose.toml \
--release-target verzly \
--all \
--pruneFor a single newly added tag, use either the version or full tag form:
verzly github-release update-floating-tags --config datarose.toml --release-target verzly --version 1.2.3
verzly github-release update-floating-tags --config datarose.toml --release-target verzly --tag v1.2.3--all --prune recalculates vX, vX.Y, latest, and next from the release tags that still exist, and removes stale moving tags that no longer have a matching release line.
Source changes, action surfaces, documentation, and releases all live in this repository.
.github/workflows/ Quality and release workflows
actions/ Public GitHub Actions stored in this repo
actions/_shared/ Shared composite-action installer scripts
crates/verzly/ Unified executable entrypoint
crates/*/ Modular tool implementations
action.yml Root setup/run action
Cargo.toml Rust workspace
datarose.toml Release, quality, cache, and build configuration
hk.pkl Git hook and quality gate configuration
mise.toml Local tool/task configuration
schemas/ Public online schema for TOML-backed config
Run workspace checks from the repository root:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
cargo test --workspace --docThe lint policy is defined through repository-managed Cargo lint tables and .clippy.toml; the CI command keeps -D warnings so warnings remain pull-request failures.
cargo-nextest is the default unit/integration test runner. It runs the existing Rust #[test] tests with better CI output and scheduling, while doctests stay on Cargo because nextest does not run doctests. CI installs cargo-nextest with taiki-e/install-action@v2 and pins the installed tool as cargo-nextest@0.9.137, avoiding the moving @nextest alias while keeping CI fast. Use rstest for parameterized/table-driven tests and insta for snapshot assertions when exact diagnostics or generated output should be reviewed.
Run selected commands from source:
cargo run -p verzly -- repository check
cargo run -p verzly -- github-release plan --config datarose.toml --release-target verzly --version 1.2.3
cargo run -p verzly -- cargo-release build --config datarose.toml --release-target verzly --version 1.2.3 --target linux-x64 --dry-run
cargo run -p verzly -- ios-signing check-env --skip-apple-team-idKeep the boundary between executable, tools, and actions clear:
crates/verzlyis the unified executable and should mostly dispatch.- Each tool crate owns its CLI contract, command logic, tests, and reusable
run_fromentrypoint. - Prefer
rstestfor case matrices andinstafor stable snapshots of diagnostics, plans, and generated configuration. - Standalone binaries remain compatibility wrappers.
- GitHub Actions should call the released
verzlybinary, not duplicate tool logic. - Repository standards should be enforced through
repository check, not copied into ad hoc scripts.
- Do not print secret values.
- Prefer explicit inputs and documented outputs.
- Signing checks should support optional mode so release workflows can skip unavailable mobile artifacts.
- Use
actions/_shared/install-verzly.shfor installing released assets. - Keep action examples copy-pasteable for downstream repositories.
- Keep tool-specific actions thin; command behavior belongs in Rust.
Before opening a PR, run:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
cargo test --workspace --docDo not reintroduce:
.verzly/distributions
release workflows that publish to separate verzly/<tool> repositories
distribution sync workflows
DISTRIBUTION_REPO_TOKEN requirements for the normal release path
Public usage must go through verzly/toolchain, either with the root action, subpath actions under actions/, or the verzly release assets.
Start with the command closest to the failing surface: repository check for repository structure, github-release plan for release configuration, cargo-release doctor for Rust binary assets, tauri-release doctor for Tauri builds, and signing check-env commands for mobile secrets.
Run:
verzly repository check
verzly repository doctorIf a workflow was intentionally removed, update the repository expectations in the repository crate and datarose.toml together. The check should reflect the current public surface, not old migration leftovers.
Preview the release before preparing it:
verzly github-release plan --config datarose.toml --release-target verzly --version 1.2.3Check whether the release/tag already exists:
gh release view v1.2.3 --repo verzly/toolchain
git ls-remote --tags https://github.com/verzly/toolchain.git refs/tags/v1.2.3Abort a prepared release branch if build or test jobs fail:
verzly github-release abort --config datarose.toml --release-target verzly --version 1.2.3Check Android signing:
verzly android-signing doctor
verzly android-signing check-env
verzly android-signing check-env --require-fingerprintCheck iOS signing:
verzly ios-signing doctor
verzly ios-signing check-env
verzly ios-signing check-env --skip-apple-team-idUse optional: "true" in app workflows when mobile signing is not required for every release.
Print the resolved environment:
verzly rust-cache envRun a command through the cache wrapper:
verzly rust-cache run -- cargo build --workspacePreview generated cleanup before deleting anything:
verzly rust-cache clean-generated --dry-runThis project would not exist without the Rust, Tauri, GitHub Actions, and open-source release tooling ecosystems.
It is open source and released under the GNU Affero General Public License v3.0 (AGPL-3.0). We are grateful to the maintainers and contributors of Rust, Tauri, Cargo, GitHub CLI, and GitHub Actions for the tooling foundations that make this project possible.
Copyright (C) 2020–present Zoltán Rózsa & Verzly