How updates work

How the Manager updates itself, how you update the CLI, and how to check what version each is running.

Manager

The Manager updates through the normal WordPress plugin update path. includes/class-captaincore-manager-updater.php hooks plugins_api, site_transient_update_plugins and upgrader_pre_download and supplies the update itself.

The flow:

  1. request() fetches https://raw.githubusercontent.com/CaptainCore/captaincore-manager/master/manifest.json with a 30 second timeout and caches the result in the captaincore_manager_updater transient for one hour. If the fetch fails, the bundled manifest.json shipped in the plugin is used and the failure is cached for five minutes.
  2. update() compares CAPTAINCORE_VERSION against the manifest’s version with version_compare(). It refuses to offer the update at all unless the manifest’s download_url passes is_our_package_url() (https, host in github.com / objects.githubusercontent.com / codeload.github.com, path starting with /CaptainCore/captaincore-manager/) and the manifest publishes a non-empty sha256.
  3. verify_package() intercepts the download, fetches the zip with download_url( $package, 300 ), and compares hash_file( 'sha256', $file ) to the manifest hash with hash_equals(). A mismatch deletes the file and returns captaincore_bad_package_hash. A manifest with no hash returns captaincore_missing_package_hash.
  4. purge() clears the transient after a successful plugin update.

Apply an update:

wp plugin update captaincore-manager

Force a fresh manifest read:

wp transient delete captaincore_manager_updater

The version lives in three places: the Version: plugin header, the CAPTAINCORE_VERSION constant in captaincore.php, and version in manifest.json. bin/build-zip.sh fails the build if they disagree, or if the manifest download_url does not point at the matching /v{version}/ release asset. A build whose header disagrees with its own manifest would either offer an update forever or never offer one.

Schema migrations ride along with the code. After an update, the next init or admin_init runs captaincore_maybe_upgrade_db(), which compares the captaincore_db_version site option against CaptainCore\DB::REQUIRED_VERSION and runs DB::upgrade() under a lock.

CLI

The CLI updates itself:

captaincore upgrade

It resolves the latest tag from the GitHub releases page, compares it with the running version, downloads captaincore_<Os>_<Arch>.tar.gz and checksums.txt for that release, verifies the sha256, extracts the binary next to the current one, runs its version command as a sanity check, then renames it over the running binary. If the directory is not writable it falls back to sudo mv. The new binary unpacks its embedded app/ and lib/ scripts on its first run, so scripts and binary always move together.

Flag Effect
--check report the current and latest versions, exit 1 when behind, change nothing
--yes skip the confirmation prompt
--force reinstall even when already current
--version=v1.2.0 install a specific release

captaincore upgrade --check is safe to run from cron as a reminder.

A source checkout is different. When ~/.captaincore/.git exists the CLI was built in place, and upgrade refuses (without --force) and tells you to pull and rebuild instead:

cd ~/.captaincore
git pull
go build -o captaincore .

Restart captaincore server after either kind of update so the Manager talks to the new build. upgrade prints the restart hint when it sees a running server process.

Releases are cut with git tags and GoReleaser, which cross-compiles for Linux and macOS on x86_64, arm64 and armv7 and publishes the archives plus checksums.txt as release assets. install.sh and upgrade both read releases/latest/download/, so a tag without those assets is not an installable release.

Checking versions

For the CLI:

captaincore version

It prints the version, the Go version it was built with, and the platform:

captaincore 1.0.0
- go version: go1.25.0
- platform: linux/amd64

Release builds stamp the version at build time with -ldflags "-X github.com/CaptainCore/captaincore/version.Version=...". A source build reports the value in version/version.go, which is bumped in the same commit as each release, so both paths agree. Check git describe in ~/.captaincore if you need the exact source revision of a checkout.

For the Manager:

wp plugin list --name=captaincore-manager --fields=name,status,version

Or read the Version: header in wp-content/plugins/captaincore-manager/captaincore.php. The dashboard’s Settings section does not show a version number.