1+ name : CI/CD Pipeline
2+
3+ on :
4+ push :
5+ branches : [main, develop]
6+ tags : ["v*"]
7+ paths-ignore :
8+ - " **.md"
9+ - " docs/**"
10+ - " .github/ISSUE_TEMPLATE/**"
11+ pull_request :
12+ branches : [main, develop]
13+ paths-ignore :
14+ - " **.md"
15+ - " docs/**"
16+ - " .github/ISSUE_TEMPLATE/**"
17+ release :
18+ types : [published]
19+ workflow_dispatch :
20+ inputs :
21+ reason :
22+ description : ' Reason for manual trigger'
23+ required : false
24+ default : ' Manual trigger'
25+
26+ env :
27+ GO_VERSION : " 1.23"
28+ REGISTRY_IMAGE : johandevl/export-trakt-4-letterboxd
29+ GITHUB_REGISTRY : ghcr.io
30+ GITHUB_IMAGE : ghcr.io/johandevl/export_trakt_4_letterboxd
31+
32+ jobs :
33+ # Job 1: Test and Build Go Application
34+ test-and-build :
35+ name : Test and Build Go Application
36+ runs-on : ubuntu-latest
37+ outputs :
38+ version : ${{ steps.version.outputs.version }}
39+ is_tag : ${{ steps.version.outputs.is_tag }}
40+ coverage : ${{ steps.coverage.outputs.coverage }}
41+
42+ steps :
43+ - name : Checkout code
44+ uses : actions/checkout@v4
45+
46+ - name : Set up Go
47+ uses : actions/setup-go@v4
48+ with :
49+ go-version : ${{ env.GO_VERSION }}
50+ cache : true
51+
52+ - name : Install dependencies
53+ run : go mod download
54+
55+ - name : Run Go tests
56+ run : go test -v ./...
57+
58+ - name : Check test coverage
59+ id : coverage
60+ run : |
61+ # Run tests with coverage, excluding main package
62+ go test -coverprofile=coverage.out ./pkg/...
63+ COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | tr -d '%')
64+ echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT
65+ echo "Total coverage (excluding main package): $COVERAGE%"
66+ if (( $(echo "$COVERAGE < 70" | bc -l) )); then
67+ echo "⚠️ Code coverage is below 70%. Current: $COVERAGE%"
68+ echo "This is acceptable for now, but aim to improve coverage."
69+ else
70+ echo "✅ Coverage check passed! Current: $COVERAGE%, Target: 70%"
71+ fi
72+
73+ - name : Generate coverage report
74+ run : go tool cover -html=coverage.out -o coverage.html
75+
76+ - name : Upload coverage report
77+ uses : actions/upload-artifact@v4
78+ with :
79+ name : coverage-report
80+ path : coverage.html
81+
82+ - name : Get version info
83+ id : version
84+ run : |
85+ if [[ "${{ github.ref }}" == refs/tags/* ]]; then
86+ # For tag pushes, use the tag directly
87+ VERSION="${{ github.ref_name }}"
88+ echo "version=$VERSION" >> $GITHUB_OUTPUT
89+ echo "is_tag=true" >> $GITHUB_OUTPUT
90+ echo "🏷️ Building from tag: $VERSION"
91+ else
92+ # For branch pushes, get the latest tag
93+ git fetch --tags
94+ LATEST_TAG=$(git tag -l "v*" | grep -v "-" | sort -V | tail -n 1)
95+ if [ -z "$LATEST_TAG" ]; then
96+ LATEST_TAG="v1.0.0"
97+ fi
98+ echo "version=$LATEST_TAG" >> $GITHUB_OUTPUT
99+ echo "is_tag=false" >> $GITHUB_OUTPUT
100+ echo "📋 Building from branch, using latest tag: $LATEST_TAG"
101+ fi
102+
103+ - name : Build Go application
104+ run : |
105+ mkdir -p build
106+ go build -v -ldflags "-X main.version=${{ steps.version.outputs.version }} -X main.buildDate=$(date -u +'%Y-%m-%dT%H:%M:%SZ') -X main.gitCommit=${{ github.sha }}" -o build/export_trakt ./cmd/export_trakt
107+
108+ - name : Upload build artifact
109+ uses : actions/upload-artifact@v4
110+ with :
111+ name : export-trakt-binary
112+ path : build/export_trakt
113+
114+ # Job 2: Build and Push Docker Images
115+ docker :
116+ name : Build and Push Docker Images
117+ runs-on : ubuntu-latest
118+ needs : test-and-build
119+ if : github.event_name != 'pull_request'
120+ permissions :
121+ contents : read
122+ packages : write
123+ security-events : write
124+
125+ steps :
126+ - name : Checkout repository
127+ uses : actions/checkout@v4
128+
129+ - name : Download build artifact
130+ uses : actions/download-artifact@v4
131+ with :
132+ name : export-trakt-binary
133+ path : build
134+
135+ - name : Make binary executable
136+ run : chmod +x build/export_trakt
137+
138+ - name : Set up QEMU
139+ uses : docker/setup-qemu-action@v3
140+
141+ - name : Set up Docker Buildx
142+ uses : docker/setup-buildx-action@v3
143+
144+ - name : Extract metadata for Docker
145+ id : meta
146+ uses : docker/metadata-action@v5
147+ with :
148+ images : |
149+ ${{ env.REGISTRY_IMAGE }}
150+ ${{ env.GITHUB_IMAGE }}
151+ tags : |
152+ # Latest tag - ONLY for git tags (semantic versions/releases)
153+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
154+ # Main tag - for git tags (semantic versions) AND main branch pushes
155+ type=raw,value=main,enable=${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }}
156+ # Semantic version tag - ONLY for git tags (releases)
157+ type=raw,value=${{ needs.test-and-build.outputs.version }},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
158+ # Develop branch tag
159+ type=raw,value=develop,enable=${{ github.ref == 'refs/heads/develop' }}
160+ # PR tags
161+ type=ref,event=pr,prefix=PR-
162+
163+ - name : Log in to Docker Hub
164+ uses : docker/login-action@v3
165+ with :
166+ username : ${{ secrets.DOCKERHUB_USERNAME }}
167+ password : ${{ secrets.DOCKERHUB_TOKEN }}
168+
169+ - name : Log in to GitHub Container Registry
170+ uses : docker/login-action@v3
171+ with :
172+ registry : ${{ env.GITHUB_REGISTRY }}
173+ username : ${{ github.actor }}
174+ password : ${{ secrets.GITHUB_TOKEN }}
175+
176+ - name : Set build date
177+ id : build_date
178+ run : echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
179+
180+ - name : Build and push Docker image
181+ uses : docker/build-push-action@v6
182+ with :
183+ context : .
184+ file : ./Dockerfile
185+ push : true
186+ platforms : linux/amd64,linux/arm64,linux/arm/v7
187+ tags : ${{ steps.meta.outputs.tags }}
188+ labels : ${{ steps.meta.outputs.labels }}
189+ cache-from : type=gha,scope=${{ github.workflow }}-${{ github.ref_name }}
190+ cache-to : type=gha,mode=max,scope=${{ github.workflow }}-${{ github.ref_name }}
191+ build-args : |
192+ VERSION=${{ needs.test-and-build.outputs.version }}
193+ COMMIT_SHA=${{ github.sha }}
194+ BUILD_DATE=${{ steps.build_date.outputs.BUILD_DATE }}
195+
196+ - name : Scan image for vulnerabilities
197+ uses : aquasecurity/trivy-action@master
198+ with :
199+ image-ref : ${{ env.REGISTRY_IMAGE }}:${{ needs.test-and-build.outputs.version }}
200+ format : " sarif"
201+ output : " trivy-results.sarif"
202+
203+ - name : Upload Trivy scan results to GitHub Security tab
204+ uses : github/codeql-action/upload-sarif@v3
205+ with :
206+ sarif_file : " trivy-results.sarif"
207+
208+ # Job 3: Test Docker Image
209+ docker-test :
210+ name : Test Docker Image
211+ needs : [test-and-build, docker]
212+ runs-on : ubuntu-latest
213+ if : github.event_name != 'pull_request'
214+
215+ steps :
216+ - name : Checkout repository
217+ uses : actions/checkout@v4
218+
219+ - name : Set up Docker Buildx
220+ uses : docker/setup-buildx-action@v3
221+
222+ - name : Log in to Docker Hub
223+ uses : docker/login-action@v3
224+ with :
225+ username : ${{ secrets.DOCKERHUB_USERNAME }}
226+ password : ${{ secrets.DOCKERHUB_TOKEN }}
227+
228+ - name : Pull image for testing
229+ run : docker pull ${{ env.REGISTRY_IMAGE }}:${{ needs.test-and-build.outputs.version }}
230+
231+ - name : Test Docker image
232+ run : |
233+ # Create test directories
234+ mkdir -p ./test_config ./test_logs ./test_exports
235+
236+ # Basic image test - check if it runs properly
237+ docker run --rm \
238+ -v $(pwd)/test_config:/app/config \
239+ -v $(pwd)/test_logs:/app/logs \
240+ -v $(pwd)/test_exports:/app/exports \
241+ ${{ env.REGISTRY_IMAGE }}:${{ needs.test-and-build.outputs.version }} --help
242+
243+ echo "✅ Docker image tests passed successfully"
244+
245+ # Job 4: Notification and Summary
246+ notify :
247+ name : Notify and Summarize
248+ needs : [test-and-build, docker, docker-test]
249+ runs-on : ubuntu-latest
250+ if : always()
251+
252+ steps :
253+ - name : Check overall result
254+ id : check
255+ run : |
256+ if ${{ needs.test-and-build.result == 'success' && (needs.docker.result == 'success' || needs.docker.result == 'skipped') && (needs.docker-test.result == 'success' || needs.docker-test.result == 'skipped') }}; then
257+ echo "status=success" >> $GITHUB_OUTPUT
258+ echo "✅ All jobs completed successfully"
259+ else
260+ echo "status=failure" >> $GITHUB_OUTPUT
261+ echo "❌ One or more jobs failed"
262+ fi
263+
264+ - name : Create summary
265+ run : |
266+ echo "## 🎬 Export Trakt 4 Letterboxd CI/CD Summary" >> $GITHUB_STEP_SUMMARY
267+ echo "" >> $GITHUB_STEP_SUMMARY
268+ echo "### 📊 Build Results" >> $GITHUB_STEP_SUMMARY
269+ echo "- **Go Tests & Build**: ${{ needs.test-and-build.result }}" >> $GITHUB_STEP_SUMMARY
270+ echo "- **Coverage**: ${{ needs.test-and-build.outputs.coverage }}%" >> $GITHUB_STEP_SUMMARY
271+ echo "- **Version**: ${{ needs.test-and-build.outputs.version }}" >> $GITHUB_STEP_SUMMARY
272+ if [ "${{ github.event_name }}" != "pull_request" ]; then
273+ echo "- **Docker Build**: ${{ needs.docker.result }}" >> $GITHUB_STEP_SUMMARY
274+ echo "- **Docker Test**: ${{ needs.docker-test.result }}" >> $GITHUB_STEP_SUMMARY
275+ fi
276+ echo "" >> $GITHUB_STEP_SUMMARY
277+ if [ "${{ steps.check.outputs.status }}" == "success" ]; then
278+ echo "### ✅ Pipeline Status: SUCCESS" >> $GITHUB_STEP_SUMMARY
279+ if [ "${{ github.event_name }}" != "pull_request" ]; then
280+ echo "" >> $GITHUB_STEP_SUMMARY
281+ echo "### 🐳 Docker Images Published" >> $GITHUB_STEP_SUMMARY
282+ echo "- Docker Hub: \`${{ env.REGISTRY_IMAGE }}:${{ needs.test-and-build.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
283+ echo "- GitHub Packages: \`${{ env.GITHUB_IMAGE }}:${{ needs.test-and-build.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
284+ fi
285+ else
286+ echo "### ❌ Pipeline Status: FAILED" >> $GITHUB_STEP_SUMMARY
287+ echo "Please check the failed jobs above for more details." >> $GITHUB_STEP_SUMMARY
288+ fi
289+
290+ - name : Create release comment
291+ if : github.event_name == 'release' && steps.check.outputs.status == 'success'
292+ uses : actions/github-script@v7
293+ with :
294+ github-token : ${{ secrets.GITHUB_TOKEN }}
295+ script : |
296+ github.rest.issues.createComment({
297+ issue_number: context.issue.number,
298+ owner: context.repo.owner,
299+ repo: context.repo.repo,
300+ body: `🎉 **Release ${{ needs.test-and-build.outputs.version }} Successfully Deployed!**
301+
302+ ### 📦 Docker Images Available:
303+ - **Docker Hub**: \`${{ env.REGISTRY_IMAGE }}:${{ needs.test-and-build.outputs.version }}\`
304+ - **GitHub Packages**: \`${{ env.GITHUB_IMAGE }}:${{ needs.test-and-build.outputs.version }}\`
305+
306+ ### 🏗️ Build Information:
307+ - **Test Coverage**: ${{ needs.test-and-build.outputs.coverage }}%
308+ - **Supported Platforms**: linux/amd64, linux/arm64, linux/arm/v7
309+ - **Security Scan**: ✅ Passed
310+
311+ ### 🚀 Quick Start:
312+ \`\`\`bash
313+ docker pull ${{ env.REGISTRY_IMAGE }}:${{ needs.test-and-build.outputs.version }}
314+ \`\`\`
315+
316+ All systems are go! 🚀`
317+ })
0 commit comments