Pentagi Release Cycle and Versioning Strategy: A Complete Technical Guide

Pentagi implements a fully automated, Git tag-driven semantic versioning (SemVer) strategy where scripts/version.sh dynamically generates version strings at build time, appending commit hashes for development builds while the CI pipeline publishes Docker images with hierarchical tags (latest, MAJOR, MAJOR.MINOR, and full version).

The vxcontrol/pentagi repository maintains a deterministic release cycle and versioning strategy for Pentagi that eliminates manual version management. This approach leverages Git tags, shell scripts, and GitHub Actions to produce consistent version strings and multi-tiered Docker distribution tags directly from source control metadata.

Semantic Versioning Foundation

Pentagi adheres to semantic versioning (SemVer) principles where version numbers follow the format MAJOR.MINOR.PATCH. The project treats Git tags as the single source of truth for version identifiers. When a developer pushes an annotated tag (e.g., v2.4.1), the repository recognizes this as a release trigger, while any commit not exactly matching a tag receives a development build designation.

How Version Strings Are Generated

The version generation logic resides in scripts/version.sh, which orchestrates the extraction and formatting of version metadata during the build process.

Extracting the Base Version from Git Tags

The script identifies the latest release tag using Git's describe command:


# From scripts/version.sh, lines 5-6

LATEST_TAG=$(git describe --tags --abbrev=0)
PACKAGE_VER=${LATEST_TAG#v}  # Strips the leading 'v'

This produces a clean semantic version (e.g., 2.4.1) by stripping the v prefix from the tag. If the current checkout points directly to this tag, the build is classified as a release.

Detecting Development vs. Release Commits

The script determines whether the current commit matches the tag to differentiate development from release builds:


# From scripts/version.sh, lines 15-18

TAG_COMMIT=$(git rev-list -n 1 "$LATEST_TAG")
HEAD_COMMIT=$(git rev-parse --short HEAD)
if [ "$TAG_COMMIT" != "$HEAD_COMMIT" ]; then
    PACKAGE_REV="$HEAD_COMMIT"

When commits differ, the short SHA (HEAD_COMMIT) is stored as PACKAGE_REV. If they match, PACKAGE_REV remains empty, signaling a clean release build.

Constructing the Full Version String

The final version string assembly occurs at the end of the script:


# From scripts/version.sh, lines 30-35

if [ -n "$PACKAGE_REV" ]; then
    echo "${PACKAGE_VER}-${PACKAGE_REV}"  # e.g., 2.4.1-a1b2c3

else
    echo "${PACKAGE_VER}"                 # e.g., 2.4.1

Development builds append the commit hash (2.4.1-a1b2c3), while release builds use the clean semantic version (2.4.1).

Development vs. Release Builds

Pentagi distinguishes between these build types through the presence of PACKAGE_REV:

  • Release builds occur when the current commit exactly matches a Git tag. The PACKAGE_REV variable is empty, resulting in clean version strings like 2.4.1. These builds are considered stable and suitable for production deployment.

  • Development builds occur on any commit that does not match a tag. The scripts/version.sh script appends the short commit hash, producing strings like 2.4.1-a1b2c3. This allows operators to trace running binaries back to specific source revisions.

CI/CD Pipeline and Docker Tagging Strategy

The GitHub Actions workflow defined in .github/workflows/ci.yml (lines 160-188) automates the release process triggered by Git tags.

When a tag is pushed, the workflow extracts the version and generates a cascading set of Docker tags:


# From .github/workflows/ci.yml

- name: Set version variables
  run: |
    VERSION=${LATEST_TAG#v}
    IFS='.' read -r major minor patch <<< "$VERSION"
    echo "version=${VERSION}" >> $GITHUB_OUTPUT
    # Tags generated: latest, 2, 2.4, 2.4.1

The workflow pushes four distinct tag variants to the container registry:

Tag Purpose
latest Always points to the most recent stable release
2 Floating major version, includes all 2.x.x releases
2.4 Floating minor version, includes all 2.4.x patches
2.4.1 Immutable exact version for reproducible deployments

This tagging strategy allows users to pin deployments to specific stability levels, from bleeding-edge (latest) to exact version locks.

Accessing Version Information at Runtime

The compiled version string is exposed through the backend/pkg/version/version.go package via the GetBinaryVersion() function. The main application entry point in backend/cmd/pentagi/main.go logs this at startup:

import (
    "github.com/vxcontrol/pentagi/backend/pkg/version"
    "github.com/sirupsen/logrus"
)

func main() {
    logrus.Infof("Starting PentAGI %s", version.GetBinaryVersion())
}

Similarly, the installer in backend/cmd/installer/main.go displays the same version string to ensure consistency across all binary artifacts.

Summary

  • Pentagi uses semantic versioning (SemVer) driven entirely by Git tags, with version strings generated at build time by scripts/version.sh.
  • Release builds (clean tags) produce versions like 2.4.1, while development builds append short commit hashes (2.4.1-a1b2c3).
  • The CI pipeline (.github/workflows/ci.yml) automatically creates hierarchical Docker tags (latest, MAJOR, MAJOR.MINOR, full version) triggered by Git tag pushes.
  • Version information is embedded into the binary via backend/pkg/version/version.go and logged at startup by backend/cmd/pentagi/main.go.

Frequently Asked Questions

How does Pentagi determine the version number during development?

During development, scripts/version.sh extracts the latest Git tag to establish the base version (e.g., 2.4.1), then compares the current commit hash with the tag's commit. If they differ, it appends the short commit hash (e.g., -a1b2c3) to create a development version string like 2.4.1-a1b2c3.

What Docker tags are created during a Pentagi release?

The GitHub Actions workflow generates four Docker tag variants: latest (always the newest stable release), MAJOR (e.g., 2 for all 2.x.x releases), MAJOR.MINOR (e.g., 2.4 for all 2.4.x patches), and the full exact version (e.g., 2.4.1). This allows users to choose between stability and specific version pinning.

Where is the version string stored in the Pentagi source code?

The version string is not hardcoded but dynamically generated by scripts/version.sh during the build process. The resulting value is compiled into the binary through backend/pkg/version/version.go, which exposes the GetBinaryVersion() function. This function is called by backend/cmd/pentagi/main.go to display the version at application startup.

How can I check which version of Pentagi is running?

You can verify the running version by checking the application logs at startup, where backend/cmd/pentagi/main.go logs the version using logrus.Infof("Starting PentAGI %s", version.GetBinaryVersion()). Alternatively, if you deployed via Docker, inspect the container image labels or tags, which follow the semantic versioning scheme (e.g., 2.4.1 for releases or 2.4.1-a1b2c3 for development builds).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →