Skip to content

Commit 3959219

Browse files
author
ulises-jeremias
committed
feat(dist): Docker Hub publish parity with Create-Node-App
Pin image to PyPI VERSION, multi-arch buildx tags, and smoke the ulisesjeremias/create-awesome-python-app channel.
1 parent e6b9572 commit 3959219

5 files changed

Lines changed: 155 additions & 17 deletions

File tree

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,96 @@
11
name: Publish Docker image
2+
3+
# Triggers on release tags for the CLI package. Version comes directly
4+
# from the tag ref (create-awesome-python-app@X.Y.Z).
25
on:
36
push:
47
tags:
58
- "create-awesome-python-app@*"
9+
# Manual trigger to rebuild an image for an existing version.
610
workflow_dispatch:
711
inputs:
812
version:
9-
description: "Package version"
13+
description: "Package version (e.g. 0.1.0)"
1014
required: true
15+
16+
permissions:
17+
contents: read
18+
1119
jobs:
1220
docker:
21+
name: Build and push Docker image
1322
runs-on: ubuntu-latest
1423
steps:
15-
- uses: actions/checkout@v4
16-
- name: Login to Docker Hub
17-
uses: docker/login-action@v3
24+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
25+
with:
26+
persist-credentials: false
27+
28+
- name: Resolve version
29+
id: version
30+
env:
31+
TAG_REF: ${{ github.ref_name }}
32+
INPUT_VERSION: ${{ github.event.inputs.version }}
33+
run: |
34+
if [ -n "$INPUT_VERSION" ]; then
35+
VERSION="$INPUT_VERSION"
36+
else
37+
# Tag format: create-awesome-python-app@X.Y.Z
38+
VERSION="${TAG_REF#create-awesome-python-app@}"
39+
fi
40+
MAJOR=$(echo "$VERSION" | cut -d. -f1)
41+
MINOR=$(echo "$VERSION" | cut -d. -f2)
42+
{
43+
echo "version=$VERSION"
44+
echo "major=$MAJOR"
45+
echo "minor=$MINOR"
46+
} >> "$GITHUB_OUTPUT"
47+
48+
- name: Set up QEMU
49+
# Required so buildx can cross-build linux/arm64 on the x86_64
50+
# ubuntu-latest runner.
51+
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
52+
with:
53+
platforms: linux/arm64
54+
55+
- name: Set up Docker Buildx
56+
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
57+
58+
- name: Log in to Docker Hub
59+
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
1860
with:
1961
username: ${{ secrets.DOCKERHUB_USERNAME }}
2062
password: ${{ secrets.DOCKERHUB_TOKEN }}
63+
64+
- name: Extract Docker metadata
65+
id: meta
66+
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
67+
with:
68+
images: ulisesjeremias/create-awesome-python-app
69+
# `latest` is pushed on tag pushes AND on explicit workflow_dispatch
70+
# runs (which always execute against main, so they represent the
71+
# current release).
72+
tags: |
73+
type=raw,value=latest
74+
type=raw,value=${{ steps.version.outputs.version }}
75+
type=raw,value=${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}
76+
type=raw,value=v${{ steps.version.outputs.major }}
77+
labels: |
78+
org.opencontainers.image.title=create-awesome-python-app
79+
org.opencontainers.image.description=Composable scaffolding CLI for production-ready Python apps
80+
org.opencontainers.image.url=https://github.com/Create-Python-App/create-python-app
81+
org.opencontainers.image.source=https://github.com/Create-Python-App/create-python-app
82+
org.opencontainers.image.licenses=MIT
83+
org.opencontainers.image.version=${{ steps.version.outputs.version }}
84+
2185
- name: Build and push
22-
uses: docker/build-push-action@v6
86+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
2387
with:
2488
context: .
2589
push: true
26-
tags: |
27-
${{ secrets.DOCKERHUB_USERNAME }}/create-awesome-python-app:latest
90+
platforms: linux/amd64,linux/arm64
91+
tags: ${{ steps.meta.outputs.tags }}
92+
labels: ${{ steps.meta.outputs.labels }}
93+
build-args: |
94+
VERSION=${{ steps.version.outputs.version }}
95+
cache-from: type=gha
96+
cache-to: type=gha,mode=max

