-
-
Notifications
You must be signed in to change notification settings - Fork 1
343 lines (296 loc) Β· 12 KB
/
build.yml
File metadata and controls
343 lines (296 loc) Β· 12 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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
---
name: Build
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
- cron: "0 1 * * 6" # Weekly Saturday 01:00 UTC (full build)
- cron: "0 4 * * 1" # Weekly Monday 04:00 UTC (security-focused)
env:
CI: true
COMPOSE_VERSION: "v2.32.0"
PYTHON_VERSION: "3.13"
REGISTRY: ghcr.io
permissions:
contents: read
packages: write
jobs:
# ============================================================================
# CHANGE DETECTION - Determines if only documentation files were modified
# ============================================================================
detect-changes:
runs-on: ubuntu-latest
outputs:
code-changed: ${{ steps.filter.outputs.code-changed }}
steps:
- name: π Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: π Detect changed file types
id: filter
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.sha }}
BEFORE_SHA: ${{ github.event.before }}
run: |
# Always run full pipeline for scheduled builds and manual dispatches
if [[ "$EVENT_NAME" == "schedule" ]] || \
[[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
echo "code-changed=true" >> "$GITHUB_OUTPUT"
echo "βοΈ Scheduled/manual build β running full pipeline"
exit 0
fi
# Get changed files compared to base
if [[ "$EVENT_NAME" == "pull_request" ]]; then
CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
else
CHANGED_FILES=$(git diff --name-only "$BEFORE_SHA" "$HEAD_SHA")
fi
echo "π Changed files:"
echo "$CHANGED_FILES"
# Check if any non-markdown files were changed
NON_MD_FILES=$(echo "$CHANGED_FILES" | grep -v '\.md$' || true)
if [[ -z "$NON_MD_FILES" ]]; then
echo "code-changed=false" >> "$GITHUB_OUTPUT"
echo "π Only markdown files changed β skipping tests, security, and docker jobs"
else
echo "code-changed=true" >> "$GITHUB_OUTPUT"
echo "π» Code changes detected β running full pipeline"
fi
list-sub-projects:
needs: [detect-changes]
if: needs.detect-changes.outputs.code-changed == 'true'
uses: ./.github/workflows/list-sub-projects.yml
run-code-quality:
uses: ./.github/workflows/code-quality.yml
secrets: inherit
run-security:
needs: [detect-changes, run-code-quality]
if: needs.detect-changes.outputs.code-changed == 'true'
uses: ./.github/workflows/security.yml
secrets: inherit
permissions:
contents: read
security-events: write # Required for SARIF uploads to GitHub Security tab
run-docker-compose-validate:
needs: [detect-changes, run-code-quality]
if: needs.detect-changes.outputs.code-changed == 'true'
uses: ./.github/workflows/docker-compose-validate.yml
secrets: inherit
run-docker-validate:
needs: [list-sub-projects, detect-changes, run-code-quality]
if: needs.detect-changes.outputs.code-changed == 'true'
uses: ./.github/workflows/docker-validate.yml
secrets: inherit
with:
simple-matrix: ${{ needs.list-sub-projects.outputs.simple-matrix }}
# Test workflow runs all service tests in parallel
# Each service has its own job that runs independently
run-tests:
needs: [detect-changes, run-code-quality]
if: needs.detect-changes.outputs.code-changed == 'true'
uses: ./.github/workflows/test.yml
secrets: inherit
permissions:
contents: read
pull-requests: write # Required for coverage report comments
statuses: write # Required for coverage status creation (only used on PRs)
run-e2e-tests:
needs: [detect-changes, run-code-quality]
if: needs.detect-changes.outputs.code-changed == 'true'
uses: ./.github/workflows/e2e-test.yml
secrets: inherit
permissions:
contents: read
pull-requests: write # Required for coverage report comments
statuses: write # Required for coverage status creation (only used on PRs)
# ============================================================================
# AGGREGATE RESULTS - Waits for all parallel workflows to complete
# ============================================================================
aggregate-results:
runs-on: ubuntu-latest
needs:
- detect-changes
- run-code-quality
- run-tests
- run-e2e-tests
- run-security
- run-docker-compose-validate
- run-docker-validate
if: always()
steps:
- name: π Check all pipeline results
env:
CODE_CHANGED: ${{ needs.detect-changes.outputs.code-changed }}
run: |
echo "Pipeline Results Summary:"
echo " Code Changed: $CODE_CHANGED"
echo " Code Quality: ${{ needs.run-code-quality.result }}"
echo " Tests: ${{ needs.run-tests.result }}"
echo " E2E Tests: ${{ needs.run-e2e-tests.result }}"
echo " Security: ${{ needs.run-security.result }}"
echo " Docker Compose Validate: ${{ needs.run-docker-compose-validate.result }}"
echo " Docker Validate: ${{ needs.run-docker-validate.result }}"
# Code quality must always pass
if [[ "${{ needs.run-code-quality.result }}" == "failure" ]]; then
echo "β Code quality failed"
exit 1
fi
# For docs-only changes, skipped jobs are expected
if [[ "$CODE_CHANGED" == "false" ]]; then
echo "π Docs-only change β skipped jobs are expected"
echo "β
All required pipeline workflows passed!"
exit 0
fi
# For code changes, all jobs must pass (not fail)
if [[ "${{ needs.run-tests.result }}" == "failure" ]] || \
[[ "${{ needs.run-e2e-tests.result }}" == "failure" ]] || \
[[ "${{ needs.run-security.result }}" == "failure" ]] || \
[[ "${{ needs.run-docker-compose-validate.result }}" == "failure" ]] || \
[[ "${{ needs.run-docker-validate.result }}" == "failure" ]]; then
echo "β One or more pipeline workflows failed"
exit 1
fi
echo "β
All pipeline workflows passed!"
build-discogsography:
# Wait for aggregate-results (which ensures all quality gates pass) before building
needs: [list-sub-projects, detect-changes, aggregate-results]
if: needs.detect-changes.outputs.code-changed == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
include: ${{ fromJson(needs.list-sub-projects.outputs.matrix) }}
permissions:
contents: read
packages: write
steps:
- name: β±οΈ Start timer
id: timer
run: echo "start_time=$(date +%s)" >> "$GITHUB_OUTPUT"
- name: π·οΈ Set lowercase image name
id: image
run: |
echo "IMAGE_NAME=$(echo "${{ github.repository }}" | tr "[:upper:]" "[:lower:]")" >> "$GITHUB_ENV"
- name: π Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: π§Ή Free disk space
run: |
echo "π Disk space before cleanup:"
df -h
echo "ποΈ Removing unnecessary software..."
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
echo "π§Ή Cleaning Docker..."
docker system prune -af --volumes || true
echo "π Disk space after cleanup:"
df -h
- name: π Log in to the GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: π‘οΈ Anchore security scan - discogsography/${{ matrix.name }}
uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
with:
path: ${{ matrix.name }}
- name: π§ Setup Python and UV
uses: ./.github/actions/setup-python-uv
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: πΎ Setup Docker build cache
id: docker-cache
uses: ./.github/actions/docker-build-cache
with:
service-name: ${{ matrix.name }}
dockerfile-path: ${{ matrix.name }}/Dockerfile
use-cache: ${{ matrix.use_cache }}
- name: π§ Generate uv.lock for Docker build
run: uv sync --frozen
- name: π Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.name }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=ref,event=pr
type=schedule,pattern={{date 'YYYYMMDD'}}
- name: π§ Set up Docker Buildx
uses: docker/setup-buildx-action@v4
with:
platforms: linux/amd64
driver-opts: |
image=moby/buildkit:latest
network=host
- name: π Build and push Docker image to GitHub Container Registry - discogsography/${{ matrix.name }}
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.name }}/Dockerfile
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: true
sbom: true
cache-from: ${{ steps.docker-cache.outputs.cache-from }}
cache-to: ${{ steps.docker-cache.outputs.cache-to }}
build-args: |
BUILDKIT_INLINE_CACHE=1
BUILDKIT_CACHE_MOUNT_NS=discogsography
DOCKER_BUILDKIT=1
PYTHON_VERSION=${{ env.PYTHON_VERSION }}
BUILD_DATE=${{ github.event.head_commit.timestamp || github.event.repository.updated_at }}
BUILD_VERSION=${{ steps.meta.outputs.version }}
VCS_REF=${{ github.sha }}
- # Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
name: πΎ Move cache
if: ${{ matrix.use_cache }}
run: |
rm -rf ${{ runner.temp }}/.buildx-cache
mv ${{ runner.temp }}/.buildx-cache-new ${{ runner.temp }}/.buildx-cache
- name: π§Ή Cleanup build artifacts
if: always()
run: |
echo "ποΈ Pruning Docker build cache..."
docker buildx prune -f --filter until=1h || true
docker system prune -f --filter until=1h || true
echo "π Remaining disk space:"
df -h
- name: π Collect metrics
if: always()
run: |
end_time=$(date +%s)
duration=$((end_time - ${{ steps.timer.outputs.start_time }}))
echo "::notice title=Build Metrics::Service: ${{ matrix.name }}, Duration: ${duration}s, Cache Used: ${{ matrix.use_cache }}"
# Check cache hit rate
if [[ -n "${{ steps.docker-cache.outputs.cache-hit }}" ]]; then
echo "::notice title=Cache Performance::Docker cache hit for ${{ matrix.name }}"
fi
- name: π’ Send notification to Discord
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708 # v1.16.0
if: always()
with:
title: discogsography/${{ matrix.name }}
description: |
Build duration: ${{ steps.timer.outputs.duration || 'N/A' }}s
Cache used: ${{ matrix.use_cache }}
webhook: ${{ secrets.DISCORD_WEBHOOK }}