Skip to content

About

A Rust CLI tool and library for building and managing multiple versions of WordPress plugins from any git reference (branch, tag, commit or PR).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

199 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

APVM — Automation Plugin Version Manager

CI

A Rust CLI tool and library for building and managing multiple versions of WordPress plugins from any git reference (branch, tag, commit, PR, or GitHub Release).

Built for developers and QA engineers who need to quickly build plugins from specific PRs, switch between versions, and automate version management.

Available commands

Command Description
build Build a plugin from a git reference (PR, branch, tag, commit, release)
list List all available plugins
info Show detailed information about a plugin
cache Inspect and maintain the artifact cache
config View or change configuration settings
skill Install or remove the Claude Code skill for apvm
update Update apvm to the latest version
uninstall Uninstall apvm from this system

Run apvm --help for full usage or apvm <command> --help for command-specific options.

For extended CLI documentation, see the CLI crate README.

Features

  • Build from any git ref — branch, tag, commit SHA, or PR number
  • Download from GitHub Releases — skip the build entirely and download pre-built assets from a GitHub Release (e.g., release:5.6.8). Currently available for BackWPup
  • Special keyword refs — use tag:latest-stable, tag:latest, release:latest-stable, release:latest (and their previous-* variants) to dynamically resolve the most recent tags or releases without knowing the exact version
  • Automatic ref detection — v1.0.0 resolves to a tag, develop to a branch, 123 to PR #123, and version-like inputs (e.g., 5.6.8) are checked against GitHub Releases first (when the plugin supports releases)
  • Multi-variant builds — e.g., BackWPup produces free, pro-de, and pro-en variants. You can choose which ones to build (free and pro-en are the defaults for BackWPup)
  • Persistent artifact cache — builds and release downloads are cached (SQLite-indexed, on by default): rebuilding the same commit at the same version is served from the cache, and a partial request reuses cached variants and builds only the rest. Per-artifact provenance (built/cache/downloaded), a --no-cache override, a --warm-cache mode that primes the cache without producing output, and an apvm cache maintenance command
  • Version handling — required or optional per plugin (BackWPup requires a version at build time; WP Rocket and Imagify auto-detect it from source, and when you pass --ver the requested version is rewritten into the plugin's source so the built artifact actually carries it)
  • Claude Code skill — apvm skill install installs the bundled Claude Code skill (apvm-cli) into the current project (./.claude/skills/) or globally with -g (~/.claude/skills/), so Claude knows the full CLI surface. The skill is embedded in the binary — no network needed, always version-matched
  • Self-update — apvm update downloads and replaces the binary with the latest release, verifying SHA-256 checksums (and refreshes the installed global Claude Code skill, when present)
  • Automatic update notice — every other command runs a non-blocking, background version check (throttled to once per hour) and, when a newer release exists, prints a concise notice suggesting apvm update. It never self-updates, never affects the exit code, and can be disabled with APVM_NO_UPDATE_CHECK
  • Self-uninstall — apvm uninstall cleanly removes the binary and its directory (with -y/--yes to skip confirmation)
  • GitHub token auto-resolution — automatically finds tokens from config, GITHUB_TOKEN, GH_TOKEN, gh auth token, or the gh CLI config file

Supported Plugins

Plugin Variants Version Releases Repository
BackWPup free, pro-de, pro-en Required Yes Private
WP Rocket (single) Optional No Public
Imagify (single) Optional No¹ Public

¹ Imagify publishes GitHub Releases (git tags), but they carry no downloadable build assets — distribution goes to WordPress.org. Build a specific released version from its tag instead, e.g. apvm build imagify tag:v2.3.0.

Requirements

  • Rust ≥ 1.96.1 (2024 edition)
  • Git installed and in PATH
  • GitHub authentication for private repositories (optional for public). The tool auto-detects tokens from multiple sources (config file, environment variables, gh CLI)
  • Plugin-specific build tools (npm, composer, gulp, rsync, zip, etc.) as needed (try apvm info [plugin-name] to learn more about a specific plugin)

Installation

Quick Install (recommended)

Install the latest pre-built binary with a single command — no Rust toolchain required.

If the tool was already installed, executing this again will attempt to update to latest stable version available.

macOS / Linux:

curl -fsSL https://raw.githubusercontent.com/wp-media/automation-plugin-version-manager/develop/install.sh | sh

Windows (PowerShell):

irm https://raw.githubusercontent.com/wp-media/automation-plugin-version-manager/develop/install.ps1 | iex

The installer will:

  • Detect your OS and architecture
  • Download the correct binary from the latest GitHub release
  • Verify its SHA-256 checksum
  • Install it to ~/.apvm/bin/ and add it to your PATH

To override the install directory, set APVM_INSTALL before running:

APVM_INSTALL=/opt/apvm curl -fsSL https://raw.githubusercontent.com/wp-media/automation-plugin-version-manager/develop/install.sh | sh

Install from Source

If you have the Rust toolchain installed, you can build and install from source via cargo install:

cargo install --path crates/cli

This compiles in release mode and places the apvm binary in ~/.cargo/bin/, which should already be in your PATH.

Updating to new version (Easy way)

Just execute the same script for quick installation.

Updating from source (after code changes)

After pulling changes or switching branches, reinstall with --force to overwrite the existing binary:

cargo install --path crates/cli --force

The --force flag is required when reinstalling a package at the same version number.

Development Build

For local development (unoptimized, faster compile):

cargo build

Binary at target/debug/apvm.

Node.js / N-API Bindings (apvm-napi)

This repository includes a Node.js package powered by napi-rs, exposed as apvm-napi.

import { Apvm } from 'apvm-napi';

const apvm = await Apvm.create();
const output = await apvm.buildFromBranch('wp-rocket', 'develop', '/tmp/apvm-output');
console.log(output.result.artifacts.map((a) => a.filename));

The cache is maintainable from Node too, at parity with apvm cache:

const cache = apvm.cache(); // or ApvmCache.open({ cacheDir }) — no GitHub client needed
await cache.clean({ olderThan: '30d' });
const issues = await cache.verify(); // [] = healthy; rejections carry err.code ('CacheCorrupted' → cache.repair())

For extended documentation about the Node.js bindings, see the NAPI crate README.

CLI Usage

Build a Plugin

# Build from a branch
apvm build backwpup develop -v 5.1.0

# Build from a tag
apvm build backwpup v5.1.0 -v 5.1.0

# Build from a PR number
apvm build backwpup 123 -v 5.1.0

# Build from a commit SHA
apvm build backwpup abc1234 -v 5.1.0

# Explicit ref prefixes
apvm build backwpup tag:v5.1.0 -v 5.1.0
apvm build backwpup branch:develop -v 5.1.0
apvm build backwpup commit:abc1234 -v 5.1.0
apvm build backwpup pr:123 -v 5.1.0

# Download pre-built assets from a GitHub Release (no build required)
apvm build backwpup release:5.6.8

# Special keyword refs — latest/previous tags and releases
apvm build backwpup tag:latest-stable -v 5.1.0
apvm build backwpup tag:latest -v 5.1.0
apvm build backwpup release:latest-stable
apvm build backwpup release:latest

# Build specific variants only
apvm build backwpup 123 -v 5.1.0 --variants free,pro-en

# Specify output directory (default: current directory)
apvm build backwpup 123 -v 5.1.0 ./dist

# Verbose output (shows commands and full output)
apvm --verbose build backwpup 123 -v 5.1.0

For WP Rocket and Imagify the version lives in the plugin's source, so -v is optional: omit it and the version is auto-detected (the artifact is named automatically, e.g. imagify-<version>.zip). Pass -v to override it — the requested version is rewritten into the plugin's main PHP file (both the Version: header and the runtime version constant) before packaging, so the built artifact actually carries it:

# Imagify — public repo, version in source, single artifact
apvm build imagify develop            # build from develop, version auto-detected
apvm build imagify tag:v2.3.0         # build a specific released version from its tag
apvm build imagify pr:123             # build from a pull request
apvm build imagify develop ./dist     # choose an output directory
apvm build imagify develop -v 2.9.9   # override: artifact + source report 2.9.9

When a version is auto-detected (no -v), or when a cached build of the same commit is reused, no rewrite happens. A cache hit requires the commit and the version to match: a cached build of the same commit at a different version is never served in its place — apvm rebuilds at the requested version instead.

Ref auto-detection rules:

Input Resolved as
develop Branch
v1.0.0 Tag (if exists), else branch
abc1234 Commit SHA (7–40 hex)
123 PR #123
#123 PR #123 (# stripped)
5.6.8 GitHub Release (if the plugin has releases), else tag/branch
release:5.6.8 GitHub Release (explicit)

Special keyword refs:

Keyword Resolves to
tag:latest-stable Latest tag excluding -alpha, -beta, -rc suffixes
tag:previous-stable Previous stable tag
tag:latest Very latest tag (including prereleases)
tag:previous-latest Tag right before the latest
release:latest-stable Latest stable GitHub Release (non-prerelease, non-draft)
release:previous-stable Previous stable release
release:latest Very latest non-draft release (including prereleases)
release:previous-latest Previous non-draft release

Tags are sorted by creation date (most recent first). Releases are fetched from the GitHub Releases API. Drafts are always excluded from release keywords.

Warm the Cache

--warm-cache runs the same pipeline as a build — resolve the ref, reuse whatever is already cached, and build or download only what is missing — then stores everything into the cache. The one difference is that it produces no output: nothing is written to an output directory. Use it to prime the cache so a later build of the same reference is an instant hit.

# Prime the cache for a branch (no output directory is written)
apvm build backwpup develop -v 5.1.0 --warm-cache

# Warm specific variants
apvm build backwpup 123 -v 5.1.0 --variants free,pro-en --warm-cache

# Warm a release's pre-built assets into the cache
apvm build backwpup release:5.6.8 --warm-cache

The summary distinguishes what was already cached (reused) from what had to be built or downloaded to warm it — all of which are cached afterwards. --warm-cache cannot be combined with --no-cache (warming is a cache operation — this is a hard error). An output directory is a no-op with --warm-cache rather than an error: passing one is accepted and simply ignored, with a note printed to explain why.

List Plugins

apvm list

Plugin Details

apvm info backwpup
apvm info wp-rocket

Shows repository, version requirement, available variants, and required build tools.

Configuration

# Show all settings
apvm config

# Get a specific value
apvm config get token

# Set GitHub token (required for private repos and PR builds)
apvm config set token ghp_xxxxxxxxxxxx

# Set custom cache directory
apvm config set cache-dir /path/to/cache

# Toggle the artifact cache (default: true)
apvm config set cache false

# Remove a value (revert to default)
apvm config unset token

# Show config file path
apvm config path

Environment Variables

  • APVM_CACHE_DIR — Overrides the artifact cache directory for this run. Highest precedence: wins over the config file's cache-dir and the default ~/.apvm/cache. Honored by both the build commands and apvm cache.
  • GITHUB_TOKEN / GH_TOKEN — GitHub token used when none is set in config (part of token auto-resolution).
  • RUST_LOG — Enables logging output (debug, trace, …), written to stderr.
  • APVM_NO_UPDATE_CHECK — Set to any non-empty value to disable the automatic background update check and its notice (see Automatic update notice).

APVM_CACHE_DIR is handy for pointing a run at a throwaway cache so it doesn't read from or warm your real ~/.apvm/cache — for example in CI or when testing:

# Build against an ephemeral cache; your real cache is left untouched
APVM_CACHE_DIR="$(mktemp -d)" apvm build imagify develop ./out

It is honored by both the build commands and apvm cache (so maintenance targets the same directory the build used).

Cache Maintenance

# Usage totals and per-project breakdown
apvm cache info

# Remove cached entries (preview with --dry-run; scope by kind/project/age)
apvm cache clean --dry-run
apvm cache clean --older-than 30d --project backwpup

# Verify integrity, remove what verify found, or recover a corrupt database
apvm cache verify --checksum
apvm cache gc --checksum
apvm cache repair

# Remove everything
apvm cache clear -y

See the CLI crate README for the full cache command reference.

Install the Claude Code Skill

# Into the current project (./.claude/skills/apvm-cli/)
apvm skill install

# Globally, for all projects (~/.claude/skills/apvm-cli/)
apvm skill install -g

# Remove it again (same -g/--global scoping; only the apvm-cli skill is removed)
apvm skill uninstall
apvm skill uninstall -g

Installs the bundled apvm-cli Claude Code skill so Claude knows the full CLI surface. The skill files are embedded in the binary at compile time, so the command works offline and the installed skill always matches the binary version. Existing installations are replaced; uninstall removes only the apvm-cli skill directory, never .claude/ or other skills. See the CLI crate README for details.

Update

apvm update

Downloads the latest release from GitHub, verifies the SHA-256 checksum, and atomically replaces the running binary. If the global Claude Code skill (~/.claude/skills/apvm-cli/) is installed, it is refreshed to match the new version (project-local copies: re-run apvm skill install per project). Supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).