.github/workflows/smoke-distribution.yml

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Distribution smoke tests
22

33
# Smoke-tests each published distribution channel:
44
# uvx — PyPI via uvx
5+
# docker — pull & run ulisesjeremias/create-awesome-python-app:latest
56
# homebrew — brew install on macos-latest
67
# aur — makepkg inside an archlinux container
78
# versions — cross-check channel versions against PyPI
@@ -30,6 +31,25 @@ jobs:
3031
uvx create-awesome-python-app@latest --version
3132
uvx create-awesome-python-app@latest --help | head -5
3233
34+
docker:
35+
name: Docker Hub
36+
runs-on: ubuntu-latest
37+
steps:
38+
- name: Pull image
39+
run: docker pull ulisesjeremias/create-awesome-python-app:latest
40+
41+
- name: Verify --version
42+
run: |
43+
VERSION=$(docker run --rm ulisesjeremias/create-awesome-python-app:latest --version 2>&1)
44+
echo "docker version: $VERSION"
45+
echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$' || {
46+
echo "::error::Unexpected version output: $VERSION"
47+
exit 1
48+
}
49+
50+
- name: Verify --help
51+
run: docker run --rm ulisesjeremias/create-awesome-python-app:latest --help | head -5
52+
3353
homebrew:
3454
name: Homebrew (Create-Python-App/tap)
3555
runs-on: macos-latest
@@ -104,6 +124,9 @@ jobs:
104124
PYPI=$(curl -sfL https://pypi.org/pypi/create-awesome-python-app/json | \
105125
python3 -c "import json,sys; print(json.load(sys.stdin)['info']['version'])")
106126
127+
DOCKER=$(docker run --rm ulisesjeremias/create-awesome-python-app:latest --version 2>&1 \
128+
|| echo "unavailable")
129+
107130
HOMEBREW=$(curl -sfL \
108131
https://raw.githubusercontent.com/Create-Python-App/homebrew-tap/main/Formula/create-awesome-python-app.rb | \
109132
grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unavailable")
@@ -115,12 +138,15 @@ jobs:
115138
116139
echo "Channel versions:"
117140
echo " pypi: $PYPI"
141+
echo " docker: $DOCKER"
118142
echo " homebrew: $HOMEBREW"
119143
echo " aur: $AUR"
120144
121-
if [ "$HOMEBREW" != "unavailable" ] && [ "$HOMEBREW" != "$PYPI" ]; then
122-
echo "::warning::Version mismatch: pypi=$PYPI homebrew=$HOMEBREW"
123-
fi
145+
for CHANNEL_VER in "$DOCKER" "$HOMEBREW"; do
146+
if [ "$CHANNEL_VER" != "unavailable" ] && [ "$CHANNEL_VER" != "$PYPI" ]; then
147+
echo "::warning::Version mismatch vs pypi=$PYPI: $CHANNEL_VER"
148+
fi
149+
done
124150
if [ "$AUR" != "unavailable" ] && [ "$AUR" != "$PYPI" ]; then
125151
echo "::warning::Version mismatch: pypi=$PYPI aur=$AUR (AUR may lag)"
126152
fi

Dockerfile

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1-
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
2-
WORKDIR /app
3-
COPY . .
4-
RUN uv sync --frozen --no-dev
5-
ENTRYPOINT ["uv", "run", "create-awesome-python-app"]
1+
# syntax=docker/dockerfile:1.9
2+
FROM python:3.12-slim-bookworm
3+
4+
# VERSION is passed at build time by the publish workflow so the image
5+
# is pinned to a specific PyPI package version (makes each image
6+
# reproducible for its tag).
7+
ARG VERSION=latest
8+
9+
# hadolint ignore=DL3013
10+
RUN useradd --create-home --uid 1000 --shell /bin/bash app \
11+
&& if [ "$VERSION" = "latest" ]; then \
12+
pip install --no-cache-dir create-awesome-python-app; \
13+
else \
14+
pip install --no-cache-dir "create-awesome-python-app==${VERSION}"; \
15+
fi
16+
17+
USER app
18+
WORKDIR /home/app
19+
20+
ENTRYPOINT ["create-awesome-python-app"]
21+
CMD ["--help"]

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[![Lint](https://github.com/Create-Python-App/create-python-app/actions/workflows/lint.yml/badge.svg)](https://github.com/Create-Python-App/create-python-app/actions/workflows/lint.yml)
55
[![Typecheck](https://github.com/Create-Python-App/create-python-app/actions/workflows/type-check.yml/badge.svg)](https://github.com/Create-Python-App/create-python-app/actions/workflows/type-check.yml)
66
[![PyPI](https://img.shields.io/pypi/v/create-awesome-python-app.svg)](https://pypi.org/project/create-awesome-python-app/)
7+
[![Docker](https://img.shields.io/docker/v/ulisesjeremias/create-awesome-python-app?style=flat-square&label=Docker&logo=docker&color=2496ED)](https://hub.docker.com/r/ulisesjeremias/create-awesome-python-app)
78
[![AUR](https://img.shields.io/aur/version/create-awesome-python-app?label=AUR&logo=archlinux)](https://aur.archlinux.org/packages/create-awesome-python-app)
89
[![Homebrew](https://img.shields.io/badge/homebrew-Create--Python--App%2Ftap-orange?logo=homebrew)](https://github.com/Create-Python-App/homebrew-tap)
910
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
@@ -39,6 +40,10 @@ brew install create-awesome-python-app
3940

4041
# AUR
4142
yay -S create-awesome-python-app
43+
44+
# Docker
45+
docker run --rm -it -v "${PWD}:/app" -w /app \
46+
ulisesjeremias/create-awesome-python-app my-app
4247
```
4348

4449
Or pin a version:
@@ -106,8 +111,18 @@ uv sync --group dev
106111

107112
## Docker
108113

114+
Published image: [`ulisesjeremias/create-awesome-python-app`](https://hub.docker.com/r/ulisesjeremias/create-awesome-python-app)
115+
116+
```bash
117+
docker run --rm ulisesjeremias/create-awesome-python-app:0.1.0 --version
118+
docker run --rm -it -v "${PWD}:/app" -w /app \
119+
ulisesjeremias/create-awesome-python-app my-app --template fastapi-starter --no-interactive
120+
```
121+
122+
Local build (installs the given PyPI version into the image):
123+
109124
```bash
110-
docker build -t create-awesome-python-app .
125+
docker build --build-arg VERSION=0.1.0 -t create-awesome-python-app .
111126
docker run --rm create-awesome-python-app --help
112127
```
113128

docs/DISTRIBUTION_SETUP.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,19 @@ Then:
4040

4141
## Docker Hub
4242

43-
Create a write token and set `DOCKERHUB_USERNAME` / `DOCKERHUB_TOKEN`.
43+
Image: [`ulisesjeremias/create-awesome-python-app`](https://hub.docker.com/r/ulisesjeremias/create-awesome-python-app)
44+
45+
Secrets (already used by CNA; reuse the same Hub account):
46+
47+
- `DOCKERHUB_USERNAME` — e.g. `ulisesjeremias`
48+
- `DOCKERHUB_TOKEN` — Hub access token with write scope
49+
50+
Verify:
51+
52+
```bash
53+
gh workflow run "Publish Docker image" --repo Create-Python-App/create-python-app -f version=0.1.0
54+
docker run --rm ulisesjeremias/create-awesome-python-app:0.1.0 --version
55+
```
4456

4557
## AUR (`AUR_SSH_PRIVATE_KEY`, `AUR_REPO_TOKEN`)
4658

0 commit comments

Comments
 (0)