diff --git a/.github/workflows/build-container.yml b/.github/workflows/build-container.yml new file mode 100644 index 0000000..1657f67 --- /dev/null +++ b/.github/workflows/build-container.yml @@ -0,0 +1,45 @@ +# https://github.com/cohere-ai/cohere-toolkit/blob/main/.github/workflows/build_and_push_docker.yml + +on: + workflow_call: + inputs: + container: + description: "A container name suffix passed from the caller workflow" + required: false + type: string + version: + description: "A version passed from the caller workflow" + required: true + type: string + +env: + REGISTRY: ghcr.io + IMAGE_NAME: farfalle + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ github.actor }}/${{ env.IMAGE_NAME }}-${{ inputs.container }} + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ inputs.version }}, ${{ steps.meta.outputs.tags }} + file: ./src/${{ inputs.container }}/Dockerfile diff --git a/.github/workflows/build_and_push_docker.yml b/.github/workflows/build_and_push_docker.yml index aae5c59..f2bc364 100644 --- a/.github/workflows/build_and_push_docker.yml +++ b/.github/workflows/build_and_push_docker.yml @@ -1,40 +1,26 @@ # https://github.com/cohere-ai/cohere-toolkit/blob/main/.github/workflows/build_and_push_docker.yml -name: Create and publish single container image +name: Create and publish container images on: release: types: [published] workflow_dispatch: -env: - REGISTRY: ghcr.io - IMAGE_NAME: farfalle - jobs: - build-and-push-image: - runs-on: ubuntu-latest + backend: + permissions: + contents: read + packages: write + uses: schmoaaaaah/farfalle/.github/workflows/build-container.yml@main + with: + container: backend + version: ${{ github.event.release.tag_name }} + frontend: permissions: contents: read packages: write - steps: - - name: Checkout repo - uses: actions/checkout@v4 - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/rashadphz/${{ env.IMAGE_NAME }} - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - file: ./standalone.Dockerfile + uses: schmoaaaaah/farfalle/.github/workflows/build-container.yml@main + with: + container: frontend + version: ${{ github.event.release.tag_name }} \ No newline at end of file diff --git a/src/backend/Dockerfile b/src/backend/Dockerfile index 7e36203..c25abd7 100644 --- a/src/backend/Dockerfile +++ b/src/backend/Dockerfile @@ -1,6 +1,8 @@ # Help from: https://github.com/cohere-ai/cohere-toolkit/blob/main/src/backend/Dockerfile -FROM python:3.11 +FROM python:3.11-slim as base + +FROM base as build ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 @@ -13,13 +15,33 @@ ENV PATH="$VIRTUAL_ENV/bin:$PATH" WORKDIR /workspace -# Copy dependency files to avoid cache invalidations -COPY pyproject.toml poetry.lock ./ +COPY --chown=1000:1000 pyproject.toml poetry.lock ./ + +COPY --chown=1000:1000 . src/backend/ + +RUN pip install --no-cache-dir poetry + +RUN poetry install --no-root + +FROM base as final + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PYTHONIOENCODING=utf-8 +ENV PYTHONPATH=/workspace/src/ +ENV POETRY_VIRTUALENVS_IN_PROJECT=true + +ENV VIRTUAL_ENV=/workspace/.venv +ENV PATH="$VIRTUAL_ENV/bin:$PATH" -COPY src/backend/ src/backend/ +COPY --from=build /workspace /workspace -RUN pip install --no-cache-dir poetry && poetry install +RUN chown -R 1000:1000 /workspace + +USER 1000:1000 EXPOSE 8000 CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"] + +HEALTHCHECK --interval=1m --timeout=3s CMD curl -f http://localhost:8000/chat || exit 1 diff --git a/src/frontend/Dockerfile b/src/frontend/Dockerfile index fd9b483..55f9162 100644 --- a/src/frontend/Dockerfile +++ b/src/frontend/Dockerfile @@ -1,16 +1,67 @@ -FROM node:18-alpine +FROM node:18-alpine AS base +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY /src/frontend/package.json /src/frontend/yarn.lock* /src/frontend/package-lock.json* /src/frontend/pnpm-lock.yaml* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY /src/frontend/ . +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next +FROM base AS runner WORKDIR /app -COPY src/frontend/package.json src/frontend/pnpm-lock.yaml ./ +ENV NODE_ENV production +# Uncomment the following line in case you want to disable telemetry during runtime. +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:nodejs .next -RUN corepack enable pnpm && pnpm i +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -COPY src/frontend/ . +USER nextjs EXPOSE 3000 -CMD pnpm dev +ENV PORT 3000 +# server.js is created by next build from the standalone output +# https://nextjs.org/docs/pages/api-reference/next-config-js/output +CMD HOSTNAME="0.0.0.0" node server.js \ No newline at end of file diff --git a/src/frontend/next.config.mjs b/src/frontend/next.config.mjs index f14dde7..e902603 100644 --- a/src/frontend/next.config.mjs +++ b/src/frontend/next.config.mjs @@ -1,5 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: 'standalone', images: { remotePatterns: [ {