feat: Add Docker build and publish CI for web app#418
Conversation
|
@vishalmakwana111 is attempting to deploy a commit to the Inbox Zero Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. WalkthroughThis update introduces a new GitHub Actions workflow for automated Docker image publishing to GHCR on pushes to the main branch and semantic version tags. A new production Dockerfile is added to build the application container with Node 22 Alpine, pnpm, Prisma, Next.js, and dummy environment variables for build-time. The Docker Compose configuration is updated to use the published image from GHCR instead of building locally. Application code changes detect a dummy Sanity project ID to conditionally skip data fetching and static parameter generation during builds. Additionally, a Prettier plugin for Tailwind CSS is added as a development dependency. Minor stylistic and formatting improvements were made in several React components and TypeScript files. Changes
Sequence Diagram(s)sequenceDiagram
participant Developer
participant GitHub
participant GitHubActions
participant DockerBuild
participant GHCR
participant DockerCompose
participant Application
Developer->>GitHub: Push to main branch or tag vX.Y.Z
GitHub->>GitHubActions: Trigger publish-docker workflow
GitHubActions->>DockerBuild: Build Docker image using Dockerfile.prod
DockerBuild->>GHCR: Push image with tags (latest, semantic version)
GitHubActions->>GitHub: Complete workflow
Developer->>DockerCompose: Use docker-compose.yml to run container
DockerCompose->>Application: Pull and run image from GHCR
Possibly related PRs
Poem
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
apps/web/app/(app)/[emailAccountId]/automation/knowledge/KnowledgeBase.tsxOops! Something went wrong! :( ESLint: 9.24.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by apps/web/app/(app)/[emailAccountId]/clean/helpers.tsOops! Something went wrong! :( ESLint: 9.24.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by apps/web/app/(app)/[emailAccountId]/automation/knowledge/KnowledgeForm.tsxOops! Something went wrong! :( ESLint: 9.24.0 ESLint couldn't find an eslint.config.(js|mjs|cjs) file. From ESLint v9.0.0, the default configuration file is now eslint.config.js. https://eslint.org/docs/latest/use/configure/migration-guide If you still have problems after following the migration guide, please stop by
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (6)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
docker/Dockerfile.prod (1)
21-26: Consider using multi-stage build for a smaller production imageThe current approach installs all dependencies including dev dependencies without pruning them after the build, which results in a larger final image.
- # Copy the rest of the application code FIRST - COPY . . - - # Install ALL dependencies (including dev, no pruning) - # This will now run postinstall scripts *after* source code is copied - RUN pnpm install --frozen-lockfile + # Copy the rest of the application code + COPY . . + + # Install dependencies and run postinstall scripts + RUN pnpm install --frozen-lockfile && \ + pnpm --filter inbox-zero-ai exec -- prisma generate && \ + pnpm --filter inbox-zero-ai exec -- next build && \ + pnpm install --frozen-lockfile --prodapps/web/app/blog/post/[slug]/page.tsx (1)
13-13: Consider using consistent quote style across files.Minor consistency note: this file uses single quotes for the dummy project ID string, while sitemap.ts uses double quotes for the same value.
- if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === 'dummy-sanity-project-id-for-build') { + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === "dummy-sanity-project-id-for-build") {.github/workflows/publish-docker.yml (2)
4-4: Remove trailing whitespace.There are trailing spaces at the end of these lines.
-name: Publish Docker Image +name: Publish Docker Image - - 'v*.*.*' # Trigger on tags like v1.0.0, v1.2.3, etc. + - 'v*.*.*' # Trigger on tags like v1.0.0, v1.2.3, etc.Also applies to: 9-9
🧰 Tools
🪛 YAMLlint (1.35.1)
[error] 4-4: trailing spaces
(trailing-spaces)
51-51: Consider removingno-cache: true.You're using GitHub Actions cache with
cache-fromandcache-to, butno-cache: truedisables the Docker build cache entirely, which could make builds slower. Unless you specifically need to ensure completely fresh builds every time, consider removing this line to benefit from both GHA and Docker caching.- no-cache: true
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
.github/workflows/publish-docker.yml(1 hunks)apps/web/app/blog/page.tsx(1 hunks)apps/web/app/blog/post/[slug]/page.tsx(2 hunks)apps/web/app/sitemap.ts(1 hunks)docker-compose.yml(1 hunks)docker/Dockerfile.prod(1 hunks)package.json(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/web/app/blog/page.tsx (1)
apps/web/sanity/lib/fetch.ts (1)
sanityFetch(12-42)
🪛 YAMLlint (1.35.1)
.github/workflows/publish-docker.yml
[error] 4-4: trailing spaces
(trailing-spaces)
[error] 9-9: trailing spaces
(trailing-spaces)
🪛 Checkov (3.2.334)
docker/Dockerfile.prod
[MEDIUM] 32-33: Basic Auth Credentials
(CKV_SECRET_4)
🔇 Additional comments (12)
package.json (1)
24-24: Good addition for consistent Tailwind CSS formattingAdding the Tailwind CSS Prettier plugin will help maintain consistent formatting of Tailwind CSS classes throughout the codebase, which improves readability and makes styles more maintainable.
docker/Dockerfile.prod (5)
1-8: LGTM: Proper base image and setupUsing Node 22 Alpine as the base image is a good choice for minimizing image size while providing the latest Node.js version. The essential tools installation looks good.
9-20: LGTM: Optimized Docker layer cachingCopying package manager files first before the rest of the application code is a great practice for optimizing Docker build caching. When package.json files don't change, Docker can reuse the cached dependency installation layer.
31-52: Dummy environment variables are properly isolated for build-time onlyThe use of dummy environment variables during build is a reasonable approach to satisfy build-time requirements without exposing sensitive information. However, ensure that these variables are only used during build and not at runtime.
Note that lines 32-33 containing dummy database URLs were flagged by static analysis as potential basic auth credentials, but since these are just placeholders for build time only, they don't represent a security risk.
Consider adding a comment clarifying that these values are never used in runtime:
- # Provide dummy build-time ENV VARS (Still needed for build) + # Provide dummy build-time ENV VARS (Still needed for build, never used at runtime)🧰 Tools
🪛 Checkov (3.2.334)
[MEDIUM] 32-33: Basic Auth Credentials
(CKV_SECRET_4)
53-58: LGTM: Proper build stepsGenerating the Prisma client followed by building the Next.js application is the correct sequence for the production build.
59-64: LGTM: Container configurationExposing port 3000 and setting the default command for starting the production server looks good.
docker-compose.yml (1)
40-44: Update the image reference before mergingThe image is currently set to use the contributor's personal GitHub Container Registry path. As noted in the PR objectives, maintainers should update this to use the organization's registry path after merging.
- image: ghcr.io/vishalmakwana111/inbox-zero:latest # Use pre-built image (updated tag) + image: ghcr.io/${{ github.repository_owner }}/inbox-zero:latest # Use pre-built image (updated tag)It might be better to use a specific version tag instead of
latestfor better stability and reproducibility, e.g.,v0.0.1.apps/web/app/blog/page.tsx (1)
209-213: LGTM: Properly handling Sanity fetch during buildGood approach to conditionally skip Sanity fetches during builds with dummy credentials. This ensures that the Docker build process won't attempt to fetch from Sanity when using the dummy project ID, avoiding build-time errors.
This change aligns with the PR's objective to modify blog components to properly handle dummy Sanity credentials during the Docker build process.
apps/web/app/sitemap.ts (1)
7-13: Good addition to handle dummy Sanity credentials during Docker builds.This conditional check prevents calling the Sanity API during build time when using placeholder credentials, which is exactly what's needed for Docker builds to succeed without real Sanity access. The early return with an empty array is an elegant solution.
apps/web/app/blog/post/[slug]/page.tsx (2)
13-15: Good addition for Docker build compatibility.This condition correctly prevents fetching post paths during builds with dummy credentials, consistent with the approach used in sitemap.ts.
73-75: Nice defensive programming with fallback UI.Adding this guard clause prevents potential runtime errors when post data is unavailable, improving the resilience of the application. The fallback message is clear and appropriate.
.github/workflows/publish-docker.yml (1)
1-52: Well-structured Docker publishing workflow.This GitHub Actions workflow is well-organized and properly configured for building and publishing Docker images to GHCR on version tag pushes. The semantic versioning tag strategy is particularly good for maintaining versioned Docker images.
🧰 Tools
🪛 YAMLlint (1.35.1)
[error] 4-4: trailing spaces
(trailing-spaces)
[error] 9-9: trailing spaces
(trailing-spaces)
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
.github/workflows/publish-docker.yml (1)
27-27: Update Docker image path after merging.As noted in the PR description, once this lands, the image path should be parameterized to use the repository owner variable instead of the contributor’s namespace.
- images: ghcr.io/vishalmakwana111/inbox-zero + images: ghcr.io/${{ github.repository_owner }}/inbox-zero
🧹 Nitpick comments (3)
.github/workflows/publish-docker.yml (3)
4-4: Remove trailing spaces.YAMLlint flagged trailing spaces on this line—please remove them to ensure clean YAML syntax.
- name: Publish Docker Image␣ + name: Publish Docker Image🧰 Tools
🪛 YAMLlint (1.35.1)
[error] 4-4: trailing spaces
(trailing-spaces)
29-33: Clean up or fill out commented tag configurations.There are several commented-out tag strategies (SHA, latest). Either remove these examples to declutter or enable the ones you intend to use.
49-51: Reconcile cache settings withno-cache.You’ve configured both
cache-from/cache-toandno-cache: true, which disables layer caching. If you intend to leverage GitHub Actions caching to speed up builds, remove theno-cache: trueflag. Otherwise, drop the cache entries. For example:- cache-from: type=gha - cache-to: type=gha,mode=max - no-cache: true + cache-from: type=gha + cache-to: type=gha,mode=max # remove `no-cache: true` to enable caching
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/publish-docker.yml(1 hunks)docker-compose.yml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- docker-compose.yml
🧰 Additional context used
🪛 YAMLlint (1.35.1)
.github/workflows/publish-docker.yml
[error] 4-4: trailing spaces
(trailing-spaces)
🔇 Additional comments (1)
.github/workflows/publish-docker.yml (1)
1-3:Details
✅ Verification successful
Verify linked documentation exists.
The header references
CICD_PLAN.mdfor details—ensure this file is present at the repository root and up to date.
🏁 Script executed:
#!/bin/bash # Check for the presence of CICD_PLAN.md in the repo root if ! fd --hidden --max-depth 1 'CICD_PLAN\.md'; then echo "Error: CICD_PLAN.md not found in the repository root." exit 1 fi echo "CICD_PLAN.md exists."Length of output: 94
CICD_PLAN.md presence verified
The workflow header in.github/workflows/publish-docker.ymlreferencesCICD_PLAN.md, and the file exists at the repository root. No further action required.
|
Just a quick note on the workflow triggers in The workflow runs on pushes to |
Adds a production Dockerfile (`docker/Dockerfile.prod`) for the `apps/web` Next.js application and a GitHub Actions workflow (`.github/workflows/publish-docker.yml`) to automatically build and publish images to GHCR.
Includes dummy environment variables in the Dockerfile and modifications to Sanity components to ensure successful builds. Configures CI triggers and tagging for `main` branch (`latest`) and version tags (`vX.Y.Z`).
Addresses the request in issue elie222#389.
|
Thanks so much for this! I will be looking at this PR later this week. There was also this PR, any overlap? |
|
I was actually working on this feature but forgot to add a comment on that issue, which caused the overlap. I suggest reviewing both PRs and accepting whichever seems better. Latest Image : |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
docker/Dockerfile.prod (2)
1-8: Validate base image and tooling setup
Usingnode:22-alpineis a solid choice for a slim production image. Installingopensslviaapk add --no-cacheis correct. Forpnpm, consider leveraging Node’s built-in Corepack (available since v16.9) instead of a global npm install to reduce the global footprint:RUN corepack enable && corepack prepare pnpm@latest --activate
82-84: Expose application port
EXPOSE 3000matches Next.js defaults. For improved resilience, consider adding a lightweightHEALTHCHECKinstruction to verify the server is up.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
.github/workflows/publish-docker.yml(1 hunks)apps/web/app/blog/page.tsx(1 hunks)apps/web/app/blog/post/[slug]/page.tsx(2 hunks)apps/web/app/sitemap.ts(1 hunks)docker-compose.yml(1 hunks)docker/Dockerfile.prod(1 hunks)package.json(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- docker-compose.yml
🚧 Files skipped from review as they are similar to previous changes (5)
- package.json
- apps/web/app/blog/post/[slug]/page.tsx
- apps/web/app/blog/page.tsx
- apps/web/app/sitemap.ts
- .github/workflows/publish-docker.yml
🧰 Additional context used
🪛 Checkov (3.2.334)
docker/Dockerfile.prod
[MEDIUM] 32-33: Basic Auth Credentials
(CKV_SECRET_4)
🔇 Additional comments (6)
docker/Dockerfile.prod (6)
9-20: Efficient layer caching for dependencies
Copying only your workspace’s rootpackage.json, lockfile, and individual package manifest files before running install steps is an excellent way to maximize Docker layer cache reuse. This hunk looks great.
21-27: Install dependencies without pruning
Runningpnpm install --frozen-lockfileafter copying the full source ensures reproducible builds and that any postinstall scripts have access to your code. This is the correct approach for the build stage.
29-29: Set NODE_ENV to production
PinningNODE_ENV=productionat build time can prune dev-only optimizations in some packages. In a single-stage build this is fine, but if you migrate to a multi-stage pattern, you may want to move this to the runtime stage to keep build-time tools intact.
53-55: Ensure Prisma client generation
You explicitly runprisma generatescoped toinbox-zero-ai, which is necessary. Please verify that this filter covers all Prisma schema locations in your monorepo (if there are others).
79-81: Build Next.js application
Building the Next.js app here is appropriate in a single-stage build. In a builder stage, ensure all build-time env (e.g. Sanity vars) are provided viaARG.
85-87: Default CMD for production start
pnpm --filter inbox-zero-ai startis consistent, but if you move to a runtime stage without a globalpnpm, ensure the runtime image has access to thepnpmbinary (either via Corepack or copying it over).
| # Provide dummy build-time ENV VARS (Still needed for build) | ||
| ENV DATABASE_URL="postgresql://dummy:dummy@dummy:5432/dummy?schema=public" | ||
| ENV DIRECT_URL="postgresql://dummy:dummy@dummy:5432/dummy?schema=public" | ||
| ENV NEXTAUTH_SECRET="dummy_secret_for_build_only" | ||
| ENV NEXTAUTH_URL="http://localhost:3000" | ||
| ENV GOOGLE_CLIENT_ID="dummy_id_for_build_only" | ||
| ENV GOOGLE_CLIENT_SECRET="dummy_secret_for_build_only" | ||
| ENV GOOGLE_ENCRYPT_SECRET="dummy_encrypt_secret_for_build_only" | ||
| ENV GOOGLE_ENCRYPT_SALT="dummy_encrypt_salt_for_build_only" | ||
| ENV GOOGLE_PUBSUB_TOPIC_NAME="dummy_topic_for_build_only" | ||
| ENV GOOGLE_PUBSUB_VERIFICATION_TOKEN="dummy_pubsub_token_for_build" | ||
| ENV INTERNAL_API_KEY="dummy_apikey_for_build_only" | ||
| ENV API_KEY_SALT="dummy_salt_for_build_only" | ||
| ENV UPSTASH_REDIS_URL="http://dummy-redis-for-build:6379" | ||
| ENV UPSTASH_REDIS_TOKEN="dummy_redis_token_for_build" | ||
| ENV REDIS_URL="redis://dummy:dummy@dummy:6379" | ||
| ENV QSTASH_TOKEN="dummy_qstash_token_for_build" | ||
| ENV QSTASH_CURRENT_SIGNING_KEY="dummy_qstash_curr_key_for_build" | ||
| ENV QSTASH_NEXT_SIGNING_KEY="dummy_qstash_next_key_for_build" | ||
| ENV NEXT_PUBLIC_SANITY_PROJECT_ID="dummy-sanity-project-id-for-build" | ||
| ENV NEXT_PUBLIC_SANITY_DATASET="dummy-sanity-dataset-for-build" | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Consolidate and isolate dummy environment variables
There are multiple duplicated blocks of dummy credentials (lines 31–52, 57–67, 69–74, 76–78), which increases image size and risks retaining build-only values in the final artifact. Recommended refactor:
- Switch from
ENVtoARGfor build-time defaults to avoid persisting fake secrets. - Use a multi-stage build:
- builder stage with all
ARGvariables and full install+build - runtime stage that only copies built output, sets
NODE_ENV=production, and does not include dummy envs
- builder stage with all
Example skeleton:
# Builder stage
FROM node:22-alpine AS builder
WORKDIR /app
RUN apk add --no-cache openssl
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
# ...copy individual package.json files...
RUN pnpm install --frozen-lockfile
ARG DATABASE_URL="postgresql://dummy:dummy@dummy:5432/dummy?schema=public"
ARG NEXT_PUBLIC_SANITY_PROJECT_ID="dummy-sanity-id-for-build"
# ...other build args...
RUN pnpm --filter inbox-zero-ai exec -- prisma generate \
&& pnpm --filter inbox-zero-ai exec -- next build
# Runtime stage
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
ENV NODE_ENV=production
EXPOSE 3000
CMD ["pnpm", "--filter", "inbox-zero-ai", "start"]This reduces final image size, confines dummy values to the builder, and suppresses false positives from Checkov (CKV_SECRET_4).
Also applies to: 57-67, 69-78
🧰 Tools
🪛 Checkov (3.2.334)
[MEDIUM] 32-33: Basic Auth Credentials
(CKV_SECRET_4)
|
@vishalmakwana111 could you sign the CLA please |
|
@elie222 Done, just signed it. |
|
Thanks! |
|
@elie222 I think we need your approval for this to really go live: https://github.com/elie222/inbox-zero/actions/runs/14678737529 |
|
Hey. I accepted the pr but then removed as it was breaking the build. I'll try look at it in the next day to make sure it's working as expected and not breaking the build either. |
Fixed React Error elie222#418 hydration mismatch in archive-queue.ts that was causing infinite flickering on bulk-archive/analyze pages. The issue occurred when atomWithStorage tried to access localStorage during SSR, creating different initial values between server and client renders. Changes: - Modified createStorage() to return no-op storage during SSR - Changed getOnInit from true to false to delay localStorage read - Added optional chaining for safer null handling Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This PR adds Docker support for the
apps/webapplication and a GitHub Actions workflow to automatically build and publish images to GHCR.Changes:
docker/Dockerfile.prod: New Dockerfile to build the web application production image..github/workflows/publish-docker.yml: New GitHub Action workflow that:v*.*.*).Dockerfile.prod.imagespath in this workflow toghcr.io/${{ github.repository_owner }}/inbox-zeroafter merging.apps/web/...): Modified blog/sitemap components to correctly handle dummy Sanity credentials during the Docker build process (next build).Testing & Local Usage:
vishalmakwana111/inbox-zero). Pushing a version tag built and pushed a working image. You can test this specific image:docker pull ghcr.io/vishalmakwana111/inbox-zero:v0.0.11docker build -t inbox-zero-test -f docker/Dockerfile.prod .docker-compose.ymlfile:build:section for thewebservice (if present).image:line for thewebservice to point to the desired image tag, e.g.,image: ghcr.io/elie222/inbox-zero:vX.Y.Z(replaceelie222andvX.Y.Zas needed). Use the tag mentioned above for testing from the fork../apps/web/.envfile contains the necessary runtime environment variables.docker compose up -d.Addresses the request for an official Docker image raised in #389.
Summary by CodeRabbit