From 461cb098f05f9234d486c7537bba3c33a781a2b3 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Sat, 18 Oct 2025 01:07:49 +0200 Subject: [PATCH] chore(ci): build and release docker image, build documentation (#7) We expand the tests CI run to first build the main image, build and deploy the documentation pages, run the test suite and publish the docker image in the github docker registry. --- .github/workflows/build-docker.yml | 147 +++++++++++++++++++++++++++++ .github/workflows/test.yml | 60 ------------ README.md | 2 +- docker/test/Dockerfile | 4 +- 4 files changed, 149 insertions(+), 64 deletions(-) create mode 100644 .github/workflows/build-docker.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml new file mode 100644 index 0000000..a3c6cfb --- /dev/null +++ b/.github/workflows/build-docker.yml @@ -0,0 +1,147 @@ +name: Build and test + +on: + push: + branches: + - main + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + REGISTRY: ghcr.io + APP_IMAGE_NAME: ${{ github.repository }} + NGINX_IMAGE_NAME: ${{ github.repository }}-nginx + +jobs: + build-test-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for application image + id: meta-app + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.APP_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Extract metadata for nginx image + id: meta-nginx + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.NGINX_IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build application image + uses: docker/build-push-action@v5 + with: + context: . + file: docker/production/Dockerfile + load: true + tags: kompass:test + cache-from: | + type=gha,scope=app-${{ github.ref_name }} + type=gha,scope=app-master + type=gha,scope=app-main + type=registry,ref=ghcr.io/${{ github.repository }}:latest + cache-to: type=gha,mode=max,scope=app-${{ github.ref_name }} + build-args: | + BUILDKIT_INLINE_CACHE=1 + + - name: Build documentation + run: | + # Create output directory with proper permissions + mkdir -p docs-output + chmod 777 docs-output + + # Run sphinx-build inside the container + docker run --rm \ + -v ${{ github.workspace }}/docs:/app/docs:ro \ + -v ${{ github.workspace }}/docs-output:/app/docs-output \ + kompass:test \ + bash -c "cd /app/docs && sphinx-build -b html source /app/docs-output" + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs-output + destination_dir: ${{ github.ref == 'refs/heads/main' && '.' || github.ref_name }} + keep_files: true + + - name: Run tests + run: make test-only + + - name: Check coverage + run: | + COVERAGE=$(python3 -c "import json; data=json.load(open('docker/test/htmlcov/coverage.json')); print(data['totals']['percent_covered'])") + echo "Coverage: ${COVERAGE}%" + if (( $(echo "$COVERAGE < 100" | bc -l) )); then + echo "Error: Coverage is ${COVERAGE}%, must be 100%" + exit 1 + fi + + - name: Tag and push application image + if: github.event_name != 'pull_request' + run: | + # Tag the built image with all required tags + echo "${{ steps.meta-app.outputs.tags }}" | while read -r tag; do + docker tag kompass:test "$tag" + docker push "$tag" + done + + - name: Build and push nginx image + if: github.event_name != 'pull_request' + uses: docker/build-push-action@v5 + with: + context: docker/production/nginx + file: docker/production/nginx/Dockerfile + push: true + tags: ${{ steps.meta-nginx.outputs.tags }} + labels: ${{ steps.meta-nginx.outputs.labels }} + cache-from: | + type=gha,scope=nginx-${{ github.ref_name }} + type=gha,scope=nginx-master + type=gha,scope=nginx-main + type=registry,ref=ghcr.io/${{ github.repository }}-nginx:latest + cache-to: type=gha,mode=max,scope=nginx-${{ github.ref_name }} + build-args: | + BUILDKIT_INLINE_CACHE=1 + + - name: Output image tags + if: github.event_name != 'pull_request' + run: | + echo "Application image tags:" + echo "${{ steps.meta-app.outputs.tags }}" + echo "" + echo "Nginx image tags:" + echo "${{ steps.meta-nginx.outputs.tags }}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 7605281..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Tests - -on: - push: - branches: - - main - pull_request: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Cache Docker layers - uses: actions/cache@v4 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ hashFiles('requirements.txt', 'docker/test/Dockerfile') }} - restore-keys: | - ${{ runner.os }}-buildx- - - - name: Build Docker image with cache - run: | - cd docker/test - docker buildx build \ - --cache-from type=local,src=/tmp/.buildx-cache \ - --cache-to type=local,dest=/tmp/.buildx-cache-new,mode=max \ - --load \ - -t kompass:test \ - -f Dockerfile \ - ../../ - - - name: Move cache - run: | - rm -rf /tmp/.buildx-cache - mv /tmp/.buildx-cache-new /tmp/.buildx-cache - - - name: Run tests - run: make test-only - - - name: Check coverage - run: | - COVERAGE=$(python3 -c "import json; data=json.load(open('docker/test/htmlcov/coverage.json')); print(data['totals']['percent_covered'])") - echo "Coverage: ${COVERAGE}%" - if (( $(echo "$COVERAGE < 100" | bc -l) )); then - echo "Error: Coverage is ${COVERAGE}%, must be 100%" - exit 1 - fi diff --git a/README.md b/README.md index 9fc1c8c..e5c47f0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # jdav Kompass -![Build Status](https://github.com/chrisflav/kompass/actions/workflows/test.yml/badge.svg?branch=main) +![Build Status](https://github.com/chrisflav/kompass/actions/workflows/build-docker.yml/badge.svg?branch=main) Kompass is an administration platform designed for local sections of the Young German Alpine Club. It provides tools to contact and (automatically) manage members, groups, material, excursions and statements. diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile index 3005167..47f31d4 100644 --- a/docker/test/Dockerfile +++ b/docker/test/Dockerfile @@ -22,8 +22,6 @@ ENV PATH="/app/.local/bin:$PATH" # install requirements COPY --chown=app:app ./requirements.txt /app/requirements.txt -# we install uwsgi here to check if packages dependencies are resolved, but we don't actually -# need uwsgi in test -RUN pip install coverage uwsgi -r requirements.txt +RUN pip install uwsgi -r requirements.txt COPY --chown=app:app . /app