mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-01-16 19:02:09 +01:00
Adds a comprehensive Taskfile.yml to centralize all project scripts for building, testing, linting, and Docker image management. The GitHub Actions CI workflow is refactored to utilize these `task` commands, resulting in a cleaner, more readable, and maintainable configuration. This approach ensures consistency between local development and CI environments.
442 lines
17 KiB
YAML
442 lines
17 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: ['master', 'develop']
|
|
pull_request:
|
|
branches: ['master', 'develop', 'feature/*']
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE_NAME: ${{ github.repository }}
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
test:
|
|
name: Test
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
strategy:
|
|
matrix:
|
|
go:
|
|
- 1.21.x
|
|
- 1.22.x
|
|
- 1.23.x
|
|
- 1.24.x
|
|
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
|
|
- name: Set up Go ${{ matrix.go }}
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version: ${{ matrix.go }}
|
|
check-latest: true
|
|
|
|
- name: Install Task
|
|
uses: go-task/setup-task@v1
|
|
|
|
- name: Show build info
|
|
run: task info
|
|
|
|
- name: Download dependencies
|
|
run: task deps
|
|
|
|
- name: Build
|
|
run: task build
|
|
|
|
- name: Run tests with enhanced reporting
|
|
id: test
|
|
run: |
|
|
echo "## 🔧 Test Environment" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Go Version:** ${{ matrix.go }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **OS:** ubuntu-latest" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Timestamp:** $(date -u)" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
echo "Running tests with coverage..."
|
|
task test:coverage 2>&1 | tee test-output.log
|
|
|
|
# Extract test results for summary
|
|
TEST_STATUS=$?
|
|
TOTAL_TESTS=$(grep -c "=== RUN" test-output.log || echo "0")
|
|
PASSED_TESTS=$(grep -c "--- PASS:" test-output.log || echo "0")
|
|
FAILED_TESTS=$(grep -c "--- FAIL:" test-output.log || echo "0")
|
|
SKIPPED_TESTS=$(grep -c "--- SKIP:" test-output.log || echo "0")
|
|
|
|
# Generate test summary
|
|
echo "## 🧪 Test Results (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Total Tests | $TOTAL_TESTS |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Passed | ✅ $PASSED_TESTS |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Failed | ❌ $FAILED_TESTS |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Skipped | ⏭️ $SKIPPED_TESTS |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Status | $([ $TEST_STATUS -eq 0 ] && echo "✅ PASSED" || echo "❌ FAILED") |" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Add package breakdown
|
|
echo "### 📦 Package Test Results" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Package | Status |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|---------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Extract package results
|
|
grep "^ok\|^FAIL" test-output.log | while read line; do
|
|
if [[ $line == ok* ]]; then
|
|
pkg=$(echo $line | awk '{print $2}')
|
|
echo "| $pkg | ✅ PASS |" >> $GITHUB_STEP_SUMMARY
|
|
elif [[ $line == FAIL* ]]; then
|
|
pkg=$(echo $line | awk '{print $2}')
|
|
echo "| $pkg | ❌ FAIL |" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
done
|
|
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Add detailed results if tests failed
|
|
if [ $TEST_STATUS -ne 0 ]; then
|
|
echo "### ❌ Failed Tests Details" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
grep -A 10 "--- FAIL:" test-output.log | head -100 >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
# Set outputs for other steps
|
|
echo "test-status=$TEST_STATUS" >> $GITHUB_OUTPUT
|
|
echo "total-tests=$TOTAL_TESTS" >> $GITHUB_OUTPUT
|
|
echo "passed-tests=$PASSED_TESTS" >> $GITHUB_OUTPUT
|
|
echo "failed-tests=$FAILED_TESTS" >> $GITHUB_OUTPUT
|
|
|
|
# Exit with the original test status
|
|
exit $TEST_STATUS
|
|
|
|
- name: Generate coverage report
|
|
if: always()
|
|
run: |
|
|
if [ -f coverage/coverage.out ]; then
|
|
COVERAGE=$(go tool cover -func=coverage/coverage.out | grep total | awk '{print $3}')
|
|
|
|
echo "## 📊 Code Coverage (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Total Coverage: $COVERAGE**" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Add coverage by package
|
|
echo "<details>" >> $GITHUB_STEP_SUMMARY
|
|
echo "<summary>Click to expand 📋 Coverage by Package details</summary>" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Package | Coverage |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|---------|----------|" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Create temporary file for package coverage aggregation
|
|
temp_coverage=$(mktemp)
|
|
|
|
# Extract package-level coverage data
|
|
go tool cover -func=coverage/coverage.out | grep -v total | while read line; do
|
|
if [[ $line == *".go:"* ]]; then
|
|
# Extract package path from file path (everything before the filename)
|
|
filepath=$(echo "$line" | awk '{print $1}')
|
|
pkg_path=$(dirname "$filepath" | sed 's|github.com/kjanat/articulate-parser/||' | sed 's|^\./||')
|
|
coverage=$(echo "$line" | awk '{print $3}' | sed 's/%//')
|
|
|
|
# Use root package if no subdirectory
|
|
if [[ "$pkg_path" == "." || "$pkg_path" == "" ]]; then
|
|
pkg_path="root"
|
|
fi
|
|
|
|
echo "$pkg_path $coverage" >> "$temp_coverage"
|
|
fi
|
|
done
|
|
|
|
# Aggregate coverage by package (average)
|
|
awk '{
|
|
packages[$1] += $2;
|
|
counts[$1]++
|
|
}
|
|
END {
|
|
for (pkg in packages) {
|
|
avg = packages[pkg] / counts[pkg]
|
|
printf "| %s | %.1f%% |\n", pkg, avg
|
|
}
|
|
}' $temp_coverage | sort >> $GITHUB_STEP_SUMMARY
|
|
|
|
rm -f $temp_coverage
|
|
|
|
echo "</details>" >> $GITHUB_STEP_SUMMARY
|
|
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "## ⚠️ Coverage Report" >> $GITHUB_STEP_SUMMARY
|
|
echo "No coverage file generated" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
- name: Upload test artifacts
|
|
if: failure()
|
|
uses: actions/upload-artifact@v5
|
|
with:
|
|
name: test-results-go-${{ matrix.go }}
|
|
path: |
|
|
test-output.log
|
|
coverage/
|
|
retention-days: 7
|
|
|
|
- name: Run linters
|
|
run: |
|
|
echo "## 🔍 Static Analysis (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Run go vet
|
|
VET_OUTPUT=$(task lint:vet 2>&1 || echo "")
|
|
VET_STATUS=$?
|
|
|
|
if [ $VET_STATUS -eq 0 ]; then
|
|
echo "✅ **go vet:** No issues found" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "❌ **go vet:** Issues found" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "$VET_OUTPUT" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Run go fmt check
|
|
FMT_OUTPUT=$(task lint:fmt 2>&1 || echo "")
|
|
FMT_STATUS=$?
|
|
|
|
if [ $FMT_STATUS -eq 0 ]; then
|
|
echo "✅ **go fmt:** All files properly formatted" >> $GITHUB_STEP_SUMMARY
|
|
else
|
|
echo "❌ **go fmt:** Files need formatting" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "$FMT_OUTPUT" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
fi
|
|
|
|
# Exit with error if any linter failed
|
|
if [ $VET_STATUS -ne 0 ] || [ $FMT_STATUS -ne 0 ]; then
|
|
exit 1
|
|
fi
|
|
|
|
- name: Job Summary
|
|
if: always()
|
|
run: |
|
|
echo "## 📋 Job Summary (Go ${{ matrix.go }})" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Step | Status |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Dependencies | ✅ Success |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Build | ✅ Success |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Tests | ${{ steps.test.outcome == 'success' && '✅ Success' || '❌ Failed' }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Coverage | ${{ job.status == 'success' && '✅ Generated' || '⚠️ Partial' }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Static Analysis | ${{ job.status == 'success' && '✅ Clean' || '❌ Issues' }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Code Formatting | ${{ job.status == 'success' && '✅ Clean' || '❌ Issues' }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Upload coverage reports to Codecov
|
|
uses: codecov/codecov-action@v5
|
|
with:
|
|
files: ./coverage/coverage.out
|
|
flags: Go ${{ matrix.go }}
|
|
slug: kjanat/articulate-parser
|
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
|
|
- name: Upload test results to Codecov
|
|
if: ${{ !cancelled() }}
|
|
uses: codecov/test-results-action@v1
|
|
with:
|
|
flags: Go ${{ matrix.go }}
|
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
|
|
docker-test:
|
|
name: Docker Build Test
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version-file: go.mod
|
|
check-latest: true
|
|
|
|
- name: Install Task
|
|
uses: go-task/setup-task@v1
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Build Docker image using Task
|
|
run: task docker:build
|
|
|
|
- name: Test Docker image using Task
|
|
run: |
|
|
echo "## 🧪 Docker Image Tests" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Run Task docker test
|
|
task docker:test
|
|
|
|
echo "**Testing help command:**" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
docker run --rm articulate-parser:latest --help >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Test image size
|
|
IMAGE_SIZE=$(docker image inspect articulate-parser:latest --format='{{.Size}}' | numfmt --to=iec-i --suffix=B)
|
|
echo "**Image size:** $IMAGE_SIZE" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
dependency-review:
|
|
name: Dependency Review
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
if: github.event_name == 'pull_request'
|
|
steps:
|
|
- name: 'Checkout Repository'
|
|
uses: actions/checkout@v5
|
|
|
|
- name: 'Dependency Review'
|
|
uses: actions/dependency-review-action@v4
|
|
with:
|
|
fail-on-severity: moderate
|
|
comment-summary-in-pr: always
|
|
|
|
docker:
|
|
name: Docker Build & Push
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
needs: ['test']
|
|
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/feature/docker'))
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Login to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
|
|
- name: Log in to GitHub Container Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Extract metadata
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: |
|
|
${{ env.IMAGE_NAME }}
|
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
labels: |
|
|
org.opencontainers.image.title=Articulate Parser
|
|
org.opencontainers.image.description=A powerful CLI tool to parse Articulate Rise courses and export them to multiple formats including Markdown HTML and DOCX. Supports media extraction content cleaning and batch processing for educational content conversion.
|
|
org.opencontainers.image.vendor=kjanat
|
|
org.opencontainers.image.licenses=MIT
|
|
org.opencontainers.image.url=https://github.com/${{ github.repository }}
|
|
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
|
org.opencontainers.image.documentation=https://github.com/${{ github.repository }}/blob/master/DOCKER.md
|
|
|
|
- name: Build and push Docker image
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
# Multi-architecture build - Docker automatically provides TARGETOS, TARGETARCH, etc.
|
|
# Based on Go's supported platforms from 'go tool dist list'
|
|
platforms: |
|
|
linux/amd64
|
|
linux/arm64
|
|
linux/arm/v7
|
|
linux/386
|
|
linux/ppc64le
|
|
linux/s390x
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
annotations: ${{ steps.meta.outputs.labels }}
|
|
build-args: |
|
|
VERSION=${{ github.ref_type == 'tag' && github.ref_name || github.sha }}
|
|
BUILD_TIME=${{ github.event.head_commit.timestamp }}
|
|
GIT_COMMIT=${{ github.sha }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=A powerful CLI tool to parse Articulate Rise courses and export them to multiple formats including Markdown HTML and DOCX. Supports media extraction content cleaning and batch processing for educational content conversion.
|
|
sbom: true
|
|
provenance: true
|
|
|
|
- name: Generate Docker summary
|
|
run: |
|
|
echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Image:** \`ghcr.io/${{ github.repository }}\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Tags built:**" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Features:**" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Platforms:** linux/amd64, linux/arm64, linux/arm/v7, linux/386, linux/ppc64le, linux/s390x" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Architecture optimization:** ✅ Native compilation for each platform" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Multi-arch image description:** ✅ Enabled" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **SBOM (Software Bill of Materials):** ✅ Generated" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Provenance attestation:** ✅ Generated" >> $GITHUB_STEP_SUMMARY
|
|
echo "- **Security scanning:** Ready for vulnerability analysis" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Usage:**" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
|
echo "# Pull the image" >> $GITHUB_STEP_SUMMARY
|
|
echo "docker pull ghcr.io/${{ github.repository }}:latest" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "# Run with help" >> $GITHUB_STEP_SUMMARY
|
|
echo "docker run --rm ghcr.io/${{ github.repository }}:latest --help" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "# Process a local file (mount current directory)" >> $GITHUB_STEP_SUMMARY
|
|
echo "docker run --rm -v \$(pwd):/workspace ghcr.io/${{ github.repository }}:latest /workspace/input.json markdown /workspace/output.md" >> $GITHUB_STEP_SUMMARY
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
|
|
# Security and quality analysis workflows
|
|
codeql-analysis:
|
|
name: CodeQL Analysis
|
|
uses: ./.github/workflows/codeql.yml
|
|
permissions:
|
|
security-events: write
|
|
packages: read
|
|
actions: read
|
|
contents: read
|