Skip to content

Commit fceb132

Browse files
authored
Merge pull request #5 from synacker/feature/docker
Add docker and safe dir option
2 parents 88b4399 + 29d3e35 commit fceb132

6 files changed

Lines changed: 311 additions & 8 deletions

File tree

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
name: CI
1+
name: Build
22

33
on:
44
push:
55
branches: [main, "release/*"]
66
pull_request:
77

88
jobs:
9-
build:
9+
package:
1010
runs-on: ubuntu-latest
1111

1212
outputs:
@@ -83,7 +83,7 @@ jobs:
8383

8484
test:
8585
runs-on: ubuntu-latest
86-
needs: build
86+
needs: package
8787

8888
strategy:
8989
matrix:
@@ -119,6 +119,35 @@ jobs:
119119
- name: Run tests from repo
120120
run: python -m pytest tests/ -v
121121

122+
docker:
123+
runs-on: ubuntu-latest
124+
needs: package
125+
126+
steps:
127+
- uses: actions/checkout@v6.0.3
128+
with:
129+
fetch-depth: 0
130+
131+
- name: Download built package
132+
uses: actions/download-artifact@v4
133+
with:
134+
name: dist
135+
path: dist/
136+
137+
- name: Build Docker image
138+
run: |
139+
docker build -t ghcr.io/synacker/git-version-utils:${{ needs.package.outputs.version }} \
140+
--build-arg WHEEL=dist/*.whl .
141+
docker tag ghcr.io/synacker/git-version-utils:${{ needs.package.outputs.version }} \
142+
ghcr.io/synacker/git-version-utils:latest
143+
144+
- name: Show version info
145+
run: |
146+
docker run --rm \
147+
-v "$(pwd):/workspace" -w /workspace \
148+
ghcr.io/synacker/git-version-utils:latest \
149+
git-version --safe-directory '*' --property env
150+
122151
lint:
123152
runs-on: ubuntu-latest
124153

.github/workflows/publish.yml

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
name: Publish to PyPI
1+
name: Publish to PyPI and Docker
22

33
on:
44
workflow_dispatch:
55

6+
env:
7+
REGISTRY: ghcr.io
8+
IMAGE_NAME: ${{ github.repository }}
9+
610
jobs:
711
publish:
812
runs-on: ubuntu-latest
9-
if: github.ref == 'refs/heads/main'
13+
if: github.ref_name == 'main' || startsWith(github.ref_name, 'release/')
1014

1115
steps:
1216
- uses: actions/checkout@v6.0.3
@@ -32,4 +36,42 @@ jobs:
3236
- name: Publish to PyPI
3337
uses: pypa/gh-action-pypi-publish@v1.14.0
3438
with:
35-
password: ${{ secrets.PYPI_API_TOKEN }}
39+
password: ${{ secrets.PYPI_API_TOKEN }}
40+
41+
publish-docker:
42+
needs: publish
43+
runs-on: ubuntu-latest
44+
if: github.ref_name == 'main' || startsWith(github.ref_name, 'release/')
45+
permissions:
46+
contents: read
47+
packages: write
48+
49+
steps:
50+
- uses: actions/checkout@v6.0.3
51+
with:
52+
fetch-depth: 0
53+
54+
- name: Log in to GitHub Container Registry
55+
uses: docker/login-action@v3
56+
with:
57+
registry: ${{ env.REGISTRY }}
58+
username: ${{ github.actor }}
59+
password: ${{ secrets.GITHUB_TOKEN }}
60+
61+
- name: Extract metadata for Docker
62+
id: meta
63+
uses: docker/metadata-action@v5
64+
with:
65+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
66+
tags: |
67+
type=semver,pattern={{version}}
68+
type=semver,pattern={{major}}.{{minor}}
69+
type=raw,value=latest,enable={{is_default_branch}}
70+
71+
- name: Build and push Docker image
72+
uses: docker/build-push-action@v6
73+
with:
74+
context: .
75+
push: true
76+
tags: ${{ steps.meta.outputs.tags }}
77+
labels: ${{ steps.meta.outputs.labels }}

Dockerfile

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# syntax=docker/dockerfile:1
2+
#
3+
# git-version-utils Docker image
4+
#
5+
# Provides a lightweight container with git-version-utils pre-installed
6+
# for use in CI pipelines (GitHub Actions, GitLab CI, etc.).
7+
#
8+
# Build:
9+
# # From local source:
10+
# docker build -t ghcr.io/synacker/git-version-utils:latest .
11+
#
12+
# # From pre-built wheel (used in CI after package build):
13+
# docker build -t ghcr.io/synacker/git-version-utils:latest \
14+
# --build-arg WHEEL=dist/git_version_utils-*.whl .
15+
#
16+
# Usage:
17+
# docker run --rm -v $(pwd):/workspace -w /workspace \
18+
# ghcr.io/synacker/git-version-utils:latest \
19+
# git-version --safe-directory '*' --property env
20+
21+
# ============================================================
22+
# Stage 1: Builder -- install the package
23+
# ============================================================
24+
FROM python:3.13-alpine AS builder
25+
26+
RUN apk add --no-cache git
27+
28+
ARG WHEEL
29+
COPY . /build/
30+
31+
# Install from wheel if provided, otherwise from local source
32+
RUN if [ -n "$WHEEL" ]; then \
33+
wheel_path=$(ls /build/$WHEEL 2>/dev/null | head -1) && \
34+
pip install --no-cache-dir "$wheel_path"; \
35+
else \
36+
pip install --no-cache-dir /build; \
37+
fi
38+
39+
# Record the installed version for labelling the runtime image
40+
RUN python -c "from git_version import __version__; print(__version__)" > /version.txt
41+
42+
# ============================================================
43+
# Stage 2: Runtime -- minimal image with git + the package
44+
# ============================================================
45+
FROM python:3.13-alpine AS runtime
46+
47+
# git is required -- git-version-utils calls the git CLI via subprocess
48+
RUN apk add --no-cache git
49+
50+
# Copy only the git_version package (not the entire site-packages with pip/setuptools)
51+
COPY --from=builder /usr/local/lib/python3.13/site-packages/git_version /usr/local/lib/python3.13/site-packages/git_version
52+
COPY --from=builder /usr/local/lib/python3.13/site-packages/git_version_utils-*.dist-info /usr/local/lib/python3.13/site-packages/
53+
54+
# Copy the git-version entrypoint script
55+
COPY --from=builder /usr/local/bin/git-version /usr/local/bin/git-version
56+
57+
# Copy version label
58+
COPY --from=builder /version.txt /version.txt

README.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,159 @@ execute_process(
101101
```dockerfile
102102
RUN pip install git-version-utils
103103
RUN source <(git-version --property env) && echo "Building $BUILD_VERSION"
104+
```
105+
106+
## Docker CI Container
107+
108+
A pre-built Docker image with `git-version-utils` is available at
109+
`ghcr.io/synacker/git-version-utils`.
110+
111+
The image is based on `python:3.13-slim` and includes `git` + `git-version-utils`.
112+
It is designed to be used as the **job container** in CI pipelines.
113+
114+
### Usage
115+
116+
```bash
117+
# Run git-version inside the container
118+
docker run --rm \
119+
-v $(pwd):/workspace -w /workspace \
120+
ghcr.io/synacker/git-version-utils:latest \
121+
git-version --safe-directory '*' --property env
122+
```
123+
124+
### GitHub Actions — Job Outputs
125+
126+
Use `container:` to run the job inside the image, then parse `git-version --property env`
127+
into `$GITHUB_OUTPUT`. Downstream jobs consume the values via `needs.set-version.outputs.*`.
128+
129+
```yaml
130+
name: CI with job outputs
131+
132+
on:
133+
push:
134+
branches: [main, "release/*"]
135+
136+
jobs:
137+
set-version:
138+
runs-on: ubuntu-latest
139+
container:
140+
image: ghcr.io/synacker/git-version-utils:latest
141+
options: --workdir /__w/${{ github.event.repository.name }}/${{ github.event.repository.name }}
142+
outputs:
143+
BUILD_VERSION: ${{ steps.git_version.outputs.BUILD_VERSION }}
144+
BUILD_VERSION_MAJOR: ${{ steps.git_version.outputs.BUILD_VERSION_MAJOR }}
145+
BUILD_VERSION_MINOR: ${{ steps.git_version.outputs.BUILD_VERSION_MINOR }}
146+
BUILD_VERSION_PATCH: ${{ steps.git_version.outputs.BUILD_VERSION_PATCH }}
147+
BUILD_VERSION_BUILD: ${{ steps.git_version.outputs.BUILD_VERSION_BUILD }}
148+
BUILD_VERSION_TAG: ${{ steps.git_version.outputs.BUILD_VERSION_TAG }}
149+
BUILD_VERSION_FULL: ${{ steps.git_version.outputs.BUILD_VERSION_FULL }}
150+
BUILD_VERSION_EXTENDED: ${{ steps.git_version.outputs.BUILD_VERSION_EXTENDED }}
151+
BUILD_VERSION_SHORT: ${{ steps.git_version.outputs.BUILD_VERSION_SHORT }}
152+
BUILD_VERSION_COMMIT: ${{ steps.git_version.outputs.BUILD_VERSION_COMMIT }}
153+
BUILD_VERSION_BRANCH: ${{ steps.git_version.outputs.BUILD_VERSION_BRANCH }}
154+
BUILD_VERSION_DEFAULT_BRANCH: ${{ steps.git_version.outputs.BUILD_VERSION_DEFAULT_BRANCH }}
155+
BUILD_VERSION_RELEASE_BRANCHES: ${{ steps.git_version.outputs.BUILD_VERSION_RELEASE_BRANCHES }}
156+
157+
steps:
158+
- uses: actions/checkout@v6.0.3
159+
with:
160+
fetch-depth: 0
161+
162+
- name: Extract version info
163+
id: git_version
164+
run: |
165+
while IFS='=' read -r key value; do
166+
echo "$key=$value" >> "$GITHUB_OUTPUT"
167+
done < <(git-version --property env)
168+
169+
build:
170+
runs-on: ubuntu-latest
171+
needs: set-version
172+
steps:
173+
- uses: actions/checkout@v6.0.3
174+
175+
- name: Use version info
176+
run: |
177+
echo "Building version: ${{ needs.set-version.outputs.BUILD_VERSION }}"
178+
echo "Tag: ${{ needs.set-version.outputs.BUILD_VERSION_TAG }}"
179+
```
180+
181+
### GitHub Actions — Env File Artifact
182+
183+
A simpler approach: write the version info to a file and share it as an artifact.
184+
185+
```yaml
186+
name: CI with env file
187+
188+
on:
189+
push:
190+
branches: [main, "release/*"]
191+
192+
jobs:
193+
set-version:
194+
runs-on: ubuntu-latest
195+
container:
196+
image: ghcr.io/synacker/git-version-utils:latest
197+
options: --workdir /__w/${{ github.event.repository.name }}/${{ github.event.repository.name }}
198+
steps:
199+
- uses: actions/checkout@v6.0.3
200+
with:
201+
fetch-depth: 0
202+
203+
- name: Generate version.env
204+
run: git-version --property env > version.env
205+
206+
- name: Upload version env file
207+
uses: actions/upload-artifact@v4
208+
with:
209+
name: version-env
210+
path: version.env
211+
212+
build:
213+
runs-on: ubuntu-latest
214+
needs: set-version
215+
steps:
216+
- uses: actions/checkout@v6.0.3
217+
218+
- name: Download version env file
219+
uses: actions/download-artifact@v4
220+
with:
221+
name: version-env
222+
223+
- name: Load version and use it
224+
run: |
225+
source version.env
226+
echo "Building version: $BUILD_VERSION"
227+
echo "Tag: $BUILD_VERSION_TAG"
228+
```
229+
230+
### GitLab CI
231+
232+
Use the image directly and share version info via
233+
[dotenv artifacts](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportsdotenv).
234+
235+
```yaml
236+
stages:
237+
- set-version
238+
- build
239+
240+
set-version:
241+
stage: set-version
242+
image: ghcr.io/synacker/git-version-utils:latest
243+
variables:
244+
GIT_DEPTH: 0
245+
script:
246+
- git-version --property env > version.env
247+
artifacts:
248+
reports:
249+
dotenv: version.env
250+
251+
build:
252+
stage: build
253+
image: python:3.13-slim
254+
needs:
255+
- job: set-version
256+
artifacts: true
257+
script:
258+
- echo "Building version: $BUILD_VERSION"
259+
- echo "Tag: $BUILD_VERSION_TAG"

src/git_version/cli.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ def main() -> None:
2222
default="v[0-9]*",
2323
help="Glob pattern to match version tags (default: v[0-9]*)",
2424
)
25+
parser.add_argument(
26+
"--safe-directory",
27+
default=None,
28+
help="Pass ``-c safe.directory=<value>`` to git commands. "
29+
"Use ``'*'`` to allow all directories (useful in Docker containers).",
30+
)
2531
parser.add_argument(
2632
"--property", "-P",
2733
choices=[
@@ -34,7 +40,11 @@ def main() -> None:
3440
)
3541
args = parser.parse_args()
3642

37-
gv = GitVersion(repo_path=args.repo, tag_pattern=args.tag_pattern)
43+
gv = GitVersion(
44+
repo_path=args.repo,
45+
tag_pattern=args.tag_pattern,
46+
safe_directory=args.safe_directory,
47+
)
3848

3949
if args.property == "env":
4050
for key, value in gv.env(prefix=args.prefix).items():

0 commit comments

Comments
 (0)