mirror of
https://github.com/domcyrus/rustnet.git
synced 2026-05-13 07:20:05 -05:00
d32dd17aaa
- Add all-published gate job so bsd/homebrew/chocolatey/aur triggers wait for publish-crates/docker/copr/ppa, not just publish-release. Prevents Homebrew/Chocolatey downstream "release.yml still running" bail-out race. - Grant actions: write on trigger-aur-update so GITHUB_TOKEN can dispatch the same-repo aur-update.yml workflow.
796 lines
30 KiB
YAML
796 lines
30 KiB
YAML
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 }}
|
|
|
|
# Gate job that waits for all publish steps to finish before firing
|
|
# downstream package-manager triggers. Without this gate, triggers that
|
|
# run as soon as publish-release completes race against publish-crates,
|
|
# publish-docker, update-copr, and release-ppa — and the downstream
|
|
# Homebrew/Chocolatey workflows bail because their "is release.yml still
|
|
# running?" safety check sees the parent run as in-progress.
|
|
all-published:
|
|
name: all-published
|
|
runs-on: ubuntu-latest
|
|
needs:
|
|
- publish-release
|
|
- publish-crates
|
|
- publish-docker
|
|
- update-copr
|
|
- release-ppa
|
|
steps:
|
|
- name: All publish steps complete
|
|
run: echo "Release pipeline fully published; safe to fire downstream triggers."
|
|
|
|
trigger-bsd-build:
|
|
name: trigger-bsd-build
|
|
runs-on: ubuntu-latest
|
|
needs: all-published
|
|
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: all-published
|
|
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: all-published
|
|
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 }}
|
|
|
|
# Dispatching a same-repo workflow via gh workflow run requires
|
|
# actions: write on the token. The top-level permissions block grants
|
|
# contents/packages/attestations/id-token only, so explicitly opt in here.
|
|
trigger-aur-update:
|
|
name: trigger-aur-update
|
|
runs-on: ubuntu-latest
|
|
needs: all-published
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
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
|