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_REVvariable is empty, resulting in clean version strings like2.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.shscript appends the short commit hash, producing strings like2.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.goand logged at startup bybackend/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →