Skip to content

Logos: Add CI/CD workflows and container registry support#407

Open
wasnertobias wants to merge 4 commits intomainfrom
logos/add-workflows-merged
Open

Logos: Add CI/CD workflows and container registry support#407
wasnertobias wants to merge 4 commits intomainfrom
logos/add-workflows-merged

Conversation

@wasnertobias
Copy link
Member

@wasnertobias wasnertobias commented Jan 30, 2026

Summary

  • Add GitHub Actions build workflow that builds and pushes 3 Docker images to GitHub Container Registry:
    • ghcr.io/ls1intum/edutelligence/logos (server)
    • ghcr.io/ls1intum/edutelligence/logos-ui
    • ghcr.io/ls1intum/edutelligence/logos-landing
  • Add deploy workflow that automatically deploys to production VM after successful build
  • Update docker-compose.yaml to pull pre-built images from the registry (for production)
  • Add docker-compose.dev.yaml for local development with local builds

Usage

# Production (pull pre-built images)
docker compose -f logos/docker-compose.yaml up -d

# Development (build locally)
docker compose -f logos/docker-compose.dev.yaml up -d --build

Test plan

  • Verify build workflow triggers on push to main and PRs affecting logos/
  • Verify images are pushed to ghcr.io with correct tags
  • Verify deploy workflow triggers after successful build
  • Test local development with docker-compose.dev.yaml

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Added automated build workflow to build and tag container images.
    • Added automated production deployment workflow triggered after successful builds.
    • Switched production deployment to pull pre-built images for faster releases.
  • New Features

    • Added a dedicated development compose setup with reverse proxy, database, and local service orchestration.
  • Documentation

    • Documented separate production and development compose workflows and usage.

✏️ Tip: You can customize this high-level summary in your review settings.

wasnertobias and others added 2 commits January 30, 2026 11:57
- Add build workflow that builds and pushes 3 images to ghcr.io:
  - logos (server), logos-ui, logos-landing
- Add deploy workflow that pulls and deploys to prod VM
- Update docker-compose.yaml to use pre-built images from registry
- Add docker-compose.dev.yaml for local development with local builds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 30, 2026 11:19
@wasnertobias wasnertobias requested review from a team as code owners January 30, 2026 11:19
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Adds CI/CD workflows for building and deploying Logos Docker images, introduces a development docker-compose with Traefik and PostgreSQL, and modifies the production docker-compose to use pre-built images from the registry instead of local builds.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/logos_build-and-push-docker.yml, .github/workflows/logos_deploy-prod.yml
Adds a reusable build workflow that computes image tags based on event context and pushes three images (logos, logos-ui, logos-landing); adds a production deploy workflow that SSHs to the host, checks out the ref, pulls images, runs docker-compose up, and prunes images.
Docker Compose — Development
logos/docker-compose.dev.yaml
Adds a full development compose file with Traefik v3.6.1 reverse proxy, TLS config, service definitions for logos-server, logos-ui, logos-landing, PostgreSQL 17 with healthcheck and volumes, and internal/external networks for local development.
Docker Compose — Production
logos/docker-compose.yaml
Replaces per-service build directives with image references from ghcr.io and removes development-specific Traefik/dev routing blocks; keeps environment, ports, volumes and networking largely unchanged.
Docs
logos/README.md
Documents the two compose files and clarifies separate production (pull/up) and development (build/up) workflows and commands.

Sequence Diagram(s)

sequenceDiagram
    participant Event as Event (PR/Push/Release)
    participant GHA as GitHub Actions
    participant Compute as compute-tag job
    participant Reusable as Reusable build-and-push workflow
    participant Registry as ghcr.io
    participant DeployWF as Deploy workflow
    participant Host as Production Host (SSH)
    participant Compose as docker-compose services

    Event->>GHA: trigger build workflow
    GHA->>Compute: run compute-tag
    Compute-->>GHA: tag result, is_release, release_tag
    GHA->>Reusable: invoke build jobs for server/ui/landing with tags
    Reusable->>Registry: build & push image(s)
    Registry-->>Reusable: push complete
    Reusable-->>GHA: jobs complete
    GHA->>DeployWF: on main build success (or manual)
    DeployWF->>Host: SSH and run deployment script
    Host->>Registry: pull images
    Host->>Compose: docker-compose up -d
    Compose-->>Host: services started
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hop through tags and tiny builds,

