Merge pull request #566 from nanotaboada/release/v2.1.1-eriksson #7
Workflow file for this run
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
| # Building, testing, and publishing Python releases | |
| # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python | |
| name: Python CD | |
| on: | |
| push: | |
| tags: | |
| - "v*.*.*-*" | |
| env: | |
| PYTHON_VERSION_FILE: ".python-version" | |
| PACKAGE_NAME: nanotaboada/python-samples-fastapi-restful | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6.2.0 | |
| with: | |
| python-version-file: ${{ env.PYTHON_VERSION_FILE }} | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v8.0.0 | |
| with: | |
| version: "latest" | |
| enable-cache: true | |
| - name: Install test dependencies | |
| run: | | |
| uv venv | |
| uv pip install --group dev | |
| - name: Run tests with pytest | |
| run: | | |
| uv run pytest -v | |
| release: | |
| needs: test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Verify tag commit is reachable from master | |
| run: | | |
| if ! git merge-base --is-ancestor ${{ github.sha }} origin/master; then | |
| echo "Error: Tag commit ${{ github.sha }} is not reachable from master." | |
| echo "Tags must only be created from master after the release PR is merged." | |
| exit 1 | |
| fi | |
| echo "Verified: tag commit ${{ github.sha }} is reachable from master." | |
| - name: Extract version from tag | |
| id: version | |
| run: | | |
| TAG_NAME=${GITHUB_REF#refs/tags/} | |
| # Extract semver (e.g., v1.0.0-ancelotti -> 1.0.0) | |
| SEMVER=$(echo $TAG_NAME | sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') | |
| # Extract coach name (e.g., v1.0.0-ancelotti -> ancelotti) | |
| COACH=$(echo $TAG_NAME | sed -E 's/^v[0-9]+\.[0-9]+\.[0-9]+-//') | |
| # Validate semver format (X.Y.Z) | |
| if ! echo "$SEMVER" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "❌ Error: Invalid semantic version '$SEMVER' extracted from tag '$TAG_NAME'" | |
| echo "Expected format: v{MAJOR}.{MINOR}.{PATCH}-{COACH} (e.g., v1.0.0-ancelotti)" | |
| exit 1 | |
| fi | |
| # Valid coach names (A-Z from CHANGELOG.md) | |
| VALID_COACHES="ancelotti bielsa capello delbosque eriksson ferguson guardiola heynckes inzaghi klopp kovac low mourinho nagelsmann ottmar pochettino queiroz ranieri simeone tuchel unai vangaal wenger xavi yozhef zeman" | |
| # Validate coach name against the list | |
| if [ -z "$COACH" ]; then | |
| echo "❌ Error: Coach name is empty in tag '$TAG_NAME'" | |
| echo "Expected format: v{MAJOR}.{MINOR}.{PATCH}-{COACH} (e.g., v1.0.0-ancelotti)" | |
| exit 1 | |
| fi | |
| if ! echo "$VALID_COACHES" | grep -qw "$COACH"; then | |
| echo "❌ Error: Invalid coach name '$COACH' in tag '$TAG_NAME'" | |
| echo "Valid coaches (A-Z): $VALID_COACHES" | |
| echo "See CHANGELOG.md for the complete list" | |
| exit 1 | |
| fi | |
| # Export validated outputs | |
| echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT | |
| echo "semver=$SEMVER" >> $GITHUB_OUTPUT | |
| echo "coach=$COACH" >> $GITHUB_OUTPUT | |
| echo "📦 Release version: $SEMVER" | |
| echo "♟️ Coach name: $COACH" | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4.1.0 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4.0.0 | |
| - name: Build and push Docker image to GitHub Container Registry | |
| id: push | |
| uses: docker/build-push-action@v7.0.0 | |
| with: | |
| context: . | |
| push: true | |
| platforms: linux/amd64,linux/arm64 | |
| provenance: mode=max | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| tags: | | |
| ghcr.io/${{ env.PACKAGE_NAME }}:${{ steps.version.outputs.semver }} | |
| ghcr.io/${{ env.PACKAGE_NAME }}:${{ steps.version.outputs.coach }} | |
| ghcr.io/${{ env.PACKAGE_NAME }}:latest | |
| - name: Attest build provenance | |
| uses: actions/attest-build-provenance@v4.1.0 | |
| with: | |
| subject-name: ghcr.io/${{ env.PACKAGE_NAME }} | |
| subject-digest: ${{ steps.push.outputs.digest }} | |
| push-to-registry: true | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| # Get the previous tag (second most recent tag matching the version pattern) | |
| PREVIOUS_TAG=$(git tag -l 'v*.*.*-*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+-' | sed -n '2p') | |
| if [ -z "$PREVIOUS_TAG" ]; then | |
| echo "📝 First release - no previous tag found" | |
| CHANGELOG="No changes (first release)" | |
| else | |
| echo "📝 Generating changelog from $PREVIOUS_TAG to ${{ steps.version.outputs.tag_name }}" | |
| CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges ${PREVIOUS_TAG}..${{ steps.version.outputs.tag_name }}) | |
| # Guard against empty changelog (e.g., re-tagging same commit) | |
| if [ -z "$CHANGELOG" ]; then | |
| CHANGELOG="No new changes since $PREVIOUS_TAG" | |
| fi | |
| fi | |
| # Set output for use in release body | |
| { | |
| echo "changelog<<EOF" | |
| echo "$CHANGELOG" | |
| echo "EOF" | |
| } >> $GITHUB_OUTPUT | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2.6.1 | |
| with: | |
| name: "v${{ steps.version.outputs.semver }} - ${{ steps.version.outputs.coach }} ♟️" | |
| tag_name: ${{ steps.version.outputs.tag_name }} | |
| body: | | |
| # Release ${{ steps.version.outputs.semver }} - ${{ steps.version.outputs.coach }} ♟️ | |
| ## Docker Images | |
| Pull this release using any of the following tags: | |
| ```bash | |
| # By semantic version (recommended) | |
| docker pull ghcr.io/${{ env.PACKAGE_NAME }}:${{ steps.version.outputs.semver }} | |
| # By coach name | |
| docker pull ghcr.io/${{ env.PACKAGE_NAME }}:${{ steps.version.outputs.coach }} | |
| # Latest | |
| docker pull ghcr.io/${{ env.PACKAGE_NAME }}:latest | |
| ``` | |
| ## Changelog | |
| ${{ steps.changelog.outputs.changelog }} | |
| --- | |
| 📦 **Package:** [ghcr.io/${{ env.PACKAGE_NAME }}](https://github.com/${{ github.repository }}/pkgs/container/python-samples-fastapi-restful) | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true |