Release v1.2.0 #66
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 | |
| # This workflow builds and packages Rustnet for multiple platforms. | |
| # | |
| # For macOS code signing and notarization, configure these GitHub repository secrets: | |
| # - APPLE_DEVELOPER_CERTIFICATE_P12: Base64-encoded .p12 certificate file | |
| # (Export from Keychain: Developer ID Application certificate → Export as .p12 → base64 encode) | |
| # - APPLE_DEVELOPER_CERTIFICATE_PASSWORD: Password for the .p12 file | |
| # - APPLE_ID_ISSUER_ID: App Store Connect API issuer ID (from App Store Connect → Users and Access → Keys) | |
| # - APPLE_ID_KEY_ID: App Store Connect API key ID | |
| # - APPLE_ID_KEY: App Store Connect API key content (.p8 file contents) | |
| # | |
| # Without these secrets, the DMG will be unsigned and users must use "Open Anyway" in | |
| # System Settings → Privacy & Security to bypass Gatekeeper protection. | |
| # | |
| # To obtain Apple Developer credentials: | |
| # 1. Join the Apple Developer Program ($99/year): https://developer.apple.com/programs/ | |
| # 2. Create a Developer ID Application certificate in your Apple Developer account | |
| # 3. Create an App Store Connect API key with "Developer" role | |
| # 4. Export certificate as .p12, encode with: base64 -i certificate.p12 | pbcopy | |
| on: | |
| push: | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| env: | |
| RUST_BACKTRACE: 1 | |
| RUSTNET_ASSET_DIR: assets | |
| jobs: | |
| # Build all platforms using shared workflow | |
| build: | |
| uses: ./.github/workflows/build-platforms.yml | |
| with: | |
| create-archives: true | |
| strip-symbols: true | |
| version: ${{ github.ref_name }} | |
| create-release: | |
| name: create-release | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| path: artifacts | |
| - name: Extract changelog for release | |
| run: | | |
| # Extract the version-specific section from CHANGELOG.md | |
| VERSION="${{ github.ref_name }}" | |
| # Remove 'v' prefix if present for matching changelog headers | |
| VERSION_NUMBER="${VERSION#v}" | |
| # Extract content between version headers | |
| awk -v version="$VERSION_NUMBER" ' | |
| /^## \[/ { | |
| if (found) exit | |
| if ($0 ~ "\\[" version "\\]") { | |
| found=1 | |
| next | |
| } | |
| } | |
| found { print } | |
| ' CHANGELOG.md > release_notes.md | |
| # Check if we extracted any content | |
| if [ ! -s release_notes.md ]; then | |
| echo "⚠️ No changelog entry found for $VERSION, will use auto-generated notes" | |
| echo "USE_GENERATED_NOTES=true" >> "$GITHUB_ENV" | |
| else | |
| echo "✅ Extracted changelog content for $VERSION" | |
| cat release_notes.md | |
| fi | |
| - name: Create Release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Create the release if it doesn't exist | |
| if ! gh release view ${{ github.ref_name }} 2>/dev/null; then | |
| if [ "$USE_GENERATED_NOTES" = "true" ]; then | |
| # Fallback to auto-generated notes if changelog extraction failed | |
| gh release create ${{ github.ref_name }} \ | |
| --title "Release ${{ github.ref_name }}" \ | |
| --draft \ | |
| --generate-notes | |
| else | |
| # Use extracted changelog content | |
| gh release create ${{ github.ref_name }} \ | |
| --title "Release ${{ github.ref_name }}" \ | |
| --draft \ | |
| --notes-file release_notes.md | |
| fi | |
| fi | |
| # Upload all build artifacts | |
| for artifact in artifacts/build-*/rustnet-*; do | |
| gh release upload ${{ github.ref_name }} "$artifact" --clobber | |
| done | |
| # aarch64 static: JS actions don't work in Alpine containers on ARM runners, | |
| # so the reusable workflow can't upload artifacts. Build and upload here instead, | |
| # where we have contents: write from the workflow-level permissions. | |
| upload-arm-static: | |
| name: upload-arm-static | |
| runs-on: ubuntu-24.04-arm | |
| needs: create-release | |
| container: | |
| image: rust:alpine | |
| steps: | |
| - name: Checkout repository (ARM workaround) | |
| run: | | |
| apk add --no-cache git | |
| git config --global --add safe.directory /__w/rustnet/rustnet | |
| git clone --depth 1 "https://github.com/${{ github.repository }}.git" . | |
| # shellcheck disable=SC2193 | |
| if [[ "${{ github.ref }}" == refs/tags/* ]]; then | |
| git fetch --depth 1 origin tag "${{ github.ref_name }}" | |
| git checkout "${{ github.ref_name }}" | |
| fi | |
| - name: Install dependencies | |
| run: | | |
| apk add --no-cache \ | |
| musl-dev libpcap-dev pkgconfig build-base perl \ | |
| elfutils-dev zlib-dev zlib-static zstd-dev zstd-static \ | |
| clang llvm linux-headers | |
| rustup component add rustfmt | |
| - name: Build static binary | |
| env: | |
| CFLAGS: "-mno-outline-atomics" | |
| CXXFLAGS: "-mno-outline-atomics" | |
| RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a" | |
| run: cargo build --release | |
| - name: Verify static linking | |
| run: | | |
| file target/release/rustnet | |
| file target/release/rustnet | grep -q "static.* linked" || \ | |
| (echo "ERROR: Binary is not statically linked" && exit 1) | |
| - name: Create and upload release archive | |
| run: | | |
| staging="rustnet-${{ github.ref_name }}-aarch64-unknown-linux-musl" | |
| mkdir -p "$staging/assets" | |
| cp target/release/rustnet "$staging/" | |
| cp assets/services "$staging/assets/" 2>/dev/null || true | |
| cp README.md "$staging/" | |
| cp LICENSE "$staging/" 2>/dev/null || true | |
| tar czf "$staging.tar.gz" "$staging" | |
| apk add --no-cache github-cli | |
| gh release upload "${{ github.ref_name }}" "$staging.tar.gz" --clobber | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| upload-android-static: | |
| name: upload-${{ matrix.arch }}-android-static | |
| runs-on: ${{ matrix.runner }} | |
| needs: create-release | |
| container: | |
| image: rust:alpine | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: aarch64 | |
| runner: ubuntu-24.04-arm | |
| artifact: aarch64-linux-android-musl | |
| - arch: x86_64 | |
| runner: ubuntu-latest | |
| artifact: x86_64-linux-android-musl | |
| - arch: armv7 | |
| runner: ubuntu-latest | |
| artifact: armv7-linux-android-musl | |
| - arch: x86 | |
| runner: ubuntu-latest | |
| artifact: i686-linux-android-musl | |
| steps: | |
| # aarch64: JS actions don't work in Alpine containers on ARM runners | |
| - name: Checkout repository (aarch64 workaround) | |
| if: matrix.arch == 'aarch64' | |
| run: | | |
| apk add --no-cache git | |
| git config --global --add safe.directory /__w/rustnet/rustnet | |
| git clone --depth 1 "https://github.com/${{ github.repository }}.git" . | |
| # shellcheck disable=SC2193 | |
| if [[ "${{ github.ref }}" == refs/tags/* ]]; then | |
| git fetch --depth 1 origin tag "${{ github.ref_name }}" | |
| git checkout "${{ github.ref_name }}" | |
| fi | |
| - name: Checkout repository | |
| if: matrix.arch != 'aarch64' | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install common dependencies | |
| run: | | |
| apk add --no-cache \ | |
| musl-dev pkgconfig build-base perl \ | |
| zlib-dev zlib-static \ | |
| clang llvm linux-headers github-cli curl | |
| rustup component add rustfmt | |
| # aarch64/x86_64: libpcap-dev provides a native static libpcap. | |
| # zstd-static is required because Alpine's libpcap-dev links against zstd. | |
| - name: Install libpcap (native) | |
| if: matrix.arch == 'aarch64' || matrix.arch == 'x86_64' | |
| run: apk add --no-cache libpcap-dev elfutils-dev zstd-dev zstd-static | |
| # armv7: use zig cc as the cross-compiler (no external toolchain download needed). | |
| # zig bundles musl libc for all targets, so no -l:libzstd.a needed. | |
| - name: Install cross-compiler and libpcap (armv7) | |
| if: matrix.arch == 'armv7' | |
| run: | | |
| rustup target add armv7-unknown-linux-musleabihf | |
| apk add zig flex bison bash | |
| # zig cc wrapper: cc-rs detects it as clang and passes --target=armv7-* | |
| # which zig rejects (zig uses 'arm' not 'armv7'). Filter those args out. | |
| # shellcheck disable=SC2016 | |
| printf '%s\n' \ | |
| '#!/bin/bash' \ | |
| 'args=()' \ | |
| 'for arg in "$@"; do' \ | |
| ' case "$arg" in --target=*) ;; *) args+=("$arg") ;; esac' \ | |
| 'done' \ | |
| 'exec zig cc -target arm-linux-musleabihf "${args[@]}"' \ | |
| > /usr/local/bin/arm-linux-musleabihf-gcc | |
| chmod +x /usr/local/bin/arm-linux-musleabihf-gcc | |
| curl -fsSL --retry 3 --retry-delay 5 \ | |
| -o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz | |
| tar xzf libpcap.tar.gz | |
| cd libpcap-1.10.5 | |
| CC=arm-linux-musleabihf-gcc \ | |
| ./configure --host=arm-linux-musleabihf \ | |
| --disable-shared --enable-static \ | |
| --disable-usb --disable-dbus --disable-bluetooth --disable-remote \ | |
| --prefix=/arm-sysroot | |
| make -j"$(nproc)" && make install | |
| cd .. | |
| # x86: use zig cc as the cross-compiler (no external toolchain download needed). | |
| # zig bundles musl libc for all targets, so no -l:libzstd.a needed. | |
| - name: Install cross-compiler and libpcap (x86/i686) | |
| if: matrix.arch == 'x86' | |
| run: | | |
| rustup target add i686-unknown-linux-musl | |
| apk add zig flex bison bash | |
| # zig cc wrapper: cc-rs passes --target=i686-* which zig doesn't accept; | |
| # filter those args and use -target x86-linux-musl instead. | |
| # shellcheck disable=SC2016 | |
| printf '%s\n' \ | |
| '#!/bin/bash' \ | |
| 'args=()' \ | |
| 'for arg in "$@"; do' \ | |
| ' case "$arg" in --target=*) ;; -Wl,-melf_i386) ;; *) args+=("$arg") ;; esac' \ | |
| 'done' \ | |
| 'exec zig cc -target x86-linux-musl "${args[@]}"' \ | |
| > /usr/local/bin/i686-linux-musl-gcc | |
| chmod +x /usr/local/bin/i686-linux-musl-gcc | |
| curl -fsSL --retry 3 --retry-delay 5 \ | |
| -o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz | |
| tar xzf libpcap.tar.gz | |
| cd libpcap-1.10.5 | |
| CC=i686-linux-musl-gcc \ | |
| ./configure --host=i686-linux-musl \ | |
| --disable-shared --enable-static \ | |
| --disable-usb --disable-dbus --disable-bluetooth --disable-remote \ | |
| --prefix=/x86-sysroot | |
| make -j"$(nproc)" && make install | |
| cd .. | |
| - name: Build static binary (aarch64) | |
| if: matrix.arch == 'aarch64' | |
| env: | |
| CFLAGS: "-mno-outline-atomics" | |
| CXXFLAGS: "-mno-outline-atomics" | |
| RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a" | |
| run: cargo build --release --no-default-features | |
| - name: Build static binary (x86_64) | |
| if: matrix.arch == 'x86_64' | |
| env: | |
| RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a" | |
| run: cargo build --release --no-default-features | |
| - name: Build static binary (armv7) | |
| if: matrix.arch == 'armv7' | |
| env: | |
| # link-self-contained=no: let zig provide musl CRT (avoid duplicate _start | |
| # when both Rust's self-contained crt1.o and zig's crt1.o are linked) | |
| RUSTFLAGS: "-C strip=symbols -C link-self-contained=no" | |
| CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER: arm-linux-musleabihf-gcc | |
| PKG_CONFIG_PATH: /arm-sysroot/lib/pkgconfig | |
| PKG_CONFIG_SYSROOT_DIR: /arm-sysroot | |
| PKG_CONFIG_ALLOW_CROSS: "1" | |
| run: cargo build --release --target armv7-unknown-linux-musleabihf --no-default-features | |
| - name: Build static binary (x86/i686) | |
| if: matrix.arch == 'x86' | |
| env: | |
| RUSTFLAGS: "-C strip=symbols -C link-self-contained=no" | |
| CARGO_TARGET_I686_UNKNOWN_LINUX_MUSL_LINKER: i686-linux-musl-gcc | |
| PKG_CONFIG_PATH: /x86-sysroot/lib/pkgconfig | |
| PKG_CONFIG_SYSROOT_DIR: /x86-sysroot | |
| PKG_CONFIG_ALLOW_CROSS: "1" | |
| run: cargo build --release --target i686-unknown-linux-musl --no-default-features | |
| - name: Verify static linking | |
| run: | | |
| BIN="${{ (matrix.arch == 'armv7' && 'target/armv7-unknown-linux-musleabihf/release/rustnet') || (matrix.arch == 'x86' && 'target/i686-unknown-linux-musl/release/rustnet') || 'target/release/rustnet' }}" | |
| file "$BIN" | |
| file "$BIN" | grep -q "static.* linked" || \ | |
| (echo "ERROR: Binary is not statically linked" && exit 1) | |
| - name: Create release archive | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| ARTIFACT="${{ matrix.artifact }}" | |
| case "${{ matrix.arch }}" in | |
| armv7) BIN="target/armv7-unknown-linux-musleabihf/release/rustnet" ;; | |
| x86) BIN="target/i686-unknown-linux-musl/release/rustnet" ;; | |
| *) BIN="target/release/rustnet" ;; | |
| esac | |
| staging="rustnet-${VERSION}-${ARTIFACT}" | |
| mkdir -p "$staging/assets" | |
| cp "$BIN" "$staging/rustnet" | |
| cp assets/services "$staging/assets/" 2>/dev/null || true | |
| cp README.md "$staging/" | |
| cp LICENSE "$staging/" 2>/dev/null || true | |
| tar czf "$staging.tar.gz" "$staging" | |
| - name: Upload to release | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| ARCHIVE="rustnet-${VERSION}-${{ matrix.artifact }}.tar.gz" | |
| gh release upload "$VERSION" "$ARCHIVE" --repo "${{ github.repository }}" --clobber | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| # Publish the release (un-draft) after all assets are uploaded. | |
| # This ensures downstream jobs (Homebrew, Chocolatey) can download assets. | |
| publish-release: | |
| name: publish-release | |
| runs-on: ubuntu-latest | |
| needs: | |
| - create-release | |
| - upload-arm-static | |
| - upload-android-static | |
| - package-installers | |
| - package-macos | |
| - package-windows | |
| if: always() && needs.create-release.result == 'success' | |
| steps: | |
| - name: Publish release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh release edit ${{ github.ref_name }} --draft=false --repo ${{ github.repository }} | |
| trigger-bsd-build: | |
| name: trigger-bsd-build | |
| runs-on: ubuntu-latest | |
| needs: publish-release | |
| steps: | |
| - name: Trigger FreeBSD build | |
| run: | | |
| gh api repos/domcyrus/rustnet-bsd/dispatches \ | |
| -f event_type=freebsd-build \ | |
| -f "client_payload[tag]=${{ github.ref_name }}" \ | |
| -f "client_payload[rustnet_ref]=${{ github.sha }}" | |
| env: | |
| GH_TOKEN: ${{ secrets.BSD_DISPATCH_TOKEN }} | |
| trigger-homebrew-update: | |
| name: trigger-homebrew-update | |
| runs-on: ubuntu-latest | |
| needs: publish-release | |
| steps: | |
| - name: Trigger Homebrew formula update | |
| run: | | |
| gh workflow run update-formula.yml \ | |
| --repo domcyrus/homebrew-rustnet \ | |
| -f version=${{ github.ref_name }} | |
| env: | |
| GH_TOKEN: ${{ secrets.HOMEBREW_PAT }} | |
| trigger-chocolatey-update: | |
| name: trigger-chocolatey-update | |
| runs-on: ubuntu-latest | |
| needs: publish-release | |
| steps: | |
| - name: Trigger Chocolatey package update | |
| run: | | |
| gh workflow run update-package.yml \ | |
| --repo domcyrus/rustnet-chocolatey \ | |
| -f version=${{ github.ref_name }} | |
| env: | |
| GH_TOKEN: ${{ secrets.CHOCOLATEY_PAT }} | |
| trigger-aur-update: | |
| name: trigger-aur-update | |
| runs-on: ubuntu-latest | |
| needs: publish-release | |
| steps: | |
| - name: Trigger AUR package update | |
| run: | | |
| gh workflow run aur-update.yml \ | |
| --repo domcyrus/rustnet | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| package-installers: | |
| name: package-installers | |
| runs-on: ubuntu-22.04 | |
| needs: create-release | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| path: artifacts | |
| - name: Setup Linux dependencies | |
| uses: ./.github/actions/setup-linux-deps | |
| - name: Install Rust and tools | |
| uses: dtolnay/rust-toolchain@stable # unpinned: maintained branch by Rust team member | |
| with: | |
| targets: x86_64-unknown-linux-gnu,aarch64-unknown-linux-gnu,armv7-unknown-linux-gnueabihf | |
| components: rustfmt | |
| - name: Install packaging tools | |
| run: | | |
| cargo install cargo-deb cargo-generate-rpm | |
| - name: Package Debian packages | |
| run: | | |
| mkdir -p installers | |
| for arch in amd64 arm64 armhf; do | |
| case "$arch" in | |
| amd64) target="x86_64-unknown-linux-gnu" ;; | |
| arm64) target="aarch64-unknown-linux-gnu" ;; | |
| armhf) target="armv7-unknown-linux-gnueabihf" ;; | |
| esac | |
| # Extract binary and assets from artifact | |
| tar -xzf "artifacts/build-$target/rustnet-${{ github.ref_name }}-$target.tar.gz" | |
| mkdir -p "target/$target/release" | |
| cp "rustnet-${{ github.ref_name }}-$target/rustnet" "target/$target/release/" | |
| # Ensure services file exists for packaging | |
| mkdir -p assets | |
| if [ -f "rustnet-${{ github.ref_name }}-$target/assets/services" ]; then | |
| cp "rustnet-${{ github.ref_name }}-$target/assets/services" assets/ | |
| fi | |
| # Create deb package | |
| cargo deb --no-build --no-strip --target "$target" | |
| mv "target/$target/debian"/*.deb "installers/Rustnet_LinuxDEB_$arch.deb" | |
| # Clean up for next iteration | |
| rm -rf "rustnet-${{ github.ref_name }}-$target" "target/$target" | |
| done | |
| - name: Package RPM packages | |
| run: | | |
| for arch in x86_64 aarch64; do | |
| target="$arch-unknown-linux-gnu" | |
| # Extract binary and assets from artifact | |
| tar -xzf "artifacts/build-$target/rustnet-${{ github.ref_name }}-$target.tar.gz" | |
| mkdir -p "target/$target/release" | |
| cp "rustnet-${{ github.ref_name }}-$target/rustnet" "target/$target/release/" | |
| # Ensure services file exists for packaging | |
| mkdir -p assets | |
| if [ -f "rustnet-${{ github.ref_name }}-$target/assets/services" ]; then | |
| cp "rustnet-${{ github.ref_name }}-$target/assets/services" assets/ | |
| fi | |
| # Fix library linking if needed | |
| patchelf --replace-needed libpcap.so.0.8 libpcap.so.1 "target/$target/release/rustnet" 2>/dev/null || true | |
| # Create rpm package | |
| cargo generate-rpm --target "$target" | |
| mv "target/$target/generate-rpm"/*.rpm "installers/Rustnet_LinuxRPM_$arch.rpm" | |
| # Clean up for next iteration | |
| rm -rf "rustnet-${{ github.ref_name }}-$target" "target/$target" | |
| done | |
| - name: Upload installer packages | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Upload all installer packages | |
| for installer in installers/*; do | |
| gh release upload ${{ github.ref_name }} "$installer" --clobber | |
| done | |
| package-macos: | |
| name: package-macos | |
| runs-on: macos-latest | |
| needs: create-release | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: Intel | |
| target: x86_64-apple-darwin | |
| - arch: AppleSilicon | |
| target: aarch64-apple-darwin | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install packaging tools | |
| run: | | |
| cargo install toml-cli | |
| cargo install apple-codesign | |
| brew install create-dmg | |
| - name: Download build artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: build-${{ matrix.target }} | |
| path: artifacts | |
| - name: Package for macOS | |
| run: | | |
| # Extract binary | |
| tar -xzf "artifacts/rustnet-${{ github.ref_name }}-${{ matrix.target }}.tar.gz" | |
| # Get version and update plist | |
| VERSION=$(toml get Cargo.toml package.version --raw) | |
| sed -i'.bak' -e "s/0\.0\.0/${VERSION}/g" -e "s/fffffff/${GITHUB_SHA:0:7}/g" resources/packaging/macos/Info.plist | |
| # Create app bundle | |
| mkdir -p "Rustnet.app/Contents/"{MacOS,Resources/assets} | |
| cp resources/packaging/macos/Info.plist "Rustnet.app/Contents/" | |
| cp resources/packaging/macos/graphics/rustnet.icns "Rustnet.app/Contents/Resources/" | |
| cp "rustnet-${{ github.ref_name }}-${{ matrix.target }}/rustnet" "Rustnet.app/Contents/MacOS/" | |
| cp resources/packaging/macos/wrapper.sh "Rustnet.app/Contents/MacOS/" | |
| cp "rustnet-${{ github.ref_name }}-${{ matrix.target }}/assets/services" "Rustnet.app/Contents/Resources/assets/" | |
| chmod +x "Rustnet.app/Contents/MacOS/"{rustnet,wrapper.sh} | |
| - name: Code sign and notarize app bundle | |
| env: | |
| APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12 }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }} | |
| APPLE_API_ISSUER: ${{ secrets.APPLE_ID_ISSUER_ID }} | |
| APPLE_API_KEY_ID: ${{ secrets.APPLE_ID_KEY_ID }} | |
| APPLE_API_KEY: ${{ secrets.APPLE_ID_KEY }} | |
| run: | | |
| # Skip signing if secrets are not configured | |
| if [ -z "$APPLE_CERTIFICATE_P12" ]; then | |
| echo "⚠️ Code signing secrets not configured. DMG will be unsigned." | |
| echo "⚠️ Users will need to use 'Open Anyway' in System Settings > Privacy & Security" | |
| echo "SKIP_SIGNING=true" >> "$GITHUB_ENV" | |
| exit 0 | |
| fi | |
| # Decode certificate and save to file | |
| echo "$APPLE_CERTIFICATE_P12" | base64 --decode > certificate.p12 | |
| # Create temporary JSON for API credentials | |
| cat > api-key.json <<EOF | |
| { | |
| "issuer": "$APPLE_API_ISSUER", | |
| "key_id": "$APPLE_API_KEY_ID", | |
| "private_key": "$APPLE_API_KEY" | |
| } | |
| EOF | |
| # Sign the app bundle with hardened runtime | |
| rcodesign sign \ | |
| --p12-file certificate.p12 \ | |
| --p12-password "$APPLE_CERTIFICATE_PASSWORD" \ | |
| --code-signature-flags runtime \ | |
| Rustnet.app | |
| # Create zip for notarization (required format) | |
| ditto -c -k --keepParent Rustnet.app Rustnet.zip | |
| # Submit for notarization and wait for result | |
| rcodesign notary-submit \ | |
| --api-key-path api-key.json \ | |
| --wait \ | |
| Rustnet.zip | |
| # Staple the notarization ticket to the app | |
| rcodesign staple Rustnet.app | |
| # Cleanup sensitive files | |
| rm -f certificate.p12 api-key.json Rustnet.zip | |
| - name: Create DMG | |
| run: | | |
| create-dmg \ | |
| --volname "Rustnet Installer" \ | |
| --background "resources/packaging/macos/graphics/dmg_bg.png" \ | |
| --window-pos 200 120 \ | |
| --window-size 900 450 \ | |
| --icon-size 100 \ | |
| --app-drop-link 620 240 \ | |
| --icon "Rustnet.app" 300 240 \ | |
| --hide-extension "Rustnet.app" \ | |
| "Rustnet_macOS_${{ matrix.arch }}.dmg" \ | |
| "Rustnet.app" | |
| - name: Upload macOS package | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release upload ${{ github.ref_name }} "Rustnet_macOS_${{ matrix.arch }}.dmg" --clobber | |
| package-windows: | |
| name: package-windows | |
| runs-on: windows-latest | |
| needs: create-release | |
| strategy: | |
| matrix: | |
| include: | |
| - arch: 32-bit | |
| target: i686-pc-windows-msvc | |
| - arch: 64-bit | |
| target: x86_64-pc-windows-msvc | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install dependencies | |
| shell: powershell | |
| run: | | |
| Write-Host "::group::WiX Toolset" | |
| Invoke-WebRequest ` | |
| -Uri "https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip" ` | |
| -OutFile "$env:TEMP\wix-binaries.zip" -Verbose | |
| Expand-Archive -LiteralPath "$env:TEMP\wix-binaries.zip" -DestinationPath "$env:TEMP\wix" -Verbose | |
| # Add WiX to PATH for all subsequent steps | |
| "$env:TEMP\wix" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
| # Verify WiX installation | |
| Write-Host "WiX tools installed:" | |
| Get-ChildItem "$env:TEMP\wix\*.exe" | Select-Object Name | |
| Write-Host "::endgroup::" | |
| - name: Install Rust and tools | |
| uses: dtolnay/rust-toolchain@stable # unpinned: maintained branch by Rust team member | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install packaging tools | |
| shell: bash | |
| run: | | |
| cargo install cargo-wix | |
| cargo wix --version | |
| - name: Download build artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 | |
| with: | |
| name: build-${{ matrix.target }} | |
| path: artifacts | |
| - name: Package for Windows | |
| shell: powershell | |
| run: | | |
| # Extract binary and assets | |
| Write-Host "Extracting artifact..." | |
| Expand-Archive -LiteralPath "artifacts\rustnet-${{ github.ref_name }}-${{ matrix.target }}.zip" -DestinationPath . -Force | |
| # Create target directory structure | |
| New-Item -ItemType Directory -Path "target\${{ matrix.target }}\release" -Force | |
| # Copy binary | |
| Copy-Item -Path "rustnet-${{ github.ref_name }}-${{ matrix.target }}\rustnet.exe" -Destination "target\${{ matrix.target }}\release\" -Force | |
| Write-Host "Binary copied to target directory" | |
| # Copy services file if it exists | |
| New-Item -ItemType Directory -Path "assets" -Force | |
| if (Test-Path "rustnet-${{ github.ref_name }}-${{ matrix.target }}\assets\services") { | |
| Copy-Item -Path "rustnet-${{ github.ref_name }}-${{ matrix.target }}\assets\services" -Destination "assets\" -Force | |
| Write-Host "Services file copied successfully" | |
| } else { | |
| Write-Host "Warning: No services file found" | |
| } | |
| # Setup WiX configuration (cargo-wix expects main.wxs in wix/ directory) | |
| Write-Host "Setting up WiX configuration..." | |
| New-Item -ItemType Directory -Path "wix" -Force | |
| Copy-Item -Path "resources\packaging\windows\wix\main.wxs" -Destination "wix\main.wxs" -Force | |
| Write-Host "WiX configuration copied" | |
| # Verify candle.exe and light.exe are in PATH | |
| Write-Host "Checking WiX tools..." | |
| Get-Command candle.exe -ErrorAction SilentlyContinue | Select-Object Source | |
| Get-Command light.exe -ErrorAction SilentlyContinue | Select-Object Source | |
| # Create MSI package | |
| Write-Host "Creating MSI package..." | |
| cargo wix --no-build --nocapture --target ${{ matrix.target }} | |
| # Find and rename the MSI file | |
| $msiFiles = Get-ChildItem -Path "target\wix" -Filter "*.msi" -ErrorAction SilentlyContinue | |
| if ($msiFiles) { | |
| $msiFile = $msiFiles | Select-Object -First 1 | |
| Move-Item -Path $msiFile.FullName -Destination "Rustnet_Windows_${{ matrix.arch }}.msi" -Force | |
| Write-Host "MSI package created: Rustnet_Windows_${{ matrix.arch }}.msi" | |
| } else { | |
| Write-Error "No MSI file found in target\wix\" | |
| Write-Host "Contents of target directory:" | |
| Get-ChildItem -Path "target" -Recurse -ErrorAction SilentlyContinue | Select-Object FullName | |
| exit 1 | |
| } | |
| - name: Upload Windows package | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release upload ${{ github.ref_name }} "Rustnet_Windows_${{ matrix.arch }}.msi" --clobber | |
| publish-crates: | |
| name: Publish to crates.io | |
| needs: publish-release | |
| uses: ./.github/workflows/publish.yml | |
| secrets: inherit | |
| publish-docker: | |
| name: Publish Docker image | |
| needs: publish-release | |
| uses: ./.github/workflows/docker.yml | |
| secrets: inherit | |
| update-copr: | |
| name: Update COPR spec | |
| needs: publish-release | |
| uses: ./.github/workflows/copr-update-spec.yml | |
| secrets: inherit | |
| release-ppa: | |
| name: Release to PPA | |
| needs: publish-release | |
| uses: ./.github/workflows/ppa-release.yml | |
| secrets: inherit |