From ee46c48c0b96d47c85005aa462d8f3df223bf1f8 Mon Sep 17 00:00:00 2001 From: Echo Date: Wed, 20 May 2026 19:45:38 +0000 Subject: [PATCH] fix: RPM packaging - pre-build binary, fix ownership, fix deps, prevent stale cache --- .gitea/workflows/ci.yml | 31 +++++++++++++++-- Cargo.toml | 2 +- build-rpm.sh | 74 ++++++++++++++++++++++++++++++++++++++--- debian/changelog | 12 +++++++ linux-patch-api.spec | 28 ++++++++++------ 5 files changed, 129 insertions(+), 18 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 8edf997..0df8b35 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -249,19 +249,46 @@ jobs: - name: Install build dependencies run: | sudo dnf install -y gcc rpm-build systemd-devel pkg-config openssl-devel + - name: Clean stale RPM artifacts + run: | + rm -f ~/rpmbuild/RPMS/x86_64/linux-patch-api-*.rpm + rm -f releases/linux-patch-api-*.rpm - name: Build release binary run: cargo build --release - name: Build RPM package run: | chmod +x build-rpm.sh - ./build-rpm.sh + SKIP_CARGO_BUILD=1 ./build-rpm.sh + - name: Verify RPM package + run: | + VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*=.*"\([^"]*\)".*/\1/') + RPM_FILE=$(ls ~/rpmbuild/RPMS/x86_64/linux-patch-api-${VERSION}-*.rpm 2>/dev/null | head -1) + if [ -z "$RPM_FILE" ]; then + echo "ERROR: RPM package not found for version $VERSION!" + ls -la ~/rpmbuild/RPMS/x86_64/ 2>/dev/null || echo "RPM directory empty or missing" + exit 1 + fi + RPM_VERSION=$(rpm -qp --queryformat '%{VERSION}' "$RPM_FILE" 2>/dev/null || true) + echo "RPM file: $RPM_FILE" + echo "RPM version: $RPM_VERSION" + echo "Expected version: $VERSION" + if [ "$RPM_VERSION" != "$VERSION" ]; then + echo "ERROR: RPM version ($RPM_VERSION) does not match expected version ($VERSION)!" + exit 1 + fi + echo "RPM verification passed" - name: Upload to Gitea Release if: github.ref_type == 'tag' env: GITEA_TOKEN: ${{ secrets.GITEATOKEN }} run: | TAG_NAME=${GITHUB_REF#refs/tags/} - FILE=$(ls ~/rpmbuild/RPMS/x86_64/*.rpm 2>/dev/null | head -1) + VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*=.*"\([^"]*\)".*/\1/') + FILE=$(ls ~/rpmbuild/RPMS/x86_64/linux-patch-api-${VERSION}-*.rpm 2>/dev/null | head -1) + if [ -z "$FILE" ]; then + echo "ERROR: No RPM found with version $VERSION for upload!" + exit 1 + fi chmod +x scripts/upload-release.sh ./scripts/upload-release.sh "$TAG_NAME" "$FILE" diff --git a/Cargo.toml b/Cargo.toml index 1e74706..f45ae1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "linux-patch-api" -version = "1.1.13" +version = "1.1.14" edition = "2021" authors = ["Echo "] description = "Secure remote package management API for Linux systems" diff --git a/build-rpm.sh b/build-rpm.sh index 86815fc..e7f3886 100644 --- a/build-rpm.sh +++ b/build-rpm.sh @@ -2,19 +2,29 @@ # Build RPM Package for RHEL/CentOS/Fedora # Run on: RHEL 8/9, CentOS 8/9, Fedora 38+ # Designed for native Gitea Actions runner execution +# +# Build pattern: Pre-build binary BEFORE creating tarball (like Alpine/Arch) +# The binary is included in the source tarball so rpmbuild's %build +# section is a no-op. This avoids PATH issues where rpmbuild can't find +# cargo installed via rustup. set -e echo "=== Linux Patch API - RPM Build Script ===" echo "" +# Source cargo environment (for rustup-installed toolchain in CI) +if [ -f "$HOME/.cargo/env" ]; then + . "$HOME/.cargo/env" +fi + # Check if running on RPM-based system if ! command -v rpmbuild &> /dev/null; then echo "Installing RPM build tools..." if command -v dnf &> /dev/null; then - dnf install -y rpm-build cargo rust gcc systemd-devel + dnf install -y rpm-build elif command -v yum &> /dev/null; then - yum install -y rpm-build cargo rust gcc systemd-devel + yum install -y rpm-build else echo "Error: Cannot install rpm-build. Please install manually." exit 1 @@ -23,13 +33,38 @@ fi # Get version from Cargo.toml VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*=.*"\([^"]*\)".*/\1/') +if [ -z "$VERSION" ]; then + echo "Error: Could not determine version from Cargo.toml" + exit 1 +fi echo "Building version: $VERSION" +# Remove stale RPM artifacts to prevent uploading cached/old packages +echo "Cleaning stale RPM artifacts..." +rm -f ~/rpmbuild/RPMS/x86_64/linux-patch-api-*.rpm +rm -f releases/linux-patch-api-*.rpm + +# Build release binary (skip if already built by CI) +if [ -z "$SKIP_CARGO_BUILD" ]; then + echo "Building release binary..." + cargo build --release +else + echo "Skipping cargo build (SKIP_CARGO_BUILD is set)" +fi + +# Verify binary exists +if [ ! -f "target/release/linux-patch-api" ]; then + echo "Error: Pre-built binary not found at target/release/linux-patch-api" + echo "Run 'cargo build --release' first or unset SKIP_CARGO_BUILD" + exit 1 +fi + # Setup RPM build directory structure mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} -# Create source tarball (required by %autosetup in spec file) -echo "Creating source tarball..." +# Create source tarball with pre-built binary included +# (required by %autosetup in spec file) +echo "Creating source tarball with pre-built binary..." TMPDIR=$(mktemp -d) mkdir -p "$TMPDIR/linux-patch-api-${VERSION}" @@ -47,6 +82,12 @@ rm -rf "$TMPDIR/linux-patch-api-${VERSION}/.abuild" rm -rf "$TMPDIR/linux-patch-api-${VERSION}/apk-package" rm -rf "$TMPDIR/linux-patch-api-${VERSION}/.a0proj" +# Re-create target/release with just the pre-built binary +# This is the key change: binary is in the tarball so %build is a no-op +mkdir -p "$TMPDIR/linux-patch-api-${VERSION}/target/release" +cp target/release/linux-patch-api "$TMPDIR/linux-patch-api-${VERSION}/target/release/" +chmod 755 "$TMPDIR/linux-patch-api-${VERSION}/target/release/linux-patch-api" + tar -czf ~/rpmbuild/SOURCES/linux-patch-api-${VERSION}.tar.gz -C "$TMPDIR" "linux-patch-api-${VERSION}" rm -rf "$TMPDIR" @@ -54,10 +95,35 @@ rm -rf "$TMPDIR" echo "Preparing spec file..." sed "s/VERSION_PLACEHOLDER/$VERSION/" linux-patch-api.spec > ~/rpmbuild/SPECS/linux-patch-api.spec +# Verify VERSION replacement succeeded +if grep -q 'VERSION_PLACEHOLDER' ~/rpmbuild/SPECS/linux-patch-api.spec; then + echo "Error: VERSION_PLACEHOLDER not replaced in spec file!" + exit 1 +fi +echo "Spec file version verified: $VERSION" + # Build RPM echo "Building RPM package..." rpmbuild -ba ~/rpmbuild/SPECS/linux-patch-api.spec +# Verify RPM was actually built +RPM_FILE=$(ls ~/rpmbuild/RPMS/x86_64/linux-patch-api-${VERSION}-*.rpm 2>/dev/null | head -1) +if [ -z "$RPM_FILE" ]; then + echo "Error: RPM package not found after build!" + echo "Looking for: ~/rpmbuild/RPMS/x86_64/linux-patch-api-${VERSION}-*.rpm" + ls -la ~/rpmbuild/RPMS/x86_64/ 2>/dev/null || echo "Directory empty or missing" + exit 1 +fi + +# Verify RPM contains the correct version +RPM_VERSION=$(rpm -qp --queryformat '%{VERSION}' "$RPM_FILE" 2>/dev/null || true) +echo "RPM built: $RPM_FILE" +echo "RPM version: $RPM_VERSION" +if [ "$RPM_VERSION" != "$VERSION" ]; then + echo "Error: RPM version ($RPM_VERSION) does not match expected version ($VERSION)!" + exit 1 +fi + # Copy to releases directory echo "" echo "Copying package to releases/..." diff --git a/debian/changelog b/debian/changelog index 8645f43..3ca3dce 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +linux-patch-api (1.1.14) unstable; urgency=medium + + * Fix RPM packaging: pre-build binary before tarball (like Alpine/Arch pattern) + * Fix rpmbuild can't find cargo in PATH - binary now included in source tarball + * Fix config file ownership: add %defattr(-,root,root,-) in %files section + * Fix Requires: libsystemd -> systemd-libs for Fedora compatibility + * Remove Requires: systemd (not needed, may not exist in containers) + * Add stale RPM cleanup and version verification to build-rpm.sh + * Support SKIP_CARGO_BUILD=1 like Alpine/Arch builds + + -- Echo Tue, 20 May 2026 14:44:00 -0500 + linux-patch-api (1.1.13) unstable; urgency=medium * Fix APK backend detection for Alpine (/sbin/apk not /usr/bin/apk) diff --git a/linux-patch-api.spec b/linux-patch-api.spec index 00dc7fb..f5f6063 100644 --- a/linux-patch-api.spec +++ b/linux-patch-api.spec @@ -21,8 +21,7 @@ BuildArch: x86_64 # BuildRequires: pkgconfig(systemd) # Runtime requirements -Requires: systemd -Requires: libsystemd +Requires: systemd-libs Requires: openssl-libs Requires: ca-certificates @@ -45,10 +44,11 @@ Features: %prep %autosetup -n linux-patch-api-%{version} -# Build +# Build - no-op, binary is pre-built and included in source tarball +# The binary is built by build-rpm.sh BEFORE creating the tarball, +# so cargo does not need to be in rpmbuild's PATH. %build -export RUSTFLAGS="-C target-cpu=native" -cargo build --release --target x86_64-unknown-linux-gnu +# Binary already built - nothing to do # Install %install @@ -59,8 +59,8 @@ mkdir -p %{buildroot}/lib/systemd/system mkdir -p %{buildroot}/var/log/linux_patch_api mkdir -p %{buildroot}/var/lib/linux_patch_api -# Install binary -cp target/x86_64-unknown-linux-gnu/release/linux-patch-api %{buildroot}/usr/bin/ +# Install binary (pre-built, included in tarball at target/release/) +cp target/release/linux-patch-api %{buildroot}/usr/bin/ chmod 755 %{buildroot}/usr/bin/linux-patch-api # Install systemd service @@ -149,6 +149,7 @@ fi # Files %files +%defattr(-,root,root,-) /usr/bin/linux-patch-api /lib/systemd/system/linux-patch-api.service %config(noreplace) /etc/linux_patch_api/config.yaml.example @@ -162,6 +163,15 @@ fi # Changelog %changelog +* Tue May 20 2026 Echo - 1.1.14-1 +- Fix RPM packaging: pre-build binary before tarball (like Alpine/Arch pattern) +- Fix rpmbuild can't find cargo in PATH - binary now included in source tarball +- Fix config file ownership: add %defattr(-,root,root,-) in %files section +- Fix Requires: libsystemd -> systemd-libs for Fedora compatibility +- Remove Requires: systemd (not needed, may not exist in containers) +- Add stale RPM cleanup and version verification to build-rpm.sh +- Support SKIP_CARGO_BUILD=1 like Alpine/Arch builds + * Wed May 20 2026 Echo - 1.1.12-1 - Add APK (Alpine Linux) package manager backend - Add machine-id generation to Alpine pre-install script @@ -198,7 +208,3 @@ fi - Initial production release - Secure mTLS-authenticated REST API for remote package management - 15 API endpoints for package install/remove, patch application, system management -- Asynchronous job processing with WebSocket status streaming -- IP whitelist enforcement and comprehensive audit logging -- Systemd integration with security hardening -- Supports RHEL 8/9, CentOS 8/9, Fedora 38+