From 069a203ae993048748c93d57be81b91eb637f6b9 Mon Sep 17 00:00:00 2001 From: Marco Cadetg Date: Thu, 8 Jan 2026 17:12:21 +0100 Subject: [PATCH] Refactor CI: share build logic via reusable workflow - Add build-platforms.yml reusable workflow with full build matrix - Add composite actions for setup-linux-deps, build-rustnet, build-static - Simplify test-platform-builds.yml and release.yml to call shared workflow - Update RELEASE.md: run test builds before release prep --- .github/actions/build-rustnet/action.yml | 38 ++++ .github/actions/build-static/action.yml | 30 ++++ .github/actions/setup-linux-deps/action.yml | 11 ++ .github/workflows/build-platforms.yml | 183 +++++++++++++++++++ .github/workflows/release.yml | 188 ++------------------ .github/workflows/test-platform-builds.yml | 150 +--------------- RELEASE.md | 24 ++- 7 files changed, 300 insertions(+), 324 deletions(-) create mode 100644 .github/actions/build-rustnet/action.yml create mode 100644 .github/actions/build-static/action.yml create mode 100644 .github/actions/setup-linux-deps/action.yml create mode 100644 .github/workflows/build-platforms.yml diff --git a/.github/actions/build-rustnet/action.yml b/.github/actions/build-rustnet/action.yml new file mode 100644 index 0000000..2b2ddad --- /dev/null +++ b/.github/actions/build-rustnet/action.yml @@ -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 diff --git a/.github/actions/build-static/action.yml b/.github/actions/build-static/action.yml new file mode 100644 index 0000000..ad9b54c --- /dev/null +++ b/.github/actions/build-static/action.yml @@ -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) diff --git a/.github/actions/setup-linux-deps/action.yml b/.github/actions/setup-linux-deps/action.yml new file mode 100644 index 0000000..b9543c8 --- /dev/null +++ b/.github/actions/setup-linux-deps/action.yml @@ -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 diff --git a/.github/workflows/build-platforms.yml b/.github/workflows/build-platforms.yml new file mode 100644 index 0000000..65831e8 --- /dev/null +++ b/.github/workflows/build-platforms.yml @@ -0,0 +1,183 @@ +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: + 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@v6 + + - name: Setup Linux dependencies + if: matrix.os == 'ubuntu-22.04' + uses: ./.github/actions/setup-linux-deps + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + 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@v6 + 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 "assets" ] && [ -f "assets/services" ]; then + mkdir -p "$staging/assets" + cp "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@v6 + with: + name: build-${{ matrix.target }} + path: ${{ env.ASSET }} + if-no-files-found: error + + build-static: + name: build-linux-static + runs-on: ubuntu-latest + container: + image: rust:alpine + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Build static binary + uses: ./.github/actions/build-static + + # For test builds: upload raw binary + - name: Upload binary artifact + if: ${{ !inputs.create-archives }} + uses: actions/upload-artifact@v6 + with: + name: rustnet-linux-static + path: target/release/rustnet + if-no-files-found: error + + # For release builds: create archive + - name: Create release archive + if: ${{ inputs.create-archives }} + run: | + staging="rustnet-${{ inputs.version }}-x86_64-unknown-linux-musl" + mkdir -p "$staging/assets" + + cp target/release/rustnet "$staging/" + cp assets/services "$staging/assets/" 2>/dev/null || true + cp README.md "$staging/" + cp LICENSE "$staging/" 2>/dev/null || true + + tar czf "$staging.tar.gz" "$staging" + + - name: Upload release archive + if: ${{ inputs.create-archives }} + uses: actions/upload-artifact@v6 + with: + name: build-x86_64-unknown-linux-musl + path: rustnet-${{ inputs.version }}-x86_64-unknown-linux-musl.tar.gz + if-no-files-found: error diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bc8f553..daffb50 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,182 +33,18 @@ env: RUSTNET_ASSET_DIR: assets jobs: - build-release: - name: build-release - runs-on: ${{ matrix.os }} - strategy: - matrix: - build: - - linux-aarch64-gnu - - linux-armv7-gnueabihf - - linux-x64-gnu - - macos-aarch64 - - macos-x64 - - windows-x64-msvc - - windows-x86-msvc - include: - - os: ubuntu-22.04 # default - pinned for GLIBC 2.35 compatibility - - cargo: cargo # default; overwrite with `cross` if necessary - - build: linux-aarch64-gnu - target: aarch64-unknown-linux-gnu - cargo: cross - - build: linux-armv7-gnueabihf - target: armv7-unknown-linux-gnueabihf - cargo: cross - - build: linux-x64-gnu - target: x86_64-unknown-linux-gnu - - build: macos-aarch64 - os: macos-14 - target: aarch64-apple-darwin - - 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@v6 - - - name: Install Linux dependencies - if: matrix.os == 'ubuntu-22.04' - run: | - sudo apt-get update -y - sudo apt-get install -y libpcap-dev libelf-dev zlib1g-dev clang llvm pkg-config - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: ${{ matrix.target }} - components: rustfmt - - - name: Install cross - if: matrix.cargo == 'cross' - run: cargo install cross@0.2.5 - - - name: Build release binary - shell: bash - env: - RUSTFLAGS: "-C strip=symbols" - run: | - mkdir -p "$RUSTNET_ASSET_DIR" - # build.rs will handle Npcap SDK download on Windows - # eBPF is enabled by default on Linux, but must be disabled for other platforms - if [[ "${{ matrix.target }}" == *"linux"* ]]; then - # Linux builds: use default features (includes eBPF) - ${{ matrix.cargo }} build --verbose --release --target ${{ matrix.target }} - else - # Non-Linux builds: disable default features (no eBPF) - ${{ matrix.cargo }} build --verbose --release --target ${{ matrix.target }} --no-default-features - fi - - - name: Create release archive - shell: bash - env: - RUSTNET_BIN: ${{ contains(matrix.os, 'windows') && 'rustnet.exe' || 'rustnet' }} - run: | - staging="rustnet-${{ github.ref_name }}-${{ matrix.target }}" - mkdir -p "$staging" - - # Copy binary - cp "target/${{ matrix.target }}/release/$RUSTNET_BIN" "$staging/" - - # Copy services file if it exists - if [ -f "$RUSTNET_ASSET_DIR/services" ]; then - mkdir -p "$staging/assets" - cp "$RUSTNET_ASSET_DIR/services" "$staging/assets/" - fi - - # Copy documentation - cp README.md "$staging/" - cp LICENSE "$staging/" 2>/dev/null || true - - # Create archive - 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 build artifacts - uses: actions/upload-artifact@v6 - with: - name: build-${{ matrix.target }} - path: ${{ env.ASSET }} - if-no-files-found: error - - build-static: - name: build-static-${{ matrix.arch }} - runs-on: ${{ matrix.runner }} - strategy: - matrix: - include: - - arch: x86_64 - runner: ubuntu-latest - target: x86_64-unknown-linux-musl - # TODO: aarch64 disabled - JS actions don't work in Alpine containers on ARM runners - # See: https://github.com/actions/runner/issues/801 - # - arch: aarch64 - # runner: ubuntu-24.04-arm - # target: aarch64-unknown-linux-musl - container: - image: rust:alpine - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - 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 git - rustup component add rustfmt - - - name: Build static binary - 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 - 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) - - - name: Create release archive - run: | - staging="rustnet-${{ github.ref_name }}-${{ matrix.target }}" - mkdir -p "$staging/assets" - - cp target/release/rustnet "$staging/" - cp assets/services "$staging/assets/" 2>/dev/null || true - cp README.md "$staging/" - cp LICENSE "$staging/" 2>/dev/null || true - - tar czf "$staging.tar.gz" "$staging" - - - name: Upload static build artifact - uses: actions/upload-artifact@v6 - with: - name: build-${{ matrix.target }} - path: rustnet-${{ github.ref_name }}-${{ matrix.target }}.tar.gz - if-no-files-found: error + # 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-release, build-static] + needs: build steps: - name: Checkout repository uses: actions/checkout@v6 @@ -326,10 +162,8 @@ jobs: with: path: artifacts - - 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: Setup Linux dependencies + uses: ./.github/actions/setup-linux-deps - name: Install Rust and tools uses: dtolnay/rust-toolchain@stable @@ -625,4 +459,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - gh release upload ${{ github.ref_name }} "Rustnet_Windows_${{ matrix.arch }}.msi" --clobber \ No newline at end of file + gh release upload ${{ github.ref_name }} "Rustnet_Windows_${{ matrix.arch }}.msi" --clobber diff --git a/.github/workflows/test-platform-builds.yml b/.github/workflows/test-platform-builds.yml index 6408892..821200b 100644 --- a/.github/workflows/test-platform-builds.yml +++ b/.github/workflows/test-platform-builds.yml @@ -1,156 +1,22 @@ name: Test Platform Builds # Test builds on all supported platforms. -# Runs automatically on PRs and can be triggered manually for specific platforms. +# Uses the shared build-platforms workflow. on: workflow_dispatch: - inputs: - platform: - description: 'Platform to build' - required: true - type: choice - options: - - all - - linux - - macos - - windows pull_request: paths: - 'Cargo.toml' - 'build.rs' - 'src/**' - '.github/workflows/test-platform-builds.yml' - -permissions: - contents: read - -env: - RUST_BACKTRACE: 1 - CARGO_TERM_COLOR: always + - '.github/workflows/build-platforms.yml' + - '.github/actions/**' jobs: - build-linux: - name: Build Linux (x64) - if: ${{ github.event_name == 'pull_request' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'linux' }} - runs-on: ubuntu-22.04 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - 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: Install Rust - uses: dtolnay/rust-toolchain@stable - - - name: Build - run: cargo build --verbose --release - - - name: Run tests - run: cargo test --verbose - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: rustnet-linux-x64 - path: target/release/rustnet - if-no-files-found: error - - build-macos-intel: - name: Build macOS (Intel) - if: ${{ github.event_name == 'pull_request' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }} - runs-on: macos-14 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: x86_64-apple-darwin - - - name: Build - run: cargo build --verbose --release --target x86_64-apple-darwin --no-default-features - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: rustnet-macos-intel - path: target/x86_64-apple-darwin/release/rustnet - if-no-files-found: error - - build-macos-arm: - name: Build macOS (Apple Silicon) - if: ${{ github.event_name == 'pull_request' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }} - runs-on: macos-14 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: aarch64-apple-darwin - - - name: Build - run: cargo build --verbose --release --target aarch64-apple-darwin --no-default-features - - - name: Run tests - run: cargo test --verbose - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: rustnet-macos-arm - path: target/aarch64-apple-darwin/release/rustnet - if-no-files-found: error - - build-windows-x64: - name: Build Windows (64-bit) - if: ${{ github.event_name == 'pull_request' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }} - runs-on: windows-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: x86_64-pc-windows-msvc - - - name: Build - run: cargo build --verbose --release --target x86_64-pc-windows-msvc --no-default-features - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: rustnet-windows-x64 - path: target/x86_64-pc-windows-msvc/release/rustnet.exe - if-no-files-found: error - - build-windows-x86: - name: Build Windows (32-bit) - if: ${{ github.event_name == 'pull_request' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }} - runs-on: windows-latest - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - targets: i686-pc-windows-msvc - - - name: Build - run: cargo build --verbose --release --target i686-pc-windows-msvc --no-default-features - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: rustnet-windows-x86 - path: target/i686-pc-windows-msvc/release/rustnet.exe - if-no-files-found: error - + build: + uses: ./.github/workflows/build-platforms.yml + with: + create-archives: false + strip-symbols: false diff --git a/RELEASE.md b/RELEASE.md index fb7c53b..c8fed15 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -4,15 +4,28 @@ This document is for maintainers releasing new versions of RustNet. ## Creating a New Release -### 1. Prepare the Release +### 1. Test Platform Builds -Update version in `Cargo.toml`, `rpm/rustnet.spec`, and update `CHANGELOG.md` with release notes: +Before making any release changes, verify all platform builds succeed on the current main branch: ```bash # Ensure you're on the main branch with latest changes git checkout main git pull origin main +``` +1. Go to [Actions > Test Platform Builds](../../actions/workflows/test-platform-builds.yml) +2. Click "Run workflow" +3. Select `all` to test all platforms (including static Linux builds) +4. Wait for the workflow to complete successfully + +This catches cross-platform and static linking issues before you invest time in release prep. + +### 2. Prepare the Release + +Update version in `Cargo.toml`, `rpm/rustnet.spec`, and update `CHANGELOG.md` with release notes: + +```bash # Update Cargo.toml version (e.g., version = "0.3.0") # Update rpm/rustnet.spec Version field (e.g., Version: 0.3.0) # Update CHANGELOG.md with new version section @@ -22,7 +35,7 @@ cargo build --release cargo test ``` -### 2. Commit Release Changes +### 3. Commit Release Changes ```bash # Stage and commit the version and changelog changes @@ -34,7 +47,7 @@ git commit -m "Release v0.3.0 - And more changes" ``` -### 3. Create and Push Git Tag +### 4. Create and Push Git Tag ```bash # Create an annotated tag matching the version in Cargo.toml @@ -56,7 +69,7 @@ git push origin v0.3.0 - Create a draft GitHub release with all artifacts attached - Upload all binaries and installers to the release -### 4. Finalize the Release +### 5. Finalize the Release Once the GitHub Actions workflow completes (~15-20 minutes): @@ -97,6 +110,7 @@ The release process is fully automated via [`.github/workflows/release.yml`](.gi Before pushing the tag, ensure: +- [ ] Test Platform Builds workflow passes for all platforms (including static) - [ ] Version number updated in `Cargo.toml` - [ ] Version number updated in `rpm/rustnet.spec` (line 5: `Version: x.y.z`) - [ ] `Cargo.lock` updated (via `cargo build`)