Skip to content

Commit 0e685b2

Browse files
authored
Merge pull request #12 from jski/feature/improved-auto-workflows
Feature/improved auto workflows
2 parents d265008 + 0cbb9ee commit 0e685b2

File tree

5 files changed

+462
-139
lines changed

5 files changed

+462
-139
lines changed

.github/workflows/build.yml

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
name: Build & Deploy
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
branches:
8+
- main
9+
workflow_dispatch:
10+
inputs:
11+
promote:
12+
description: 'Promote to production tags after build'
13+
required: false
14+
type: boolean
15+
default: false
16+
17+
jobs:
18+
build:
19+
name: Build Images
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
packages: write
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
include:
28+
- python_version: "3.9"
29+
debian_version: "bullseye"
30+
- python_version: "3.10"
31+
debian_version: "bullseye"
32+
- python_version: "3.11"
33+
debian_version: "bookworm"
34+
- python_version: "3.12"
35+
debian_version: "bookworm"
36+
- python_version: "3.13"
37+
debian_version: "bookworm"
38+
- python_version: "3.14"
39+
debian_version: "bookworm"
40+
steps:
41+
- name: Checkout repository
42+
uses: actions/checkout@v6
43+
44+
- name: Set up QEMU
45+
uses: docker/setup-qemu-action@v3
46+
with:
47+
platforms: all
48+
49+
- name: Set up Docker Buildx
50+
uses: docker/setup-buildx-action@v3
51+
52+
- name: Log in to GitHub Container Registry
53+
uses: docker/login-action@v3
54+
with:
55+
registry: ghcr.io
56+
username: ${{ github.actor }}
57+
password: ${{ secrets.GITHUB_TOKEN }}
58+
59+
- name: Check if image already exists
60+
id: check
61+
run: |
62+
IMAGE="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }}"
63+
echo "Checking if ${IMAGE} already exists..."
64+
65+
if docker manifest inspect "${IMAGE}" >/dev/null 2>&1; then
66+
echo "✅ Image already exists, skipping build"
67+
echo "exists=true" >> $GITHUB_OUTPUT
68+
else
69+
echo "❌ Image does not exist, will build"
70+
echo "exists=false" >> $GITHUB_OUTPUT
71+
fi
72+
73+
- name: Extract metadata
74+
if: steps.check.outputs.exists == 'false'
75+
id: meta
76+
uses: docker/metadata-action@v5
77+
with:
78+
images: ghcr.io/${{ github.repository_owner }}/python-container-builder
79+
tags: |
80+
type=sha,prefix=${{ matrix.python_version }}-,format=long
81+
type=ref,event=pr,prefix=pr-,suffix=-${{ matrix.python_version }}
82+
83+
- name: Build and push Docker images
84+
if: steps.check.outputs.exists == 'false'
85+
uses: docker/build-push-action@v6
86+
with:
87+
context: .
88+
file: Dockerfile
89+
platforms: linux/amd64,linux/arm64
90+
push: true
91+
build-args: |
92+
DEBIAN_VERSION=${{ matrix.debian_version }}
93+
PYTHON_VERSION=${{ matrix.python_version }}
94+
tags: ${{ steps.meta.outputs.tags }}
95+
labels: ${{ steps.meta.outputs.labels }}
96+
cache-from: type=gha,scope=python-${{ matrix.python_version }}
97+
cache-to: type=gha,mode=max,scope=python-${{ matrix.python_version }}
98+
provenance: true
99+
sbom: true
100+
outputs: type=image,name=python-container-builder,annotation-index.org.opencontainers.image.description=Build your Python distroless containers with this
101+
102+
promote:
103+
name: Promote to Production
104+
runs-on: ubuntu-latest
105+
needs: [build, test, security-scan]
106+
# Promote on:
107+
# 1. Normal merge to main (not force push)
108+
# 2. Manual workflow dispatch with promote flag enabled
109+
# CRITICAL: Only runs if build, test, AND security-scan all succeed
110+
if: |
111+
(github.event_name == 'push' && github.ref == 'refs/heads/main' && !github.event.forced) ||
112+
(github.event_name == 'workflow_dispatch' && inputs.promote == true)
113+
permissions:
114+
contents: read
115+
packages: write
116+
strategy:
117+
fail-fast: false
118+
matrix:
119+
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
120+
steps:
121+
- name: Log in to GitHub Container Registry
122+
uses: docker/login-action@v3
123+
with:
124+
registry: ghcr.io
125+
username: ${{ github.actor }}
126+
password: ${{ secrets.GITHUB_TOKEN }}
127+
128+
- name: Promote commit SHA to version tag
129+
run: |
130+
# Get the full commit SHA
131+
COMMIT_SHA="${{ github.sha }}"
132+
133+
# Source image with commit SHA
134+
SOURCE_IMAGE="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${COMMIT_SHA}"
135+
136+
# Destination tags
137+
VERSION_TAG="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}"
138+
139+
echo "Promoting ${SOURCE_IMAGE} to ${VERSION_TAG}"
140+
141+
# Re-tag the existing image (no rebuild)
142+
docker buildx imagetools create \
143+
"${SOURCE_IMAGE}" \
144+
--tag "${VERSION_TAG}"
145+
146+
echo "✅ Successfully promoted ${{ matrix.python_version }} to production"
147+
148+
- name: Promote latest tag
149+
if: matrix.python_version == '3.14'
150+
run: |
151+
COMMIT_SHA="${{ github.sha }}"
152+
SOURCE_IMAGE="ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${COMMIT_SHA}"
153+
LATEST_TAG="ghcr.io/${{ github.repository_owner }}/python-container-builder:latest"
154+
155+
echo "Promoting ${SOURCE_IMAGE} to ${LATEST_TAG}"
156+
157+
docker buildx imagetools create \
158+
"${SOURCE_IMAGE}" \
159+
--tag "${LATEST_TAG}"
160+
161+
echo "✅ Successfully promoted latest tag"
162+
163+
security-scan:
164+
name: Security Scan
165+
runs-on: ubuntu-latest
166+
needs: build
167+
permissions:
168+
contents: read
169+
security-events: write
170+
steps:
171+
- name: Run Trivy vulnerability scanner
172+
uses: aquasecurity/trivy-action@master
173+
with:
174+
image-ref: ghcr.io/${{ github.repository_owner }}/python-container-builder:3.14-${{ github.sha }}
175+
format: 'sarif'
176+
output: 'trivy-results.sarif'
177+
severity: 'CRITICAL,HIGH'
178+
179+
- name: Upload Trivy results to GitHub Security
180+
uses: github/codeql-action/upload-sarif@v4
181+
if: always()
182+
with:
183+
sarif_file: 'trivy-results.sarif'
184+
185+
- name: Print Trivy results summary
186+
uses: aquasecurity/trivy-action@master
187+
if: always()
188+
with:
189+
image-ref: ghcr.io/${{ github.repository_owner }}/python-container-builder:3.14-${{ github.sha }}
190+
format: 'table'
191+
severity: 'CRITICAL,HIGH'
192+
193+
test:
194+
name: Test Images
195+
runs-on: ubuntu-latest
196+
needs: build
197+
permissions:
198+
contents: read
199+
strategy:
200+
fail-fast: false
201+
matrix:
202+
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
203+
steps:
204+
- name: Test Python version
205+
run: |
206+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} python --version
207+
208+
- name: Test uv is installed
209+
run: |
210+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} uv --version
211+
212+
- name: Test poetry is installed
213+
run: |
214+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} poetry --version
215+
216+
- name: Test pipenv is installed
217+
run: |
218+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} pipenv --version
219+
220+
- name: Test pdm is installed
221+
run: |
222+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} pdm --version
223+
224+
- name: Test pip is installed in venv
225+
run: |
226+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} /.venv/bin/pip --version
227+
228+
- name: Test venv is created
229+
run: |
230+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} sh -c 'test -d /.venv && echo "venv exists"'
231+
232+
- name: Test package installation with uv
233+
run: |
234+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} sh -c 'uv pip install requests && /.venv/bin/python -c "import requests; print(f\"requests {requests.__version__} imported successfully\")"'
235+
236+
- name: Test package installation with pip
237+
run: |
238+
docker run --rm ghcr.io/${{ github.repository_owner }}/python-container-builder:${{ matrix.python_version }}-${{ github.sha }} sh -c '/.venv/bin/pip install click && /.venv/bin/python -c "import click; print(f\"click {click.__version__} imported successfully\")"'

0 commit comments

Comments
 (0)