Add Docker image build and publish pipeline - #108
Conversation
chore: Release 1.0.0
chore: Release v1.0.1
chore: Release 1.0.2
chore: Release 1.1.0
chore(release): Prepare 1.1.1
chore: Release 1.1.2
chore: Release 2.0.0-beta.1 Ailanthus
Code Review SummaryThis PR adds a comprehensive Docker pipeline including multi-stage builds and GitHub Actions integration. The implementation is solid but can be hardened for production security. 🚀 Key Improvements
💡 Minor Suggestions
🚨 Critical Issues
|
| # This is how a single Docker image serves deployments with different API URLs | ||
| # without rebuilding — Nuxt 3 bakes runtimeConfig.public into .output/ at | ||
| # build time, so we patch the built files before starting the server. | ||
| if [ -n "$NUXT_PUBLIC_API_BASE_URL" ]; then |
There was a problem hiding this comment.
The & character is a special backreference in sed replacement strings. If NUXT_PUBLIC_API_BASE_URL contains query parameters (e.g., ?key=val&other=1), the substitution will result in corrupted files because & will be replaced by the matched placeholder string. Additionally, using | as a delimiter will fail if the URL contains a pipe character. Escaping these characters ensures robust URL substitution.
| if [ -n "$NUXT_PUBLIC_API_BASE_URL" ]; then | |
| if [ -n "$NUXT_PUBLIC_API_BASE_URL" ]; then | |
| # Escape special characters for sed: & (backreference) and | (delimiter) | |
| SAFE_URL=$(printf '%s\n' "$NUXT_PUBLIC_API_BASE_URL" | sed 's/[&|]/\\&/g') | |
| find /app/.output -type f \( -name '*.mjs' -o -name '*.js' -o -name '*.cjs' \) \ | |
| -exec sed -i "s|http://__NUXT_PUBLIC_API_BASE_URL__|${SAFE_URL}|g" {} + | |
| fi |
|
|
||
| - name: Log in to GitHub Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: |
There was a problem hiding this comment.
The current workflow duplicates build logic and lacks layer caching. Consolidating into a single build step using docker/metadata-action is more maintainable and ensures the exact same image is used for all tags. Adding type=gha cache will significantly speed up subsequent runs by persisting layers between builds.
| with: | |
| - name: Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/trakli/webui | |
| tags: | | |
| type=sha,prefix=,format=short,enable=${{ github.event_name == 'push' }} | |
| type=raw,value=latest,enable=${{ github.event_name == 'release' }} | |
| type=ref,event=tag,enable=${{ github.event_name == 'release' }} | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max |
There was a problem hiding this comment.
Pull request overview
Adds containerization and CI publishing so the web UI can be built as a reusable Docker image and pushed to GitHub Container Registry for downstream deployment templates.
Changes:
- Add a multi-stage
Dockerfileto build Nuxt output and run it in a minimal runtime image. - Add a container entrypoint script to patch Nuxt baked
runtimeConfig.publicvalues at startup. - Add a GitHub Actions workflow to build and publish images to
ghcr.io/trakli/webui, plus a.dockerignore.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| Dockerfile | Multi-stage Node build/runtime image with entrypoint + healthcheck. |
| docker/entrypoint.sh | Runtime placeholder replacement before starting Nuxt server. |
| .github/workflows/build-and-publish.yml | CI job to build and push images on dev pushes and releases. |
| .dockerignore | Reduce Docker build context size and avoid leaking local/env files. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ | ||
| CMD wget -qO- http://localhost:3000/ || exit 1 |
| # so a single image works across deployments with different API URLs. | ||
|
|
||
| # ── Stage 1: Build ────────────────────────────────────────────────────────── | ||
| FROM node:lts AS builder |
| RUN npm run build | ||
|
|
||
| # ── Stage 2: Runtime ──────────────────────────────────────────────────────── | ||
| FROM node:lts-alpine |
| if [ -n "$NUXT_PUBLIC_API_BASE_URL" ]; then | ||
| find /app/.output -type f \( -name '*.mjs' -o -name '*.js' -o -name '*.cjs' \) \ | ||
| -exec sed -i "s|http://__NUXT_PUBLIC_API_BASE_URL__|${NUXT_PUBLIC_API_BASE_URL}|g" {} + | ||
| fi |
The webui has no published Docker image today. Every downstream deployment template (compose stack, Helm, cloud providers) needs ghcr.io/trakli/webui to exist. Dockerfile (multi-stage): - Stage 1 (node:lts): npm ci + npm run build with a placeholder API URL baked into the output. Nuxt 3 runtimeConfig.public values are embedded at build time, so we cannot set the real URL here. - Stage 2 (node:lts-alpine): copies only .output/ from the build stage. Alpine-based for a small final image. docker/entrypoint.sh: - At container start, sed-replaces the build-time placeholder (http://__NUXT_PUBLIC_API_BASE_URL__) in all .output/ JS files with the actual NUXT_PUBLIC_API_BASE_URL env var, then starts the Node server. Standard Nuxt 3 pattern for a single image across deployments. .github/workflows/build-and-publish.yml: - Mirrors the webservice workflow: SHA tag on dev push, latest + release semver tag on GitHub Release publish. Pushes to ghcr.io. .dockerignore: - Excludes node_modules, .nuxt, .output, .git, .env*, tests. Issue: trakli/webservice#308
f1db0bd to
eff30fd
Compare
Deploying trakli-dev with
|
| Latest commit: |
eff30fd
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a83abf8c.trakli-dev.pages.dev |
| Branch Preview URL: | https://feat-docker-image-pr2.trakli-dev.pages.dev |
| @@ -0,0 +1,10 @@ | |||
| node_modules | |||
There was a problem hiding this comment.
Excluding the Dockerfile and the .github directory from the build context is a best practice. It prevents meta-files from being included in layers and avoids unnecessary cache invalidation if only repository metadata changes.
| node_modules | |
| node_modules | |
| Dockerfile | |
| .github |
Deploying webui with
|
| Latest commit: |
eff30fd
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b74d7dc1.webui-9fh.pages.dev |
| Branch Preview URL: | https://feat-docker-image-pr2.webui-9fh.pages.dev |
|
#109 duplicates this? |
Adds a multi-stage Dockerfile, entrypoint script, CI workflow, and .dockerignore so the webui can be published as ghcr.io/trakli/webui — the image every downstream deployment template (compose stack, Helm, cloud providers) needs.