Automatic update notice

Every command other than update and uninstall performs a non-blocking, background check for a newer release and — when one exists — prints a concise, colored notice to stderr as the last thing on screen, suggesting apvm update. Key properties:

  • Throttled — the network check runs at most once per hour, tracked in ~/.apvm/update-check.json. Repeated invocations within the hour reuse the cached result, so nearly every run does no network work and stays instant.
  • Report-only — it never downloads or replaces anything; upgrading is always an explicit apvm update.
  • Safe — the check runs concurrently with your command and is bounded by a short timeout, so a slow or unreachable GitHub never stalls the CLI, and it never changes the exit code (only apvm update itself can fail).
  • Quiet by default where it should be — suppressed when stderr is not a TTY (pipes, CI), rendered without color when NO_COLOR is set (to any non-empty value), and disabled entirely by APVM_NO_UPDATE_CHECK=1.

Running apvm update also refreshes this state, so the notice stays in sync and does not immediately re-report.

Uninstall

# Interactive (asks for confirmation)
apvm uninstall

# Skip confirmation
apvm uninstall -y

Removes the apvm binary and its bin/ directory, plus the global Claude Code skill (~/.claude/skills/apvm-cli/) if installed. Does not remove configuration, build artifacts, or project-local skills (use apvm skill uninstall inside each project first).

