forked from pescheckit/python-gpt-po
-
Notifications
You must be signed in to change notification settings - Fork 0
318 lines (280 loc) · 12.7 KB
/
ci-cd.yml
File metadata and controls
318 lines (280 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
name: Python Package CI
on:
push:
branches:
- main
tags:
# Trigger on any tag (matches 0.0.1, v0.0.1 etc.)
- 'v[0-9]+.[0-9]+.[0-9]+' # Matches vX.Y.Z
- '[0-9]+.[0-9]+.[0-9]+' # Matches X.Y.Z
pull_request:
branches:
- main
release:
types: [published] # Trigger if you create releases via GitHub UI based on tags
# Define permissions required for the workflow jobs
permissions:
contents: read # Needed for actions/checkout
packages: write # Needed for pushing Docker images to GHCR
jobs:
lint:
name: Lint (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false # Don't cancel other jobs if one lint version fails
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
# No fetch-depth needed for linting typically
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip' # Cache pip dependencies
- name: Install lint dependencies
run: |
python -m pip install --upgrade pip
# Install linters AND setuptools (might be needed by some implicitly)
pip install ruff flake8 pylint isort setuptools
# Install project dependencies if linters need to import them
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
# Optional: Install package if pylint needs deep import checks
# pip install .
- name: Analysing the code with pylint
run: |
# Adjust path if needed
pylint python_gpt_po/ || echo "Pylint found issues but continuing..." # Or remove || to fail on issues
- name: Check code style with flake8
run: |
# Adjust path if needed
flake8 python_gpt_po/
- name: Check import order with isort
run: |
isort --check-only --diff .
- name: Linting with Ruff
run: |
# Uses git ls-files to find only tracked python files
ruff check $(git ls-files '*.py')
test:
name: Test (Python ${{ matrix.python-version }})
needs: lint # Run tests only if linting passes
runs-on: ubuntu-latest
strategy:
fail-fast: false # Don't cancel other jobs if one test version fails
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Fetch depth and tags might be needed if tests rely on git history/version
with:
fetch-depth: 0
fetch-tags: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y gettext
- name: Install test dependencies
run: |
python -m pip install --upgrade pip
# Install runtime dependencies
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
# Install test runner (e.g., pytest) and any test-specific packages
pip install pytest pytest-cov
# Install the package itself in editable mode for testing
pip install -e .
- name: Run tests with pytest
run: |
pytest --cov=python_gpt_po --cov-report=xml --cov-report=term-missing
docker-test-build:
name: Docker Test Build (Python ${{ matrix.python-version }})
# Renamed job to be clearer it's for testing the build, not deploying
needs: test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for git describe
fetch-tags: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers for Test Build
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache-test # Separate cache path for test builds
# Key includes python version and commit SHA for distinct caching per commit
key: ${{ runner.os }}-buildx-test-${{ matrix.python-version }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-test-${{ matrix.python-version }}-
- name: Get Version using git describe
# Use git describe for non-tag builds (main, PRs) and tags
id: get_version
run: |
git fetch --tags --force --prune --unshallow || echo "Fetching tags failed, proceeding..."
# Use git describe. Outputs exact tag (e.g., 0.1.0) or dev version (0.1.0-3-gddfce44)
GIT_DESCRIBE=$(git describe --tags --always --dirty 2>/dev/null || echo "0.0.0")
# Remove 'v' prefix if tags have it (adjust if your tags don't use 'v')
VERSION=${GIT_DESCRIBE#v}
echo "Using version for Docker build arg: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
- name: Build Docker Image (Test) - Python ${{ matrix.python-version }}
uses: docker/build-push-action@v5
with:
context: .
# Load image into Docker daemon for local testing, don't push
load: true
tags: local/gpt-po-translator:py${{ matrix.python-version }}-test
build-args: |
PYTHON_VERSION=${{ matrix.python-version }}
VERSION=${{ steps.get_version.outputs.VERSION }}
cache-from: type=gha,scope=test-${{ matrix.python-version }}
cache-to: type=gha,mode=max,scope=test-${{ matrix.python-version }}
- name: Test Docker Image - Basic Commands
run: |
docker run --rm local/gpt-po-translator:py${{ matrix.python-version }}-test --version
docker run --rm local/gpt-po-translator:py${{ matrix.python-version }}-test --help
echo "✅ Basic command tests passed for Docker image (Python ${{ matrix.python-version }})"
- name: Test Docker Image - CLI Options Help
run: |
docker run --rm local/gpt-po-translator:py${{ matrix.python-version }}-test --provider openai --help
docker run --rm local/gpt-po-translator:py${{ matrix.python-version }}-test --provider anthropic --help
echo "✅ CLI provider help test passed for Docker image (Python ${{ matrix.python-version }})"
- name: Test Docker Image - Volume Mount Help
# This verifies the entrypoint script and basic arg parsing work with volumes
run: |
mkdir -p ./test-po-dir # Create a temporary directory on the runner
echo 'msgid "Test"\nmsgstr ""' > ./test-po-dir/sample.po
docker run --rm \
-v $(pwd)/test-po-dir:/app/po_files \
local/gpt-po-translator:py${{ matrix.python-version }}-test \
--folder /app/po_files --help
rm -rf ./test-po-dir # Clean up
echo "✅ Volume mount help test passed for Docker image (Python ${{ matrix.python-version }})"
deploy:
name: Deploy to PyPI and GHCR
needs: [test, docker-test-build] # Depends on successful tests and Docker builds
runs-on: ubuntu-latest
# Condition: Run only on pushing a tag matching the pattern
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
# Optional: Define environment for secrets or protection rules
environment: release
# Permissions: Add id-token write if using PyPI Trusted Publishing
permissions:
contents: read # For checkout
packages: write # For GHCR push
# id-token: write # Uncomment if using PyPI Trusted Publishing`
strategy:
fail-fast: true # If one deployment fails, stop others
matrix:
# Define ALL python versions for Docker images
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
# Designate ONE primary version for PyPI publish and Docker 'latest' tag
primary-py: ['3.11'] # Choose your primary Python version
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Needed for setuptools_scm
fetch-tags: true
# === PyPI Deployment (runs only once for the primary Python version) ===
- name: Set up Python for PyPI deploy
# Run ONLY for the designated primary version
if: matrix.python-version == matrix.primary-py
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.primary-py }}
- name: Install PyPI build dependencies
if: matrix.python-version == matrix.primary-py
run: python -m pip install --upgrade pip build twine
- name: Verify Git state before PyPI build
if: matrix.python-version == matrix.primary-py
run: |
echo "Current Git Ref: ${{ github.ref }}"
git status
git describe --tags --exact-match # Should match the tag exactly
- name: Build package for PyPI
# Uses setuptools_scm automatically via pyproject.toml build-system config
if: matrix.python-version == matrix.primary-py
run: python -m build
- name: Verify built package metadata for PyPI
if: matrix.python-version == matrix.primary-py
run: twine check dist/*
- name: Publish package to PyPI
if: matrix.python-version == matrix.primary-py
uses: pypa/gh-action-pypi-publish@release/v1
with:
# --- API Token Authentication ---
# Ensure PYPI_API_TOKEN is set in GitHub Secrets
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
# --- Or use Trusted Publishing (recommended) ---
# Requires 'id-token: write' permission at job level
# Requires configuration on PyPI website first
# trust-token: true
# === Docker Deployment (runs for EACH Python version in the matrix) ===
# No 'if' condition on these Docker steps, they run for all matrix versions
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers for Deploy
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache-deploy # Separate cache path for deploy builds
# Cache key includes python version and the specific tag ref being built
key: ${{ runner.os }}-deploy-buildx-${{ matrix.python-version }}-${{ github.ref }}
restore-keys: |
${{ runner.os }}-deploy-buildx-${{ matrix.python-version }}-
- name: Log in to GitHub Container Registry (GHCR)
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
# GITHUB_TOKEN is automatically available, no secret needed
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
# Use the Git tag from the event ref as the base version
# Assumes tags are like '0.1.0' or 'v0.1.0' - metadata-action handles 'v' prefix
tags: |
# Generate tags like: 0.1.0-py3.9, 0.1-py3.9, 0-py3.9
type=semver,pattern={{version}}-py${{ matrix.python-version }}
type=semver,pattern={{major}}.{{minor}}-py${{ matrix.python-version }}
type=semver,pattern={{major}}-py${{ matrix.python-version }}
# Add 'latest' tag ONLY for the primary python version build
type=raw,value=latest,enable=${{ matrix.python-version == matrix.primary-py }}
- name: Get Version from Tag for Build Arg
# Extract the clean tag name (e.g., 0.1.0) to pass as build arg
id: get_version_tag
run: |
# Get the tag name from the ref (e.g., refs/tags/0.1.0 -> 0.1.0)
GIT_TAG=${GITHUB_REF#refs/tags/}
# Remove 'v' prefix if present (adjust if your tags don't use 'v')
VERSION=${GIT_TAG#v}
echo "Using version for Docker build arg: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
- name: Build and Push Docker Image to GHCR
uses: docker/build-push-action@v5
with:
context: .
push: true # Push the image to GHCR
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
PYTHON_VERSION=${{ matrix.python-version }}
VERSION=${{ steps.get_version_tag.outputs.VERSION }}
cache-from: type=gha,scope=deploy-${{ matrix.python-version }}
cache-to: type=gha,mode=max,scope=deploy-${{ matrix.python-version }}