deps(backend): bump pillow from 11.0.0 to 12.2.0 in /backend #45
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] | |
| tags: ["v*"] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write # release job needs to create GitHub Releases | |
| packages: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| # ----- Backend ----------------------------------------------------------- | |
| backend: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: backend | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: backend/requirements.txt | |
| - run: pip install -r requirements.txt | |
| - name: Django system checks | |
| env: | |
| DJANGO_SECRET_KEY: ci-only | |
| DJANGO_DEBUG: "false" | |
| run: python manage.py check | |
| - name: Run tests | |
| env: | |
| DJANGO_SECRET_KEY: ci-only | |
| DJANGO_DEBUG: "false" | |
| run: python manage.py test --noinput --verbosity 2 | |
| # ----- Frontend ---------------------------------------------------------- | |
| frontend: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: frontend | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-node@v5 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| cache-dependency-path: frontend/package-lock.json | |
| - run: npm ci | |
| - run: npm run lint | |
| - run: npx vue-tsc --noEmit | |
| - run: npm run build | |
| - name: i18n parity (en/fr key counts match) | |
| run: | | |
| python3 - <<'PY' | |
| import json, sys | |
| en = json.load(open('src/locales/en.json')) | |
| fr = json.load(open('src/locales/fr.json')) | |
| def keys(d, p=''): | |
| out = set() | |
| for k, v in d.items(): | |
| q = f'{p}.{k}' if p else k | |
| out |= keys(v, q) if isinstance(v, dict) else {q} | |
| return out | |
| en_k, fr_k = keys(en), keys(fr) | |
| if en_k != fr_k: | |
| missing_in_fr = en_k - fr_k | |
| missing_in_en = fr_k - en_k | |
| print(f"locale parity FAILED. en={len(en_k)}, fr={len(fr_k)}") | |
| if missing_in_fr: print("missing in fr:", sorted(missing_in_fr)[:20]) | |
| if missing_in_en: print("missing in en:", sorted(missing_in_en)[:20]) | |
| sys.exit(1) | |
| print(f"locale parity OK ({len(en_k)} keys)") | |
| PY | |
| # ----- Docker image ------------------------------------------------------ | |
| # Publish to GHCR ONLY on tag v* — every release goes through an explicit | |
| # ``git tag vX.Y.Z`` so the registry never holds untagged main-branch builds. | |
| # PRs and main pushes still run backend+frontend for lint/test feedback; | |
| # they just don't produce an image. | |
| docker: | |
| needs: [backend, frontend] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: docker/setup-qemu-action@v4 | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Compute image tags | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ghcr.io/${{ github.repository_owner }}/hostcraft | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest | |
| - name: Build & push multi-arch image | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: backend/Dockerfile | |
| 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 | |
| # ----- GitHub Release --------------------------------------------------- | |
| # Every published image gets a matching GitHub Release. The job is gated | |
| # on a successful image build, so the registry and the Releases page can | |
| # never drift: if an image is published, its release exists; if it | |
| # doesn't, neither does. | |
| # | |
| # Notes come from the tag annotation (``git tag -a vX.Y.Z -m "..."``) | |
| # when present, otherwise we let GitHub auto-generate from the commit | |
| # history since the previous tag. | |
| release: | |
| needs: [docker] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 # need full history for `git tag -l` | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${GITHUB_REF_NAME}" | |
| ANNOTATION="$(git tag -l --format='%(contents)' "$VERSION")" | |
| IMAGE="ghcr.io/${{ github.repository_owner }}/hostcraft:${VERSION}" | |
| FOOTER=$(cat <<EOF | |
| --- | |
| Container image: \`${IMAGE}\` (also tagged \`:${VERSION#v}\`, \`:latest\`). | |
| EOF | |
| ) | |
| if [ -n "$ANNOTATION" ]; then | |
| printf '%s\n%s' "$ANNOTATION" "$FOOTER" > /tmp/notes.md | |
| gh release create "$VERSION" \ | |
| --title "$VERSION" \ | |
| --notes-file /tmp/notes.md | |
| else | |
| gh release create "$VERSION" \ | |
| --title "$VERSION" \ | |
| --generate-notes \ | |
| --notes "$FOOTER" | |
| fi |