Clean up workflow #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
| --- | |
| name: Release (DockerHub + GitHub) | |
| on: | |
| workflow_dispatch: | |
| push: | |
| tags: | |
| - "v*.*.*" # e.g. v1.2.3 | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.read_ver.outputs.tag }} | |
| file_ver: ${{ steps.read_ver.outputs.file_ver }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13" | |
| - name: Install build tools | |
| run: pip install tomli setuptools wheel build | |
| - name: Read version from pyproject.toml & verify tag | |
| id: read_ver | |
| shell: python | |
| run: | | |
| import os, sys | |
| try: | |
| import tomllib as toml # Python 3.11+ | |
| except Exception: | |
| import tomli as toml # fallback | |
| # Read pyproject version | |
| with open("pyproject.toml","rb") as f: | |
| ver = toml.load(f)["project"]["version"] | |
| # Extract tag from GITHUB_REF (e.g. "refs/tags/v1.2.3" -> "1.2.3") | |
| ref = os.environ["GITHUB_REF"] | |
| # Safety checks & strip prefix | |
| prefix = "refs/tags/v" | |
| if not ref.startswith(prefix): | |
| print(f"Unexpected GITHUB_REF: {ref}", file=sys.stderr) | |
| sys.exit(1) | |
| tag = ref[len(prefix):] | |
| if tag != ver: | |
| print(f"Tag ({tag}) != pyproject.toml ({ver})", file=sys.stderr) | |
| sys.exit(1) | |
| # Expose outputs | |
| with open(os.environ["GITHUB_OUTPUT"], "a") as fh: | |
| fh.write(f"tag={tag}\n") | |
| fh.write(f"file_ver={ver}\n") | |
| print(f"Version OK: tag={tag} matches pyproject.toml={ver}") | |
| - name: Build wheel | |
| run: | | |
| python -m build --wheel --sdist | |
| - name: Upload wheels and sdist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist-wyoming-faster-whisper | |
| path: | | |
| dist/*.whl | |
| dist/*.tar.gz | |
| if-no-files-found: error | |
| # Enables building foreign architectures (e.g., arm64 on amd64 runner) | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to DockerHub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| rhasspy/wyoming-whisper:latest | |
| rhasspy/wyoming-whisper:${{ steps.read_ver.outputs.tag }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| changelog: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Extract release notes (fail if missing) | |
| id: notes | |
| shell: python | |
| env: | |
| TAG: ${{ needs.build.outputs.tag }} | |
| run: | | |
| import os, sys, re, pathlib | |
| ver = os.environ["TAG"] # e.g., 1.0.2 | |
| chlog_path = pathlib.Path("CHANGELOG.md") | |
| if not chlog_path.exists(): | |
| print("CHANGELOG.md not found", file=sys.stderr) | |
| sys.exit(2) | |
| lines = chlog_path.read_text(encoding="utf-8").splitlines() | |
| def is_heading_for_version(s: str) -> bool: | |
| s = s.strip() | |
| if not s.startswith("##"): | |
| return False | |
| s = s[2:].strip() # drop "##" | |
| s = s.strip("[]") # allow [1.0.2] | |
| s = re.sub(r"\s*-\s*.*$", "", s) # drop " - date" | |
| s = s.lstrip("v") # allow v1.0.2 | |
| return s == ver | |
| start_idx = None | |
| for i, line in enumerate(lines): | |
| if is_heading_for_version(line): | |
| start_idx = i + 1 # start AFTER heading | |
| break | |
| if start_idx is None: | |
| print(f"No changelog section found for {ver}", file=sys.stderr) | |
| sys.exit(3) | |
| end_idx = len(lines) | |
| for j in range(start_idx, len(lines)): | |
| if lines[j].lstrip().startswith("## "): | |
| end_idx = j | |
| break | |
| section_lines = lines[start_idx:end_idx] | |
| while section_lines and not section_lines[0].strip(): | |
| section_lines.pop(0) | |
| while section_lines and not section_lines[-1].strip(): | |
| section_lines.pop() | |
| section = "\n".join(section_lines) | |
| only_links = re.fullmatch(r"(?:\[[^\]]+\]:\s*\S+\s*(?:\n|$))*", section or "", flags=re.MULTILINE) | |
| if not section or only_links: | |
| print(f"Changelog section for {ver} is empty", file=sys.stderr) | |
| sys.exit(4) | |
| pathlib.Path("RELEASE_NOTES.md").write_text(section, encoding="utf-8") | |
| - name: Upload release notes | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-notes | |
| path: RELEASE_NOTES.md | |
| publish: | |
| needs: [build, changelog] | |
| runs-on: ubuntu-latest | |
| environment: pypi | |
| permissions: | |
| id-token: write # required for PyPI Trusted Publishing (OIDC) | |
| contents: read | |
| steps: | |
| - name: Download all dists | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: dist-* | |
| merge-multiple: true | |
| path: dist | |
| - name: Publish to PyPI via OIDC | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| verbose: true | |
| github_release: | |
| needs: [build, changelog] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download all dists | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: dist-* | |
| merge-multiple: true | |
| path: dist | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: release-notes | |
| path: . | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: ${{ github.ref_name }} | |
| body_path: RELEASE_NOTES.md | |
| files: | | |
| dist/* |