Work with latest Cloud Run, python:3.14-slim-trixie base image

This commit is contained in:
Simon Willison 2025-10-15 10:05:31 -07:00
commit 8ade10cad0
7 changed files with 98 additions and 11 deletions

View file

@ -1,4 +1,4 @@
FROM python:3.11.0-slim-bullseye as build
FROM python:3.14-slim-trixie as build
# Version of Datasette to install, e.g. 0.55
# docker build . -t datasette --build-arg VERSION=0.55

View file

@ -3,7 +3,7 @@ import click
import json
import os
import re
from subprocess import check_call, check_output
from subprocess import CalledProcessError, check_call, check_output
from .common import (
add_common_publish_arguments_and_options,
@ -55,13 +55,32 @@ def publish_subcommand(publish):
@click.option(
"--max-instances",
type=int,
help="Maximum Cloud Run instances",
default=1,
show_default=True,
help="Maximum Cloud Run instances (use 0 to remove the limit)",
)
@click.option(
"--min-instances",
type=int,
help="Minimum Cloud Run instances",
)
@click.option(
"--artifact-repository",
default="datasette",
show_default=True,
help="Artifact Registry repository to store the image",
)
@click.option(
"--artifact-region",
default="us",
show_default=True,
help="Artifact Registry location (region or multi-region)",
)
@click.option(
"--artifact-project",
default=None,
help="Project ID for Artifact Registry (defaults to the active project)",
)
def cloudrun(
files,
metadata,
@ -91,6 +110,9 @@ def publish_subcommand(publish):
apt_get_extras,
max_instances,
min_instances,
artifact_repository,
artifact_region,
artifact_project,
):
"Publish databases to Datasette running on Cloud Run"
fail_if_publish_binary_not_installed(
@ -100,6 +122,21 @@ def publish_subcommand(publish):
"gcloud config get-value project", shell=True, universal_newlines=True
).strip()
artifact_project = artifact_project or project
# Ensure Artifact Registry exists for the target image
_ensure_artifact_registry(
artifact_project=artifact_project,
artifact_region=artifact_region,
artifact_repository=artifact_repository,
)
artifact_host = (
artifact_region
if artifact_region.endswith("-docker.pkg.dev")
else f"{artifact_region}-docker.pkg.dev"
)
if not service:
# Show the user their current services, then prompt for one
click.echo("Please provide a service name for this deployment\n")
@ -117,6 +154,11 @@ def publish_subcommand(publish):
click.echo("")
service = click.prompt("Service name", type=str)
image_id = (
f"{artifact_host}/{artifact_project}/"
f"{artifact_repository}/datasette-{service}"
)
extra_metadata = {
"title": title,
"license": license,
@ -173,7 +215,6 @@ def publish_subcommand(publish):
print(fp.read())
print("\n====================\n")
image_id = f"gcr.io/{project}/datasette-{service}"
check_call(
"gcloud builds submit --tag {}{}".format(
image_id, " --timeout {}".format(timeout) if timeout else ""
@ -187,7 +228,7 @@ def publish_subcommand(publish):
("--max-instances", max_instances),
("--min-instances", min_instances),
):
if value:
if value is not None:
extra_deploy_options.append("{} {}".format(option, value))
check_call(
"gcloud run deploy --allow-unauthenticated --platform=managed --image {} {}{}".format(
@ -199,6 +240,52 @@ def publish_subcommand(publish):
)
def _ensure_artifact_registry(artifact_project, artifact_region, artifact_repository):
"""Ensure Artifact Registry API is enabled and the repository exists."""
enable_cmd = (
"gcloud services enable artifactregistry.googleapis.com "
f"--project {artifact_project} --quiet"
)
try:
check_call(enable_cmd, shell=True)
except CalledProcessError as exc:
raise click.ClickException(
"Failed to enable artifactregistry.googleapis.com. "
"Please ensure you have permissions to manage services."
) from exc
describe_cmd = (
"gcloud artifacts repositories describe {repo} --project {project} "
"--location {location} --quiet"
).format(
repo=artifact_repository,
project=artifact_project,
location=artifact_region,
)
try:
check_call(describe_cmd, shell=True)
return
except CalledProcessError:
create_cmd = (
"gcloud artifacts repositories create {repo} --repository-format=docker "
'--location {location} --project {project} --description "Datasette Cloud Run images" --quiet'
).format(
repo=artifact_repository,
location=artifact_region,
project=artifact_project,
)
try:
check_call(create_cmd, shell=True)
click.echo(f"Created Artifact Registry repository '{artifact_repository}'")
except CalledProcessError as exc:
raise click.ClickException(
"Failed to create Artifact Registry repository. "
"Use --artifact-repository/--artifact-region to point to an existing repo "
"or create one manually."
) from exc
def get_existing_services():
services = json.loads(
check_output(

View file

@ -412,7 +412,7 @@ def make_dockerfile(
"/usr/lib/x86_64-linux-gnu/mod_spatialite.so"
)
return """
FROM python:3.11.0-slim-bullseye
FROM python:3.14-slim-trixie
COPY . /app
WORKDIR /app
{apt_get_extras}

View file

@ -1,4 +1,4 @@
FROM python:3.11.0-slim-bullseye
FROM python:3.14-slim-trixie
RUN apt-get update && \
apt-get install -y apache2 supervisor && \

View file

@ -150,7 +150,7 @@ Here's example output for the package command::
datasette package parlgov.db --extra-options="--setting sql_time_limit_ms 2500"
Sending build context to Docker daemon 4.459MB
Step 1/7 : FROM python:3.11.0-slim-bullseye
Step 1/7 : FROM python:3.14-slim-trixie
---> 79e1dc9af1c1
Step 2/7 : COPY . /app
---> Using cache

View file

@ -12,7 +12,7 @@ class CaptureDockerfile:
EXPECTED_DOCKERFILE = """
FROM python:3.11.0-slim-bullseye
FROM python:3.14-slim-trixie
COPY . /app
WORKDIR /app

View file

@ -242,7 +242,7 @@ def test_publish_cloudrun_plugin_secrets(
)
expected = textwrap.dedent(
r"""
FROM python:3.11.0-slim-bullseye
FROM python:3.14-slim-trixie
COPY . /app
WORKDIR /app
@ -309,7 +309,7 @@ def test_publish_cloudrun_apt_get_install(
)
expected = textwrap.dedent(
r"""
FROM python:3.11.0-slim-bullseye
FROM python:3.14-slim-trixie
COPY . /app
WORKDIR /app