diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 586eba3..3df228c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ env: IMAGE_NAME: cartsnitch/cartsnitch AUTH_IMAGE_NAME: cartsnitch/auth RECEIPTWITNESS_IMAGE_NAME: cartsnitch/receiptwitness + API_IMAGE_NAME: cartsnitch/api jobs: lint: @@ -234,9 +235,57 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-and-push-api: + runs-on: runners-cartsnitch + needs: [lint, test] + outputs: + calver_tag: ${{ steps.calver.outputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate CalVer tag + id: calver + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + DATE_TAG=$(date -u +%Y.%m.%d) + EXISTING=$(git tag -l "v${DATE_TAG}*" | sort -V | tail -1) + if [ -z "$EXISTING" ]; then VERSION="$DATE_TAG" + elif [ "$EXISTING" = "v${DATE_TAG}" ]; then VERSION="${DATE_TAG}.2" + else BUILD_NUM=$(echo "$EXISTING" | sed "s/v${DATE_TAG}\.//"); VERSION="${DATE_TAG}.$((BUILD_NUM + 1))"; fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Log in to GHCR + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (API) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.API_IMAGE_NAME }} + tags: | + type=sha,prefix=sha- + type=raw,value=${{ steps.calver.outputs.version }},enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + + - name: Build and push API Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./api/Dockerfile + push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + deploy-dev: runs-on: runners-cartsnitch - needs: [build-and-push, build-and-push-auth, build-and-push-receiptwitness] + needs: [build-and-push, build-and-push-auth, build-and-push-receiptwitness, build-and-push-api] if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Generate GitHub App token @@ -268,6 +317,7 @@ jobs: kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }} kustomize edit set image ghcr.io/cartsnitch/auth:${{ needs.build-and-push-auth.outputs.calver_tag }} kustomize edit set image ghcr.io/cartsnitch/receiptwitness:${{ needs.build-and-push-receiptwitness.outputs.calver_tag }} + kustomize edit set image ghcr.io/cartsnitch/api:${{ needs.build-and-push-api.outputs.calver_tag }} - name: Commit and push to infra run: | @@ -275,5 +325,5 @@ jobs: git config user.name "cartsnitch-ci[bot]" git config user.email "cartsnitch-ci[bot]@users.noreply.github.com" git add apps/overlays/dev/kustomization.yaml - git commit -m "ci(dev): update cartsnitch, auth, and receiptwitness images" + git commit -m "ci(dev): update cartsnitch, auth, receiptwitness, and api images" git push origin main diff --git a/api/Dockerfile b/api/Dockerfile index 8eef88d..e271e94 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,3 +1,5 @@ +# Stage 1: Build dependencies +# Build context is the repo root. Paths below are relative to the root. FROM python:3.12-slim AS build RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -6,18 +8,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* WORKDIR /app -COPY pyproject.toml ./ -COPY src/ ./src/ +COPY api/pyproject.toml ./ +COPY api/src/ ./src/ RUN pip install --no-cache-dir --prefix=/install . +# Stage 2: Production image FROM python:3.12-slim AS prod +RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/* + WORKDIR /app RUN adduser --system --group --uid 1000 app COPY --from=build /install /usr/local -COPY src/ ./src/ -COPY alembic.ini ./ -COPY alembic/ ./alembic/ +COPY api/src/ ./src/ +COPY api/alembic.ini ./ +COPY api/alembic/ ./alembic/ USER 1000 EXPOSE 8000