pushing images over fields of guilds.
Traefik hums and Postgres sings,
Deploy at dawn—oh joyous things!
A rabbit cheers: "Containers, take wing!"

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding CI/CD workflows (GitHub Actions) and container registry support (GitHub Container Registry integration).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch logos/add-workflows-merged

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@wasnertobias wasnertobias changed the title Logos: Add CI/CD workflows and container registry support Logos: Add CI/CD workflows and container registry support Jan 30, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a proper CI/CD pipeline and container-registry-based deployment strategy for Logos, separating production use of pre-built images from local development builds.

Changes:

  • Switch logos/docker-compose.yaml to use pre-built images from GitHub Container Registry for the server, UI, and landing page.
  • Add logos/docker-compose.dev.yaml that keeps local build instructions for all Logos services for development usage.
  • Introduce Logos - Build and Logos - Deploy to Prod GitHub Actions workflows to build/push Docker images to GHCR and automatically deploy to the production VM after successful builds on main.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
logos/docker-compose.yaml Updated Logos services (server, UI, landing) to use ghcr.io/ls1intum/edutelligence/*:latest images instead of local builds, aligning production deployment with the new build workflow.
logos/docker-compose.dev.yaml Added a dev-only compose file mirroring the production topology but retaining local build instructions, enabling --build-based local development independent of GHCR.
.github/workflows/logos_build-and-push-docker.yml New workflow that computes tags and builds/pushes three Logos images to GHCR on PRs, pushes to main, and releases, reusing the existing shared build-and-push-docker-image workflow pattern used by Iris/Nebula.
.github/workflows/logos_deploy-prod.yml New deployment workflow that, on manual trigger or successful Logos - Build runs on main, SSHes into the prod VM, updates the repo, pulls the latest Logos images, and restarts the stack via the production compose file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @.github/workflows/logos_build-and-push-docker.yml:
- Around line 31-32: The workflow currently pins GitHub Actions using floating
tags (e.g., uses: actions/github-script@v7 and other occurrences referenced at
lines 66, 76, 86); replace each floating tag with the corresponding full
40-character commit SHA for that action and add an inline comment containing the
human-readable tag (e.g., // actions/github-script@v7) for traceability; update
uses entries such as actions/github-script, any reusable workflows referenced at
the noted locations, and ensure each replacement is the exact commit SHA from
the action's repository.

In @.github/workflows/logos_deploy-prod.yml:
- Line 19: Replace the floating action ref "appleboy/ssh-action@v1" with the
immutable commit SHA
"appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2" and add a
human-readable comment nearby noting the release version "v1.2.5" for context;
update the workflow step that uses the action (the line containing uses:
appleboy/ssh-action@v1) to use the SHA-pinned ref.
- Around line 25-29: The current checkout uses github.ref_name and then pulls,
which can move the working tree past the commit that produced the images;
replace the checkout/pull logic to fetch and checkout the exact SHA from the
triggering run by using github.event.workflow_run.head_sha (and remove the
subsequent git pull), i.e., fetch origin and git checkout the head_sha value so
the deployment uses the exact commit that built the images (update the lines
referencing git checkout ${{ github.ref_name }} and git pull origin ${{
github.ref_name }} to use github.event.workflow_run.head_sha and eliminate the
pull).

In `@logos/docker-compose.dev.yaml`:
- Around line 132-138: The Traefik router labels for the local dev router (e.g.
the "internal-ui-dev" router with labels like
"traefik.http.routers.internal-ui-dev.tls.certresolver=letsencrypt") are
requesting Let's Encrypt certs for localhost, which will fail; remove the
tls.certresolver=letsencrypt label (and similar certresolver labels on other
localhost routers) so Traefik stops attempting ACME for localhost, or
alternatively replace that label with configuration for a local CA (e.g. mkcert)
if you need real trusted certs for local development.

In `@logos/docker-compose.yaml`:
- Line 28: Replace the mutable image tag usage "image:
ghcr.io/ls1intum/edutelligence/logos:latest" with a pinned, immutable tag or
digest and make it override-friendly (e.g., use an environment variable like
${LOGOS_IMAGE}); update the docker-compose service that currently defines image:
ghcr.io/ls1intum/edutelligence/logos:latest to reference the env var or a
specific SemVer/SHA so deployments are deterministic, and document that the
value should be provided via a .env.prod file or CI pipeline at deploy time.

- Pin GitHub Actions to commit SHAs for supply chain security:
  - actions/github-script@v7 -> f28e40c7f34bde8b3046d885e986cb6290c5673b
  - appleboy/ssh-action@v1.2.5 -> 0ff4204d59e8e51228ff73bce53f80d53301dee2
  - ls1intum/.github@main -> e479a4eccbba098760e5a3ff47b73c7c7dfdb15b
- Deploy workflow: use head_sha for deterministic deployments
- docker-compose.dev.yaml: remove Let's Encrypt for localhost (use self-signed)
- docker-compose.yaml: use env var LOGOS_IMAGE_TAG for flexibility
- docker-compose.yaml: remove localhost dev routers (prod only)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.github/workflows/logos_build-and-push-docker.yml:
- Around line 64-95: Add a top-level permissions block granting packages: write
and contents: read so the reusable jobs (build-and-push-server,
build-and-push-ui, build-and-push-landing) can push to GHCR; reusable workflows
cannot elevate token permissions so the caller workflow must explicitly set
these permissions (add a top-level permissions stanza in the workflow YAML that
includes packages: write and contents: read).

In `@logos/README.md`:
- Around line 55-56: Update the ambiguous phrase "docker-compose file" in the
README to explicitly name which compose file to edit depending on environment
(e.g., docker-compose.yml for production or docker-compose.dev.yml for local
development) and instruct the reader to modify the environment section of the
logos-server container (the INITIAL_LLM_PROVIDER / initial LLM provider
variable) in the appropriate file so the initial provider is set on startup;
make the wording concise and show both options so users know which file to
change for prod vs dev.
- Around line 69-72: The Markdown table under README.md is not column-aligned
and triggers MD060; update the table so the pipes line up vertically by adding
spaces to align each column cell (e.g., align the header and each row so the |
characters form straight columns) for the two rows referencing
`docker-compose.yaml` and `docker-compose.dev.yaml`; ensure the separator row
(the ---- row) matches column widths and that there is a single space padding
left/right of each cell to satisfy markdownlint.

Comment on lines +64 to +95
build-and-push-server:
name: Build and Push Logos Server
needs: [compute-tag]
# ls1intum/.github@main
uses: ls1intum/.github/.github/workflows/build-and-push-docker-image.yml@e479a4eccbba098760e5a3ff47b73c7c7dfdb15b
with:
image-name: ghcr.io/ls1intum/edutelligence/logos
docker-file: ./logos/Dockerfile
docker-context: .
tags: ${{ needs.compute-tag.outputs.is_release == 'true' && format('ghcr.io/ls1intum/edutelligence/logos:latest,ghcr.io/ls1intum/edutelligence/logos:{0}', needs.compute-tag.outputs.release_tag) || format('ghcr.io/ls1intum/edutelligence/logos:{0}', needs.compute-tag.outputs.result) }}

build-and-push-ui:
name: Build and Push Logos UI
needs: [compute-tag]
# ls1intum/.github@main
uses: ls1intum/.github/.github/workflows/build-and-push-docker-image.yml@e479a4eccbba098760e5a3ff47b73c7c7dfdb15b
with:
image-name: ghcr.io/ls1intum/edutelligence/logos-ui
docker-file: ./logos/logos-ui/Dockerfile
docker-context: .
tags: ${{ needs.compute-tag.outputs.is_release == 'true' && format('ghcr.io/ls1intum/edutelligence/logos-ui:latest,ghcr.io/ls1intum/edutelligence/logos-ui:{0}', needs.compute-tag.outputs.release_tag) || format('ghcr.io/ls1intum/edutelligence/logos-ui:{0}', needs.compute-tag.outputs.result) }}

build-and-push-landing:
name: Build and Push Logos Landing
needs: [compute-tag]
# ls1intum/.github@main
uses: ls1intum/.github/.github/workflows/build-and-push-docker-image.yml@e479a4eccbba098760e5a3ff47b73c7c7dfdb15b
with:
image-name: ghcr.io/ls1intum/edutelligence/logos-landing
docker-file: ./logos/logos-landing/Dockerfile
docker-context: .
tags: ${{ needs.compute-tag.outputs.is_release == 'true' && format('ghcr.io/ls1intum/edutelligence/logos-landing:latest,ghcr.io/ls1intum/edutelligence/logos-landing:{0}', needs.compute-tag.outputs.release_tag) || format('ghcr.io/ls1intum/edutelligence/logos-landing:{0}', needs.compute-tag.outputs.result) }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd .github/workflows && cat -n logos_build-and-push-docker.yml | head -100

Repository: ls1intum/edutelligence

Length of output: 4775


🏁 Script executed:

find .github/workflows -name "*.yml" -o -name "*.yaml" | xargs grep -l "ghcr.io" | head -5

Repository: ls1intum/edutelligence

Length of output: 316


🏁 Script executed:

head -30 .github/workflows/athena_build-and-push-docker.yml

Repository: ls1intum/edutelligence

Length of output: 864


🏁 Script executed:

grep -n "permissions:" .github/workflows/*.yml | head -20

Repository: ls1intum/edutelligence

Length of output: 159


🌐 Web query:

GitHub Actions GHCR push permissions requirements reusable workflows

💡 Result:

To push a container image to GHCR (ghcr.io) from GitHub Actions, the job must run with a GITHUB_TOKEN that has packages: write permission, and (for most builds) contents: read. If you’re doing this via a reusable workflow, those permissions must be granted by the caller (the called workflow can only keep or reduce token permissions, not increase them). [2][3]

Minimal permissions (caller workflow)

Set permissions on the job that calls the reusable workflow (or at workflow top-level):

jobs:
  build_and_push:
    permissions:
      contents: read
      packages: write
    uses: org/repo/.github/workflows/push-ghcr.yml@v1

If you don’t set jobs.<job_id>.permissions, the called workflow only gets your repo/org default GITHUB_TOKEN permissions (often read-only), which commonly breaks GHCR pushes. [2][4]

In the reusable workflow (called)

You generally just log in with GITHUB_TOKEN and push:

on:
  workflow_call:

jobs:
  push:
    runs-on: ubuntu-latest
    steps:
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      # build + push steps...

The called workflow automatically has access to github.token / secrets.GITHUB_TOKEN; the key is that the caller must grant packages: write. [2][3]

Two common “still denied” causes

  1. Repo/org default token is read-only, so you must explicitly request packages: write as shown above (GitHub has been moving defaults toward least privilege). [4][2]
  2. Package access controls: for registries with granular permissions (like GHCR), ensure the package allows GitHub Actions/repository access as needed (package “Actions access” / inherited permissions). [1]

When you might also need contents: write

If your workflow creates/updates git refs (e.g., pushes tags/branches), GitHub requires explicit contents: write in addition to package permissions. [5]


Sources: About permissions for GitHub Packages [1], Reusing workflows / permissions behavior [2][3], GitHub Actions default token permissions update [4], GitHub changelog on needing contents: write for creating refs [5].


🏁 Script executed:

find .github -name "*.yml" -o -name "*.yaml" | xargs grep -l "token_permissions\|default.*permissions" 2>/dev/null || echo "No defaults found"

Repository: ls1intum/edutelligence

Length of output: 83


Add permissions block to enable GHCR push.

Reusable workflows cannot elevate token permissions, so the caller must explicitly grant packages: write (and contents: read) for GHCR pushes to succeed. Add this block to the workflow:

 on:
   pull_request:
     paths:
       - "logos/**"
       - ".github/workflows/logos_build-and-push-docker.yml"
       - "!logos/README.md"
   push:
     branches:
       - main
     paths:
       - "logos/**"
       - ".github/workflows/logos_build-and-push-docker.yml"
       - "!logos/README.md"
   release:
     types:
       - created

+permissions:
+  contents: read
+  packages: write
🤖 Prompt for AI Agents
In @.github/workflows/logos_build-and-push-docker.yml around lines 64 - 95, Add
a top-level permissions block granting packages: write and contents: read so the
reusable jobs (build-and-push-server, build-and-push-ui, build-and-push-landing)
can push to GHCR; reusable workflows cannot elevate token permissions so the
caller workflow must explicitly set these permissions (add a top-level
permissions stanza in the workflow YAML that includes packages: write and
contents: read).

Comment on lines +55 to 56
In the docker-compose file, adjust the environment section of the logos-server
container to specify the initial LLM provider that Logos should connect to after startup.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify which compose file to edit for provider configuration.

Now that there are separate production and dev compose files, “docker-compose file” is ambiguous.

✏️ Suggested wording
-   In the docker-compose file, adjust the environment section of the logos-server
+   In `docker-compose.yaml` (production) or `docker-compose.dev.yaml` (development),
+   adjust the environment section of the logos-server
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
In the docker-compose file, adjust the environment section of the logos-server
container to specify the initial LLM provider that Logos should connect to after startup.
In `docker-compose.yaml` (production) or `docker-compose.dev.yaml` (development),
adjust the environment section of the logos-server
container to specify the initial LLM provider that Logos should connect to after startup.
🤖 Prompt for AI Agents
In `@logos/README.md` around lines 55 - 56, Update the ambiguous phrase
"docker-compose file" in the README to explicitly name which compose file to
edit depending on environment (e.g., docker-compose.yml for production or
docker-compose.dev.yml for local development) and instruct the reader to modify
the environment section of the logos-server container (the INITIAL_LLM_PROVIDER
/ initial LLM provider variable) in the appropriate file so the initial provider
is set on startup; make the wording concise and show both options so users know
which file to change for prod vs dev.

Comment on lines +69 to +72
| File | Purpose |
|------|---------|
| `docker-compose.yaml` | **Production** - pulls pre-built images from GitHub Container Registry |
| `docker-compose.dev.yaml` | **Development** - builds images locally from source |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align the table pipes to satisfy markdownlint (MD060).

Static analysis flags table column alignment in this section.

🧹 Suggested alignment
-| File | Purpose |
-|------|---------|
-| `docker-compose.yaml` | **Production** - pulls pre-built images from GitHub Container Registry |
-| `docker-compose.dev.yaml` | **Development** - builds images locally from source |
+| File                      | Purpose                                                             |
+|---------------------------|---------------------------------------------------------------------|
+| `docker-compose.yaml`     | **Production** - pulls pre-built images from GitHub Container Registry |
+| `docker-compose.dev.yaml` | **Development** - builds images locally from source                 |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| File | Purpose |
|------|---------|
| `docker-compose.yaml` | **Production** - pulls pre-built images from GitHub Container Registry |
| `docker-compose.dev.yaml` | **Development** - builds images locally from source |
| File | Purpose |
|---------------------------|---------------------------------------------------------------------|
| `docker-compose.yaml` | **Production** - pulls pre-built images from GitHub Container Registry |
| `docker-compose.dev.yaml` | **Development** - builds images locally from source |
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 71-71: Table column style
Table pipe does not align with header for style "aligned"

(MD060, table-column-style)


[warning] 71-71: Table column style
Table pipe does not align with header for style "aligned"

(MD060, table-column-style)


[warning] 72-72: Table column style
Table pipe does not align with header for style "aligned"

(MD060, table-column-style)


[warning] 72-72: Table column style
Table pipe does not align with header for style "aligned"

(MD060, table-column-style)

🤖 Prompt for AI Agents
In `@logos/README.md` around lines 69 - 72, The Markdown table under README.md is
not column-aligned and triggers MD060; update the table so the pipes line up
vertically by adding spaces to align each column cell (e.g., align the header
and each row so the | characters form straight columns) for the two rows
referencing `docker-compose.yaml` and `docker-compose.dev.yaml`; ensure the
separator row (the ---- row) matches column widths and that there is a single
space padding left/right of each cell to satisfy markdownlint.

@github-actions
Copy link

github-actions bot commented Feb 6, 2026

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions github-actions bot added the stale label Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant