chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
name: Update AUR Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-aur:
|
||||
name: update-aur
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
# Determine the tag based on trigger type
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
# Get the latest tag when manually triggered
|
||||
git fetch --tags
|
||||
TAG=$(git tag --sort=-version:refname | head -n1)
|
||||
else
|
||||
# For release event, use the release tag
|
||||
TAG="${{ github.event.release.tag_name }}"
|
||||
fi
|
||||
|
||||
# Extract version by removing 'v' prefix
|
||||
VERSION="${TAG#v}"
|
||||
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "Extracted version: $VERSION"
|
||||
echo "Tag: $TAG"
|
||||
|
||||
- name: Download release assets and calculate checksums
|
||||
id: checksums
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="${{ steps.version.outputs.tag }}"
|
||||
|
||||
# Define asset names
|
||||
ASSET_X64="rustnet-${TAG}-x86_64-unknown-linux-gnu.tar.gz"
|
||||
ASSET_ARM64="rustnet-${TAG}-aarch64-unknown-linux-gnu.tar.gz"
|
||||
|
||||
echo "Downloading release assets..."
|
||||
|
||||
# Download assets
|
||||
gh release download "$TAG" -p "$ASSET_X64" -p "$ASSET_ARM64"
|
||||
|
||||
# Calculate SHA256 checksums
|
||||
SHA256_X64=$(sha256sum "$ASSET_X64" | awk '{print $1}')
|
||||
SHA256_ARM64=$(sha256sum "$ASSET_ARM64" | awk '{print $1}')
|
||||
|
||||
echo "x64_checksum=$SHA256_X64" >> $GITHUB_OUTPUT
|
||||
echo "arm64_checksum=$SHA256_ARM64" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "Checksums calculated:"
|
||||
echo " x86_64: $SHA256_X64"
|
||||
echo " aarch64: $SHA256_ARM64"
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Verify checksums match release assets
|
||||
run: |
|
||||
TAG="${{ steps.version.outputs.tag }}"
|
||||
EXPECTED_X64="${{ steps.checksums.outputs.x64_checksum }}"
|
||||
EXPECTED_ARM64="${{ steps.checksums.outputs.arm64_checksum }}"
|
||||
FAILED=0
|
||||
|
||||
echo "Re-downloading assets to verify checksums are stable..."
|
||||
|
||||
ASSET_X64="rustnet-${TAG}-x86_64-unknown-linux-gnu.tar.gz"
|
||||
ASSET_ARM64="rustnet-${TAG}-aarch64-unknown-linux-gnu.tar.gz"
|
||||
|
||||
gh release download "$TAG" -p "$ASSET_X64" -p "$ASSET_ARM64" -D /tmp/verify
|
||||
|
||||
ACTUAL_X64=$(sha256sum "/tmp/verify/$ASSET_X64" | awk '{print $1}')
|
||||
ACTUAL_ARM64=$(sha256sum "/tmp/verify/$ASSET_ARM64" | awk '{print $1}')
|
||||
rm -rf /tmp/verify
|
||||
|
||||
if [ "$ACTUAL_X64" != "$EXPECTED_X64" ]; then
|
||||
echo "::error::x86_64 checksum mismatch: expected $EXPECTED_X64, got $ACTUAL_X64"
|
||||
FAILED=1
|
||||
else
|
||||
echo " ✓ x86_64 checksum verified"
|
||||
fi
|
||||
|
||||
if [ "$ACTUAL_ARM64" != "$EXPECTED_ARM64" ]; then
|
||||
echo "::error::aarch64 checksum mismatch: expected $EXPECTED_ARM64, got $ACTUAL_ARM64"
|
||||
FAILED=1
|
||||
else
|
||||
echo " ✓ aarch64 checksum verified"
|
||||
fi
|
||||
|
||||
if [ "$FAILED" -ne 0 ]; then
|
||||
echo "::error::Checksum verification failed — aborting to prevent pushing stale checksums to AUR"
|
||||
exit 1
|
||||
fi
|
||||
echo "All checksums verified successfully"
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Setup SSH for AUR
|
||||
env:
|
||||
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
# Pass the key via env, not inline interpolation, so its bytes are never
|
||||
# spliced into the rendered command line (matches the GPG handling in
|
||||
# ppa-release.yml). printf avoids echo's escape interpretation.
|
||||
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur
|
||||
chmod 600 ~/.ssh/aur
|
||||
ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts
|
||||
|
||||
# Configure SSH to use the AUR key
|
||||
cat > ~/.ssh/config <<EOF
|
||||
Host aur.archlinux.org
|
||||
IdentityFile ~/.ssh/aur
|
||||
User aur
|
||||
EOF
|
||||
|
||||
- name: Clone AUR repository
|
||||
run: |
|
||||
git clone ssh://aur@aur.archlinux.org/rustnet-bin.git aur-rustnet-bin
|
||||
cd aur-rustnet-bin
|
||||
|
||||
# Configure git
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Update PKGBUILD
|
||||
run: |
|
||||
cd aur-rustnet-bin
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="${{ steps.version.outputs.tag }}"
|
||||
SHA256_X64="${{ steps.checksums.outputs.x64_checksum }}"
|
||||
SHA256_ARM64="${{ steps.checksums.outputs.arm64_checksum }}"
|
||||
|
||||
echo "Updating PKGBUILD to version $VERSION..."
|
||||
|
||||
# Update version and release
|
||||
sed -i "s/^pkgver=.*/pkgver=$VERSION/" PKGBUILD
|
||||
sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD
|
||||
|
||||
# Update source URLs (need to escape special characters for sed)
|
||||
ESCAPED_TAG=$(echo "$TAG" | sed 's/[\/&]/\\&/g')
|
||||
sed -i "s|rustnet-v[0-9.]*-\${arch}|rustnet-${ESCAPED_TAG}-\${arch}|g" PKGBUILD
|
||||
|
||||
# Update checksums
|
||||
sed -i "s/sha256sums_x86_64=.*/sha256sums_x86_64=('$SHA256_X64')/" PKGBUILD
|
||||
sed -i "s/sha256sums_aarch64=.*/sha256sums_aarch64=('$SHA256_ARM64')/" PKGBUILD
|
||||
|
||||
echo "PKGBUILD updated successfully"
|
||||
echo ""
|
||||
echo "Version info:"
|
||||
grep "^pkgver=" PKGBUILD
|
||||
grep "^pkgrel=" PKGBUILD
|
||||
echo ""
|
||||
echo "Checksums:"
|
||||
grep "sha256sums_" PKGBUILD
|
||||
|
||||
- name: Generate .SRCINFO
|
||||
run: |
|
||||
# Use Arch Linux container to run makepkg as the host user
|
||||
docker run --rm -v "$PWD/aur-rustnet-bin:/pkg" --user $(id -u):$(id -g) archlinux:latest bash -c '
|
||||
cd /pkg &&
|
||||
makepkg --printsrcinfo > .SRCINFO
|
||||
' || {
|
||||
# If that fails (no write permissions), install tools as root then run as user
|
||||
docker run --rm -v "$PWD/aur-rustnet-bin:/pkg" archlinux:latest bash -c "
|
||||
pacman -Sy --noconfirm binutils fakeroot sudo &&
|
||||
useradd -m -u $(id -u) builder &&
|
||||
cd /pkg &&
|
||||
su builder -c 'makepkg --printsrcinfo > .SRCINFO'
|
||||
"
|
||||
}
|
||||
|
||||
echo ".SRCINFO generated successfully"
|
||||
|
||||
- name: Commit and push to AUR
|
||||
run: |
|
||||
cd aur-rustnet-bin
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet PKGBUILD .SRCINFO; then
|
||||
echo "No changes to PKGBUILD or .SRCINFO (already at version $VERSION)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Show changes
|
||||
echo "Changes to be committed:"
|
||||
git diff PKGBUILD .SRCINFO
|
||||
|
||||
# Commit and push
|
||||
git add PKGBUILD .SRCINFO
|
||||
git commit -m "Update to version $VERSION"
|
||||
git push origin master
|
||||
|
||||
echo "Pushed changes to AUR repository"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
echo "## 🎉 AUR Package Updated" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Package**: rustnet-bin" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Version**: $VERSION" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Architectures**: x86_64, aarch64" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Checksums" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **x86_64**: \`${{ steps.checksums.outputs.x64_checksum }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **aarch64**: \`${{ steps.checksums.outputs.arm64_checksum }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[View AUR Package →](https://aur.archlinux.org/packages/rustnet-bin)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -0,0 +1,222 @@
|
||||
name: Build Android Musl Static
|
||||
|
||||
# One-off workflow to build and upload android musl static binaries
|
||||
# for multiple architectures to an existing release.
|
||||
#
|
||||
# Supported architectures:
|
||||
# aarch64 - native on ARM runner (existing)
|
||||
# x86_64 - native on x86_64 runner
|
||||
# armv7 - cross-compiled on x86_64 using musl.cc armv7l-linux-musleabihf toolchain
|
||||
# x86 - cross-compiled on x86_64 using musl.cc i686-linux-musl toolchain
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release tag (e.g., v1.0.0)'
|
||||
required: true
|
||||
ref:
|
||||
description: 'Git ref to build from (default: tag)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
name: build-${{ matrix.arch }}-android-static
|
||||
runs-on: ${{ matrix.runner }}
|
||||
container:
|
||||
image: rust:alpine
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
artifact: aarch64-linux-android-musl
|
||||
outline-atomics: true
|
||||
- arch: x86_64
|
||||
runner: ubuntu-latest
|
||||
artifact: x86_64-linux-android-musl
|
||||
outline-atomics: false
|
||||
- arch: armv7
|
||||
runner: ubuntu-latest
|
||||
artifact: armv7-linux-android-musl
|
||||
outline-atomics: true
|
||||
- arch: x86
|
||||
runner: ubuntu-latest
|
||||
artifact: i686-linux-android-musl
|
||||
outline-atomics: false
|
||||
|
||||
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 .
|
||||
git fetch --depth 1 origin tag "${{ inputs.version }}"
|
||||
git checkout "${{ inputs.version }}"
|
||||
|
||||
- name: Checkout repository
|
||||
if: matrix.arch != 'aarch64'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ inputs.version }}
|
||||
|
||||
- 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.
|
||||
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
|
||||
|
||||
# libpcap is pinned by version + sha256 (Dependabot does not track
|
||||
# tarballs fetched in run steps, so bump the URL and the hash together).
|
||||
curl -fsSL --retry 3 --retry-delay 5 \
|
||||
-o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz
|
||||
echo "37ced90a19a302a7f32e458224a00c365c117905c2cd35ac544b6880a81488f0 libpcap.tar.gz" | sha256sum -c -
|
||||
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.
|
||||
printf '%s\n' \
|
||||
'#!/bin/bash' \
|
||||
'args=()' \
|
||||
'for arg in "$@"; do' \
|
||||
' case "$arg" in --target=*) ;; *) 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
|
||||
|
||||
# libpcap is pinned by version + sha256 (Dependabot does not track
|
||||
# tarballs fetched in run steps, so bump the URL and the hash together).
|
||||
curl -fsSL --retry 3 --retry-delay 5 \
|
||||
-o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz
|
||||
echo "37ced90a19a302a7f32e458224a00c365c117905c2cd35ac544b6880a81488f0 libpcap.tar.gz" | sha256sum -c -
|
||||
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="${{ inputs.version }}"
|
||||
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 crates/rustnet-core/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="${{ inputs.version }}"
|
||||
ARCHIVE="rustnet-${VERSION}-${{ matrix.artifact }}.tar.gz"
|
||||
gh release upload "$VERSION" "$ARCHIVE" --repo "${{ github.repository }}" --clobber
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,73 @@
|
||||
name: Build ARM64 Musl Static
|
||||
|
||||
# One-off workflow to build and upload aarch64 musl static binary
|
||||
# to an existing release. Used when the regular release pipeline
|
||||
# fails to upload this artifact.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release tag (e.g., v1.0.0)'
|
||||
required: true
|
||||
ref:
|
||||
description: 'Git ref to build from (default: tag)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-musl-arm64:
|
||||
name: build-aarch64-musl-static
|
||||
runs-on: ubuntu-24.04-arm
|
||||
container:
|
||||
image: rust:alpine
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
run: |
|
||||
apk add --no-cache git
|
||||
git config --global --add safe.directory /__w/rustnet/rustnet
|
||||
REF="${{ inputs.ref || inputs.version }}"
|
||||
git clone --depth 1 https://github.com/${{ github.repository }}.git .
|
||||
git fetch --depth 1 origin tag "${{ inputs.version }}"
|
||||
git checkout "${{ inputs.version }}"
|
||||
|
||||
- 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 github-cli
|
||||
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
|
||||
file target/release/rustnet
|
||||
file target/release/rustnet | grep -q "static.* linked" || \
|
||||
(echo "ERROR: Binary is not statically linked" && exit 1)
|
||||
|
||||
- name: Create release archive
|
||||
run: |
|
||||
VERSION="${{ inputs.version }}"
|
||||
staging="rustnet-${VERSION}-aarch64-unknown-linux-musl"
|
||||
mkdir -p "$staging/assets"
|
||||
cp target/release/rustnet "$staging/"
|
||||
cp crates/rustnet-core/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="${{ inputs.version }}"
|
||||
ARCHIVE="rustnet-${VERSION}-aarch64-unknown-linux-musl.tar.gz"
|
||||
gh release upload "$VERSION" "$ARCHIVE" --clobber
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,353 @@
|
||||
name: Build Platforms
|
||||
|
||||
# Reusable workflow for building RustNet on all platforms.
|
||||
# Called by both test-platform-builds.yml and release.yml
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
create-archives:
|
||||
description: 'Create release archives with README/LICENSE (true for release, false for test)'
|
||||
type: boolean
|
||||
default: false
|
||||
strip-symbols:
|
||||
description: 'Strip debug symbols from binaries'
|
||||
type: boolean
|
||||
default: false
|
||||
version:
|
||||
description: 'Version string for archive naming (e.g., v0.3.0)'
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
name: build-${{ matrix.build }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build:
|
||||
- linux-x64-gnu
|
||||
- linux-aarch64-gnu
|
||||
- linux-armv7-gnueabihf
|
||||
- macos-aarch64
|
||||
- macos-x64
|
||||
- windows-x64-msvc
|
||||
- windows-x86-msvc
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
- cargo: cargo
|
||||
- build: linux-x64-gnu
|
||||
target: x86_64-unknown-linux-gnu
|
||||
run-tests: true
|
||||
- build: linux-aarch64-gnu
|
||||
target: aarch64-unknown-linux-gnu
|
||||
cargo: cross
|
||||
- build: linux-armv7-gnueabihf
|
||||
target: armv7-unknown-linux-gnueabihf
|
||||
cargo: cross
|
||||
- build: macos-aarch64
|
||||
os: macos-14
|
||||
target: aarch64-apple-darwin
|
||||
run-tests: true
|
||||
- build: macos-x64
|
||||
os: macos-14
|
||||
target: x86_64-apple-darwin
|
||||
- build: windows-x64-msvc
|
||||
os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- build: windows-x86-msvc
|
||||
os: windows-latest
|
||||
target: i686-pc-windows-msvc
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup Linux dependencies
|
||||
if: matrix.os == 'ubuntu-22.04'
|
||||
uses: ./.github/actions/setup-linux-deps
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable # unpinned: maintained branch by Rust team member
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
components: rustfmt
|
||||
|
||||
- name: Install cross
|
||||
if: matrix.cargo == 'cross'
|
||||
run: cargo install cross@0.2.5
|
||||
|
||||
- name: Build
|
||||
uses: ./.github/actions/build-rustnet
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cargo-command: ${{ matrix.cargo }}
|
||||
default-features: ${{ contains(matrix.target, 'linux') && 'true' || 'false' }}
|
||||
strip-symbols: ${{ inputs.strip-symbols && 'true' || 'false' }}
|
||||
|
||||
- name: Run tests
|
||||
if: matrix.run-tests
|
||||
run: cargo test --verbose
|
||||
|
||||
# For test builds: upload raw binary
|
||||
- name: Upload binary artifact
|
||||
if: ${{ !inputs.create-archives }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: rustnet-${{ matrix.build }}
|
||||
path: target/${{ matrix.target }}/release/rustnet${{ contains(matrix.os, 'windows') && '.exe' || '' }}
|
||||
if-no-files-found: error
|
||||
|
||||
# For release builds: create archive with README/LICENSE
|
||||
- name: Create release archive
|
||||
if: ${{ inputs.create-archives }}
|
||||
shell: bash
|
||||
env:
|
||||
RUSTNET_BIN: ${{ contains(matrix.os, 'windows') && 'rustnet.exe' || 'rustnet' }}
|
||||
run: |
|
||||
staging="rustnet-${{ inputs.version }}-${{ matrix.target }}"
|
||||
mkdir -p "$staging"
|
||||
|
||||
cp "target/${{ matrix.target }}/release/$RUSTNET_BIN" "$staging/"
|
||||
|
||||
if [ -d "crates/rustnet-core/assets" ] && [ -f "crates/rustnet-core/assets/services" ]; then
|
||||
mkdir -p "$staging/assets"
|
||||
cp "crates/rustnet-core/assets/services" "$staging/assets/"
|
||||
fi
|
||||
|
||||
cp README.md "$staging/"
|
||||
cp LICENSE "$staging/" 2>/dev/null || true
|
||||
|
||||
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
|
||||
7z a "$staging.zip" "$staging"
|
||||
echo "ASSET=$staging.zip" >> $GITHUB_ENV
|
||||
else
|
||||
tar czf "$staging.tar.gz" "$staging"
|
||||
echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Upload release archive
|
||||
if: ${{ inputs.create-archives }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: build-${{ matrix.target }}
|
||||
path: ${{ env.ASSET }}
|
||||
if-no-files-found: error
|
||||
|
||||
build-static:
|
||||
permissions:
|
||||
contents: read
|
||||
name: build-linux-static-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
runner: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
- arch: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
target: aarch64-unknown-linux-musl
|
||||
features: '--features default'
|
||||
- arch: aarch64-android
|
||||
runner: ubuntu-24.04-arm
|
||||
target: aarch64-linux-android-musl # Note: it's not a real rust target but we rename the artifact
|
||||
features: '--no-default-features'
|
||||
- arch: x86_64-android
|
||||
runner: ubuntu-latest
|
||||
target: x86_64-linux-android-musl
|
||||
features: '--no-default-features'
|
||||
- arch: armv7-android
|
||||
runner: ubuntu-latest
|
||||
target: armv7-linux-android-musl
|
||||
features: '--no-default-features'
|
||||
- arch: x86-android
|
||||
runner: ubuntu-latest
|
||||
target: i686-linux-android-musl
|
||||
features: '--no-default-features'
|
||||
container:
|
||||
image: rust:alpine
|
||||
steps:
|
||||
# x86_64: use standard checkout action
|
||||
- name: Checkout repository
|
||||
if: matrix.arch == 'x86_64' || matrix.arch == 'x86_64-android' || matrix.arch == 'armv7-android' || matrix.arch == 'x86-android'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
# aarch64: JS actions don't work in Alpine containers on ARM runners
|
||||
- name: Checkout repository (ARM workaround)
|
||||
if: contains(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 .
|
||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||
git fetch --depth 1 origin tag "${{ github.ref_name }}"
|
||||
git checkout "${{ github.ref_name }}"
|
||||
fi
|
||||
|
||||
# x86_64: use composite action
|
||||
- name: Build static binary
|
||||
if: matrix.arch == 'x86_64'
|
||||
uses: ./.github/actions/build-static
|
||||
|
||||
# aarch64 & android: inline build (composite actions don't work with ARM workaround/zig wrapper easily here)
|
||||
- name: Install dependencies (ARM & Android)
|
||||
if: matrix.arch != 'x86_64'
|
||||
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 curl github-cli
|
||||
rustup component add rustfmt
|
||||
|
||||
- name: Install cross-compiler and libpcap (armv7)
|
||||
if: matrix.arch == 'armv7-android'
|
||||
run: |
|
||||
rustup target add armv7-unknown-linux-musleabihf
|
||||
apk add zig flex bison bash
|
||||
|
||||
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
|
||||
|
||||
# libpcap is pinned by version + sha256 (Dependabot does not track
|
||||
# tarballs fetched in run steps, so bump the URL and the hash together).
|
||||
curl -fsSL --retry 3 --retry-delay 5 \
|
||||
-o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz
|
||||
echo "37ced90a19a302a7f32e458224a00c365c117905c2cd35ac544b6880a81488f0 libpcap.tar.gz" | sha256sum -c -
|
||||
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 ..
|
||||
|
||||
- name: Install cross-compiler and libpcap (x86/i686)
|
||||
if: matrix.arch == 'x86-android'
|
||||
run: |
|
||||
rustup target add i686-unknown-linux-musl
|
||||
apk add zig flex bison bash
|
||||
|
||||
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
|
||||
|
||||
# libpcap is pinned by version + sha256 (Dependabot does not track
|
||||
# tarballs fetched in run steps, so bump the URL and the hash together).
|
||||
curl -fsSL --retry 3 --retry-delay 5 \
|
||||
-o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz
|
||||
echo "37ced90a19a302a7f32e458224a00c365c117905c2cd35ac544b6880a81488f0 libpcap.tar.gz" | sha256sum -c -
|
||||
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 / AArch64 Android)
|
||||
if: matrix.arch == 'aarch64' || matrix.arch == 'aarch64-android'
|
||||
env:
|
||||
# -mno-outline-atomics: Prevent GCC from generating calls to __aarch64_ldadd4_sync etc.
|
||||
# which aren't in Alpine's libatomic. Uses inline atomics instead.
|
||||
CFLAGS: "-mno-outline-atomics"
|
||||
CXXFLAGS: "-mno-outline-atomics"
|
||||
RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a"
|
||||
run: cargo build --release ${{ matrix.features }}
|
||||
|
||||
- name: Build static binary (x86_64 Android)
|
||||
if: matrix.arch == 'x86_64-android'
|
||||
env:
|
||||
RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a"
|
||||
run: cargo build --release ${{ matrix.features }}
|
||||
|
||||
- name: Build static binary (armv7-android)
|
||||
if: matrix.arch == 'armv7-android'
|
||||
env:
|
||||
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 ${{ matrix.features }}
|
||||
|
||||
- name: Build static binary (x86-android)
|
||||
if: matrix.arch == 'x86-android'
|
||||
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 ${{ matrix.features }}
|
||||
|
||||
- name: Verify static linking (ARM & Android)
|
||||
if: matrix.arch != 'x86_64'
|
||||
run: |
|
||||
BIN="${{ (matrix.arch == 'armv7-android' && 'target/armv7-unknown-linux-musleabihf/release/rustnet') || (matrix.arch == 'x86-android' && '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)
|
||||
|
||||
# For test builds: upload raw binary
|
||||
- name: Upload binary artifact
|
||||
if: ${{ !inputs.create-archives }}
|
||||
continue-on-error: ${{ matrix.arch != 'x86_64' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: rustnet-linux-static-${{ matrix.arch }}
|
||||
path: ${{ (matrix.arch == 'armv7-android' && 'target/armv7-unknown-linux-musleabihf/release/rustnet') || (matrix.arch == 'x86-android' && 'target/i686-unknown-linux-musl/release/rustnet') || 'target/release/rustnet' }}
|
||||
if-no-files-found: error
|
||||
|
||||
# For release builds: create archive
|
||||
- name: Create release archive
|
||||
if: ${{ inputs.create-archives }}
|
||||
run: |
|
||||
BIN="${{ (matrix.arch == 'armv7-android' && 'target/armv7-unknown-linux-musleabihf/release/rustnet') || (matrix.arch == 'x86-android' && 'target/i686-unknown-linux-musl/release/rustnet') || 'target/release/rustnet' }}"
|
||||
|
||||
staging="rustnet-${{ inputs.version }}-${{ matrix.target }}"
|
||||
mkdir -p "$staging/assets"
|
||||
|
||||
cp "$BIN" "$staging/rustnet"
|
||||
cp crates/rustnet-core/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 release archive
|
||||
if: ${{ inputs.create-archives }}
|
||||
# JS actions don't work in Alpine containers on ARM runners;
|
||||
# release.yml has dedicated upload-arm-static/upload-android-static jobs as fallback
|
||||
continue-on-error: ${{ contains(matrix.arch, 'aarch64') }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: build-${{ matrix.target }}
|
||||
path: rustnet-${{ inputs.version }}-${{ matrix.target }}.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
name: Update COPR Spec Version
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-spec:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
# Extract version by removing 'v' prefix
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Extracted version: $VERSION"
|
||||
|
||||
- name: Update RPM spec file
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
SPEC_FILE="rpm/rustnet.spec"
|
||||
|
||||
# Update the Version: line in the spec file
|
||||
sed -i "s/^Version:.*/Version: $VERSION/" "$SPEC_FILE"
|
||||
|
||||
echo "Updated $SPEC_FILE to version $VERSION"
|
||||
echo ""
|
||||
echo "Version line:"
|
||||
grep "^Version:" "$SPEC_FILE"
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
# Configure git
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --quiet rpm/rustnet.spec; then
|
||||
echo "No changes to rpm/rustnet.spec (already at version $VERSION)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Commit and push
|
||||
git add rpm/rustnet.spec
|
||||
git commit -m "chore: update RPM spec to version $VERSION"
|
||||
git push origin HEAD:main
|
||||
|
||||
echo "Pushed changes to main branch"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "## 🎉 RPM Spec Updated" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Version**: ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **File**: rpm/rustnet.spec" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "COPR will automatically rebuild the package via webhook." >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[View COPR Builds →](https://copr.fedorainfracloud.org/coprs/domcyrus/rustnet/builds/)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -0,0 +1,72 @@
|
||||
name: Docker Build and Publish
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
|
||||
|
||||
- name: Log in to Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
# Enable Kubernetes pod/container attribution in the container image.
|
||||
# It is dependency-free and runtime-gated (`--kubernetes auto`, the
|
||||
# default, is inert outside a pod), so a single image serves both
|
||||
# general container use and kubectl-rustnet. Native installs
|
||||
# (cargo/brew/deb/rpm) leave the feature off and stay lean.
|
||||
build-args: |
|
||||
CARGO_FEATURES=kubernetes
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Generate artifact attestation
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.build-and-push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
@@ -0,0 +1,146 @@
|
||||
name: Release to OBS
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release-obs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: |
|
||||
# Extract version by removing 'v' prefix if it exists, fallback to Cargo.toml
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
else
|
||||
# Read the binary's [package] version, not [workspace.package] (the
|
||||
# latter is the 0.x library version and comes first in Cargo.toml).
|
||||
VERSION=$(awk -F'"' '/^\[package\]/{p=1} p && /^version = /{print $2; exit}' Cargo.toml)
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Version: $VERSION"
|
||||
|
||||
- name: Setup Linux dependencies
|
||||
uses: ./.github/actions/setup-linux-deps
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable # unpinned: maintained branch by Rust team member
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install OBS dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y osc obs-build build-essential
|
||||
|
||||
- name: Configure OBS credentials
|
||||
run: |
|
||||
cat <<EOF > ~/.oscrc
|
||||
[general]
|
||||
apiurl = https://api.opensuse.org
|
||||
|
||||
[https://api.opensuse.org]
|
||||
user = ${{ secrets.OBS_USER }}
|
||||
pass = ${{ secrets.OBS_PASS }}
|
||||
EOF
|
||||
chmod 600 ~/.oscrc
|
||||
|
||||
- name: Download source tarball
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
PACKAGE_NAME="rustnet"
|
||||
|
||||
# Output filename must match Source0's basename (v%{version}.tar.gz)
|
||||
# so the offline OBS build can resolve it; the internal dir prefix
|
||||
# stays rustnet-%{version}/ to match %autosetup -n.
|
||||
RELEASE_TAG="v${VERSION}"
|
||||
if git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then
|
||||
echo "✓ Found release tag: $RELEASE_TAG"
|
||||
git archive --format=tar --prefix="${PACKAGE_NAME}-${VERSION}/" "$RELEASE_TAG" | gzip > "v${VERSION}.tar.gz"
|
||||
else
|
||||
echo "⚠ Release tag $RELEASE_TAG not found, using HEAD"
|
||||
git archive --format=tar --prefix="${PACKAGE_NAME}-${VERSION}/" HEAD | gzip > "v${VERSION}.tar.gz"
|
||||
fi
|
||||
|
||||
- name: Vendor dependencies
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
PACKAGE_NAME="rustnet"
|
||||
RELEASE_TAG="v${VERSION}"
|
||||
|
||||
mkdir -p build-vendor
|
||||
if git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then
|
||||
git archive --format=tar "$RELEASE_TAG" | tar -x -C build-vendor
|
||||
else
|
||||
git archive --format=tar HEAD | tar -x -C build-vendor
|
||||
fi
|
||||
|
||||
cd build-vendor
|
||||
# Capture the source-replacement config that cargo prints. The OBS
|
||||
# build has no network, so it needs this at .cargo/config.toml to
|
||||
# resolve crates from the vendored directory instead of crates.io.
|
||||
mkdir -p .cargo
|
||||
cargo vendor vendor > .cargo/config.toml
|
||||
|
||||
# Clean up static libs to reduce size
|
||||
find vendor -name "*.a" -delete
|
||||
find vendor -name "*.lib" -delete
|
||||
|
||||
# Bundle .cargo/config.toml alongside vendor/ so `%autosetup -a 1`
|
||||
# drops both into the source tree for the offline build.
|
||||
tar --use-compress-program="zstd -T0 -10" -cf ../vendor.tar.zst .cargo vendor
|
||||
cd ..
|
||||
rm -rf build-vendor
|
||||
|
||||
- name: Prepare and push to OBS
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
OBS_PROJECT="${{ secrets.OBS_PROJECT }}"
|
||||
OBS_PACKAGE="rustnet"
|
||||
|
||||
if [ -z "$OBS_PROJECT" ]; then
|
||||
echo "::error::OBS_PROJECT secret is not set!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Checkout OBS project
|
||||
osc checkout "$OBS_PROJECT" "$OBS_PACKAGE"
|
||||
cd "$OBS_PROJECT/$OBS_PACKAGE"
|
||||
|
||||
# Clean old files
|
||||
rm -f *.tar.gz *.tar.zst *.spec
|
||||
|
||||
# Copy new files
|
||||
cp ../../v${VERSION}.tar.gz .
|
||||
cp ../../vendor.tar.zst .
|
||||
cp ../../rpm/rustnet.spec .
|
||||
|
||||
# Update version in spec file
|
||||
sed -i "s/^Version:.*/Version: $VERSION/" rustnet.spec
|
||||
|
||||
# Prepend a changelog entry. OBS has no rpmautospec, so the spec's
|
||||
# %changelog is empty on SUSE and the changelog lives here instead.
|
||||
STAMP="$(LC_ALL=C date -u '+%a %b %e %T %Z %Y')"
|
||||
{
|
||||
printf -- '-------------------------------------------------------------------\n'
|
||||
printf '%s - %s@opensuse.org\n\n' "$STAMP" "${{ secrets.OBS_USER }}"
|
||||
printf -- '- Update to version %s\n\n' "$VERSION"
|
||||
[ -f rustnet.changes ] && cat rustnet.changes
|
||||
} > rustnet.changes.new
|
||||
mv rustnet.changes.new rustnet.changes
|
||||
|
||||
# Add new files, remove deleted ones
|
||||
osc addremove
|
||||
|
||||
# Commit to OBS
|
||||
osc commit -m "Update to version $VERSION"
|
||||
@@ -0,0 +1,254 @@
|
||||
name: Release to Ubuntu PPA
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tarball_suffix:
|
||||
description: 'Tarball suffix (e.g., ds1, ds2)'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ubuntu_release:
|
||||
description: 'Ubuntu release codename'
|
||||
required: true
|
||||
default: 'resolute'
|
||||
type: choice
|
||||
options:
|
||||
- questing # 25.10
|
||||
- resolute # 26.04 LTS
|
||||
tarball_suffix:
|
||||
description: 'Tarball suffix (e.g., ds1, ds2) - leave empty for new releases'
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
DEBEMAIL: cadetg@gmail.com
|
||||
DEBFULLNAME: Marco Cadetg
|
||||
PPA: ppa:domcyrus/rustnet
|
||||
|
||||
jobs:
|
||||
set-matrix:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
releases: ${{ steps.set.outputs.releases }}
|
||||
steps:
|
||||
- id: set
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo 'releases=["${{ inputs.ubuntu_release }}"]' >> $GITHUB_OUTPUT
|
||||
echo "Dispatch: building only ${{ inputs.ubuntu_release }}"
|
||||
else
|
||||
echo 'releases=["questing","resolute"]' >> $GITHUB_OUTPUT
|
||||
echo "Auto: building questing and resolute"
|
||||
fi
|
||||
|
||||
build-and-upload:
|
||||
needs: set-matrix
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ubuntu_release: ${{ fromJSON(needs.set-matrix.outputs.releases) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
debhelper \
|
||||
devscripts \
|
||||
dput \
|
||||
gnupg \
|
||||
libpcap-dev \
|
||||
libelf-dev \
|
||||
elfutils \
|
||||
zlib1g-dev \
|
||||
clang \
|
||||
llvm \
|
||||
pkg-config
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable # unpinned: maintained branch by Rust team member
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Import GPG key
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
run: |
|
||||
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
gpg --list-secret-keys
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: |
|
||||
# Read the binary's [package] version, not [workspace.package]. Since
|
||||
# the workspace split, Cargo.toml has two `version =` lines and the
|
||||
# workspace one (0.x libs) comes first, so `head -1` read the wrong
|
||||
# value. Anchor on the [package] section.
|
||||
VERSION=$(awk -F'"' '/^\[package\]/{p=1} p && /^version = /{print $2; exit}' Cargo.toml)
|
||||
|
||||
# Add tarball suffix if provided (e.g., +ds1, +ds2)
|
||||
TARBALL_SUFFIX="${{ inputs.tarball_suffix }}"
|
||||
if [ -n "$TARBALL_SUFFIX" ]; then
|
||||
TARBALL_VERSION="${VERSION}+${TARBALL_SUFFIX}"
|
||||
echo "version=$TARBALL_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Using tarball version: $TARBALL_VERSION"
|
||||
else
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Using version: $VERSION"
|
||||
fi
|
||||
|
||||
- name: Update changelog
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
# Per-series version suffix so each Ubuntu release has an independent
|
||||
# version line on Launchpad. The `+series1` suffix sorts strictly
|
||||
# higher than a plain `-1ubuntu1`, so future uploads always supersede
|
||||
# any previously published package.
|
||||
NEW_VERSION="${VERSION}-1ubuntu1+${{ matrix.ubuntu_release }}1"
|
||||
|
||||
DEBFULLNAME="${{ env.DEBFULLNAME }}" DEBEMAIL="${{ env.DEBEMAIL }}" \
|
||||
dch --newversion "$NEW_VERSION" \
|
||||
--distribution "${{ matrix.ubuntu_release }}" \
|
||||
"Build for ${{ matrix.ubuntu_release }} - upstream release $VERSION"
|
||||
|
||||
echo "✓ Changelog set to $NEW_VERSION (${{ matrix.ubuntu_release }})"
|
||||
|
||||
- name: Pin Rust toolchain version per release
|
||||
run: |
|
||||
# Each Ubuntu release ships a different set of versioned rustc/cargo
|
||||
# packages. We pin to the lowest version available on the target
|
||||
# release that still satisfies our >= 1.88 floor (let-chains).
|
||||
case "${{ matrix.ubuntu_release }}" in
|
||||
questing) RUST_VERSION=1.88 ;;
|
||||
resolute) RUST_VERSION=1.91 ;;
|
||||
*) echo "::error::Unknown release ${{ matrix.ubuntu_release }} - add it to the case statement"; exit 1 ;;
|
||||
esac
|
||||
echo "Pinning to rustc-$RUST_VERSION / cargo-$RUST_VERSION for ${{ matrix.ubuntu_release }}"
|
||||
|
||||
sed -i -E "s/(cargo|rustc|rustdoc)-1\.[0-9]+/\1-${RUST_VERSION}/g" debian/control debian/rules
|
||||
|
||||
echo "--- debian/control build-depends ---"
|
||||
grep -E "(cargo|rustc)-1\." debian/control || true
|
||||
echo "--- debian/rules toolchain exports ---"
|
||||
grep -E "(CARGO|RUSTC|RUSTDOC) *=" debian/rules || true
|
||||
|
||||
- name: Build source package
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
# Binary [package] version (drives the release tag to check out below).
|
||||
BASE_VERSION=$(awk -F'"' '/^\[package\]/{p=1} p && /^version = /{print $2; exit}' Cargo.toml)
|
||||
PACKAGE_NAME="rustnet-monitor"
|
||||
|
||||
# Create build directory
|
||||
mkdir -p build-ppa
|
||||
|
||||
# Extract source from release tag
|
||||
RELEASE_TAG="v${BASE_VERSION}"
|
||||
if git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then
|
||||
echo "✓ Found release tag: $RELEASE_TAG"
|
||||
git archive --format=tar --prefix="${PACKAGE_NAME}-${VERSION}/" "$RELEASE_TAG" | tar -x -C build-ppa
|
||||
else
|
||||
echo "⚠ Release tag $RELEASE_TAG not found, using HEAD"
|
||||
git archive --format=tar --prefix="${PACKAGE_NAME}-${VERSION}/" HEAD | tar -x -C build-ppa
|
||||
fi
|
||||
|
||||
# Vendor dependencies separately from orig tarball
|
||||
echo "Vendoring Rust dependencies..."
|
||||
cd build-ppa/${PACKAGE_NAME}-${VERSION}
|
||||
|
||||
cargo vendor vendor
|
||||
|
||||
# Remove prebuilt static libraries (keep .dll for tests)
|
||||
echo "Cleaning vendor directory..."
|
||||
find vendor -name "*.a" -delete
|
||||
find vendor -name "*.lib" -delete
|
||||
|
||||
# Pack vendor directory as separate tarball in debian/
|
||||
echo "Creating vendor tarball..."
|
||||
tar -cJf ../vendor.tar.xz vendor
|
||||
rm -rf vendor
|
||||
|
||||
# Create orig tarball (without vendor directory)
|
||||
echo "Creating orig tarball..."
|
||||
cd ..
|
||||
ORIG_TARBALL="${PACKAGE_NAME}_${VERSION}.orig.tar.gz"
|
||||
tar -czf "${ORIG_TARBALL}" "${PACKAGE_NAME}-${VERSION}"
|
||||
|
||||
# Add debian directory and vendor tarball
|
||||
cp -r "$GITHUB_WORKSPACE/debian" "${PACKAGE_NAME}-${VERSION}/"
|
||||
mv vendor.tar.xz "${PACKAGE_NAME}-${VERSION}/debian/"
|
||||
|
||||
# Build source package
|
||||
cd "${PACKAGE_NAME}-${VERSION}"
|
||||
|
||||
# Always use -sa to include orig tarball
|
||||
# Launchpad will reuse existing file if hash matches
|
||||
debuild -S -sa -d -us -uc
|
||||
|
||||
- name: Sign and upload
|
||||
env:
|
||||
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
|
||||
run: |
|
||||
cd build-ppa
|
||||
# Locate the source.changes file produced by debuild. The version
|
||||
# suffix (+seriesN) is dynamic, so glob rather than reconstruct.
|
||||
CHANGES_FILE=$(ls rustnet-monitor_*_source.changes | head -1)
|
||||
if [ -z "$CHANGES_FILE" ]; then
|
||||
echo "::error::No source.changes file found in build-ppa"
|
||||
exit 1
|
||||
fi
|
||||
echo "Signing and uploading $CHANGES_FILE"
|
||||
|
||||
# Sign
|
||||
debsign -k${GPG_KEY_ID} ${CHANGES_FILE}
|
||||
|
||||
# Verify
|
||||
gpg --verify ${CHANGES_FILE}
|
||||
|
||||
# Upload to PPA
|
||||
dput ${{ env.PPA }} ${CHANGES_FILE}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ppa-source-${{ matrix.ubuntu_release }}
|
||||
path: |
|
||||
build-ppa/*.dsc
|
||||
build-ppa/*.tar.gz
|
||||
build-ppa/*.tar.xz
|
||||
build-ppa/*.changes
|
||||
build-ppa/*.buildinfo
|
||||
retention-days: 30
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
cd build-ppa
|
||||
CHANGES_FILE=$(ls rustnet-monitor_*_source.changes | head -1)
|
||||
FULL_VERSION=$(echo "$CHANGES_FILE" | sed 's/rustnet-monitor_\(.*\)_source\.changes/\1/')
|
||||
|
||||
echo "## 🎉 PPA Upload Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Package**: rustnet-monitor" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Version**: $FULL_VERSION" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Ubuntu**: ${{ matrix.ubuntu_release }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Installation" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```bash' >> $GITHUB_STEP_SUMMARY
|
||||
echo "sudo add-apt-repository ppa:domcyrus/rustnet" >> $GITHUB_STEP_SUMMARY
|
||||
echo "sudo apt update && sudo apt install rustnet" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "[View PPA](https://launchpad.net/~domcyrus/+archive/ubuntu/rustnet/+packages)" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Publish to crates.io
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y libpcap-dev libelf-dev zlib1g-dev clang llvm pkg-config
|
||||
|
||||
- name: Set up Rust
|
||||
uses: dtolnay/rust-toolchain@stable # unpinned: maintained branch by Rust team member
|
||||
|
||||
- name: Publish to crates.io
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
|
||||
run: |
|
||||
# True if $crate@$VERSION is already on crates.io. `cargo search` does
|
||||
# fuzzy matching and prints `name = "version" # description`, so we
|
||||
# anchor on the exact `name = "version"` prefix. A bare `grep VERSION`
|
||||
# would false-match the version string appearing in a description, and
|
||||
# `--limit 1` alone isn't guaranteed to be the exact crate.
|
||||
is_published() {
|
||||
cargo search "$1" --limit 20 \
|
||||
| grep -qF "$1 = \"$2\""
|
||||
}
|
||||
|
||||
# The workspace must be published in dependency order: rustnet-core
|
||||
# first, then the crates that depend on it, then the binary last.
|
||||
# Each crate's path deps also carry a version, so dependents resolve
|
||||
# against crates.io once their dependencies are indexed.
|
||||
for crate in rustnet-core rustnet-capture rustnet-host rustnet-monitor; do
|
||||
VERSION=$(cargo metadata --no-deps --format-version 1 \
|
||||
| jq -r --arg n "$crate" '.packages[] | select(.name == $n) | .version')
|
||||
if is_published "$crate" "$VERSION"; then
|
||||
echo "⚠️ $crate@$VERSION already published to crates.io, skipping"
|
||||
continue
|
||||
fi
|
||||
echo "📦 Publishing $crate@$VERSION"
|
||||
cargo publish -p "$crate"
|
||||
# Wait for the new version to appear in the index before publishing
|
||||
# a dependent that requires it.
|
||||
if [ "$crate" != "rustnet-monitor" ]; then
|
||||
for _ in $(seq 1 30); do
|
||||
if is_published "$crate" "$VERSION"; then
|
||||
break
|
||||
fi
|
||||
echo "⏳ waiting for $crate@$VERSION to appear on crates.io..."
|
||||
sleep 10
|
||||
done
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,817 @@
|
||||
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- 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 crates/rustnet-core/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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- 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
|
||||
|
||||
# libpcap is pinned by version + sha256 (Dependabot does not track
|
||||
# tarballs fetched in run steps, so bump the URL and the hash together).
|
||||
curl -fsSL --retry 3 --retry-delay 5 \
|
||||
-o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz
|
||||
echo "37ced90a19a302a7f32e458224a00c365c117905c2cd35ac544b6880a81488f0 libpcap.tar.gz" | sha256sum -c -
|
||||
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
|
||||
|
||||
# libpcap is pinned by version + sha256 (Dependabot does not track
|
||||
# tarballs fetched in run steps, so bump the URL and the hash together).
|
||||
curl -fsSL --retry 3 --retry-delay 5 \
|
||||
-o libpcap.tar.gz https://www.tcpdump.org/release/libpcap-1.10.5.tar.gz
|
||||
echo "37ced90a19a302a7f32e458224a00c365c117905c2cd35ac544b6880a81488f0 libpcap.tar.gz" | sha256sum -c -
|
||||
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 crates/rustnet-core/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
|
||||
- release-obs
|
||||
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- 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 crates/rustnet-core/assets
|
||||
if [ -f "rustnet-${{ github.ref_name }}-$target/assets/services" ]; then
|
||||
cp "rustnet-${{ github.ref_name }}-$target/assets/services" crates/rustnet-core/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 crates/rustnet-core/assets
|
||||
if [ -f "rustnet-${{ github.ref_name }}-$target/assets/services" ]; then
|
||||
cp "rustnet-${{ github.ref_name }}-$target/assets/services" crates/rustnet-core/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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- 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
|
||||
# Verify the toolchain that builds the shipped MSI. Pinned by version +
|
||||
# sha256; bump both together (Dependabot does not track this download).
|
||||
$expected = "2c1888d5d1dba377fc7fa14444cf556963747ff9a0a289a3599cf09da03b9e2e"
|
||||
$actual = (Get-FileHash "$env:TEMP\wix-binaries.zip" -Algorithm SHA256).Hash.ToLower()
|
||||
if ($actual -ne $expected) {
|
||||
throw "WiX zip checksum mismatch: expected $expected, got $actual"
|
||||
}
|
||||
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. `-p rustnet-monitor` is required since the
|
||||
# workspace split: cargo-wix refuses to guess the package in a
|
||||
# workspace ("Workspace detected. Please pass a package name.").
|
||||
Write-Host "Creating MSI package..."
|
||||
cargo wix -p rustnet-monitor --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
|
||||
|
||||
release-obs:
|
||||
name: Release to OBS
|
||||
needs: publish-release
|
||||
uses: ./.github/workflows/obs-release.yml
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'build.rs'
|
||||
- 'benches/**'
|
||||
- 'Dockerfile'
|
||||
- 'deny.toml'
|
||||
- '.github/workflows/rust.yml'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'crates/**'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'build.rs'
|
||||
- 'benches/**'
|
||||
- 'Dockerfile'
|
||||
- 'deny.toml'
|
||||
- '.github/workflows/rust.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libpcap-dev libelf-dev zlib1g-dev clang llvm pkg-config
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
# --workspace --all-targets so the library crates (rustnet-core/-capture/
|
||||
# -host) are linted, built, and tested too. The workspace root is a real
|
||||
# package (rustnet-monitor), so a bare `cargo {clippy,build,test}` only
|
||||
# selects the binary and silently skips the library crates' lints, tests,
|
||||
# and doctests.
|
||||
- name: Run clippy
|
||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||
- name: Build
|
||||
run: cargo build --workspace --all-targets --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --workspace --verbose
|
||||
- name: Run cargo-deny
|
||||
# Checks RustSec advisories and yanked crates (yanked = "deny" in
|
||||
# deny.toml, so a withdrawn dependency pinned in Cargo.lock is caught),
|
||||
# plus license policy, wildcard bans, and registry sources.
|
||||
uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20
|
||||
with:
|
||||
command: check
|
||||
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
|
||||
- name: Build Docker image
|
||||
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
load: true
|
||||
tags: rustnet:ci-test
|
||||
- name: Verify Docker image
|
||||
run: docker run --rm rustnet:ci-test --version
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Security audit
|
||||
|
||||
# Scheduled advisory check against the committed Cargo.lock. PR/push CI
|
||||
# (rust.yml) already runs the full `cargo deny check`, but new RustSec
|
||||
# advisories can be published against an unchanged lockfile — this catches
|
||||
# those without requiring a push. Licenses/bans/sources can only change with
|
||||
# a lockfile change, so the scheduled job checks advisories only.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '37 5 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
advisories:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Check RustSec advisories
|
||||
uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20
|
||||
with:
|
||||
command: check advisories
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Test Platform Builds
|
||||
|
||||
# Test builds on all supported platforms.
|
||||
# Uses the shared build-platforms workflow.
|
||||
# Also triggers a FreeBSD test build in the rustnet-bsd repo.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'build.rs'
|
||||
- 'Cross.toml'
|
||||
- 'src/**'
|
||||
- '.github/workflows/test-platform-builds.yml'
|
||||
- '.github/workflows/build-platforms.yml'
|
||||
- '.github/actions/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: ./.github/workflows/build-platforms.yml
|
||||
with:
|
||||
create-archives: false
|
||||
strip-symbols: false
|
||||
|
||||
trigger-freebsd-build:
|
||||
name: trigger-freebsd-build
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger FreeBSD test build
|
||||
run: |
|
||||
REF="${{ github.sha }}"
|
||||
TAG="test-$(date +%Y%m%d-%H%M%S)"
|
||||
|
||||
gh api repos/domcyrus/rustnet-bsd/dispatches \
|
||||
-f event_type=freebsd-build \
|
||||
-f "client_payload[tag]=$TAG" \
|
||||
-f "client_payload[rustnet_ref]=$REF" \
|
||||
-f "client_payload[create_release]=false"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.BSD_DISPATCH_TOKEN }}
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Update OUI Database
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 1 * *' # 1st of every month at 06:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-oui:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Download latest IEEE OUI database
|
||||
run: |
|
||||
curl -fsSL 'https://standards-oui.ieee.org/oui/oui.txt' -o oui-raw.txt
|
||||
grep '(base 16)' oui-raw.txt | sed 's/[[:space:]]*(base 16)[[:space:]]*/\t/' > oui-clean.txt
|
||||
gzip -9 -c oui-clean.txt > crates/rustnet-core/assets/oui.gz
|
||||
rm oui-raw.txt oui-clean.txt
|
||||
|
||||
- name: Create pull request if changed
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
if git diff --quiet crates/rustnet-core/assets/oui.gz; then
|
||||
echo "OUI database is already up to date."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BRANCH="update-oui-db"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git checkout -B "$BRANCH"
|
||||
git add crates/rustnet-core/assets/oui.gz
|
||||
git commit -m "Update OUI vendor database"
|
||||
git push -f origin "$BRANCH"
|
||||
|
||||
# Only create PR if one doesn't already exist for this branch
|
||||
if ! gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number' | grep -q .; then
|
||||
gh pr create \
|
||||
--title "Update OUI vendor database" \
|
||||
--body "Automated monthly update of the IEEE OUI (MAC vendor) database.
|
||||
|
||||
Source: https://standards-oui.ieee.org/oui/oui.txt" \
|
||||
--label dependencies
|
||||
fi
|
||||
Reference in New Issue
Block a user