Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions .github/workflows/ci.yml → .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name: CI
name: Build

on:
push:
branches: [main, "release/*"]
pull_request:

jobs:
build:
package:
runs-on: ubuntu-latest

outputs:
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:

test:
runs-on: ubuntu-latest
needs: build
needs: package

strategy:
matrix:
Expand Down Expand Up @@ -119,6 +119,35 @@ jobs:
- name: Run tests from repo
run: python -m pytest tests/ -v

docker:
runs-on: ubuntu-latest
needs: package

steps:
- uses: actions/checkout@v6.0.3
with:
fetch-depth: 0

- name: Download built package
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Build Docker image
run: |
docker build -t ghcr.io/synacker/git-version-utils:${{ needs.package.outputs.version }} \
--build-arg WHEEL=dist/*.whl .
docker tag ghcr.io/synacker/git-version-utils:${{ needs.package.outputs.version }} \
ghcr.io/synacker/git-version-utils:latest

- name: Show version info
run: |
docker run --rm \
-v "$(pwd):/workspace" -w /workspace \
ghcr.io/synacker/git-version-utils:latest \
git-version --safe-directory '*' --property env

lint:
runs-on: ubuntu-latest

Expand Down
48 changes: 45 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
name: Publish to PyPI
name: Publish to PyPI and Docker

on:
workflow_dispatch:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
publish:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
if: github.ref_name == 'main' || startsWith(github.ref_name, 'release/')

steps:
- uses: actions/checkout@v6.0.3
Expand All @@ -32,4 +36,42 @@ jobs:
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@v1.14.0
with:
password: ${{ secrets.PYPI_API_TOKEN }}
password: ${{ secrets.PYPI_API_TOKEN }}

publish-docker:
needs: publish
runs-on: ubuntu-latest
if: github.ref_name == 'main' || startsWith(github.ref_name, 'release/')
permissions:
contents: read
packages: write

steps:
- uses: actions/checkout@v6.0.3
with:
fetch-depth: 0

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
58 changes: 58 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# syntax=docker/dockerfile:1
#
# git-version-utils Docker image
#
# Provides a lightweight container with git-version-utils pre-installed
# for use in CI pipelines (GitHub Actions, GitLab CI, etc.).
#
# Build:
# # From local source:
# docker build -t ghcr.io/synacker/git-version-utils:latest .
#
# # From pre-built wheel (used in CI after package build):
# docker build -t ghcr.io/synacker/git-version-utils:latest \
# --build-arg WHEEL=dist/git_version_utils-*.whl .
#
# Usage:
# docker run --rm -v $(pwd):/workspace -w /workspace \
# ghcr.io/synacker/git-version-utils:latest \
# git-version --safe-directory '*' --property env

# ============================================================
# Stage 1: Builder -- install the package
# ============================================================
FROM python:3.13-alpine AS builder

RUN apk add --no-cache git

ARG WHEEL
COPY . /build/

# Install from wheel if provided, otherwise from local source
RUN if [ -n "$WHEEL" ]; then \
wheel_path=$(ls /build/$WHEEL 2>/dev/null | head -1) && \
pip install --no-cache-dir "$wheel_path"; \
else \
pip install --no-cache-dir /build; \
fi

# Record the installed version for labelling the runtime image
RUN python -c "from git_version import __version__; print(__version__)" > /version.txt

# ============================================================
# Stage 2: Runtime -- minimal image with git + the package
# ============================================================
FROM python:3.13-alpine AS runtime

# git is required -- git-version-utils calls the git CLI via subprocess
RUN apk add --no-cache git

# Copy only the git_version package (not the entire site-packages with pip/setuptools)
COPY --from=builder /usr/local/lib/python3.13/site-packages/git_version /usr/local/lib/python3.13/site-packages/git_version
COPY --from=builder /usr/local/lib/python3.13/site-packages/git_version_utils-*.dist-info /usr/local/lib/python3.13/site-packages/

# Copy the git-version entrypoint script
COPY --from=builder /usr/local/bin/git-version /usr/local/bin/git-version

# Copy version label
COPY --from=builder /version.txt /version.txt
156 changes: 156 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,159 @@ execute_process(
```dockerfile
RUN pip install git-version-utils
RUN source <(git-version --property env) && echo "Building $BUILD_VERSION"
```

## Docker CI Container

A pre-built Docker image with `git-version-utils` is available at
`ghcr.io/synacker/git-version-utils`.

The image is based on `python:3.13-slim` and includes `git` + `git-version-utils`.
It is designed to be used as the **job container** in CI pipelines.

### Usage

```bash
# Run git-version inside the container
docker run --rm \
-v $(pwd):/workspace -w /workspace \
ghcr.io/synacker/git-version-utils:latest \
git-version --safe-directory '*' --property env
```

### GitHub Actions — Job Outputs

Use `container:` to run the job inside the image, then parse `git-version --property env`
into `$GITHUB_OUTPUT`. Downstream jobs consume the values via `needs.set-version.outputs.*`.

```yaml
name: CI with job outputs

on:
push:
branches: [main, "release/*"]

jobs:
set-version:
runs-on: ubuntu-latest
container:
image: ghcr.io/synacker/git-version-utils:latest
options: --workdir /__w/${{ github.event.repository.name }}/${{ github.event.repository.name }}
outputs:
BUILD_VERSION: ${{ steps.git_version.outputs.BUILD_VERSION }}
BUILD_VERSION_MAJOR: ${{ steps.git_version.outputs.BUILD_VERSION_MAJOR }}
BUILD_VERSION_MINOR: ${{ steps.git_version.outputs.BUILD_VERSION_MINOR }}
BUILD_VERSION_PATCH: ${{ steps.git_version.outputs.BUILD_VERSION_PATCH }}
BUILD_VERSION_BUILD: ${{ steps.git_version.outputs.BUILD_VERSION_BUILD }}
BUILD_VERSION_TAG: ${{ steps.git_version.outputs.BUILD_VERSION_TAG }}
BUILD_VERSION_FULL: ${{ steps.git_version.outputs.BUILD_VERSION_FULL }}
BUILD_VERSION_EXTENDED: ${{ steps.git_version.outputs.BUILD_VERSION_EXTENDED }}
BUILD_VERSION_SHORT: ${{ steps.git_version.outputs.BUILD_VERSION_SHORT }}
BUILD_VERSION_COMMIT: ${{ steps.git_version.outputs.BUILD_VERSION_COMMIT }}
BUILD_VERSION_BRANCH: ${{ steps.git_version.outputs.BUILD_VERSION_BRANCH }}
BUILD_VERSION_DEFAULT_BRANCH: ${{ steps.git_version.outputs.BUILD_VERSION_DEFAULT_BRANCH }}
BUILD_VERSION_RELEASE_BRANCHES: ${{ steps.git_version.outputs.BUILD_VERSION_RELEASE_BRANCHES }}

steps:
- uses: actions/checkout@v6.0.3
with:
fetch-depth: 0

- name: Extract version info
id: git_version
run: |
while IFS='=' read -r key value; do
echo "$key=$value" >> "$GITHUB_OUTPUT"
done < <(git-version --property env)

build:
runs-on: ubuntu-latest
needs: set-version
steps:
- uses: actions/checkout@v6.0.3

- name: Use version info
run: |
echo "Building version: ${{ needs.set-version.outputs.BUILD_VERSION }}"
echo "Tag: ${{ needs.set-version.outputs.BUILD_VERSION_TAG }}"
```

### GitHub Actions — Env File Artifact

A simpler approach: write the version info to a file and share it as an artifact.

```yaml
name: CI with env file

on:
push:
branches: [main, "release/*"]

jobs:
set-version:
runs-on: ubuntu-latest
container:
image: ghcr.io/synacker/git-version-utils:latest
options: --workdir /__w/${{ github.event.repository.name }}/${{ github.event.repository.name }}
steps:
- uses: actions/checkout@v6.0.3
with:
fetch-depth: 0

- name: Generate version.env
run: git-version --property env > version.env

- name: Upload version env file
uses: actions/upload-artifact@v4
with:
name: version-env
path: version.env

build:
runs-on: ubuntu-latest
needs: set-version
steps:
- uses: actions/checkout@v6.0.3

- name: Download version env file
uses: actions/download-artifact@v4
with:
name: version-env

- name: Load version and use it
run: |
source version.env
echo "Building version: $BUILD_VERSION"
echo "Tag: $BUILD_VERSION_TAG"
```

### GitLab CI

Use the image directly and share version info via
[dotenv artifacts](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportsdotenv).

```yaml
stages:
- set-version
- build

set-version:
stage: set-version
image: ghcr.io/synacker/git-version-utils:latest
variables:
GIT_DEPTH: 0
script:
- git-version --property env > version.env
artifacts:
reports:
dotenv: version.env

build:
stage: build
image: python:3.13-slim
needs:
- job: set-version
artifacts: true
script:
- echo "Building version: $BUILD_VERSION"
- echo "Tag: $BUILD_VERSION_TAG"
12 changes: 11 additions & 1 deletion src/git_version/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ def main() -> None:
default="v[0-9]*",
help="Glob pattern to match version tags (default: v[0-9]*)",
)
parser.add_argument(
"--safe-directory",
default=None,
help="Pass ``-c safe.directory=<value>`` to git commands. "
"Use ``'*'`` to allow all directories (useful in Docker containers).",
)
parser.add_argument(
"--property", "-P",
choices=[
Expand All @@ -34,7 +40,11 @@ def main() -> None:
)
args = parser.parse_args()

gv = GitVersion(repo_path=args.repo, tag_pattern=args.tag_pattern)
gv = GitVersion(
repo_path=args.repo,
tag_pattern=args.tag_pattern,
safe_directory=args.safe_directory,
)

if args.property == "env":
for key, value in gv.env(prefix=args.prefix).items():
Expand Down
Loading
Loading