Skip to content

feat: Add Docker build and publish CI for web app #418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
919b82a
target fork GHCR for testing CI workflow
vishalmakwana111 Apr 21, 2025
5d92a4f
Update Dockerfile for production: ignore scripts during dependency in…
vishalmakwana111 Apr 21, 2025
95db09d
Add dummy DB URLs to Dockerfile for build-time Prisma schema loading
vishalmakwana111 Apr 21, 2025
61bbf73
Refactor Dockerfile for production: run prisma generate during build …
vishalmakwana111 Apr 21, 2025
4d20b6f
Add dummy environment variables for build-time in Dockerfile.prod
vishalmakwana111 Apr 21, 2025
5314216
Add dummy Redis and QStash environment variables for build-time in Do…
vishalmakwana111 Apr 21, 2025
6805597
Add dummy Sanity environment variables for build-time in Dockerfile.prod
vishalmakwana111 Apr 21, 2025
eec8f81
Refactor Dockerfile.prod: replace multiple dummy environment variable…
vishalmakwana111 Apr 21, 2025
dc31789
Enhance Dockerfile.prod: add comprehensive dummy environment variable…
vishalmakwana111 Apr 21, 2025
5842eb2
Update dummy Sanity environment variable format in Dockerfile.prod fo…
vishalmakwana111 Apr 21, 2025
3ffb32b
Add dummy Sanity environment variables for build-time in Dockerfile.prod
vishalmakwana111 Apr 22, 2025
4d54e72
fix(build): centralize sanity fetch skip for dummy build
vishalmakwana111 Apr 22, 2025
fcb4a2f
fix(blog): add dummy build check to generateStaticParams
vishalmakwana111 Apr 22, 2025
2de547a
Merge branch 'feat/docker-publish-ci' of https://github.com/vishalmak…
vishalmakwana111 Apr 23, 2025
d277431
fix(docker): correct dummy Sanity project ID format in Dockerfile.prod
vishalmakwana111 Apr 23, 2025
c1a540b
fix(docker): update dummy Sanity dataset variable format in Dockerfil…
vishalmakwana111 Apr 23, 2025
bd16b96
fix(docker): enable no-cache option in Docker publish workflow for im…
vishalmakwana111 Apr 23, 2025
36267bf
fix(docker): update dummy Sanity environment variable comments in Doc…
vishalmakwana111 Apr 23, 2025
c8eb138
chore(env): remove .env.build file and clean up dummy environment var…
vishalmakwana111 Apr 24, 2025
0d3bdef
fix(blog): skip Sanity fetch during build with dummy credentials
vishalmakwana111 Apr 24, 2025
e6bd788
chore(deps): add prettier-plugin-tailwindcss to package.json and upda…
vishalmakwana111 Apr 26, 2025
c9210c0
chore(docker): switch to pre-built web image in docker-compose.yml an…
vishalmakwana111 Apr 26, 2025
840a1af
chore(docker): install pnpm globally in Dockerfile.prod for improved …
vishalmakwana111 Apr 26, 2025
3b79018
chore(docker): update image tag to 'latest' in docker-compose.yml and…
vishalmakwana111 Apr 26, 2025
afe2633
chore(docker): modify CMD in Dockerfile.prod to use next-server.js fo…
vishalmakwana111 Apr 26, 2025
927b74e
chore(docker): add debugging commands in Dockerfile.prod to assist wi…
vishalmakwana111 Apr 26, 2025
d9f4c1c
chore(docker): add listing command for next binary in Dockerfile.prod…
vishalmakwana111 Apr 26, 2025
441bf27
chore(docker): re-install production dependencies and add debug comma…
vishalmakwana111 Apr 26, 2025
d28e9bf
chore(docker): comment out pruning and reinstall commands in Dockerfi…
vishalmakwana111 Apr 26, 2025
271660b
chore(docker): simplify Dockerfile.prod by removing unnecessary comme…
vishalmakwana111 Apr 26, 2025
374409a
chore(docker): adjust Dockerfile.prod to copy application code before…
vishalmakwana111 Apr 26, 2025
7e2bb3a
chore(docker): remove non-root user creation from Dockerfile.prod to …
vishalmakwana111 Apr 26, 2025
2b15101
chore(docker): clean up comments in docker-compose.yml and ensure tag…
vishalmakwana111 Apr 26, 2025
8118c41
chore(docker): update publish-docker.yml to trigger on main branch pu…
vishalmakwana111 Apr 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# GitHub Actions workflow for building and publishing Docker images
# See CICD_PLAN.md for details

name: Publish Docker Image

