diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6820937..d0cbb8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,35 @@ concurrency: cancel-in-progress: true jobs: + # ── Detect which apps changed ────────────────────────────────────── + changes: + name: Detect changes + runs-on: ubuntu-latest + outputs: + api: ${{ steps.filter.outputs.api }} + scanner: ${{ steps.filter.outputs.scanner }} + banner: ${{ steps.filter.outputs.banner }} + admin-ui: ${{ steps.filter.outputs.admin-ui }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + api: + - 'apps/api/**' + scanner: + - 'apps/scanner/**' + banner: + - 'apps/banner/**' + admin-ui: + - 'apps/admin-ui/**' + + # ── API ──────────────────────────────────────────────────────────── api-lint: name: API Lint + needs: changes + if: needs.changes.outputs.api == 'true' runs-on: ubuntu-latest defaults: run: @@ -29,6 +56,8 @@ jobs: api-test: name: API Tests + needs: changes + if: needs.changes.outputs.api == 'true' runs-on: ubuntu-latest defaults: run: @@ -74,8 +103,11 @@ jobs: DATABASE_URL: postgresql://consentos_test:consentos_test@localhost:5432/consentos_test - run: pytest --cov=src --cov-report=term-missing -v + # ── Scanner ──────────────────────────────────────────────────────── scanner-lint: name: Scanner Lint + needs: changes + if: needs.changes.outputs.scanner == 'true' runs-on: ubuntu-latest defaults: run: @@ -92,6 +124,8 @@ jobs: scanner-test: name: Scanner Tests + needs: changes + if: needs.changes.outputs.scanner == 'true' runs-on: ubuntu-latest defaults: run: @@ -105,8 +139,11 @@ jobs: - run: pip install -e ".[dev]" - run: pytest --cov=src --cov-report=term-missing -v + # ── Banner ───────────────────────────────────────────────────────── banner-lint: name: Banner Lint & Typecheck + needs: changes + if: needs.changes.outputs.banner == 'true' runs-on: ubuntu-latest defaults: run: @@ -123,6 +160,8 @@ jobs: banner-test: name: Banner Tests + needs: changes + if: needs.changes.outputs.banner == 'true' runs-on: ubuntu-latest defaults: run: @@ -139,8 +178,9 @@ jobs: banner-build: name: Banner Build - runs-on: ubuntu-latest needs: [banner-test, banner-lint] + if: needs.changes.outputs.banner == 'true' + runs-on: ubuntu-latest defaults: run: working-directory: apps/banner @@ -163,8 +203,11 @@ jobs: echo "::warning::consent-loader.js is ${LOADER_SIZE} bytes (>20KB) — consider optimising" fi + # ── Admin UI ─────────────────────────────────────────────────────── admin-ui-lint: name: Admin UI Typecheck + needs: changes + if: needs.changes.outputs.admin-ui == 'true' runs-on: ubuntu-latest defaults: run: @@ -181,6 +224,8 @@ jobs: admin-ui-test: name: Admin UI Tests + needs: changes + if: needs.changes.outputs.admin-ui == 'true' runs-on: ubuntu-latest defaults: run: @@ -197,8 +242,9 @@ jobs: admin-ui-build: name: Admin UI Build - runs-on: ubuntu-latest needs: [admin-ui-test, admin-ui-lint] + if: needs.changes.outputs.admin-ui == 'true' + runs-on: ubuntu-latest defaults: run: working-directory: apps/admin-ui diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..45ceb42 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,30 @@ +name: PR Title + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +jobs: + lint: + name: Conventional commit title + runs-on: ubuntu-latest + steps: + - name: Check PR title + uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + chore + refactor + docs + test + style + perf + ci + build + requireScope: false + subjectPattern: ^.+$ + subjectPatternError: "PR title must follow conventional commits: type: description (e.g. feat: add cookie categories)" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..27b01a3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,181 @@ +# Cut a release and build container images. +# +# Triggered manually via ``workflow_dispatch`` (Actions tab → Run +# workflow) when you're ready to ship. Not on every PR merge. +# +# Flow: +# 1. ``ietf-tools/semver-action`` derives the next version from +# conventional commit messages since the last tag (feat → minor, +# fix → patch, breaking → major). +# 2. ``requarks/changelog-action`` generates release notes from the +# commit diff between the new and previous tags. +# 3. ``ncipollo/release-action`` creates the GitHub Release. +# 4. ``requarks/changelog-action`` writes CHANGELOG.md and +# ``stefanzweifel/git-auto-commit-action`` commits it back to +# master with ``[skip ci]``. +# 5. All three container images are built and pushed to GHCR, +# tagged with the semver version + ``latest``. + +name: Release + +on: + workflow_dispatch: + +jobs: + # ── Version + Release + Changelog ──────────────────────────────── + version: + runs-on: ubuntu-latest + permissions: + contents: write + outputs: + new: ${{ steps.semver.outputs.next }} + newStrict: ${{ steps.semver.outputs.nextStrict }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get next version + id: semver + uses: ietf-tools/semver-action@v1 + with: + token: ${{ github.token }} + branch: master + + - name: Generate release notes + id: changelog + uses: requarks/changelog-action@v1 + with: + token: ${{ github.token }} + fromTag: master + toTag: ${{ steps.semver.outputs.current }} + writeToFile: false + + - name: Create release + uses: ncipollo/release-action@v1.12.0 + with: + allowUpdates: true + draft: false + makeLatest: true + tag: ${{ steps.semver.outputs.next }} + body: ${{ steps.changelog.outputs.changes }} + token: ${{ github.token }} + + - name: Write CHANGELOG.md + uses: requarks/changelog-action@v1 + with: + token: ${{ github.token }} + fromTag: ${{ steps.semver.outputs.next }} + toTag: ${{ steps.semver.outputs.current }} + writeToFile: true + + - name: Commit CHANGELOG.md + uses: stefanzweifel/git-auto-commit-action@v4 + with: + branch: master + commit_message: "docs: update CHANGELOG.md for ${{ steps.semver.outputs.next }} [skip ci]" + file_pattern: CHANGELOG.md + + # ── Build and push container images ────────────────────────────── + build-api: + name: API image + needs: version + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/consentos/consentos-api + tags: | + type=semver,pattern={{version}},value=${{ needs.version.outputs.newStrict }} + type=semver,pattern={{major}}.{{minor}},value=${{ needs.version.outputs.newStrict }} + type=raw,value=latest + + - uses: docker/build-push-action@v6 + with: + context: apps/api + file: apps/api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + build-scanner: + name: Scanner image + needs: version + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/consentos/consentos-scanner + tags: | + type=semver,pattern={{version}},value=${{ needs.version.outputs.newStrict }} + type=semver,pattern={{major}}.{{minor}},value=${{ needs.version.outputs.newStrict }} + type=raw,value=latest + + - uses: docker/build-push-action@v6 + with: + context: apps/scanner + file: apps/scanner/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + build-admin-ui: + name: Admin UI image + needs: version + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v4 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - uses: docker/metadata-action@v5 + id: meta + with: + images: ghcr.io/consentos/consentos-admin-ui + tags: | + type=semver,pattern={{version}},value=${{ needs.version.outputs.newStrict }} + type=semver,pattern={{major}}.{{minor}},value=${{ needs.version.outputs.newStrict }} + type=raw,value=latest + + # Context is the repo root — the admin-ui Dockerfile pulls in + # apps/banner/ alongside apps/admin-ui/ and bundles the banner + # output at the nginx root. + - uses: docker/build-push-action@v6 + with: + context: . + file: apps/admin-ui/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bcae43..364523a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - ## [0.1.0] - 2026-03-18 Initial public release of ConsentOS.