Logos: Add CI/CD workflows and container registry support#407
Logos: Add CI/CD workflows and container registry support#407wasnertobias wants to merge 4 commits intomainfrom
Logos: Add CI/CD workflows and container registry support#407Conversation
- 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>
📝 WalkthroughWalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
Logos: Add CI/CD workflows and container registry support
There was a problem hiding this comment.
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.yamlto use pre-built images from GitHub Container Registry for the server, UI, and landing page. - Add
logos/docker-compose.dev.yamlthat keeps localbuildinstructions for all Logos services for development usage. - Introduce
Logos - BuildandLogos - Deploy to ProdGitHub Actions workflows to build/push Docker images to GHCR and automatically deploy to the production VM after successful builds onmain.
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>
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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.
| 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) }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd .github/workflows && cat -n logos_build-and-push-docker.yml | head -100Repository: ls1intum/edutelligence
Length of output: 4775
🏁 Script executed:
find .github/workflows -name "*.yml" -o -name "*.yaml" | xargs grep -l "ghcr.io" | head -5Repository: ls1intum/edutelligence
Length of output: 316
🏁 Script executed:
head -30 .github/workflows/athena_build-and-push-docker.ymlRepository: ls1intum/edutelligence
Length of output: 864
🏁 Script executed:
grep -n "permissions:" .github/workflows/*.yml | head -20Repository: 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@v1If 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
- Repo/org default token is read-only, so you must explicitly request
packages: writeas shown above (GitHub has been moving defaults toward least privilege). [4][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).
| 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. |
There was a problem hiding this comment.
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.
| 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.
| | File | Purpose | | ||
| |------|---------| | ||
| | `docker-compose.yaml` | **Production** - pulls pre-built images from GitHub Container Registry | | ||
| | `docker-compose.dev.yaml` | **Development** - builds images locally from source | |
There was a problem hiding this comment.
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.
| | 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.
|
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. |
Summary
ghcr.io/ls1intum/edutelligence/logos(server)ghcr.io/ls1intum/edutelligence/logos-uighcr.io/ls1intum/edutelligence/logos-landingdocker-compose.yamlto pull pre-built images from the registry (for production)docker-compose.dev.yamlfor local development with local buildsUsage
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Chores
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.