Automatic Version Management in CI/CD
Overview
The ProductifyFW CLI generates CI/CD pipelines that automatically increment project versions on main branch pushes. This ensures every deployment has a unique, incrementing version number without manual intervention.
How It Works
On Every Push to Main
- Version Bump: CLI increments patch version in
project.yaml - Commit: Changes committed with
[skip ci]to avoid loops - Build: Docker image built with new version
- Tag: Image tagged with version number only (no SHA/branch tags)
- Push: Versioned image pushed to registry
Version Tags
Only semantic version tags are used:
my-app:1.2.3- Exact version numbermy-app:latest- Latest main branch build
No commit/branch tags:
- [NO]
(SHA)my-app:abc123def - [NO]
(branch)my-app:main - [NO]
(branch slug)my-app:feature-xyz
GitHub Actions Workflow
Generated CI Pipeline
version-and-push:
runs-on: ubuntu-latest
needs: build-and-scan
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v6
- name: Install pfy CLI
run: |
curl -LO https://github.com/ProductifyFW/cli/releases/latest/download/pfy-linux-amd64
chmod +x pfy-linux-amd64
sudo mv pfy-linux-amd64 /usr/local/bin/pfy
- name: Bump version
run: |
pfy version bump --part patch
echo "NEW_VERSION=$(pfy version show)" >> $GITHUB_ENV
- name: Commit version bump
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add project.yaml
git commit -m "chore: bump version to $NEW_VERSION [skip ci]"
git push
- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/my-app:${{ env.NEW_VERSION }}
ghcr.io/my-app:latestExecution Flow
Push to main
↓
Run tests & security scans (on version 1.2.3)
↓
Bump version: 1.2.3 → 1.2.4
↓
Commit project.yaml with [skip ci]
↓
Push to repository
↓
Build Docker image
↓
Tag: my-app:1.2.4, my-app:latest
↓
Push to registryGitLab CI Workflow
Generated Pipeline
push:
stage: deploy
script:
- |
if [ "$CI_COMMIT_BRANCH" == "main" ]; then
git config user.name "GitLab CI"
git config user.email "ci@gitlab.com"
pfy version bump --part patch
NEW_VERSION=$(pfy version show)
git add project.yaml
git commit -m "chore: bump version to $NEW_VERSION [skip ci]"
git push https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git
docker tag $IMAGE_NAME:0.1.0 $IMAGE_NAME:$NEW_VERSION
docker tag $IMAGE_NAME:0.1.0 $IMAGE_NAME:latest
docker push $IMAGE_NAME:$NEW_VERSION
docker push $IMAGE_NAME:latest
fiBuild Stage
Both platforms build using the current version from project.yaml:
# Current version (before bump)
tags: ghcr.io/my-app:0.1.0Configuration
Required Permissions
GitHub Actions:
permissions:
contents: write # Push version commits
packages: write # Push Docker imagesGitLab CI:
- Repository write access via
CI_JOB_TOKEN - Registry access via
CI_REGISTRY_PASSWORD
Skip CI Commits
Version bump commits include [skip ci] to prevent infinite loops:
git commit -m "chore: bump version to 1.2.4 [skip ci]"Version Strategy
Automatic Patch Bumps
Every main branch push increments patch version:
1.0.0 → 1.0.1 → 1.0.2 → 1.0.3 ...Manual Minor/Major Bumps
Use CLI for feature releases or breaking changes:
pfy version bump --part minor # 1.0.3 → 1.1.0
pfy version bump --part major # 1.1.0 → 2.0.0Example Deployment Scenario
Initial Setup
pfy init --name my-service # version: 0.1.0
pfy ci update
git add .
git commit -m "Initial setup"
git push origin mainCI runs: Tests → Bump (0.1.0 → 0.1.1) → Build → Push my-service:0.1.1
Development Cycle
Feature branch:
git checkout -b feature/auth
git push origin feature/authCI runs tests only (no version bump, no push).
Merge to main:
git checkout main
git merge feature/auth
git push origin mainCI runs: Tests → Bump (0.1.1 → 0.1.2) → Build → Push
Release Preparation
pfy version bump --part minor # 0.1.2 → 0.2.0
git commit -am "Release v0.2.0"
git push origin mainCI runs: Tests → Bump (0.2.0 → 0.2.1) → Build → Push
Version History Tracking
Git Log Shows All Versions
$ git log --oneline --grep="bump version"
abc123d chore: bump version to 0.2.1 [skip ci]
def456e chore: bump version to 0.2.0 [skip ci]
789abc0 chore: bump version to 0.1.2 [skip ci]
012def3 chore: bump version to 0.1.1 [skip ci]Registry Shows All Images
$ docker images
my-service 0.2.1 ...
my-service 0.2.0 ...
my-service 0.1.2 ...
my-service 0.1.1 ...
my-service latest ... (points to 0.2.1)Troubleshooting
CI Loop: Version Keeps Bumping
Ensure [skip ci] is in commit message:
git commit -m "chore: bump version to X.Y.Z [skip ci]"Permission Denied on Push
# GitHub: Use GITHUB_TOKEN
- uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
# GitLab: Use CI_JOB_TOKEN
git push https://oauth2:${CI_JOB_TOKEN}@...Version Not Incrementing
Ensure CLI is installed:
- name: Install pfy
run: |
curl -LO https://github.com/ProductifyFW/cli/releases/latest/download/pfy-linux-amd64
chmod +x pfy-linux-amd64
sudo mv pfy-linux-amd64 /usr/local/bin/pfyBest Practices
1. Let CI Handle Version Bumps
# [OK] Use CLI
pfy version bump --part patch
# [NO] Don't manually edit
sed -i 's/0.1.0/0.1.1/' project.yaml2. Use Minor/Major for Releases
pfy version bump --part minor # Feature release
pfy version bump --part major # Breaking changes3. Tag Releases in Git
VERSION=$(pfy version show)
git tag "v$VERSION"
git push --tagsIntegration with Deployments
Kubernetes
containers:
- name: app
image: ghcr.io/my-service:1.2.3 # Specific version
# or
image: ghcr.io/my-service:latest # Always latestDocker Compose
services:
app:
image: ghcr.io/my-service:${VERSION:-latest}Deploy: VERSION=1.2.3 docker-compose up -d
See Also
- Version Management - Manual version commands
- CI/CD Commands - CI template generation
- Project Configuration - project.yaml reference