GitHub Token Resolution

The CLI and NAPI bindings resolve a GitHub token from multiple sources, checked in order:

  1. Config file — apvm config set token ghp_xxx
  2. GITHUB_TOKEN environment variable
  3. GH_TOKEN environment variable
  4. gh auth token command (gh CLI ≥ 2.17.0)
  5. gh CLI config file — ~/.config/gh/hosts.yml

A token is required for private repositories (e.g., BackWPup) and for building from PR numbers. It is optional for public repositories but recommended for higher API rate limits (5,000 vs 60 requests/hour).

Debug Logging

RUST_LOG=debug apvm build backwpup 123 -v 5.1.0
RUST_LOG=trace apvm build backwpup 123 -v 5.1.0

Tracing is only enabled when RUST_LOG is set. See the tracing-subscriber EnvFilter docs for filter syntax.

Project Structure

crates/
├── cli/        CLI binary (clap-based), owns defaults and user interaction
├── config/     Pure configuration types (no I/O, no defaults)
├── core/       Core library: building, git, GitHub API, version detection
├── napi/       Node.js N-API bindings (napi-rs cdylib)
└── storage/    SQLite-backed artifact cache (builds + release assets)

Architecture

  • Config — pure serde types for configuration. No file I/O, no hardcoded paths.
  • Core — orchestrates builds (resolve ref → cache fast path → clone → checkout → detect version → build → collect artifacts), downloads from GitHub Releases, integrates the artifact cache (serve on hit, reuse partial variants, warm after builds), implements the cache-maintenance actions behind apvm cache (one implementation, built to be shared by every front end), git operations, and GitHub API via octocrab. For extended documentation, see the Core crate README.
  • Storage — the SQLite-backed artifact cache: one embedded database indexes builds (keyed by project/version/commit, per-variant artifacts) and release assets (keyed by tag/filename), with health-checked lookups, LRU-style aging, and maintenance operations (clean, gc, verify, repair) that never touch files the store did not create. For extended documentation, see the Storage crate README.
  • NAPI — Node.js bindings via napi-rs. Exposes the core library as a native addon with async support on the tokio runtime. For extended documentation, see the NAPI crate README.
  • CLI — owns default paths (~/.apvm, ~/.apvm/cache), provides progress display via indicatif, and delegates all logic to core. For extended documentation, see the CLI crate README.

