diff --git a/.github/workflows/build_and_publish_docker.yml b/.github/workflows/build_and_publish_docker.yml new file mode 100644 index 0000000000..6eafb7ebcc --- /dev/null +++ b/.github/workflows/build_and_publish_docker.yml @@ -0,0 +1,116 @@ +name: "Build Inbox Zero Docker Image" +run-name: "Build Inbox Zero Docker Image" + +on: + push: + branches: ["main"] + paths-ignore: + - version.txt + +permissions: + contents: write # Needed to commit version bump and push tags + packages: write # Needed to push Docker image to GHCR + +env: + DOCKER_IMAGE_REGISTRY: "ghcr.io" + DOCKER_USERNAME: "elie222" + +jobs: + set-version: + if: github.repository == 'elie222/inbox-zero' + runs-on: ubuntu-latest + + outputs: + version: ${{ steps.set_version.outputs.version }} + + steps: + - name: Checkout + uses: actions/checkout@v4.1.1 + + - name: Set version + id: set_version + run: | + version=$(cat version.txt) + echo "version=$version" >> $GITHUB_OUTPUT + + build-docker: + if: github.repository == 'elie222/inbox-zero' + name: "Build Docker Image" + runs-on: ubuntu-latest + needs: + - set-version + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_IMAGE_REGISTRY }} + username: ${{ env.DOCKER_USERNAME }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and Push Docker Image + uses: docker/build-push-action@v4 + with: + context: . + file: docker/Dockerfile.prod + platforms: linux/arm64, linux/amd64 + push: true + cache-from: type=gha + cache-to: type=gha, mode=max + tags: | + ghcr.io/${{ env.DOCKER_USERNAME }}/inbox-zero:latest + ghcr.io/${{ env.DOCKER_USERNAME }}/inbox-zero:${{ needs.set-version.outputs.version }} + + update_version_txt: + if: github.repository == 'elie222/inbox-zero' + needs: + - set-version + - build-docker + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4.1.1 + with: + ref: main + fetch-depth: 0 + + - name: Bump version on main branch + id: update_version + shell: bash + run: | + set -x + BASE_VERSION=${{ needs.set-version.outputs.version }} + IFS='.' read -r -a version_parts <<< "$BASE_VERSION" + for i in {0..2}; do + version_parts[$i]=${version_parts[$i]:-0} + done + version_parts[2]=$((version_parts[2] + 1)) + new_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}" + echo "$new_version" > version.txt + echo "new_version=${new_version}" >> $GITHUB_OUTPUT + + - name: Commit updated version file + shell: bash + run: | + git config --local user.email "github-actions@inboxzero.com" + git config --local user.name "github-actions" + git tag -a "${{ needs.set-version.outputs.version }}" -m "Release version ${{ needs.set-version.outputs.version }}" + git commit -a -m "Bump version from ${{ needs.set-version.outputs.version }} to ${{ steps.update_version.outputs.new_version }}" + echo "Tagged version ${{ needs.set-version.outputs.version }}. Updated version.txt to ${{ steps.update_version.outputs.new_version }} on main." >> $GITHUB_STEP_SUMMARY + + - name: Push changes + uses: ad-m/github-push-action@v0.8.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref }} + force_with_lease: true + tags: true diff --git a/apps/web/app/blog/page.tsx b/apps/web/app/blog/page.tsx index 7112a73116..f369c1d6c5 100644 --- a/apps/web/app/blog/page.tsx +++ b/apps/web/app/blog/page.tsx @@ -1,10 +1,10 @@ -import Image from "next/image"; -import Link from "next/link"; +import type { Post as PostType } from "@/app/blog/types"; import { BlogLayout } from "@/components/layouts/BlogLayout"; +import { Card, CardContent } from "@/components/ui/card"; import { sanityFetch } from "@/sanity/lib/fetch"; import { postsQuery } from "@/sanity/lib/queries"; -import type { Post as PostType } from "@/app/blog/types"; -import { Card, CardContent } from "@/components/ui/card"; +import Image from "next/image"; +import Link from "next/link"; type Post = { title: string; @@ -208,7 +208,7 @@ export const revalidate = 60; export default async function BlogContentsPage() { // 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') { + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID !== "project123") { posts = await sanityFetch({ query: postsQuery }); } diff --git a/apps/web/app/blog/post/[slug]/page.tsx b/apps/web/app/blog/post/[slug]/page.tsx index 8b680ff28e..88fdfa7110 100644 --- a/apps/web/app/blog/post/[slug]/page.tsx +++ b/apps/web/app/blog/post/[slug]/page.tsx @@ -1,16 +1,16 @@ -import type { ResolvingMetadata } from "next"; -import { sanityFetch } from "@/sanity/lib/fetch"; -import { postPathsQuery, postQuery } from "@/sanity/lib/queries"; -import { client } from "@/sanity/lib/client"; -import imageUrlBuilder from "@sanity/image-url"; import { Post } from "@/app/blog/post/[slug]/Post"; import type { Post as PostType } from "@/app/blog/types"; +import { client } from "@/sanity/lib/client"; +import { sanityFetch } from "@/sanity/lib/fetch"; +import { postPathsQuery, postQuery } from "@/sanity/lib/queries"; import { captureException } from "@/utils/error"; +import imageUrlBuilder from "@sanity/image-url"; +import type { ResolvingMetadata } from "next"; export const revalidate = 60; export async function generateStaticParams() { - if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === 'dummy-sanity-project-id-for-build') { + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === "project123") { return []; } const posts = await client.fetch(postPathsQuery); diff --git a/apps/web/app/sitemap.ts b/apps/web/app/sitemap.ts index 4007d30188..65cb37e10f 100644 --- a/apps/web/app/sitemap.ts +++ b/apps/web/app/sitemap.ts @@ -1,14 +1,11 @@ -import type { MetadataRoute } from "next"; -import { unstable_noStore } from "next/cache"; import { sanityFetch } from "@/sanity/lib/fetch"; import { postSlugsQuery } from "@/sanity/lib/queries"; +import type { MetadataRoute } from "next"; +import { unstable_noStore } from "next/cache"; 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" - ) { + if (process.env.NEXT_PUBLIC_SANITY_PROJECT_ID === "project123") { return []; // Return empty array directly } const posts = await sanityFetch<{ slug: string; date: string }[]>({ diff --git a/docker-compose.yml b/docker-compose.yml index c3d750681c..99bb169343 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,10 +38,13 @@ services: - inbox-zero-network web: + image: ghcr.io/elie222/inbox-zero:latest + pull_policy: if_not_present + # The pre-built image will be used by default. For local development, + # use 'docker compose build web' to build from source instead. build: context: . - dockerfile: ./docker/Dockerfile.web - # image: ghcr.io/elie222/inbox-zero:latest + dockerfile: ./docker/Dockerfile.prod env_file: - ./apps/web/.env depends_on: diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod index 8c838a04aa..69f649bfc4 100644 --- a/docker/Dockerfile.prod +++ b/docker/Dockerfile.prod @@ -18,12 +18,16 @@ 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/ +# Install ALL dependencies (including dev, no pruning) +# This will now run postinstall scripts *after* source code is copied +RUN pnpm install --frozen-lockfile --prefer-offline --ignore-scripts + # 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 + +# Run Prisma generate with build-time variables (cached) +RUN pnpm --filter inbox-zero-ai exec -- prisma generate # Set NODE_ENV for build and runtime ENV NODE_ENV=production @@ -47,36 +51,10 @@ 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_PROJECT_ID="project123" ENV NEXT_PUBLIC_SANITY_DATASET="dummy-sanity-dataset-for-build" # Ensure prisma generate runs -RUN pnpm --filter inbox-zero-ai exec -- prisma generate - -# Provide dummy DB URLs for build-time Prisma schema loading -ENV DATABASE_URL="postgresql://dummy:dummy@dummy:5432/dummy?schema=public" -ENV DIRECT_URL="postgresql://dummy:dummy@dummy:5432/dummy?schema=public" -# Provide dummy values for other required build-time ENV VARS -ENV NEXTAUTH_SECRET="dummy_secret_for_build_only" -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 INTERNAL_API_KEY="dummy_apikey_for_build_only" - -# Provide dummy Redis/QStash vars needed for build analysis -ENV UPSTASH_REDIS_URL="http://dummy-redis-for-build:6379" -ENV UPSTASH_REDIS_TOKEN="dummy_redis_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 QSTASH_TOKEN="dummy_qstash_token_for_build" - -# Provide dummy Sanity vars needed for build -ENV NEXT_PUBLIC_SANITY_PROJECT_ID="dummy_sanity_id_for_build" -ENV NEXT_PUBLIC_SANITY_DATASET="dummy_sanity_dataset_for_build" - -# Build the Next.js application only (skip prisma migrate deploy during build) RUN pnpm --filter inbox-zero-ai exec -- next build # Expose port 3000 diff --git a/version.txt b/version.txt new file mode 100644 index 0000000000..60453e6906 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +v1.0.0 \ No newline at end of file