1- # .github/workflows/ci-cd.yml
2-
31name : Python Package CI
42
53on :
64 push :
75 branches :
86 - main
97 tags :
10- # Trigger on any tag (matches 0.0.1 style)
118 - ' *'
129 pull_request :
1310 branches :
1411 - main
1512 release :
16- types : [published] # If you create releases via GitHub UI based on tags
13+ types : [published]
1714
1815permissions :
19- contents : read # Needed for checkout
20- packages : write # Needed for GitHub Packages (Docker registry)
16+ contents : read
17+ packages : write
2118
2219jobs :
2320 lint :
2623 matrix :
2724 python-version : ["3.8", "3.9", "3.10", "3.11", "3.12"]
2825 steps :
29- - name : Checkout
30- uses : actions/checkout@v4
26+ - uses : actions/checkout@v4
3127 with :
3228 fetch-depth : 0 # Fetch all history for proper versioning
3329 fetch-tags : true # Explicitly fetch all tags
@@ -36,11 +32,10 @@ jobs:
3632 with :
3733 python-version : ${{ matrix.python-version }}
3834 cache : ' pip'
39- - name : Install lint dependencies
35+ - name : Install dependencies
4036 run : |
4137 python -m pip install --upgrade pip
4238 pip install ruff flake8 pylint isort setuptools
43- # Install runtime deps if linters need them (e.g., pylint requires imports)
4439 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
4540 - name : Analysing the code with pylint
4641 run : |
6257 matrix :
6358 python-version : ["3.8", "3.9", "3.10", "3.11", "3.12"]
6459 steps :
65- - name : Checkout
66- uses : actions/checkout@v4
60+ - uses : actions/checkout@v4
6761 with :
6862 fetch-depth : 0 # Fetch all history for proper versioning
6963 fetch-tags : true # Explicitly fetch all tags
@@ -72,20 +66,17 @@ jobs:
7266 with :
7367 python-version : ${{ matrix.python-version }}
7468 cache : ' pip'
75- - name : Install test dependencies
69+ - name : Install dependencies
7670 run : |
7771 python -m pip install --upgrade pip
78- # Install runtime dependencies AND test dependencies (like pytest)
7972 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
8073 pip install pytest
81- # Install the package itself in editable mode for testing
8274 pip install -e .
8375 - name : Run tests
8476 run : |
8577 python -m pytest
8678
8779 docker :
88- # Builds Docker images for testing, does not push
8980 needs : test
9081 runs-on : ubuntu-latest
9182 strategy :
@@ -95,177 +86,215 @@ jobs:
9586 - name : Checkout
9687 uses : actions/checkout@v4
9788 with :
98- fetch-depth : 0 # Needed for git describe
99- fetch-tags : true
89+ fetch-depth : 0 # Fetch all history for proper versioning
90+ fetch-tags : true # Explicitly fetch all tags
10091
10192 - name : Set up Docker Buildx
10293 uses : docker/setup-buildx-action@v3
103-
104- - name : Cache Docker layers for Test Build
94+
95+ - name : Cache Docker layers
10596 uses : actions/cache@v4
10697 with :
107- path : /tmp/.buildx-cache-test # Separate cache path
108- key : ${{ runner.os }}-buildx-test-${{ matrix.python-version }}- ${{ github.sha }}
98+ path : /tmp/.buildx-cache
99+ key : ${{ runner.os }}-buildx-${{ github.sha }}
109100 restore-keys : |
110- ${{ runner.os }}-buildx-test-${{ matrix.python-version }}-
101+ ${{ runner.os }}-buildx-
111102
112- - name : Get Version for Docker Build Args (Handles 0.0.1 style tags)
103+ - name : Get Version for Docker Build
113104 id : get_version
114105 run : |
115- # Ensure we have tags fetched completely
116- git fetch --tags --force --prune --unshallow || echo "Fetching tags failed, proceeding..."
117- # Use git describe. Outputs exact tag (0.0.1) or dev version (0.0.1-3-gddfce44)
118- GIT_DESCRIBE=$(git describe --tags --always --dirty 2>/dev/null || echo "0.0.0")
119- # No 'v' prefix removal needed if tags are like '0.0.1'
120- VERSION=$GIT_DESCRIBE
121- echo "Using version for Docker build arg: $VERSION"
106+ # Ensure we have tags
107+ git fetch --tags --force
108+
109+ # For tagged builds, use the exact tag without v prefix for PACKAGE_VERSION
110+ if [[ "$GITHUB_REF" == refs/tags/* ]]; then
111+ TAG=${GITHUB_REF#refs/tags/}
112+ VERSION="${TAG#v}"
113+ echo "Using tag version: $VERSION"
114+ else
115+ # Use git version without v prefix
116+ VERSION=$(git describe --tags --always 2>/dev/null | sed 's/^v//' || echo "0.1.0")
117+ echo "Using git version: $VERSION"
118+ fi
119+
120+ # Output for GitHub Actions
122121 echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
123122
124123 - name : Build Docker Image - Python ${{ matrix.python-version }}
125124 uses : docker/build-push-action@v5
126125 with :
127126 context : .
128- load : true # Load image for local testing steps below
129- # Use a distinct tag for the test build image
130- tags : gpt-po-translator:py${{ matrix.python-version }}-test
127+ load : true
128+ tags : gpt-po-translator:py${{ matrix.python-version }}
131129 build-args : |
132130 PYTHON_VERSION=${{ matrix.python-version }}
133131 VERSION=${{ steps.get_version.outputs.VERSION }}
134- # Use distinct cache scope for test build
135- cache-from : type=gha,scope=test-${{ matrix.python-version }}
136- cache-to : type=gha,mode=max,scope=test-${{ matrix.python-version }}
132+ cache-from : type=gha
133+ cache-to : type=gha,mode=max
137134
138135 - name : Test Docker Image - Help Text
139136 run : |
140- # Use the -test tag for running tests
141- docker run gpt-po-translator:py${{ matrix.python-version }}-test --version
142- docker run gpt-po-translator:py${{ matrix.python-version }}-test --help
137+ # Run Docker image to verify it works
138+ docker run gpt-po-translator:py${{ matrix.python-version }} --version
139+
140+ # Check help output (should exit with 0)
141+ docker run gpt-po-translator:py${{ matrix.python-version }} --help
142+
143143 echo "✅ Basic Docker image tests passed for Python ${{ matrix.python-version }}"
144144
145145 - name : Test Docker Image - CLI Options
146146 run : |
147- # Use the -test tag
148- docker run gpt-po-translator:py${{ matrix.python-version }}-test --provider openai --help
149- docker run gpt-po-translator:py${{ matrix.python-version }}-test --provider anthropic --help
147+ # Test with --help flag for different providers (doesn't require API key)
148+ docker run gpt-po-translator:py${{ matrix.python-version }} --provider openai --help
149+ docker run gpt-po-translator:py${{ matrix.python-version }} --provider anthropic --help
150+
150151 echo "✅ CLI option test passed for Python ${{ matrix.python-version }}"
151-
152+
152153 - name : Test Docker Image with Sample PO file
153154 run : |
154- # Create test directory and dummy PO file
155+ # Create test directory with sample PO file
155156 mkdir -p test-po-files
156- echo 'msgid "Hello"\nmsgstr ""' > test-po-files/test-sample.po
157- # cp .github/workflows/test-sample.po test-po-files/ # Or use your existing file
158-
159- # Use the -test tag
157+ cp .github/workflows/test-sample.po test-po-files/
158+
159+ # Check if the tool can access the mounted files (without API operations)
160+ # Just verify help works with the folder mounted
160161 docker run \
161162 -v $(pwd)/test-po-files:/test \
162- gpt-po-translator:py${{ matrix.python-version }}-test \
163+ gpt-po-translator:py${{ matrix.python-version }} \
163164 --folder /test --help
165+
164166 echo "✅ Docker volume mount test passed for Python ${{ matrix.python-version }}"
165167
166-
167168 deploy :
168- needs : [test, docker] # Depends on successful tests and docker test-builds
169+ needs : [test, docker]
169170 runs-on : ubuntu-latest
170- # Trigger deployment only on tag pushes (any tag format)
171171 if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
172- # environment: release # Uncomment if using GitHub environments for secrets/protection
173-
174- permissions :
175- contents : read # Needed for checkout
176- packages : write # Needed for GitHub Packages (Docker registry)
177-
172+ environment : release
178173 strategy :
179174 matrix :
180175 python-version : ["3.8", "3.9", "3.10", "3.11", "3.12"]
181- # Flag to indicate the primary version for single-run steps like PyPI deploy
182- is_primary_py : [ ${{ matrix.python-version == '3.11' }} ] # Choose your primary Python version
183-
184176 steps :
185- - name : Checkout
186- uses : actions/checkout@v4
177+ - uses : actions/checkout@v4
187178 with :
188- fetch-depth : 0 # Crucial for setuptools_scm
189- fetch-tags : true
190-
191- # === PyPI Deployment (runs only once for the primary Python version) ===
192- - name : Set up Python for PyPI deploy
193- if : matrix.is_primary_py
179+ fetch-depth : 0 # Fetch all history for proper versioning
180+ fetch-tags : true # Explicitly fetch all tags
181+
182+ # PyPI deployment ( only for Python 3.x representative)
183+ - name : Set up Python
184+ if : matrix.python-version == '3.11'
194185 uses : actions/setup-python@v5
195186 with :
196- python-version : ${{ matrix.python-version }} # Use the designated primary version
197-
198- - name : Install build tool for PyPI
199- if : matrix.is_primary_py
200- run : python -m pip install --upgrade pip build
201-
202- - name : Build PyPI package
203- if : matrix.is_primary_py
204- run : python -m build
205- # setuptools_scm automatically determines version from git tag
206-
207- - name : Publish package to PyPI
208- if : matrix.is_primary_py
209- uses : pypa/gh-action-pypi-publish@release/v1
187+ python-version : ' 3.x'
188+
189+ - name : Set Package Version for PyPI
190+ if : matrix.python-version == '3.11'
191+ run : |
192+ # Get tag name without 'refs/tags/' prefix
193+ TAG=${GITHUB_REF#refs/tags/}
194+ # Remove 'v' prefix if present
195+ VERSION="${TAG#v}"
196+ # Set as environment variable
197+ echo "PACKAGE_VERSION=$VERSION" >> $GITHUB_ENV
198+ echo "Using version $VERSION for PyPI package"
199+
200+ - name : Install dependencies
201+ if : matrix.python-version == '3.11'
202+ run : |
203+ python -m pip install --upgrade pip
204+ pip install -r requirements.txt
205+ pip install build
206+
207+ - name : Build package
208+ if : matrix.python-version == '3.11'
209+ run : |
210+ # Remove old build artifacts if any exist
211+ rm -rf dist build *.egg-info
212+
213+ # Explicitly set the version for the build tool
214+ if [ -n "$PACKAGE_VERSION" ]; then
215+ echo "Building package with version: $PACKAGE_VERSION"
216+ else
217+ echo "WARNING: PACKAGE_VERSION not set! Using git-based version."
218+ fi
219+
220+ # Build wheel and sdist with isolated environment
221+ python -m build
222+
223+ # Verify the wheel metadata
224+ pip install twine
225+ twine check dist/*
226+
227+ - name : Publish package
228+ if : matrix.python-version == '3.11'
229+ 210230 with :
211231 user : __token__
212- password : ${{ secrets.PYPI_API_TOKEN }} # Ensure this secret is set in repo settings
213-
214- # === Docker Deployment (runs for each Python version in the matrix) ===
232+ password : ${{ secrets.PYPI_API_TOKEN }}
233+
234+ # Docker deployment for all Python versions
215235 - name : Set up Docker Buildx
216236 uses : docker/setup-buildx-action@v3
217-
218- - name : Cache Docker layers for Deploy
237+
238+ - name : Cache Docker layers
219239 uses : actions/cache@v4
220240 with :
221- path : /tmp/.buildx-cache-deploy # Use separate cache path for deploy
222- key : ${{ runner.os }}-deploy- buildx-${{ matrix.python-version }}-${{ github.ref }} # Use git ref for tag builds
241+ path : /tmp/.buildx-cache
242+ key : ${{ runner.os }}-buildx-${{ github.sha }}
223243 restore-keys : |
224- ${{ runner.os }}-deploy- buildx-${{ matrix.python-version }} -
225-
244+ ${{ runner.os }}-buildx-
245+
226246 - name : Login to GitHub Container Registry
227247 uses : docker/login-action@v3
228248 with :
229249 registry : ghcr.io
230250 username : ${{ github.actor }}
231- password : ${{ secrets.GITHUB_TOKEN }} # Built-in token
251+ password : ${{ secrets.GITHUB_TOKEN }}
232252
233- - name : Extract metadata (tags, labels) for Docker (Handles 0.0.1 style tags)
253+ - name : Extract metadata for Docker
234254 id : meta
235255 uses : docker/metadata-action@v5
236256 with :
237- images : ghcr.io/${{ github.repository }} # Correct variable for owner/repo
238- # Generate tags based on the Git tag (e.g., 0.0.1 from refs/tags/0.0.1)
257+ images : |
258+ ghcr.io/${{ github.repository }}
239259 tags : |
260+ type=ref,event=branch,suffix=-py${{ matrix.python-version }}
240261 type=semver,pattern={{version}}-py${{ matrix.python-version }}
241262 type=semver,pattern={{major}}.{{minor}}-py${{ matrix.python-version }}
242263 type=semver,pattern={{major}}-py${{ matrix.python-version }}
243- type=raw,value=latest-py${{ matrix.python-version }}
244- type=raw,value=latest,enable=${{ matrix.is_primary_py }}
245- # Also tag with the exact version number (e.g., 0.0.1-py3.11)
246- type=match,pattern=(\d+\.\d+\.\d+.*),group=1,suffix=-py${{ matrix.python-version }}
264+ type=sha,format=short,suffix=-py${{ matrix.python-version }}
265+ flavor : |
266+ latest=${{ matrix.python-version == '3.11' }}
247267
248- - name : Get Version for Docker Build Args (Handles 0.0.1 style tags)
249- id : get_version_docker # Use different id from test job step
268+ - name : Get Version for Docker Build
269+ id : get_version
250270 run : |
251- git fetch --tags --force --prune --unshallow || echo "Fetching tags failed, proceeding..."
252- # Use git describe. Outputs exact tag (0.0.1) or dev version (0.0.1-3-gddfce44)
253- GIT_DESCRIBE=$(git describe --tags --always --dirty 2>/dev/null || echo "0.0.0")
254- # No 'v' prefix removal needed if tags are like '0.0.1'
255- VERSION=$GIT_DESCRIBE
256- echo "Using version for Docker build arg: $VERSION"
271+ # Ensure we have tags
272+ git fetch --tags --force
273+
274+ # For tagged builds, use the exact tag
275+ if [[ "$GITHUB_REF" == refs/tags/* ]]; then
276+ TAG=${GITHUB_REF#refs/tags/}
277+ # Remove v prefix if present for Docker build arg
278+ VERSION="${TAG#v}"
279+ echo "Using tag version: $VERSION"
280+ else
281+ # Use git version without v prefix
282+ VERSION=$(git describe --tags --always 2>/dev/null | sed 's/^v//' || echo "0.1.0")
283+ echo "Using git version: $VERSION"
284+ fi
285+
286+ # Output for GitHub Actions
257287 echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
258-
288+
259289 - name : Build and Push Docker Image
260290 uses : docker/build-push-action@v5
261291 with :
262292 context : .
263- push : true # Push the image to GHCR
293+ push : true
264294 tags : ${{ steps.meta.outputs.tags }}
265295 labels : ${{ steps.meta.outputs.labels }}
266296 build-args : |
267297 PYTHON_VERSION=${{ matrix.python-version }}
268- VERSION=${{ steps.get_version_docker.outputs.VERSION }} # Use correct step id
269- # The comment that likely caused the error was here - now removed
270- cache-from : type=gha,scope=deploy-${{ matrix.python-version }}
271- cache-to : type=gha,mode=max,scope=deploy-${{ matrix.python-version }}
298+ VERSION=${{ steps.get_version.outputs.VERSION }}
299+ cache-from : type=gha
300+ cache-to : type=gha,mode=max
0 commit comments