chore: import upstream snapshot with attribution
Rust / build (push) Failing after 1s
Rust / docker (push) Failing after 0s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:29:44 +08:00
commit 19dc5d82a0
236 changed files with 224662 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
# GitHub Actions PPA Setup
## Add GitHub Secrets
Go to: **Settings****Secrets and variables****Actions****New repository secret**
### 1. GPG_PRIVATE_KEY
```bash
# Display your CI private key
cat ci-signing-key.asc
```
Copy the entire output (including `-----BEGIN PGP PRIVATE KEY BLOCK-----` and `-----END...`)
- Name: `GPG_PRIVATE_KEY`
- Value: [paste the entire key]
### 2. GPG_KEY_ID
```bash
# Get your key ID
gpg --list-keys cadetg@gmail.com
```
Copy the long hex string (e.g., `ABC123...`)
- Name: `GPG_KEY_ID`
- Value: [paste just the key ID]
## Test the Workflow
```bash
# Create and push a test tag
git tag v1.0.0-test
git push origin v1.0.0-test
```
Check: **Actions** tab in GitHub → **Release to Ubuntu PPA**
## Remove Test Tag (if needed)
```bash
git tag -d v1.0.0-test
git push origin :refs/tags/v1.0.0-test
```
## Done!
From now on, just push version tags and GitHub will handle the PPA release automatically! 🚀
+38
View File
@@ -0,0 +1,38 @@
name: 'Build RustNet'
description: 'Build RustNet binary for a specific target'
inputs:
target:
description: 'Rust target triple (e.g., x86_64-unknown-linux-gnu)'
required: true
cargo-command:
description: 'Cargo command to use (cargo or cross)'
required: false
default: 'cargo'
default-features:
description: 'Enable default features including eBPF (true/false)'
required: false
default: 'true'
strip-symbols:
description: 'Strip debug symbols from binary (true/false)'
required: false
default: 'true'
runs:
using: 'composite'
steps:
- name: Build binary
shell: bash
env:
RUSTFLAGS: ${{ inputs.strip-symbols == 'true' && '-C strip=symbols' || '' }}
run: |
mkdir -p assets # Ensure asset dir exists for build.rs
BUILD_CMD="${{ inputs.cargo-command }} build --verbose --release --target ${{ inputs.target }}"
if [[ "${{ inputs.default-features }}" != "true" ]]; then
BUILD_CMD="$BUILD_CMD --no-default-features"
fi
echo "Running: $BUILD_CMD"
$BUILD_CMD
+30
View File
@@ -0,0 +1,30 @@
name: 'Build Static Binary'
description: 'Build a statically-linked Linux binary using musl'
runs:
using: 'composite'
steps:
- name: Install dependencies
shell: sh
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 git
rustup component add rustfmt
- name: Build static binary
shell: sh
env:
# -C strip=symbols: Strip debug symbols for smaller binary
# -C link-arg=-l:libzstd.a: Fix elfutils 0.189+ zstd dependency (libbpf/bpftool#152)
RUSTFLAGS: "-C strip=symbols -C link-arg=-l:libzstd.a"
run: cargo build --release
- name: Verify static linking
shell: sh
run: |
file target/release/rustnet
# Use file command to verify (ldd behaves differently inside Alpine)
file target/release/rustnet | grep -q "static.* linked" || \
(echo "ERROR: Binary is not statically linked" && exit 1)
@@ -0,0 +1,11 @@
name: 'Setup Linux Build Dependencies'
description: 'Install dependencies required for building RustNet on Linux'
runs:
using: 'composite'
steps:
- name: Install dependencies
shell: bash
run: |
sudo apt-get update -y
sudo apt-get install -y libpcap-dev libelf-dev zlib1g-dev clang llvm pkg-config
+46
View File
@@ -0,0 +1,46 @@
version: 2
updates:
# Maintain Cargo dependencies
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 3
semver-major-days: 7
semver-minor-days: 4
semver-patch-days: 3
groups:
rust-dependencies:
patterns:
- "*"
update-types:
- "major"
- "minor"
- "patch"
# Maintain GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
actions:
patterns:
- "*"
update-types:
- "major"
- "minor"
- "patch"
# Maintain Docker base images (Dockerfile FROM pins)
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
groups:
docker:
patterns:
- "*"
+56
View File
@@ -0,0 +1,56 @@
<!--
Thanks for contributing to rustnet. Please read CONTRIBUTING.md before
opening the PR, then fill in the sections below.
CONTRIBUTING.md: https://github.com/domcyrus/rustnet/blob/main/CONTRIBUTING.md
PRs that ship without the checklist completed will usually be asked to
update before review.
-->
## Summary
<!-- What does this PR do, and why? One or two paragraphs. -->
## Linked issue
<!-- For features and non-trivial changes, link the issue where the
approach was discussed. If there is no issue, please open one first
(see CONTRIBUTING.md: https://github.com/domcyrus/rustnet/blob/main/CONTRIBUTING.md#development-workflow).
Bug fixes and typo corrections do not need a prior issue. -->
Closes #
## Verification
Per [CONTRIBUTING.md > Code Quality Requirements](https://github.com/domcyrus/rustnet/blob/main/CONTRIBUTING.md#code-quality-requirements),
I ran the following locally and they all pass:
- [ ] `cargo fmt --all -- --check`
- [ ] `cargo clippy --all-targets --all-features -- -D warnings`
- [ ] `cargo test --all-features`
- [ ] `cargo build --release`
<!-- If any of these did not run, say which and why. CI will also run
them, but local verification catches issues faster. -->
## Scope
- [ ] One feature or fix per PR (no unrelated cleanups bundled in)
- [ ] No new dependencies, or rationale provided in the summary above
- [ ] No `#[allow(clippy::...)]` suppressions, or rationale provided
(see [CONTRIBUTING.md](https://github.com/domcyrus/rustnet/blob/main/CONTRIBUTING.md#code-quality-requirements))
## AI-assisted contributions
If you used an AI assistant (Copilot, Claude, ChatGPT, Cursor, etc.) to
write any of this code, that is fine, but please confirm:
- [ ] I have read every line I am submitting
- [ ] I ran the verification commands above myself
- [ ] The PR description reflects what the code actually does
## Notes for the reviewer
<!-- Anything that would help review: tricky edge cases, deliberate
trade-offs, follow-up work you plan to do in a separate PR. -->
+221
View File
@@ -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
+222
View File
@@ -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 }}
+73
View File
@@ -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 }}
+353
View File
@@ -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
+72
View File
@@ -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
+72
View File
@@ -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
+146
View File
@@ -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"
+254
View File
@@ -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
+64
View File
@@ -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
+817
View File
@@ -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
+78
View File
@@ -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
+25
View File
@@ -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 }}
+50
View File
@@ -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