Running Tests

# All tests
cargo test

# Specific crate
cargo test -p apvm-core
cargo test -p apvm-storage
cargo test -p apvm-config
cargo test -p apvm

# Node.js tests (Vitest)
npm test

Key Dependencies

Crate Version Purpose
clap 4 CLI argument parsing
tokio 1 Async runtime
octocrab 0.54 GitHub API client
reqwest 0.13 HTTP client (release asset downloads)
serde 1 Serialization/deserialization
napi 3 Node.js N-API bindings
indicatif 0.18 Progress bars and spinners
thiserror 2 Error type derivation
tracing 0.1 Structured logging
chrono 0.4 Date/time for manifests
sha2 0.11 Artifact hashing
zip 8 ZIP archive creation
which 8 Tool dependency checking
walkdir 2 Recursive directory traversal
self-replace 1 Atomic binary self-replacement
semver 1 Semantic version parsing

CI

The project uses GitHub Actions for continuous integration (ci.yml).

Job Description
Check cargo fmt --check, cargo clippy -D warnings, cargo doc -D warnings
Build NAPI Builds native .node binaries for 5 targets (macOS ARM64/x64, Linux x64/ARM64, Windows x64)
Test Runs cargo test --workspace + npm test on Linux, macOS, and Windows
Commit Artifacts Auto-commits updated .node binaries on push to develop
CI (gate) Single required status check for branch protection

