Publish Docker image #16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish Docker image | |
| # Runs after Release succeeds (PyPI already uploaded), or manually to | |
| # rebuild an image for an existing version. Avoids racing the tag push | |
| # that also starts Release. | |
| on: | |
| workflow_run: | |
| workflows: ["Release"] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Package version (e.g. 0.1.0)" | |
| required: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| docker: | |
| name: Build and push Docker image | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| startsWith(github.event.workflow_run.head_branch, 'create-awesome-python-app@')) | |
| runs-on: ubuntu-latest | |
| environment: pypi | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| - name: Resolve version | |
| id: version | |
| env: | |
| INPUT_VERSION: ${{ github.event.inputs.version }} | |
| RUN_BRANCH: ${{ github.event.workflow_run.head_branch }} | |
| run: | | |
| set -euo pipefail | |
| if [ -n "${INPUT_VERSION:-}" ]; then | |
| VERSION="$INPUT_VERSION" | |
| else | |
| VERSION="${RUN_BRANCH#create-awesome-python-app@}" | |
| fi | |
| if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9.-]+)?$'; then | |
| echo "::error::Invalid Docker image version: $VERSION" | |
| exit 1 | |
| fi | |
| MAJOR=$(echo "$VERSION" | cut -d. -f1) | |
| MINOR=$(echo "$VERSION" | cut -d. -f2) | |
| { | |
| echo "version=$VERSION" | |
| echo "major=$MAJOR" | |
| echo "minor=$MINOR" | |
| } >> "$GITHUB_OUTPUT" | |
| # JSON API can return 200 before the simple index / CDN edges that pip | |
| # hits inside the Docker build. Require JSON + simple-index listing + | |
| # a reachable wheel URL before building (see #217). | |
| - name: Wait for PyPI package | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| PACKAGE: create-awesome-python-app | |
| run: | | |
| set -euo pipefail | |
| attempts=36 | |
| delay=15 | |
| normalized="${PACKAGE//-/_}" | |
| for attempt in $(seq 1 "$attempts"); do | |
| json_ok=0 | |
| simple_ok=0 | |
| wheel_ok=0 | |
| if curl -sfL "https://pypi.org/pypi/${PACKAGE}/${VERSION}/json" \ | |
| | jq -e '.urls[] | select(.packagetype=="sdist") | .digests.sha256' >/dev/null; then | |
| json_ok=1 | |
| fi | |
| if curl -sfL "https://pypi.org/simple/${PACKAGE}/" \ | |
| | grep -Fq "${normalized}-${VERSION}"; then | |
| simple_ok=1 | |
| fi | |
| wheel_url="$(curl -sfL "https://pypi.org/pypi/${PACKAGE}/${VERSION}/json" \ | |
| | jq -r '.urls[] | select(.packagetype=="bdist_wheel") | .url' \ | |
| | head -n1 || true)" | |
| if [ -n "${wheel_url}" ] && curl -sfI "$wheel_url" >/dev/null; then | |
| wheel_ok=1 | |
| fi | |
| if [ "$json_ok" -eq 1 ] && [ "$simple_ok" -eq 1 ] && [ "$wheel_ok" -eq 1 ]; then | |
| echo "PyPI ready for ${PACKAGE}==${VERSION} (json+simple+wheel)" | |
| exit 0 | |
| fi | |
| echo "::warning::PyPI not ready for ${PACKAGE}==${VERSION}" | |
| echo "::warning::json=${json_ok} simple=${simple_ok} wheel=${wheel_ok} attempt=${attempt}/${attempts}; sleep ${delay}s" | |
| sleep "$delay" | |
| done | |
| echo "::error::Timed out waiting for ${PACKAGE}==${VERSION} on PyPI" | |
| exit 1 | |
| - name: Set up QEMU | |
| # Required so buildx can cross-build linux/arm64 on the x86_64 | |
| # ubuntu-latest runner. | |
| uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 | |
| with: | |
| platforms: linux/arm64 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Extract Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 | |
| with: | |
| images: ulisesjeremias/create-awesome-python-app | |
| tags: | | |
| type=raw,value=latest | |
| type=raw,value=${{ steps.version.outputs.version }} | |
| type=raw,value=${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }} | |
| type=raw,value=v${{ steps.version.outputs.major }} | |
| labels: | | |
| org.opencontainers.image.title=create-awesome-python-app | |
| org.opencontainers.image.description=Composable scaffolding CLI for production-ready Python apps | |
| org.opencontainers.image.url=https://github.com/Create-Python-App/create-python-app | |
| org.opencontainers.image.source=https://github.com/Create-Python-App/create-python-app | |
| org.opencontainers.image.licenses=MIT | |
| org.opencontainers.image.version=${{ steps.version.outputs.version }} | |
| - name: Build and push | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| push: true | |
| platforms: linux/amd64,linux/arm64 | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| VERSION=${{ steps.version.outputs.version }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max |