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