Skip to content

feat: publish frontend Docker image to GHCR (issue #239)#240

Merged
AmintaCCCP merged 3 commits into
mainfrom
feat/frontend-ghcr-image-239
Jul 11, 2026
Merged

feat: publish frontend Docker image to GHCR (issue #239)#240
AmintaCCCP merged 3 commits into
mainfrom
feat/frontend-ghcr-image-239

Conversation

@AmintaCCCP

@AmintaCCCP AmintaCCCP commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #239. Publish the project's frontend as a pre-built Docker image to GHCR, mirroring the existing backend image, so users can either docker run the image directly or use docker-compose up -d without cloning/building locally.

Changes

  • New .github/workflows/docker-publish-frontend.yml — independent of the backend workflow; image github-stars-manager-frontend; triggers on main/tag/workflow_dispatch; multi-arch amd64+arm64; latest/semver/sha- tags (identical scheme to backend).
  • New nginx.conf.template + modified Dockerfile — backend proxy upstream made configurable via BACKEND_HOST env (default backend:3000), rendered at startup with envsubst.
  • New .dockerignore — slims the frontend build context (excludes .git, server, node_modules, etc.).
  • Modified docker-compose.yml — frontend now pulls the published GHCR image (FRONTEND_IMAGE_TAG), keeps a commented local build: . option, sets BACKEND_HOST.
  • Docs DOCKER.md / README.md / README_zh.md — document the frontend image, FRONTEND_IMAGE_TAG and BACKEND_HOST.
  • Removed dead nginx.conf (superseded by the template).

Protected (untouched)

  • docker-publish.yml (backend image), build-desktop.yml (desktop packaging), server/Dockerfile.

Verification

  • Local docker build succeeds; container serves / (HTTP 200) and proxies /api/ to the configured upstream for both default and external BACKEND_HOST.

Note

Do not merge to main — the maintainer will test and merge manually.

Summary by CodeRabbit

  • New Features
    • Added pre-built frontend Docker image support for Docker Compose and direct runs.
    • Enabled multi-architecture frontend Docker image publishing with automated version/SHA tags.
    • Frontend now renders Nginx config at startup to route /api via BACKEND_HOST.
  • Bug Fixes
    • Improved Nginx behavior by making the DNS resolver configurable and refining SPA CORS headers.
  • Documentation
    • Updated Docker/README (English + Chinese) with FRONTEND_IMAGE_TAG, expanded image tag guidance, and clearer quick-start commands.
  • Chores
    • Added .dockerignore to reduce Docker build context.

- Add docker-publish-frontend.yml workflow mirroring the backend one
  (image github-stars-manager-frontend, multi-arch amd64/arm64,
  latest/semver/sha tags)
- Make backend proxy upstream configurable via BACKEND_HOST env
  (default backend:3000) using nginx.conf.template + envsubst
- Add .dockerignore to slim the frontend build context
- Switch docker-compose frontend to the published GHCR image,
  keeping a local build option
- Update DOCKER.md / README docs for the frontend image
- Remove dead nginx.conf (superseded by nginx.conf.template)
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68a9d318-37ec-489b-b055-0d97e0d6e087

📥 Commits

Reviewing files that changed from the base of the PR and between 4f265a3 and a287924.

📒 Files selected for processing (2)
  • Dockerfile
  • nginx.conf.template
🚧 Files skipped from review as they are similar to previous changes (2)
  • Dockerfile
  • nginx.conf.template

📝 Walkthrough

Walkthrough

The frontend now publishes multi-architecture Docker images to GHCR, uses tagged prebuilt images in Compose, configures Nginx with BACKEND_HOST at startup, and documents prebuilt and local deployment options.

Changes

Frontend GHCR deployment

Layer / File(s) Summary
Configurable frontend runtime
Dockerfile, nginx.conf.template, docker-compose.yml
The production container renders Nginx configuration from BACKEND_HOST and RESOLVER, proxies /api/ requests through the configured host, and Compose uses a parameterized prebuilt frontend image.
GHCR image publishing pipeline
.dockerignore, .github/workflows/docker-publish-frontend.yml
GitHub Actions builds and publishes linux/amd64 and linux/arm64 frontend images with generated tags and caching; Docker build context exclusions are added.
Prebuilt-image deployment guidance
DOCKER.md, README.md, README_zh.md
Deployment documentation adds frontend image pulls, image tag pinning, backend host configuration, and local build instructions.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DockerCompose
  participant GHCR
  participant FrontendContainer
  participant BackendService
  DockerCompose->>GHCR: Pull tagged frontend image
  GHCR-->>DockerCompose: Return image
  DockerCompose->>FrontendContainer: Start with BACKEND_HOST
  FrontendContainer->>FrontendContainer: Render Nginx configuration
  FrontendContainer->>BackendService: Proxy /api/ requests
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: publishing the frontend Docker image to GHCR.
Linked Issues check ✅ Passed The PR matches #239 by publishing the frontend image, tagging it, updating compose, and documenting the new deployment flow.
Out of Scope Changes check ✅ Passed The additional Dockerfile, nginx, and ignore-file changes support the GHCR publishing workflow and runtime configurability, so no clear out-of-scope work stands out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/frontend-ghcr-image-239

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces pre-built frontend Docker images published to GHCR, updates the documentation and docker-compose configuration to support them, and makes the Nginx backend API proxy host configurable via a BACKEND_HOST environment variable using envsubst. Feedback on these changes highlights two issues in nginx.conf.template: first, using a variable in proxy_pass forces runtime DNS resolution which can break in non-Docker environments, so directly substituting BACKEND_HOST into proxy_pass is recommended; second, there is a CORS specification violation where Access-Control-Allow-Origin is set to '*' while Access-Control-Allow-Credentials is set to 'true'.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread nginx.conf.template
Comment on lines +36 to 37
set $backend_upstream ${BACKEND_HOST};
proxy_pass http://$backend_upstream;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using a variable in proxy_pass (e.g., proxy_pass http://$backend_upstream;) forces Nginx to resolve the hostname at runtime, which requires a resolver directive. Currently, nginx.conf.template hardcodes resolver 127.0.0.11; (the Docker internal DNS). This breaks DNS resolution in non-Docker environments like Kubernetes, AWS ECS, or custom networks where 127.0.0.11 is not the DNS server.

By directly substituting the BACKEND_HOST into proxy_pass (i.e., proxy_pass http://${BACKEND_HOST};), Nginx will resolve the hostname at startup using the system's standard resolver (from /etc/resolv.conf), making the image fully compatible with Kubernetes and other platforms without needing a hardcoded resolver directive.

Note: Once this is applied, you can also safely remove or comment out the resolver directive on line 8.

            proxy_pass http://${BACKEND_HOST};

Comment thread nginx.conf.template Outdated
Comment on lines 71 to 72
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

According to the CORS specification, the Access-Control-Allow-Origin header cannot be set to the wildcard * when Access-Control-Allow-Credentials is set to true. If a client attempts to make a credentialed request (e.g., including cookies or authorization headers) to these static assets, the browser will block the request and throw a CORS error.

Since these are static assets served by Nginx, credentials are not required for CORS. Removing the Access-Control-Allow-Credentials header resolves this conflict and ensures standard-compliant CORS behavior.

            add_header 'Access-Control-Allow-Origin' '*' always;

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
.dockerignore (1)

20-20: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add .env to .dockerignore to prevent secret leakage.

.env files often contain secrets (API keys, encryption keys). If the Dockerfile build stage uses COPY . ., these would be included in the build context sent to the Docker daemon.

🔒 Proposed addition
 *.md
 .DS_Store
+.env
+.env.*
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.dockerignore at line 20, Add .env to the ignore patterns in .dockerignore
so environment files containing secrets are excluded from the Docker build
context, including builds that use COPY . ..
.github/workflows/docker-publish-frontend.yml (1)

9-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add concurrency control to cancel redundant builds.

Multiple pushes to main in quick succession will trigger concurrent builds, wasting CI minutes and potentially causing GHA cache conflicts. Adding a concurrency block cancels superseded runs.

♻️ Proposed addition (top-level, after `env:`)
 env:
   REGISTRY: ghcr.io
   IMAGE_NAME: ${{ github.repository_owner }}/github-stars-manager-frontend
+
+concurrency:
+  group: docker-publish-frontend-${{ github.ref }}
+  cancel-in-progress: true
 
 jobs:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-publish-frontend.yml around lines 9 - 13, Add a
top-level concurrency configuration after the env block in the workflow, using a
stable group identifier for this workflow and enabling cancellation of
in-progress runs so newer pushes supersede redundant builds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docker-publish-frontend.yml:
- Around line 44-52: Update the semver tag entries in the workflow’s tags
configuration to include the “v” prefix, so version tags such as v0.6.2, v0.6,
and v0 are published and match the documented FRONTEND_IMAGE_TAG value. Preserve
the existing latest and SHA tag behavior.

In `@docker-compose.yml`:
- Around line 12-13: Update the BACKEND_HOST entry in the Compose service
environment to use Docker Compose variable substitution with a backend:3000
default, matching the existing FRONTEND_IMAGE_TAG and BACKEND_IMAGE_TAG pattern
so .env overrides are honored.

In `@DOCKER.md`:
- Around line 155-158: Update the local build section’s `docker run` command to
pass `BACKEND_HOST=host.docker.internal:3000`, matching the preceding pre-built
image example so standalone containers can reach the host backend.

---

Nitpick comments:
In @.dockerignore:
- Line 20: Add .env to the ignore patterns in .dockerignore so environment files
containing secrets are excluded from the Docker build context, including builds
that use COPY . ..

In @.github/workflows/docker-publish-frontend.yml:
- Around line 9-13: Add a top-level concurrency configuration after the env
block in the workflow, using a stable group identifier for this workflow and
enabling cancellation of in-progress runs so newer pushes supersede redundant
builds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0625513-58b2-4df0-99da-7d5f38a53fe7

📥 Commits

Reviewing files that changed from the base of the PR and between ce6fb38 and 58f1143.

📒 Files selected for processing (8)
  • .dockerignore
  • .github/workflows/docker-publish-frontend.yml
  • DOCKER.md
  • Dockerfile
  • README.md
  • README_zh.md
  • docker-compose.yml
  • nginx.conf.template

Comment thread .github/workflows/docker-publish-frontend.yml
Comment thread docker-compose.yml Outdated
Comment thread DOCKER.md
- nginx.conf.template: use static proxy_pass http://${BACKEND_HOST} (substituted
  at container start) instead of a runtime variable, and drop the hardcoded
  Docker-only resolver so the image works on Kubernetes/ECS too (gemini HIGH)
- nginx.conf.template: remove invalid Access-Control-Allow-Credentials:true
  alongside Access-Control-Allow-Origin:* (gemini MEDIUM)
- docker-compose.yml: BACKEND_HOST now uses ${BACKEND_HOST:-backend:3000} so
  .env overrides are honored (coderabbitai MAJOR)
- Docs: use unprefixed semver tags (0.6.2) consistent with the backend image
  (coderabbitai MAJOR); add -e BACKEND_HOST to the local-build docker run
  example (coderabbitai MINOR)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
nginx.conf.template (2)

5-8: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Preserve backend DNS re-resolution. BACKEND_HOST is rendered into a literal proxy_pass target at startup, so Nginx resolves it once and will keep using that IP until reload. If the backend container is recreated or the hostname changes, /api/ can start returning 502s. Add a runtime resolver or reload the frontend when the backend address changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nginx.conf.template` around lines 5 - 8, Update the nginx http configuration
around the mime-type and default-type directives to preserve runtime DNS
re-resolution for the backend used by the rendered proxy_pass target. Add an
appropriate resolver configuration, or otherwise ensure the frontend reloads
when BACKEND_HOST changes, so recreated backend containers do not leave proxy
requests targeting a stale IP.

35-40: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Forward the upstream host. Host $host sends the frontend hostname to the backend; $proxy_host matches the configured BACKEND_HOST and avoids host-based routing mismatches.

Proposed fix
-            proxy_set_header Host $host;
+            proxy_set_header Host $proxy_host;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@nginx.conf.template` around lines 35 - 40, Update the proxy_set_header Host
directive in the proxy_pass configuration to forward $proxy_host instead of
$host, ensuring the backend receives the configured BACKEND_HOST while leaving
the other forwarded headers unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@nginx.conf.template`:
- Around line 5-8: Update the nginx http configuration around the mime-type and
default-type directives to preserve runtime DNS re-resolution for the backend
used by the rendered proxy_pass target. Add an appropriate resolver
configuration, or otherwise ensure the frontend reloads when BACKEND_HOST
changes, so recreated backend containers do not leave proxy requests targeting a
stale IP.
- Around line 35-40: Update the proxy_set_header Host directive in the
proxy_pass configuration to forward $proxy_host instead of $host, ensuring the
backend receives the configured BACKEND_HOST while leaving the other forwarded
headers unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51db0c86-b2ee-4d3c-acb1-f91b6d4c53b8

📥 Commits

Reviewing files that changed from the base of the PR and between 58f1143 and 4f265a3.

📒 Files selected for processing (5)
  • DOCKER.md
  • README.md
  • README_zh.md
  • docker-compose.yml
  • nginx.conf.template
✅ Files skipped from review due to trivial changes (3)
  • README.md
  • README_zh.md
  • DOCKER.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docker-compose.yml

- nginx.conf.template: restore a configurable resolver (RESOLVER env, default
  127.0.0.11) so the backend upstream is re-resolved at runtime (recreated
  backend containers no longer leave /api/ hitting a stale IP) — coderabbitai
  Major stability finding
- nginx.conf.template: proxy_set_header Host $proxy_host (forwards the
  configured BACKEND_HOST) instead of $host — coderabbitai Minor
- Dockerfile: export RESOLVER (default 127.0.0.11) and include it in the
  envsubst substitution so the resolver is overridable for Kubernetes/ECS
  (keeps the image portable, addressing the earlier gemini portability concern
  without a hardcoded Docker-only resolver)
@AmintaCCCP AmintaCCCP merged commit 39d4a25 into main Jul 11, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Publish frontend Docker images to GHCR

1 participant