From 8c12615f76631935cd8af8a41d387222125346e3 Mon Sep 17 00:00:00 2001 From: Marcelo Robert Santos Date: Mon, 9 Feb 2026 11:09:05 -0300 Subject: [PATCH 1/2] feat: add ghcr image publishing Closes #1739 --- .github/workflows/deploy-containers.yaml | 128 +++++++++++++++++++++++ backend/.dockerignore | 5 + backend/Dockerfile | 5 - docker-compose-next.yml | 74 +++++++++++++ docker-compose.k6.yml | 2 - docker-compose.test.yml | 1 - docker-compose.yml | 6 +- 7 files changed, 210 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/deploy-containers.yaml create mode 100644 backend/.dockerignore create mode 100644 docker-compose-next.yml diff --git a/.github/workflows/deploy-containers.yaml b/.github/workflows/deploy-containers.yaml new file mode 100644 index 000000000..68c9a48c3 --- /dev/null +++ b/.github/workflows/deploy-containers.yaml @@ -0,0 +1,128 @@ +name: Publish GHCR Images + +on: + workflow_dispatch: + +concurrency: + group: publish-ghcr-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + packages: write + +env: + REGISTRY: ghcr.io + +jobs: + build-and-push-backend: + runs-on: ubuntu-latest + outputs: + digest: ${{ steps.build.outputs.digest }} + steps: + - uses: actions/checkout@v4 + + - name: Compute image prefix + id: meta + run: echo "prefix=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push backend image + id: build + uses: docker/build-push-action@v6 + with: + context: ./backend + push: true + tags: | + ${{ steps.meta.outputs.prefix }}/dashboard-backend:latest + ${{ steps.meta.outputs.prefix }}/dashboard-backend:${{ github.sha }} + + build-and-push-dashboard: + runs-on: ubuntu-latest + outputs: + digest: ${{ steps.build.outputs.digest }} + steps: + - uses: actions/checkout@v4 + + - name: Compute image prefix + id: meta + run: echo "prefix=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push dashboard image + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: ./dashboard/Dockerfile + push: true + tags: | + ${{ steps.meta.outputs.prefix }}/dashboard-frontend:latest + ${{ steps.meta.outputs.prefix }}/dashboard-frontend:${{ github.sha }} + + build-and-push-proxy: + runs-on: ubuntu-latest + outputs: + digest: ${{ steps.build.outputs.digest }} + steps: + - uses: actions/checkout@v4 + + - name: Compute image prefix + id: meta + run: echo "prefix=${REGISTRY}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push proxy image + id: build + uses: docker/build-push-action@v6 + with: + context: ./proxy + push: true + tags: | + ${{ steps.meta.outputs.prefix }}/dashboard-proxy:latest + ${{ steps.meta.outputs.prefix }}/dashboard-proxy:${{ github.sha }} + + verify: + if: always() + needs: + - build-and-push-backend + - build-and-push-dashboard + - build-and-push-proxy + runs-on: ubuntu-latest + steps: + - name: Image publish summary + run: | + echo "### Published GHCR Images" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Image | Digest |" >> "$GITHUB_STEP_SUMMARY" + echo "| ----- | ------ |" >> "$GITHUB_STEP_SUMMARY" + echo "| dashboard-backend | \`${{ needs.build-and-push-backend.outputs.digest }}\` |" >> "$GITHUB_STEP_SUMMARY" + echo "| dashboard-frontend | \`${{ needs.build-and-push-dashboard.outputs.digest }}\` |" >> "$GITHUB_STEP_SUMMARY" + echo "| dashboard-proxy | \`${{ needs.build-and-push-proxy.outputs.digest }}\` |" >> "$GITHUB_STEP_SUMMARY" diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 000000000..5f2647394 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,5 @@ +__pycache__ +.pytest_cache +*.pyc + +tests_submissions diff --git a/backend/Dockerfile b/backend/Dockerfile index 1619ca69c..5045f576d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -45,11 +45,6 @@ RUN O='0 1 2' \ PYTHONOPTIMIZE=$N poetry run python -m compileall -q $D || exit 1; \ done -ARG BACKEND_VOLUME_DIR -ENV BACKEND_VOLUME_DIR=${BACKEND_VOLUME_DIR} -RUN echo "Building with volume on: $BACKEND_VOLUME_DIR" -VOLUME ${BACKEND_VOLUME_DIR} - # Expose both application and metrics ports EXPOSE 8000 8001 diff --git a/docker-compose-next.yml b/docker-compose-next.yml new file mode 100644 index 000000000..74b2ea33e --- /dev/null +++ b/docker-compose-next.yml @@ -0,0 +1,74 @@ +volumes: + backend-data: + runtime-data: + static-data: + dashboard-db-data: + +networks: + public: + private: + +services: + dashboard_db: + image: postgres:17 + env_file: + - .env.db + volumes: + - dashboard-db-data:/var/lib/postgresql/data + networks: + - private + ports: + - "5434:5432" + + redis: + image: redis:8.0-M04-alpine + restart: always + networks: + - private + ports: + - 6379:6379 + + backend: + image: ${IMAGE_REGISTRY:-ghcr.io}/${IMAGE_OWNER}/${IMAGE_REPOSITORY}/dashboard-backend:${IMAGE_TAG} + volumes: + - backend-data:${BACKEND_VOLUME_DIR:-/volume_data} + restart: always + networks: + - private + - public + ports: + - target: 8000 + published: 8000 + protocol: tcp + - target: 8001 + published: 8001 + protocol: tcp + depends_on: + - redis + - dashboard_db + env_file: + - .env.backend + environment: + DB_DEFAULT_HOST: ${DB_DEFAULT_HOST:-dashboard_db} + + dashboard: + image: ${IMAGE_REGISTRY:-ghcr.io}/${IMAGE_OWNER}/${IMAGE_REPOSITORY}/dashboard-frontend:${IMAGE_TAG} + volumes: + - static-data:/data/static + + proxy: + image: ${IMAGE_REGISTRY:-ghcr.io}/${IMAGE_OWNER}/${IMAGE_REPOSITORY}/dashboard-proxy:${IMAGE_TAG} + restart: always + depends_on: + - backend + - dashboard + networks: + - public + volumes: + - static-data:/data/static + ports: + - target: 80 + published: 80 + protocol: tcp + env_file: + - .env.proxy diff --git a/docker-compose.k6.yml b/docker-compose.k6.yml index 509959592..bfc951338 100644 --- a/docker-compose.k6.yml +++ b/docker-compose.k6.yml @@ -38,8 +38,6 @@ services: test-backend: build: context: ./backend - args: - BACKEND_VOLUME_DIR: /volume_data volumes: - test-backend-data:/volume_data networks: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 359c689fb..6fbf3b382 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -32,7 +32,6 @@ services: build: context: ./backend args: - BACKEND_VOLUME_DIR: /volume_data INSTALL_DEV_DEPS: "true" volumes: - test-backend-data:/volume_data diff --git a/docker-compose.yml b/docker-compose.yml index 82e92778e..169b13039 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,8 +47,7 @@ services: backend: build: context: ./backend - args: - BACKEND_VOLUME_DIR: ${BACKEND_VOLUME_DIR:-/volume_data} + image: ghcr.io/${IMAGE_NAME:-local}/backend:latest volumes: - backend-data:${BACKEND_VOLUME_DIR:-/volume_data} restart: always @@ -78,12 +77,13 @@ services: build: context: . dockerfile: ./dashboard/Dockerfile - image: dashboard:latest + image: ghcr.io/${IMAGE_NAME:-local}/dashboard:latest volumes: - static-data:/data/static proxy: build: ./proxy + image: ghcr.io/${IMAGE_NAME:-local}/proxy:latest restart: always depends_on: - backend From 3327dfb2f1a24a9253dc87dab5cc810ee08c1b6c Mon Sep 17 00:00:00 2001 From: Marcelo Robert Santos Date: Fri, 20 Feb 2026 08:49:00 -0300 Subject: [PATCH 2/2] docs: add doc for ghcr publishing --- README.md | 19 +++++++++++++++++++ docs/Onboarding.md | 3 +++ 2 files changed, 22 insertions(+) diff --git a/README.md b/README.md index 5445e34c3..5b199c9b0 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ A web app built with [React](https://react.dev/) + [Typescript](https://www.type A Python http server built with [Django](https://www.djangoproject.com/) + [DRF](https://www.django-rest-framework.org/), to see more information check the backend [README](/backend/README.md). +## Quick run + +If you want to just run the project, you can try out pre-built images with the [docker-compose-next.yml](./docker-compose-next.yml) file. This pulls images from GHCR and runs them locally without needing to rebuild them. You may still need to set up environment variables, so read the docs. + ## Build ### Frontend @@ -133,6 +137,21 @@ docker compose up --build To deploy to prod you need to push a tag in the `release/YYYYMMDD.N` format like: `release/20240910.0` +### Publishing container images to GHCR + +The workflow `.github/workflows/deploy-containers.yaml` publishes Docker images for the three services used by the dashboard stack: + +- `dashboard-backend` (from `./backend`) +- `dashboard-frontend` (from `./dashboard/Dockerfile`) +- `dashboard-proxy` (from `./proxy`) + +This workflow is manual for now (`workflow_dispatch`) and pushes images to GHCR under `ghcr.io//` with two tags for each image: + +- `latest` +- `${{ github.sha }}` + +At the end of the run, the workflow writes an image digest summary in the GitHub Actions job summary. + ## Test results email reports See details about our new [notifications](docs/notifications.md) system. diff --git a/docs/Onboarding.md b/docs/Onboarding.md index 41131f8f6..b30f86b42 100644 --- a/docs/Onboarding.md +++ b/docs/Onboarding.md @@ -171,6 +171,9 @@ docker logs Definition of Done: The KernelCI Dashboard (backend and frontend) is running via Docker and accessible locally. +> [!NOTE] +> The Task 6 teaches you how to build and run the project locally during development. Note that you can also run the project using pre-built images generated by the [deploy-containers](../.github/workflows/deploy-containers.yaml) workflow, using the [docker-compose-next](../docker-compose-next.yml) file to pull and run these pre-built images without rebuilding them. + ### Task 7: Complete a real Task 1. Go to https://github.com/kernelci/dashboard/issues and search for issues with the label `good first issue`. 2. Pick any of those and assign to yourself.