Skip to content

Repository files navigation

verzly/toolchain

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/toolchain and verzly/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-signing and ios-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.

How it works

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.

Single repository

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@v1

or use a focused tool action:

- uses: verzly/toolchain/actions/tauri-release@v1
  with:
    args: build --config .github/release/app.tauri-release.toml --platform linux

Unified executable

All 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 --help

Common 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-env

Action-first surface

The 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.

Compatibility commands

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 --help

These commands are compatibility entrypoints. Internally they delegate to the same Rust command logic that powers verzly <tool>.

Cache and generated output

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-run

Get started

Use verzly/toolchain from GitHub Actions for released toolchain usage. Build from source only when developing this repository.

Use in GitHub Actions

Install the latest released verzly binary:

- uses: verzly/toolchain@v1

This adds verzly to PATH. By default, the action also creates compatibility shims for the previous standalone command names.

Run one command

Use the root action when a job only needs one command:

- uses: verzly/toolchain@v1
  with:
    command: verzly repository check

Install once and run multiple commands

Use 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.3

Pin a toolchain version

Use a fixed release version for reproducible release workflows:

- uses: verzly/toolchain@v1
  with:
    version: v1.2.3

Accepted version values:

latest
v1.2.3
1.2.3

Build locally

Build the unified executable from the workspace:

cargo build --release -p verzly
.cache/rust/packages/toolchain/target/release/verzly --help

Run 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.3

Usage

Each tool is available through the unified executable and through a matching GitHub Action. Prefer the verzly <tool> form for new scripts.

CLI overview

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-env

Root action

Use the root action when a workflow wants to install the toolchain or run arbitrary verzly commands.

- uses: verzly/toolchain@v1
  with:
    command: verzly repository check

Inputs:

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.

Tool actions

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 linux

Common 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

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 tui

Manage 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 --yes

GitHub Action:

- uses: verzly/toolchain/actions/repository@v1
  with:
    args: check

Rust quality policy

Rust 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.

Managed configuration placement

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 schema

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 check

If 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 name

datarose.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 = 1

The toolchain repository itself may use the local schema file while developing the schema:

#:schema ./schemas/datarose.toml.schema.json
version = 1

Runtime 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

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 app

Configured floating tags are updated as part of finalization:

verzly github-release finalize \
  --version 1.2.3 \
  --config datarose.toml \
  --release-target app \
  --assets dist/release

The 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 --prune

GitHub Action:

- uses: verzly/toolchain/actions/github-release@v1
  with:
    args: prepare --version 1.2.3 --config datarose.toml --release-target app

github-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

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 verzly

GitHub Action:

- uses: verzly/toolchain/actions/cargo-release@v1
  with:
    args: build --config datarose.toml --release-target verzly --version 1.2.3 --target linux-x64

Use this for toolchain-style Rust binaries. Use tauri-release for Tauri desktop/mobile app artifacts.

tauri-release

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.toml

GitHub Action:

- uses: verzly/toolchain/actions/tauri-release@v1
  with:
    args: build --config .github/release/app.tauri-release.toml --platform linux

Mobile 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

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 doctor

GitHub Action:

- uses: verzly/toolchain/actions/rust-cache@v1
  with:
    args: env

Export 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

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_PATH

Standard 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 android

Action-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

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_ID

Standard 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 ios

Action-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.

Example app release workflow

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/release

Release management

This section describes how verzly/toolchain releases itself. Consuming projects normally only need the usage examples above.

Toolchain release flow

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:

  1. validates that the workflow is running from master,
  2. checks that the target release/tag does not already exist,
  3. prepares a release branch through verzly github-release prepare,
  4. updates workspace crate versions and matching Cargo.lock package entries,
  5. runs formatting, Clippy, and tests,
  6. builds release assets through verzly cargo-release build,
  7. finalizes the release in verzly/toolchain, uploads assets, and updates floating tags through verzly github-release finalize.

Release assets

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 = true

Tokens and permissions

The 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/verzly

Manual maintenance is handled by github-release update-floating-tags:

verzly github-release update-floating-tags \
  --config datarose.toml \
  --release-target verzly \
  --all \
  --prune

Tag 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.

Delete release

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.3

The delete command removes the configured release/tag surface and repairs configured floating tags from the remaining release tags.

Floating tag maintenance

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 \
  --prune

For 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.

Development

Source changes, action surfaces, documentation, and releases all live in this repository.

Repository layout

.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

Quality checks

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 --doc

The 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-id

Implementation boundaries

Keep the boundary between executable, tools, and actions clear:

  • crates/verzly is the unified executable and should mostly dispatch.
  • Each tool crate owns its CLI contract, command logic, tests, and reusable run_from entrypoint.
  • Prefer rstest for case matrices and insta for stable snapshots of diagnostics, plans, and generated configuration.
  • Standalone binaries remain compatibility wrappers.
  • GitHub Actions should call the released verzly binary, not duplicate tool logic.
  • Repository standards should be enforced through repository check, not copied into ad hoc scripts.

Action quality rules

  • 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.sh for installing released assets.
  • Keep action examples copy-pasteable for downstream repositories.
  • Keep tool-specific actions thin; command behavior belongs in Rust.

Contribution rules

Before opening a PR, run:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
cargo test --workspace --doc

Do 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.

Debugging

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.

Repository check failures

Run:

verzly repository check
verzly repository doctor

If 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.

Release failures

Preview the release before preparing it:

verzly github-release plan --config datarose.toml --release-target verzly --version 1.2.3

Check 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.3

Abort a prepared release branch if build or test jobs fail:

verzly github-release abort --config datarose.toml --release-target verzly --version 1.2.3

Signing failures

Check Android signing:

verzly android-signing doctor
verzly android-signing check-env
verzly android-signing check-env --require-fingerprint

Check iOS signing:

verzly ios-signing doctor
verzly ios-signing check-env
verzly ios-signing check-env --skip-apple-team-id

Use optional: "true" in app workflows when mobile signing is not required for every release.

Cache issues

Print the resolved environment:

verzly rust-cache env

Run a command through the cache wrapper:

verzly rust-cache run -- cargo build --workspace

Preview generated cleanup before deleting anything:

verzly rust-cache clean-generated --dry-run

License & Acknowledgments

This 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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages