Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
85ba3d1
Speed up deploy.sh via lockfile-hash npm ci guard, IAM policy cache, …
christophervoelpel Sep 23, 2026
d087d17
Drop IAM cache, use cmp -s lockfile guard, overlap UI build, and surf…
christophervoelpel Sep 23, 2026
0745f18
perf(deploy): use E2_HIGHCPU_8 machineType in cloudbuild.yaml
christophervoelpel Sep 23, 2026
dab530f
perf(deploy): keep cloudbuild.yaml on default pre-warmed worker pool
christophervoelpel Sep 23, 2026
8c4b265
perf(deploy): use e2-highcpu-8 only on cold Cloud Builds and merge main
christophervoelpel Sep 23, 2026
aa64949
perf(deploy): single-stage slim Dockerfile, single-read IAM snapshot,…
christophervoelpel Sep 23, 2026
69279f8
perf(docker): parallel BuildKit stages with mirror.gcr.io, force-unsa…
christophervoelpel Sep 23, 2026
cf4fcb9
perf(deploy): use E2_HIGHCPU_8 on cold builds and pre-warmed worker p…
christophervoelpel Sep 23, 2026
803fa6f
sec(docker): digest-pin uv:0.6.6 and enforce --only-binary :all: with…
christophervoelpel Sep 23, 2026
afc1a58
style(test): remove trailing blank line at EOF in test_deploy_safety.py
christophervoelpel Sep 23, 2026
5907cff
fix(deploy): remove duplicate env.ts/config.json write during backgro…
christophervoelpel Sep 23, 2026
ed61ed6
Address PR #206 review: retry Cloud Build on IAM propagation, add EXI…
christophervoelpel Sep 23, 2026
a9aeb77
Tighten Cloud Build retry regex, use deterministic WORKER_URL across …
christophervoelpel Sep 23, 2026
4cd63be
Fix PR206 rollout ordering and cached deployment safety
christophervoelpel Sep 23, 2026
033808b
Address final PR206 deployment review comments
christophervoelpel Sep 23, 2026
3675f2d
Validate --skip-ui-build early, guard IAM cache on error, and harden …
christophervoelpel Sep 25, 2026
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
63 changes: 34 additions & 29 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,50 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# --- Build stage -------------------------------------------------------------
# The full python:3.13 image carries the compilers/headers that a dependency
# without a prebuilt cp313 wheel would need. Install everything into a
# relocatable prefix (/install) that the slim runtime can drop in as-is, so the
# build can never fail for lack of a compiler on the slim base.
FROM python:3.13@sha256:e72bfff2ccf413e3c329074d643fac616d7e1dfe85ac57e527f1d13cd8e0ee6c AS builder
# ==============================================================================
# Stage 1: OS Runtime + FFmpeg (Runs concurrently with Stage 2 under BuildKit!)
# ==============================================================================
FROM python:3.13-slim@sha256:c33f0bc4364a6881bed1ec0cc2665e6c53c87a43e774aaeab88e6f17af105e4f AS runtime-base

ENV PYTHONUNBUFFERED=1

COPY requirements.txt .
RUN pip install --no-cache-dir --require-hashes --prefix=/install -r requirements.txt

# --- Runtime stage -----------------------------------------------------------
# python:3.13-slim is ~850 MB smaller than the full image: faster to push to the
# registry and faster to cold-start. It carries only ffmpeg, the dependencies
# built above, and the app — no compilers or build cruft.
FROM python:3.13-slim@sha256:c33f0bc4364a6881bed1ec0cc2665e6c53c87a43e774aaeab88e6f17af105e4f

ENV PYTHONUNBUFFERED=1

# ffmpeg is required by the worker's video actions (combine/convert). One layer,
# no recommended extras, apt lists dropped to keep the image small.
# Exclude Debian recommended GUI/X11/Mesa packages.
RUN apt-get update \
&& apt-get install -y --no-install-recommends ffmpeg \
&& rm -rf /var/lib/apt/lists/*

# Run as a non-root user with a real home, and give it a writable app dir it
# owns. The worker's video actions write temp files using bare relative names
# into the process CWD (== WORKDIR), so WORKDIR must be owned by this user.
RUN useradd --create-home --uid 10001 --shell /usr/sbin/nologin appuser \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --create-home --uid 10001 --shell /usr/sbin/nologin appuser \
&& mkdir -p /app \
&& chown appuser:appuser /app

# ==============================================================================
# Stage 2: Python Dependency Builder via official Astral uv (digest-pinned)
# (Executes in ~3-6s *while* Stage 1 is still running apt-get!)
# ==============================================================================
FROM python:3.13-slim@sha256:c33f0bc4364a6881bed1ec0cc2665e6c53c87a43e774aaeab88e6f17af105e4f AS venv-builder

COPY --from=ghcr.io/astral-sh/uv:0.12.18@sha256:3adc3706091ce7c2fe595e669628caedd6d951551b92b258b7e7dbe06d9440bc /uv /bin/uv
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
VIRTUAL_ENV=/opt/venv \
PATH="/opt/venv/bin:$PATH"

WORKDIR /app
COPY requirements.txt .
RUN uv venv /opt/venv \
&& uv pip install --no-cache --only-binary :all: --require-hashes -r requirements.txt

# ==============================================================================
# Stage 3: Final Image Assembly (< 1 second merge)
# ==============================================================================
FROM runtime-base AS final

ENV VIRTUAL_ENV=/opt/venv \
PATH="/opt/venv/bin:$PATH" \
PYTHONUNBUFFERED=1

WORKDIR /app
COPY --from=venv-builder /opt/venv /opt/venv

# Drop in the dependencies built in the full image (same python 3.13, so the
# installed packages and gunicorn entry point land on /usr/local and PATH).
# Left root-owned and world-readable — import/exec only need read access.
COPY --from=builder /install /usr/local

# Runtime files only (not the whole repo): explicit copies keep docs, examples,
# tests, deploy scripts, and .git out of the image. Root-owned but world-readable
Expand Down
11 changes: 8 additions & 3 deletions cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# Layer caching (faster repeat deploys): by default the build pulls the
# previously pushed :latest image and passes it to `docker build --cache-from`,
# so unchanged layers are reused instead of rebuilt. The slow builder-stage
# `pip install` only re-runs when requirements.txt changes. Two details make
# `uv pip install` only re-runs when requirements.txt changes. Two details make
# this actually work:
# * the pull is best-effort (`|| true`): the first build on a fresh project has
# nothing to pull and must not fail;
Expand All @@ -18,7 +18,8 @@
# build can read these layers back. Without the inline-cache arg,
# --cache-from finds the image but gets zero cache hits.
# Set _USE_CACHE=0 (deploy.sh --no-build-cache) to force a clean cold rebuild,
# e.g. a release or a dependency/CVE refresh.
# e.g. a dependency/CVE refresh. Keep inline-cache metadata on that fresh image
# so the next ordinary redeploy can reuse its unchanged layers.
steps:
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
Expand All @@ -36,7 +37,11 @@ steps:
.
else
echo "Layer cache OFF: forced cold rebuild (--no-build-cache)."
DOCKER_BUILDKIT=1 docker build -t "${_IMAGE}" .
DOCKER_BUILDKIT=1 docker build \
-t "${_IMAGE}" \
--no-cache \
--build-arg BUILDKIT_INLINE_CACHE=1 \
.
fi
substitutions:
_USE_CACHE: '1'
Expand Down
Loading
Loading