chore(release): 1.6.0 #37
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: Build and Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "Build packages but do not upload a GitHub Release" | |
| required: true | |
| default: "true" | |
| type: choice | |
| options: | |
| - "true" | |
| - "false" | |
| push: | |
| tags: | |
| - "v*" | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [windows-latest, ubuntu-latest, macos-latest] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Verify version matches tag | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| shell: bash | |
| run: | | |
| python - << 'PY' | |
| import os | |
| import re | |
| from pathlib import Path | |
| tag = os.environ["GITHUB_REF"] | |
| tag_version = tag.split("/")[-1][1:] | |
| text = Path("pyproject.toml").read_text(encoding="utf-8") | |
| match = re.search(r'^version\s*=\s*"([^"]+)"', text, re.MULTILINE) | |
| if not match: | |
| raise SystemExit("Version not found in pyproject.toml") | |
| project_version = match.group(1) | |
| if project_version != tag_version: | |
| raise SystemExit( | |
| f"Version mismatch: pyproject.toml={project_version}, tag={tag_version}" | |
| ) | |
| print(f"Version OK: {project_version}") | |
| PY | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements-tests.txt | |
| pip install pyinstaller | |
| - name: Run tests | |
| run: | | |
| pytest -q | |
| - name: Build (same as local) | |
| run: | | |
| python tools/build.py | |
| - name: Package artifact | |
| shell: bash | |
| run: | | |
| python - << 'PY' | |
| import os | |
| import zipfile | |
| from pathlib import Path | |
| os_name = os.environ["RUNNER_OS"].lower() | |
| dist = Path("dist") | |
| exe = dist / ("tapmap.exe" if os_name == "windows" else "tapmap") | |
| if not exe.exists(): | |
| raise FileNotFoundError(f"Expected build output not found: {exe}") | |
| out = Path(f"tapmap-{os_name}.zip") | |
| with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zf: | |
| zf.write(exe, exe.name) | |
| print(out) | |
| PY | |
| - name: Create SHA256 | |
| shell: bash | |
| run: | | |
| python - << 'PY' | |
| import hashlib | |
| import os | |
| from pathlib import Path | |
| os_name = os.environ["RUNNER_OS"].lower() | |
| file = Path(f"tapmap-{os_name}.zip") | |
| if not file.exists(): | |
| raise FileNotFoundError(file) | |
| digest = hashlib.sha256(file.read_bytes()).hexdigest() | |
| sha = file.with_suffix(file.suffix + ".sha256") | |
| sha.write_text(f"{digest} {file.name}\n", encoding="ascii") | |
| print(sha) | |
| PY | |
| - name: Upload workflow artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: tapmap-${{ runner.os }} | |
| path: | | |
| tapmap-${{ runner.os == 'Windows' && 'windows' || runner.os == 'macOS' && 'macos' || 'linux' }}.zip | |
| tapmap-${{ runner.os == 'Windows' && 'windows' || runner.os == 'macOS' && 'macos' || 'linux' }}.zip.sha256 | |
| if-no-files-found: error | |
| retention-days: 7 | |
| release: | |
| name: Publish GitHub Release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') && (github.event_name != 'workflow_dispatch' || inputs.dry_run != 'true') | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: release-assets | |
| - name: List release files | |
| run: | | |
| find release-assets -type f | sort | |
| # Draft release required for immutable releases | |
| # Assets must be uploaded before publish | |
| - name: Create draft GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release create "${{ github.ref_name }}" \ | |
| --draft \ | |
| --title "${{ github.ref_name }}" \ | |
| --notes "" | |
| # Upload all assets before publish | |
| # Immutable release cannot be modified after publish | |
| - name: Upload assets to draft release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release upload "${{ github.ref_name }}" \ | |
| release-assets/tapmap-Windows/tapmap-windows.zip \ | |
| release-assets/tapmap-Windows/tapmap-windows.zip.sha256 \ | |
| release-assets/tapmap-Linux/tapmap-linux.zip \ | |
| release-assets/tapmap-Linux/tapmap-linux.zip.sha256 \ | |
| release-assets/tapmap-macOS/tapmap-macos.zip \ | |
| release-assets/tapmap-macOS/tapmap-macos.zip.sha256 | |
| # Build and push Docker image before publishing release | |
| # Ensures all distribution channels are ready at publish time | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Docker smoke test (release) | |
| run: | | |
| docker build -t tapmap:release . | |
| docker run --rm tapmap:release -v | grep -q "tapmap" | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| push: true | |
| platforms: linux/amd64,linux/arm64 | |
| tags: | | |
| olalie/tapmap:latest | |
| olalie/tapmap:${{ github.ref_name }} | |
| # Finalize release | |
| # After this step the release becomes immutable | |
| - name: Publish GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release edit "${{ github.ref_name }}" --draft=false |