From 2e9db62521083513f4681f73d9bf2a1e85af09b5 Mon Sep 17 00:00:00 2001 From: John Gravois Date: Tue, 14 Apr 2026 21:25:41 +0000 Subject: [PATCH 1/7] feat: publish to ghcr on tag push --- .github/workflows/publish.yml | 48 ++++++++++++++++++++++------------- README.md | 4 +-- appcontainer/Dockerfile | 5 ++++ 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0868784..b1e5c8c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,8 +3,9 @@ name: Publish image to GHCR on: workflow_dispatch: push: - branches: - - main + tags: + - "[0-9]+.[0-9]+.[0-9]+" + - "[0-9]+.[0-9]+.[0-9]+-rc.?[0-9]+" defaults: run: @@ -19,35 +20,27 @@ jobs: uses: actions/checkout@v6 - name: Docker Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v3 - - - name: Cache Parameters - id: cache_params - run: | - CACHE_SCOPE="cal-itp" - MAIN_BRANCH_REF="refs/heads/main" - - echo "cache_from_args=type=gha,scope=${CACHE_SCOPE},ref=${MAIN_BRANCH_REF}" >> $GITHUB_OUTPUT - echo "cache_to_args=type=gha,scope=${CACHE_SCOPE},mode=max,ref=${MAIN_BRANCH_REF}" >> $GITHUB_OUTPUT + uses: docker/setup-buildx-action@v4 - name: Build, tag, and push image to GitHub Container Registry - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: builder: ${{ steps.buildx.outputs.name }} build-args: GIT-SHA=${{ github.sha }} - cache-from: ${{ steps.cache_params.outputs.cache_from_args }} - cache-to: ${{ steps.cache_params.outputs.cache_to_args }} + # https://docs.docker.com/build/ci/github-actions/cache/#github-cache + cache-from: type=gha + cache-to: type=gha,mode=max context: . platforms: linux/amd64,linux/arm64 file: appcontainer/Dockerfile @@ -55,3 +48,24 @@ jobs: tags: | ghcr.io/${{ github.repository }}:${{ github.ref_name }} ghcr.io/${{ github.repository }}:${{ github.sha }} + + release: + needs: publish + # TODO: uncomment before merging + # if: ${{ !contains(github.ref_name, '-rc') }} + runs-on: ubuntu-latest + permissions: + # https://github.com/softprops/action-gh-release#permissions + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Release + uses: softprops/action-gh-release@v2 + with: + prerelease: false + generate_release_notes: true diff --git a/README.md b/README.md index 801f921..4db9152 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ A base [Docker image](https://www.docker.com/) for Cal-ITP Python web applicatio Reference one of the `image:tag` from GitHub Container Registry in a `Dockerfile`. E.g. for the `main` branch: ```dockerfile -FROM ghcr.io/cal-itp/docker-python-web:main +FROM ghcr.io/cal-itp/docker-python-web:1.0.0 COPY my_app my_app @@ -34,7 +34,7 @@ CMD "nginx && python -m gunicorn -c $GUNICORN_CONF my_app.wsgi" Or from the command line: ```shell -docker pull ghcr.io/cal-itp/docker-python-web:main +docker pull ghcr.io/cal-itp/docker-python-web:1.0.0 ``` ## Development diff --git a/appcontainer/Dockerfile b/appcontainer/Dockerfile index e289187..db8b795 100644 --- a/appcontainer/Dockerfile +++ b/appcontainer/Dockerfile @@ -20,6 +20,11 @@ ARG PYTHON_VERSION \ USER_UID \ USER_GID +# https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images +LABEL org.opencontainers.image.source="https://github.com/calitp/docker-python-web" +LABEL org.opencontainers.image.description="A base Docker image for Cal-ITP Python web applications" +LABEL org.opencontainers.image.licenses="Apache-2.0" + # set env vars for the user, including HOME ENV PYTHONUNBUFFERED=${PYTHONUNBUFFERED} \ PYTHONDONTWRITEBYTECODE=${PYTHONDONTWRITEBYTECODE} \ From e1597b9beec2f4e610e2aac7b169f6d81cb4381f Mon Sep 17 00:00:00 2001 From: John Gravois Date: Thu, 16 Apr 2026 22:04:34 +0000 Subject: [PATCH 2/7] docs: outline tag-based release process --- README.md | 2 +- docs/guides/publish-an-image.md | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 docs/guides/publish-an-image.md diff --git a/README.md b/README.md index 4db9152..5b21e53 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ A base [Docker image](https://www.docker.com/) for Cal-ITP Python web applicatio ## Usage -Reference one of the `image:tag` from GitHub Container Registry in a `Dockerfile`. E.g. for the `main` branch: +Reference an `image:tag` from GitHub Container Registry in a `Dockerfile`. E.g. for the `1.0.0` release: ```dockerfile FROM ghcr.io/cal-itp/docker-python-web:1.0.0 diff --git a/docs/guides/publish-an-image.md b/docs/guides/publish-an-image.md new file mode 100644 index 0000000..6a8866f --- /dev/null +++ b/docs/guides/publish-an-image.md @@ -0,0 +1,22 @@ +# Publish an image + +Historically, a new image was published each time a commit was pushed to `main`. Now we use [tag-based deployment](https://github.com/cal-itp/docker-python-web/issues/73). + +The steps to release are nearly identical to [benefits](https://docs.calitp.org/benefits/guides/release/), with the exception that we use a [SemVer](https://semver.org/) numbering scheme. + +## 0. Decide on the new version number + +Given a version number `MAJOR.MINOR.PATCH`, increment the: + +- MAJOR version when you make incompatible API changes +- MINOR version when you add functionality in a backward compatible manner +- PATCH version when you make backward compatible bug fixes + +Images are published to the GitHub Container Registry when a tag is pushed with a version number similar to either of the following: + +```bash +# genuine release +1.2.3 +# release candidate +2.3.4-rc.1 +``` From 8dfaf9edf328efcc69386a36982ae7b815611b4b Mon Sep 17 00:00:00 2001 From: John Gravois Date: Thu, 16 Apr 2026 23:01:34 +0000 Subject: [PATCH 3/7] chore: try labeling from github action instead of Dockerfile --- .github/workflows/publish.yml | 8 ++++++-- appcontainer/Dockerfile | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b1e5c8c..c861d37 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -48,11 +48,15 @@ jobs: tags: | ghcr.io/${{ github.repository }}:${{ github.ref_name }} ghcr.io/${{ github.repository }}:${{ github.sha }} + # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.description="A base Docker image for Cal-ITP Python web applications" + org.opencontainers.image.licenses="Apache-2.0" release: needs: publish - # TODO: uncomment before merging - # if: ${{ !contains(github.ref_name, '-rc') }} + if: ${{ !contains(github.ref_name, '-rc') }} runs-on: ubuntu-latest permissions: # https://github.com/softprops/action-gh-release#permissions diff --git a/appcontainer/Dockerfile b/appcontainer/Dockerfile index db8b795..e289187 100644 --- a/appcontainer/Dockerfile +++ b/appcontainer/Dockerfile @@ -20,11 +20,6 @@ ARG PYTHON_VERSION \ USER_UID \ USER_GID -# https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images -LABEL org.opencontainers.image.source="https://github.com/calitp/docker-python-web" -LABEL org.opencontainers.image.description="A base Docker image for Cal-ITP Python web applications" -LABEL org.opencontainers.image.licenses="Apache-2.0" - # set env vars for the user, including HOME ENV PYTHONUNBUFFERED=${PYTHONUNBUFFERED} \ PYTHONDONTWRITEBYTECODE=${PYTHONDONTWRITEBYTECODE} \ From b1073993fb2bf63c3999372a8540cb580188bd6d Mon Sep 17 00:00:00 2001 From: John Gravois Date: Thu, 16 Apr 2026 23:10:57 +0000 Subject: [PATCH 4/7] chore: try annotations since img is multi-arch --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c861d37..acfe107 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -49,7 +49,7 @@ jobs: ghcr.io/${{ github.repository }}:${{ github.ref_name }} ghcr.io/${{ github.repository }}:${{ github.sha }} # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images - labels: | + annotations: | org.opencontainers.image.source=https://github.com/${{ github.repository }} org.opencontainers.image.description="A base Docker image for Cal-ITP Python web applications" org.opencontainers.image.licenses="Apache-2.0" From 0652d36abdd3437a53213522a6690d4716f512b1 Mon Sep 17 00:00:00 2001 From: John Gravois Date: Thu, 16 Apr 2026 23:21:37 +0000 Subject: [PATCH 5/7] chore: try metadata-action --- .github/workflows/publish.yml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index acfe107..e495f0a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,9 +3,12 @@ name: Publish image to GHCR on: workflow_dispatch: push: - tags: - - "[0-9]+.[0-9]+.[0-9]+" - - "[0-9]+.[0-9]+.[0-9]+-rc.?[0-9]+" + branches: + - feature/tags + # push: + # tags: + # - "[0-9]+.[0-9]+.[0-9]+" + # - "[0-9]+.[0-9]+.[0-9]+-rc.?[0-9]+" defaults: run: @@ -33,6 +36,17 @@ jobs: id: buildx uses: docker/setup-buildx-action@v4 + - name: Extract metadata + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ env.IMAGE_NAME }} + # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images + labels: | + org.opencontainers.image.source=https://github.com/${{ github.repository }} + org.opencontainers.image.description="A base Docker image for Cal-ITP Python web applications" + org.opencontainers.image.licenses="Apache-2.0" + - name: Build, tag, and push image to GitHub Container Registry uses: docker/build-push-action@v7 with: @@ -48,11 +62,7 @@ jobs: tags: | ghcr.io/${{ github.repository }}:${{ github.ref_name }} ghcr.io/${{ github.repository }}:${{ github.sha }} - # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#labelling-container-images - annotations: | - org.opencontainers.image.source=https://github.com/${{ github.repository }} - org.opencontainers.image.description="A base Docker image for Cal-ITP Python web applications" - org.opencontainers.image.licenses="Apache-2.0" + annotations: ${{ steps.meta.outputs.annotations }} release: needs: publish From 94dac83ee8bf6b690f9570b9825ce9dee73e8798 Mon Sep 17 00:00:00 2001 From: John Gravois Date: Thu, 16 Apr 2026 23:23:32 +0000 Subject: [PATCH 6/7] chore: fix tag name --- .github/workflows/publish.yml | 37 ++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e495f0a..cb6c7fd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -59,27 +59,28 @@ jobs: platforms: linux/amd64,linux/arm64 file: appcontainer/Dockerfile push: true + # ghcr.io/${{ github.repository }}:${{ github.ref_name }} tags: | - ghcr.io/${{ github.repository }}:${{ github.ref_name }} + # ghcr.io/${{ github.repository }}:1.0.0-rc ghcr.io/${{ github.repository }}:${{ github.sha }} annotations: ${{ steps.meta.outputs.annotations }} - release: - needs: publish - if: ${{ !contains(github.ref_name, '-rc') }} - runs-on: ubuntu-latest - permissions: - # https://github.com/softprops/action-gh-release#permissions - contents: write + # release: + # needs: publish + # if: ${{ !contains(github.ref_name, '-rc') }} + # runs-on: ubuntu-latest + # permissions: + # # https://github.com/softprops/action-gh-release#permissions + # contents: write - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 + # steps: + # - name: Checkout + # uses: actions/checkout@v6 + # with: + # fetch-depth: 0 - - name: Release - uses: softprops/action-gh-release@v2 - with: - prerelease: false - generate_release_notes: true + # - name: Release + # uses: softprops/action-gh-release@v2 + # with: + # prerelease: false + # generate_release_notes: true From dcacf01d365dec1f1c7beaff280a588a8d991aa0 Mon Sep 17 00:00:00 2001 From: John Gravois Date: Thu, 16 Apr 2026 23:28:29 +0000 Subject: [PATCH 7/7] chore: put it all together --- .github/workflows/publish.yml | 46 ++++++++++++++++------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cb6c7fd..5e9c741 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,12 +3,9 @@ name: Publish image to GHCR on: workflow_dispatch: push: - branches: - - feature/tags - # push: - # tags: - # - "[0-9]+.[0-9]+.[0-9]+" - # - "[0-9]+.[0-9]+.[0-9]+-rc.?[0-9]+" + tags: + - "[0-9]+.[0-9]+.[0-9]+" + - "[0-9]+.[0-9]+.[0-9]+-rc.?[0-9]+" defaults: run: @@ -59,28 +56,27 @@ jobs: platforms: linux/amd64,linux/arm64 file: appcontainer/Dockerfile push: true - # ghcr.io/${{ github.repository }}:${{ github.ref_name }} tags: | - # ghcr.io/${{ github.repository }}:1.0.0-rc + ghcr.io/${{ github.repository }}:${{ github.ref_name }} ghcr.io/${{ github.repository }}:${{ github.sha }} annotations: ${{ steps.meta.outputs.annotations }} - # release: - # needs: publish - # if: ${{ !contains(github.ref_name, '-rc') }} - # runs-on: ubuntu-latest - # permissions: - # # https://github.com/softprops/action-gh-release#permissions - # contents: write + release: + needs: publish + if: ${{ !contains(github.ref_name, '-rc') }} + runs-on: ubuntu-latest + permissions: + # https://github.com/softprops/action-gh-release#permissions + contents: write - # steps: - # - name: Checkout - # uses: actions/checkout@v6 - # with: - # fetch-depth: 0 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 - # - name: Release - # uses: softprops/action-gh-release@v2 - # with: - # prerelease: false - # generate_release_notes: true + - name: Release + uses: softprops/action-gh-release@v2 + with: + prerelease: false + generate_release_notes: true