diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..cd70172 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,230 @@ +name: Build .deb Package + +on: + push: + branches: [master] + tags: ["v*"] + pull_request: + branches: [master] + +env: + CARGO_TERM_COLOR: always + +jobs: + # --------------------------------------------------------------------------- + # Job 1: Rust Backend - Build + Test + # --------------------------------------------------------------------------- + build-backend: + runs-on: ubuntu-latest + container: + image: ubuntu:24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + apt-get update -qq + apt-get install -y --no-install-recommends \ + curl pkg-config libssl-dev ca-certificates + # Install Rust toolchain + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + . "$HOME/.cargo/env" + rustup default stable + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + + - name: Cache Cargo registry + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ hashFiles('Cargo.lock') }} + restore-keys: cargo- + + - name: Build (release) + run: | + . "$HOME/.cargo/env" + cargo build --release + + - name: Run Rust tests + run: | + . "$HOME/.cargo/env" + cargo test --release + + - name: Strip binaries + run: | + strip target/release/pm-web + strip target/release/pm-worker + + - name: Upload backend artifacts + uses: actions/upload-artifact@v3 + with: + name: backend-binaries + path: | + target/release/pm-web + target/release/pm-worker + + # --------------------------------------------------------------------------- + # Job 2: Frontend - Build + # --------------------------------------------------------------------------- + build-frontend: + runs-on: ubuntu-latest + container: + image: node:20-bookworm + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + working-directory: frontend + run: npm ci + + - name: Build frontend + working-directory: frontend + run: npm run build + + - name: Upload frontend artifacts + uses: actions/upload-artifact@v3 + with: + name: frontend-dist + path: frontend/dist/ + + # --------------------------------------------------------------------------- + # Job 3: Package .deb + # --------------------------------------------------------------------------- + build-deb: + needs: [build-backend, build-frontend] + runs-on: ubuntu-latest + container: + image: ubuntu:24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install packaging tools + run: | + apt-get update -qq + apt-get install -y --no-install-recommends dpkg-dev curl ca-certificates + + - name: Download backend artifacts + uses: actions/download-artifact@v3 + with: + name: backend-binaries + path: target/release/ + + - name: Download frontend artifacts + uses: actions/download-artifact@v3 + with: + name: frontend-dist + path: frontend/dist/ + + - name: Make binaries executable + run: chmod 755 target/release/pm-web target/release/pm-worker + + - name: Determine version + id: version + run: | + if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then + VERSION="${GITHUB_REF#refs/tags/v}" + else + VERSION="1.0.0-dev.$(date +%Y%m%d%H%M)" + fi + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "Building version: ${VERSION}" + + - name: Assemble .deb package + run: | + VERSION="${{ steps.version.outputs.version }}" + BUILD_DIR="package-build" + + mkdir -p "${BUILD_DIR}/DEBIAN" + mkdir -p "${BUILD_DIR}/usr/local/bin" + mkdir -p "${BUILD_DIR}/usr/share/patch-manager/frontend" + mkdir -p "${BUILD_DIR}/usr/share/patch-manager/migrations" + mkdir -p "${BUILD_DIR}/lib/systemd/system" + + # Binaries + cp target/release/pm-web "${BUILD_DIR}/usr/local/bin/pm-web" + cp target/release/pm-worker "${BUILD_DIR}/usr/local/bin/pm-worker" + cp scripts/backup.sh "${BUILD_DIR}/usr/local/bin/backup.sh" + chmod 755 "${BUILD_DIR}/usr/local/bin/pm-web" + chmod 755 "${BUILD_DIR}/usr/local/bin/pm-worker" + chmod 700 "${BUILD_DIR}/usr/local/bin/backup.sh" + + # Frontend + cp -r frontend/dist/* "${BUILD_DIR}/usr/share/patch-manager/frontend/" + + # Config + migrations + cp config/config.example.toml "${BUILD_DIR}/usr/share/patch-manager/config.example.toml" + cp migrations/*.sql "${BUILD_DIR}/usr/share/patch-manager/migrations/" + + # Systemd units + cp systemd/patch-manager-web.service "${BUILD_DIR}/lib/systemd/system/" + cp systemd/patch-manager-worker.service "${BUILD_DIR}/lib/systemd/system/" + cp systemd/patch-manager.target "${BUILD_DIR}/lib/systemd/system/" + + # DEBIAN control scripts + cp debian/postinst "${BUILD_DIR}/DEBIAN/postinst" + cp debian/prerm "${BUILD_DIR}/DEBIAN/prerm" + cp debian/postrm "${BUILD_DIR}/DEBIAN/postrm" + chmod 755 "${BUILD_DIR}/DEBIAN/postinst" "${BUILD_DIR}/DEBIAN/prerm" "${BUILD_DIR}/DEBIAN/postrm" + + # Generate control file with computed version and size + INSTALLED_SIZE=$(du -sk "${BUILD_DIR}" | cut -f1) + cat > "${BUILD_DIR}/DEBIAN/control" < + Installed-Size: ${INSTALLED_SIZE} + Depends: postgresql-16, libssl3, libc6 (>= 2.39) + Recommends: postgresql-client-16 + Suggests: gpg + Section: admin + Priority: optional + Description: Enterprise Linux Patch Management System + Linux Patch Manager is a secure, web-based management interface for + controlling patching and updates on Linux servers and workstations. + CTRL + + # Build .deb + DEB_NAME="linux-patch-manager_${VERSION}-1_amd64.deb" + dpkg-deb --build "${BUILD_DIR}" "${DEB_NAME}" + echo "deb_name=${DEB_NAME}" >> "$GITHUB_OUTPUT" + + - name: Verify package + run: | + DEB_NAME=$(ls linux-patch-manager_*.deb) + echo "=== Package Info ===" + dpkg-deb --info "${DEB_NAME}" + echo "=== Package Size ===" + du -h "${DEB_NAME}" + + - name: Upload .deb artifact + uses: actions/upload-artifact@v3 + with: + name: deb-package + path: linux-patch-manager_*.deb + + - name: Create Gitea Release + if: startsWith(github.ref, 'refs/tags/v') + run: | + DEB_NAME=$(ls linux-patch-manager_*.deb) + VERSION="${{ steps.version.outputs.version }}" + # Use Gitea API to create release and upload asset + curl -s -X POST \ + "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\": \"${GITHUB_REF_NAME}\", \"title\": \"Release ${VERSION}\", \"body\": \"Automated build from tag ${GITHUB_REF_NAME}.\"}" \ + -o release.json + # Extract release ID + RELEASE_ID=$(python3 -c "import json; print(json.load(open('release.json'))['id'])") + # Upload .deb as release asset + curl -s -X POST \ + "${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -F "attachment=@${DEB_NAME}" \ + -F "name=${DEB_NAME}" diff --git a/tasks/lessons.md b/tasks/lessons.md new file mode 100644 index 0000000..1cfab5b --- /dev/null +++ b/tasks/lessons.md @@ -0,0 +1,11 @@ +# Linux Patch Manager — Lessons Learned + +## 2026-04-24: CI/CD First, Not Manual Builds +**Pattern:** When creating release packages, set up CI/CD pipeline (Gitea Actions) FIRST before manually building. +**Why:** Manual builds are one-off and not reproducible. CI/CD ensures every push/tag produces a fresh, consistent package built on the correct target OS (Ubuntu 24.04), with proper glibc compatibility. +**Action:** Always create `.gitea/workflows/` pipeline for automated builds. Use `scripts/build-package.sh` only for local dev testing. + +## 2026-04-24: Verify Runner Before Workflow +**Pattern:** Before creating Gitea Actions workflows, verify the act-runner is registered and online. +**Why:** A workflow file without a running runner is dead code. The runner at gitea-runner-lxc.moon-dragon.us needs to be verified as operational. +**Action:** Check runner status via Gitea API (`/api/v1/repos/echo/linux_patch_manager/actions/runners`) or web UI before assuming CI/CD will work.