ci: bump codecov/codecov-action from 5 to 6 #102
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| packages: write | |
| id-token: write | |
| security-events: write | |
| attestations: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| jobs: | |
| semantic-version: | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' | |
| outputs: | |
| new_tag: ${{ steps.tag.outputs.new_tag }} | |
| changelog: ${{ steps.tag.outputs.changelog }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get latest tag | |
| id: get_tag | |
| run: | | |
| # Get the latest tag, or default to v0.0.0 | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") | |
| echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT | |
| echo "Latest tag: $LATEST_TAG" | |
| - name: Determine version bump | |
| id: bump | |
| run: | | |
| # Get the commit message | |
| COMMIT_MSG=$(git log -1 --pretty=%B) | |
| echo "Commit message: $COMMIT_MSG" | |
| # Determine bump type based on conventional commits | |
| if echo "$COMMIT_MSG" | grep -qiE '^(feat|fix|perf|refactor|style|test|docs|chore)!:' || echo "$COMMIT_MSG" | grep -qi 'BREAKING CHANGE'; then | |
| BUMP_TYPE="major" | |
| elif echo "$COMMIT_MSG" | grep -qiE '^feat(\(.+\))?:'; then | |
| BUMP_TYPE="minor" | |
| elif echo "$COMMIT_MSG" | grep -qiE '^(fix|perf)(\(.+\))?:'; then | |
| BUMP_TYPE="patch" | |
| else | |
| BUMP_TYPE="none" | |
| fi | |
| echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT | |
| echo "Bump type: $BUMP_TYPE" | |
| - name: Calculate new version | |
| id: tag | |
| run: | | |
| # Only version on main branch pushes | |
| if [ "${GITHUB_REF}" != "refs/heads/main" ]; then | |
| echo "Not on main; skipping versioning" | |
| echo "new_tag=" >> $GITHUB_OUTPUT | |
| echo "changelog=" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| LATEST_TAG="${{ steps.get_tag.outputs.latest_tag }}" | |
| BUMP_TYPE="${{ steps.bump.outputs.bump_type }}" | |
| if [ "$BUMP_TYPE" = "none" ]; then | |
| echo "No semantic commit detected, skipping versioning" | |
| echo "new_tag=" >> $GITHUB_OUTPUT | |
| echo "changelog=" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Remove 'v' prefix and split version | |
| VERSION=${LATEST_TAG#v} | |
| IFS='.' read -r -a VERSION_PARTS <<< "$VERSION" | |
| MAJOR=${VERSION_PARTS[0]:-0} | |
| MINOR=${VERSION_PARTS[1]:-0} | |
| PATCH=${VERSION_PARTS[2]:-0} | |
| # Bump version based on type | |
| case $BUMP_TYPE in | |
| major) | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| ;; | |
| minor) | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| ;; | |
| patch) | |
| PATCH=$((PATCH + 1)) | |
| ;; | |
| esac | |
| NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}" | |
| echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT | |
| echo "New tag: $NEW_TAG" | |
| # Get commit message for changelog | |
| COMMIT_MSG=$(git log -1 --pretty=%B) | |
| { | |
| echo "changelog<<CHANGELOG_EOF" | |
| echo "$COMMIT_MSG" | |
| echo "CHANGELOG_EOF" | |
| } >> $GITHUB_OUTPUT | |
| - name: Create and push tag | |
| if: steps.tag.outputs.new_tag != '' | |
| run: | | |
| NEW_TAG="${{ steps.tag.outputs.new_tag }}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag -a "$NEW_TAG" -m "Release $NEW_TAG" | |
| git push origin "$NEW_TAG" | |
| echo "✓ Created and pushed tag: $NEW_TAG" | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Validate package.json | |
| run: | | |
| if [ ! -f package.json ]; then | |
| echo "package.json not found" | |
| exit 1 | |
| fi | |
| echo "✓ package.json found" | |
| - name: Check for lockfile | |
| run: | | |
| if [ -f bun.lock ]; then | |
| echo "✓ bun.lock found" | |
| else | |
| echo "⚠ Warning: bun.lock not found" | |
| echo "Generating lockfile..." | |
| bun install --no-save | |
| fi | |
| - name: Upload lockfile if generated | |
| if: success() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: lockfile | |
| path: bun.lock | |
| retention-days: 1 | |
| if-no-files-found: ignore | |
| lint-and-test: | |
| runs-on: ubuntu-latest | |
| needs: validate | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Download lockfile if exists | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: lockfile | |
| - name: Install dependencies | |
| run: | | |
| # Skip frozen lockfile for Dependabot PRs | |
| if [[ "${{ github.actor }}" == "dependabot[bot]" ]]; then | |
| echo "Dependabot PR detected, installing without frozen lockfile..." | |
| bun install | |
| elif [ -f bun.lock ]; then | |
| echo "Installing with frozen lockfile..." | |
| bun install --frozen-lockfile | |
| else | |
| echo "Installing without lockfile..." | |
| bun install | |
| fi | |
| - name: Run linter | |
| run: bun run lint | |
| - name: Run unit tests with coverage | |
| run: bun run test:unit --coverage | |
| - name: Upload coverage to Codecov | |
| if: runner.os == 'Linux' | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./coverage/lcov.info | |
| fail_ci_if_error: false | |
| - name: Install Playwright | |
| run: bunx playwright install --with-deps | |
| - name: Build project | |
| run: bun run build | |
| - name: Run integration tests | |
| run: bun run test:integration | |
| - name: Upload built app | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: build | |
| path: build | |
| retention-days: 1 | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: test-results | |
| path: | | |
| test-results/ | |
| playwright-report/ | |
| coverage/ | |
| retention-days: 7 | |
| build-and-push: | |
| runs-on: ubuntu-latest | |
| needs: [lint-and-test, semantic-version] | |
| if: github.event_name != 'pull_request' && needs.lint-and-test.result == 'success' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Download built app | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: build | |
| path: build | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=ref,event=branch | |
| type=semver,pattern={{version}} | |
| type=sha,prefix={{branch}}-,enable={{is_default_branch}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=raw,value=${{ needs.semantic-version.outputs.new_tag }},enable=${{ needs.semantic-version.outputs.new_tag != '' }} | |
| - name: Build and push Docker image | |
| id: build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: true | |
| sbom: true | |
| - name: Generate artifact attestation | |
| uses: actions/attest-build-provenance@v4 | |
| with: | |
| subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| subject-digest: ${{ steps.build.outputs.digest }} | |
| push-to-registry: true | |
| - name: Run Trivy vulnerability scanner (SARIF) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| - name: Run Trivy vulnerability scanner (table) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} | |
| format: 'table' | |
| severity: 'CRITICAL,HIGH' | |
| validate-docker: | |
| runs-on: ubuntu-latest | |
| needs: lint-and-test | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Download built app | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: build | |
| path: build | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Build Docker image (validation only) | |
| id: build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| push: false | |
| load: true | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Run Trivy vulnerability scanner on PR | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:pr-${{ github.event.pull_request.number }} | |
| format: 'table' | |
| severity: 'CRITICAL,HIGH' | |
| exit-code: '1' | |
| create-release: | |
| runs-on: ubuntu-latest | |
| needs: [semantic-version, build-and-push] | |
| if: | | |
| github.event_name == 'push' && | |
| github.ref == 'refs/heads/main' && | |
| needs.semantic-version.outputs.new_tag != '' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.semantic-version.outputs.new_tag }} | |
| name: Release ${{ needs.semantic-version.outputs.new_tag }} | |
| body: | | |
| ## Changes | |
| ${{ needs.semantic-version.outputs.changelog }} | |
| ## Docker Image | |
| ```bash | |
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.semantic-version.outputs.new_tag }} | |
| ``` | |
| ## What's Changed | |
| **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ needs.semantic-version.outputs.new_tag }}...main | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true |