Releasing

Releases are cut by pushing a SemVer tag vX.Y.Z (e.g. v3.3.0), which runs release-cli.yml: it builds the CLI for all 5 platforms and publishes a GitHub Release with the binaries and checksums.txt.

  1. Bump the workspace version in Cargo.toml, package.json / package-lock.json, and the version line in .claude/skills/apvm-cli/SKILL.md; merge to develop.

  2. Wait for CI on develop to pass and push its chore: update NAPI binaries commit.

  3. Tag that commit, so the tag also carries the matching .node binaries for packages installed from git:

    git tag -a v3.3.0 -m "apvm v3.3.0"
    git push origin v3.3.0

The workflow rejects a tag that is not v + strict SemVer or that does not match Cargo.toml. A pre-release tag (v3.4.0-rc.1) is published as a GitHub pre-release and never marked latest, so apvm update and the install scripts ignore it. Releases up to 3.2.0 were tagged cli/vX.Y.Z; those tags are kept as history only.

The Node package can be installed straight from a tag, either exactly (github:wp-media/automation-plugin-version-manager#v3.3.0) or by range (github:wp-media/automation-plugin-version-manager#semver:^3.3.0, see npm Git URLs).

License

MIT — see package.json and crate manifests.

About

A Rust CLI tool and library for building and managing multiple versions of WordPress plugins from any git reference (branch, tag, commit or PR).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages