Deployment #32
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: Deployment | |
| permissions: | |
| contents: write | |
| packages: write | |
| actions: write | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| publish: | |
| description: "Run side-effecting release steps (tags, uploads, | |
| registry/package-manager publication)" | |
| type: boolean | |
| required: true | |
| default: true | |
| concurrency: | |
| group: deployment-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| build-test: | |
| name: Build and verify | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update || true | |
| sudo apt-get install -y --no-install-recommends \ | |
| cmake build-essential pkg-config libfontconfig1-dev libfreetype6-dev libexpat1-dev | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Verify crate | |
| run: | | |
| cargo package --allow-dirty -p h5v-macros | |
| cargo check --workspace --locked | |
| generate-changelog: | |
| name: Generate changelog and tag | |
| needs: build-test | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_body: ${{ steps.git-cliff.outputs.content }} | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Generate a changelog | |
| uses: orhun/git-cliff-action@07dda386992489f0a6151dee53f3a137713644de | |
| id: git-cliff | |
| with: | |
| # Only generate a changelog for the latest release, and bump the version in Cargo.toml | |
| args: -vv --bump --unreleased | |
| - name: Extract version | |
| id: version | |
| shell: bash | |
| run: | | |
| version=$(echo "${{ steps.git-cliff.outputs.content }}" | grep -m1 -oP '(?<=## \[)[^]]+') | |
| if [ -z "$version" ]; then | |
| echo "Failed to extract version from changelog" | |
| exit 1 | |
| fi | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| - name: Create and push tag | |
| if: ${{ inputs.publish }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| tag="v${{ steps.version.outputs.version }}" | |
| git tag "$tag" | |
| git push origin "$tag" | |
| publish-binaries: | |
| name: Publish binaries | |
| needs: generate-changelog | |
| runs-on: ${{ matrix.build.OS }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| build: | |
| - NAME: linux-x64-glibc | |
| OS: ubuntu-latest | |
| TARGET: x86_64-unknown-linux-gnu | |
| BIN: h5v | |
| ARCHIVE_FORMAT: tar.gz | |
| - NAME: windows-x64-msvc | |
| OS: windows-latest | |
| TARGET: x86_64-pc-windows-msvc | |
| BIN: h5v.exe | |
| ARCHIVE_FORMAT: zip | |
| - NAME: macos-arm64 | |
| OS: macos-14 | |
| TARGET: aarch64-apple-darwin | |
| BIN: h5v | |
| ARCHIVE_FORMAT: tar.gz | |
| env: | |
| RELEASE_VERSION: ${{ needs.generate-changelog.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Install dependencies | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| cmake build-essential pkg-config libfontconfig1-dev libfreetype6-dev libexpat1-dev | |
| - name: Install macOS dependencies | |
| if: runner.os == 'macOS' | |
| run: brew install cmake pkg-config | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.build.TARGET }} | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install cargo-wix | |
| if: runner.os == 'Windows' | |
| run: cargo install --locked cargo-wix | |
| - name: Build | |
| run: cargo build --release --target ${{ matrix.build.TARGET }} | |
| - name: Build Windows installer | |
| if: runner.os == 'Windows' | |
| shell: bash | |
| env: | |
| TARGET: ${{ matrix.build.TARGET }} | |
| run: | | |
| cargo wix \ | |
| --package h5v \ | |
| --no-build \ | |
| --target "$TARGET" \ | |
| --target-bin-dir "target/$TARGET/release" \ | |
| --install-version "${RELEASE_VERSION}" \ | |
| --output "h5v-${TARGET}-v${RELEASE_VERSION}.msi" | |
| python <<'PY' | |
| import hashlib | |
| import os | |
| import pathlib | |
| release_version = os.environ["RELEASE_VERSION"] | |
| target = os.environ["TARGET"] | |
| msi_path = pathlib.Path(f"h5v-{target}-v{release_version}.msi") | |
| if not msi_path.is_file(): | |
| raise FileNotFoundError( | |
| f"Expected MSI at {msi_path}, but cargo wix did not create it" | |
| ) | |
| digest = hashlib.sha256(msi_path.read_bytes()).hexdigest() | |
| pathlib.Path(f"{msi_path.name}.sha256").write_text( | |
| f"{digest} {msi_path.name}\n", | |
| encoding="utf-8", | |
| ) | |
| PY | |
| - name: Prepare release assets | |
| shell: bash | |
| env: | |
| TARGET: ${{ matrix.build.TARGET }} | |
| BIN_NAME: ${{ matrix.build.BIN }} | |
| ARCHIVE_FORMAT: ${{ matrix.build.ARCHIVE_FORMAT }} | |
| run: | | |
| python <<'PY' | |
| import hashlib | |
| import os | |
| import pathlib | |
| import shutil | |
| import tarfile | |
| import zipfile | |
| release_version = os.environ["RELEASE_VERSION"] | |
| target = os.environ["TARGET"] | |
| bin_name = os.environ["BIN_NAME"] | |
| archive_format = os.environ["ARCHIVE_FORMAT"] | |
| archive_stem = f"h5v-{target}-v{release_version}" | |
| root_dir = pathlib.Path(archive_stem) | |
| root_dir.mkdir(exist_ok=True) | |
| binary_path = pathlib.Path("target") / target / "release" / bin_name | |
| shutil.copy2(binary_path, root_dir / bin_name) | |
| if archive_format == "tar.gz": | |
| archive_path = pathlib.Path(f"{archive_stem}.tar.gz") | |
| with tarfile.open(archive_path, "w:gz") as tar: | |
| tar.add(root_dir, arcname=root_dir.name) | |
| elif archive_format == "zip": | |
| archive_path = pathlib.Path(f"{archive_stem}.zip") | |
| with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive: | |
| for path in root_dir.rglob("*"): | |
| archive.write(path, arcname=path.relative_to(root_dir.parent)) | |
| else: | |
| raise RuntimeError(f"Unsupported archive format: {archive_format}") | |
| digest = hashlib.sha256(archive_path.read_bytes()).hexdigest() | |
| pathlib.Path(f"{archive_path.name}.sha256").write_text( | |
| f"{digest} {archive_path.name}\n", | |
| encoding="utf-8", | |
| ) | |
| shutil.rmtree(root_dir) | |
| PY | |
| - name: Upload packaged artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-${{ matrix.build.TARGET }} | |
| path: h5v-${{ matrix.build.TARGET }}-v${{ env.RELEASE_VERSION }}* | |
| if-no-files-found: error | |
| publish-release-assets: | |
| name: Publish release assets | |
| needs: | |
| - generate-changelog | |
| - publish-binaries | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_VERSION: ${{ needs.generate-changelog.outputs.version }} | |
| steps: | |
| - name: Download packaged artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: release-* | |
| merge-multiple: true | |
| path: release-assets | |
| - name: Publish release assets to GitHub | |
| uses: svenstaro/upload-release-action@v2 | |
| if: ${{ inputs.publish }} | |
| with: | |
| repo_token: ${{ secrets.GITHUB_TOKEN }} | |
| file: release-assets/** | |
| file_glob: true | |
| overwrite: false | |
| tag: v${{ env.RELEASE_VERSION }} | |
| release_name: Release v${{ env.RELEASE_VERSION }} | |
| body: ${{ needs.generate-changelog.outputs.release_body }} | |
| package-manager-assets: | |
| name: Generate package manager assets | |
| needs: | |
| - generate-changelog | |
| - publish-binaries | |
| - publish-release-assets | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_VERSION: ${{ needs.generate-changelog.outputs.version }} | |
| REPOSITORY: ${{ github.repository }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Download packaged artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: release-* | |
| merge-multiple: true | |
| path: release-assets | |
| - name: Generate Homebrew and WinGet assets | |
| run: | | |
| python scripts/generate_package_manager_assets.py \ | |
| --version "${RELEASE_VERSION}" \ | |
| --repo "${REPOSITORY}" \ | |
| --asset-dir release-assets \ | |
| --output-dir package-manager-assets | |
| - name: Upload package manager artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: package-manager-assets | |
| path: package-manager-assets/** | |
| if-no-files-found: error | |
| - name: Publish package manager assets to GitHub | |
| uses: svenstaro/upload-release-action@v2 | |
| if: ${{ inputs.publish }} | |
| with: | |
| repo_token: ${{ secrets.GITHUB_TOKEN }} | |
| file: package-manager-assets/** | |
| file_glob: true | |
| overwrite: true | |
| tag: v${{ env.RELEASE_VERSION }} | |
| release_name: Release v${{ env.RELEASE_VERSION }} | |
| publish-package-manager-manifests: | |
| name: Publish package manager manifests | |
| needs: | |
| - generate-changelog | |
| - package-manager-assets | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_VERSION: ${{ needs.generate-changelog.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Download package manager artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: package-manager-assets | |
| path: package-manager-assets | |
| - name: Update Homebrew formula and Scoop manifest | |
| run: | | |
| mkdir -p Formula | |
| cp package-manager-assets/homebrew/h5v.rb Formula/h5v.rb | |
| mkdir -p bucket | |
| cp package-manager-assets/scoop/h5v.json bucket/h5v.json | |
| - name: Commit package manager manifest updates | |
| if: ${{ inputs.publish }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| branch="${{ github.ref_name }}" | |
| git fetch origin "$branch" | |
| git add Formula/h5v.rb | |
| git add bucket/h5v.json | |
| if git diff --cached --quiet; then | |
| echo "Package manager manifests unchanged" | |
| exit 0 | |
| fi | |
| git commit -m "chore: update package manager manifests for v${RELEASE_VERSION}" | |
| git pull --rebase origin "$branch" | |
| git push origin HEAD:"$branch" | |
| deploy-site: | |
| name: Build and deploy site | |
| needs: | |
| - generate-changelog | |
| - publish-binaries | |
| - package-manager-assets | |
| - publish-package-manager-manifests | |
| if: ${{ inputs.publish }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Generate latest changelog for site | |
| uses: orhun/git-cliff-action@07dda386992489f0a6151dee53f3a137713644de | |
| env: | |
| OUTPUT: CHANGELOG.md | |
| GITHUB_REPO: ${{ github.repository }} | |
| - name: Install and run oranda | |
| run: | | |
| curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/latest/download/oranda-installer.sh | sh | |
| oranda build | |
| touch public/.nojekyll | |
| - name: Prepare HTML for link checking | |
| run: | | |
| mkdir -p /tmp/public/ | |
| cp -R public /tmp/public/h5v | |
| - name: Check HTML for broken internal links | |
| uses: untitaker/hyperlink@0.1.29 | |
| with: | |
| args: /tmp/public/ | |
| - name: Deploy to GitHub Pages | |
| uses: JamesIves/github-pages-deploy-action@v4.4.1 | |
| with: | |
| branch: gh-pages | |
| folder: public | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| single-commit: true | |
| publish-aur: | |
| name: Publish AUR package | |
| needs: | |
| - package-manager-assets | |
| if: ${{ inputs.publish }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download package manager artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: package-manager-assets | |
| path: package-manager-assets | |
| - name: Generate .SRCINFO | |
| run: | | |
| docker run --rm \ | |
| -v "${PWD}/package-manager-assets/aur:/work" \ | |
| -w /work \ | |
| archlinux:base-devel \ | |
| bash -lc 'useradd -m builder && chown -R builder:builder /work && su builder -c "bash -lc '\''cd /work && makepkg --printsrcinfo > .SRCINFO'\''"' | |
| - name: Configure AUR SSH | |
| env: | |
| AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} | |
| run: | | |
| install -d -m 700 ~/.ssh | |
| printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur | |
| chmod 600 ~/.ssh/aur | |
| cat >> ~/.ssh/config <<'EOF' | |
| Host aur.archlinux.org | |
| IdentityFile ~/.ssh/aur | |
| IdentitiesOnly yes | |
| StrictHostKeyChecking accept-new | |
| EOF | |
| - name: Push PKGBUILD to AUR | |
| run: | | |
| git clone ssh://aur@aur.archlinux.org/h5v-bin.git aur-repo | |
| cp package-manager-assets/aur/PKGBUILD aur-repo/PKGBUILD | |
| cp package-manager-assets/aur/.SRCINFO aur-repo/.SRCINFO | |
| cd aur-repo | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add PKGBUILD .SRCINFO | |
| if git diff --cached --quiet; then | |
| echo "AUR package unchanged" | |
| exit 0 | |
| fi | |
| git commit -m "Update h5v-bin package" | |
| git push origin HEAD | |
| publish-crates-io: | |
| name: Publish crates.io | |
| needs: | |
| - generate-changelog | |
| - publish-release-assets | |
| - publish-package-manager-manifests | |
| - deploy-site | |
| - publish-aur | |
| if: ${{ inputs.publish }} | |
| runs-on: ubuntu-latest | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Publish to crates.io | |
| run: | | |
| sed -i -E "s|^version = \".*\"$|version = \"${{ needs.generate-changelog.outputs.version }}\"|" Cargo.toml | |
| sed -i -E "s|^macros = \\{ package = \"h5v-macros\", version = \".*\", path = \"\\./macros\" \\}$|macros = { package = \"h5v-macros\", version = \"${{ needs.generate-changelog.outputs.version }}\", path = \"./macros\" }|" Cargo.toml | |
| sed -i -E "s|^version = \".*\"$|version = \"${{ needs.generate-changelog.outputs.version }}\"|" macros/Cargo.toml | |
| for i in {1..5}; do | |
| cargo publish -p h5v-macros --allow-dirty && break | |
| echo "Publishing h5v-macros failed, retrying in 10s..." | |
| sleep 10 | |
| done | |
| for i in {1..10}; do | |
| cargo publish -p h5v --allow-dirty && break | |
| echo "Publishing h5v failed, retrying in 15s..." | |
| sleep 15 | |
| done |