on:
push:
tags:
- 'v*.*.*' # Trigger on tags like v1.0.0, v1.2.3, etc.

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # Needed to push to GHCR

steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/vishalmakwana111/inbox-zero
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update Docker image path after merging.

As mentioned in the PR description, the image path should be updated to use the repository owner variable after merging.

-          images: ghcr.io/vishalmakwana111/inbox-zero
+          images: ghcr.io/${{ github.repository_owner }}/inbox-zero
📝 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
images: ghcr.io/vishalmakwana111/inbox-zero
images: ghcr.io/${{ github.repository_owner }}/inbox-zero

tags: |
type=semver,pattern={{version}} # e.g., v1.2.3
type=semver,pattern={{major}}.{{minor}} # e.g., v1.2
# type=sha # Optional: Add tag for git SHA
# set latest tag for default branch - this requires triggering on push to main as well
# type=raw,value=latest,enable={{is_default_branch}}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/Dockerfile.prod
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
no-cache: true
6 changes: 5 additions & 1 deletion apps/web/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ const mdxPosts: Post[] = [
export const revalidate = 60;

export default async function BlogContentsPage() {
const posts = await sanityFetch<SanityPost[]>({ query: postsQuery });
// Skip Sanity fetch during build with dummy credentials
let posts: SanityPost[] = [];
if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== 'dummy-sanity-project-id-for-build') {
posts = await sanityFetch<SanityPost[]>({ query: postsQuery });
}

return (
<BlogLayout>
Expand Down
7 changes: 7 additions & 0 deletions apps/web/app/blog/post/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { captureException } from "@/utils/error";
export const revalidate = 60;

export async function generateStaticParams() {
if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === 'dummy-sanity-project-id-for-build') {
return [];
}
const posts = await client.fetch(postPathsQuery);
return posts;
}
Expand Down Expand Up @@ -67,5 +70,9 @@ export default async function Page(props: Props) {
const params = await props.params;
const post = await sanityFetch<PostType>({ query: postQuery, params });

if (!post) {
return <div>Blog post content unavailable.</div>;
}

return <Post post={post} />;
}
7 changes: 7 additions & 0 deletions apps/web/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { sanityFetch } from "@/sanity/lib/fetch";
import { postSlugsQuery } from "@/sanity/lib/queries";

async function getBlogPosts() {
// Skip Sanity fetch during build with dummy credentials
if (
process.env.NEXT_PUBLIC_SANITY_PROJECT_ID ===
"dummy-sanity-project-id-for-build"
) {
return []; // Return empty array directly
}
const posts = await sanityFetch<{ slug: string; date: string }[]>({
query: postSlugsQuery,
});
Expand Down
7 changes: 4 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ services:
- inbox-zero-network

web:
build:
context: .
dockerfile: ./docker/Dockerfile.web
# build:
# context: .
# dockerfile: ./docker/Dockerfile.web
image: ghcr.io/vishalmakwana111/inbox-zero:latest # Use pre-built image (updated tag)
env_file:
- ./apps/web/.env
depends_on:
Expand Down
64 changes: 64 additions & 0 deletions docker/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
FROM node:22-alpine

WORKDIR /app

# Install necessary tools
RUN apk add --no-cache openssl
RUN npm install -g pnpm

# Copy all package manager files first for caching
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc* ./
COPY apps/web/package.json apps/web/
COPY apps/unsubscriber/package.json apps/unsubscriber/
COPY apps/mcp-server/package.json apps/mcp-server/
COPY packages/eslint-config/package.json packages/eslint-config/
COPY packages/loops/package.json packages/loops/
COPY packages/resend/package.json packages/resend/
COPY packages/tinybird/package.json packages/tinybird/
COPY packages/tinybird-ai-analytics/package.json packages/tinybird-ai-analytics/
COPY packages/tsconfig/package.json packages/tsconfig/

# 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

# Set NODE_ENV for build and runtime
ENV NODE_ENV=production

# 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"

# Ensure prisma generate runs
RUN pnpm --filter inbox-zero-ai exec -- prisma generate

# Build the Next.js application
RUN pnpm --filter inbox-zero-ai exec -- next build

# Expose port 3000
EXPOSE 3000

# Set the default command to start the production server
# Use the simpler pnpm command, should work now as pnpm & next are installed
CMD pnpm --filter inbox-zero-ai start
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"husky": "9.1.7",
"lint-staged": "15.5.1",
"prettier": "3.5.3",
"prettier-plugin-tailwindcss": "0.6.11",
"turbo": "2.5.0"
},
"packageManager": "[email protected]+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677",
Expand Down
10 changes: 7 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.