diff --git a/Dockerfile.rpmbuild b/Dockerfile.rpmbuild new file mode 100644 index 0000000..c928cea --- /dev/null +++ b/Dockerfile.rpmbuild @@ -0,0 +1,102 @@ +# syntax=docker/dockerfile:1 +FROM fedora:42 + +RUN set -eux; \ + dnf -y update; \ + dnf -y install \ + rpm-build \ + rpmdevtools \ + redhat-rpm-config \ + gcc \ + make \ + findutils \ + tar \ + gzip \ + rsync \ + python3 \ + python3-devel \ + python3-setuptools \ + python3-wheel \ + pyproject-rpm-macros \ + python3-rpm-macros \ + python3-yaml \ + python3-paramiko \ + openssl-devel \ + python3-poetry-core ; \ + dnf -y clean all + +# Build runner script (copies repo, tars, runs rpmbuild) +RUN set -eux; cat > /usr/local/bin/build-rpm <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +SRC="${SRC:-/src}" +WORKROOT="${WORKROOT:-/work}" +OUT="${OUT:-/out}" +DEPS_DIR="${DEPS_DIR:-/deps}" + +# Install jinjaturtle from local rpm +# Filter out .src.rpm and debug* subpackages if present. +if [ -d "${DEPS_DIR}" ] && compgen -G "${DEPS_DIR}/*.rpm" > /dev/null; then + mapfile -t rpms < <(ls -1 "${DEPS_DIR}"/*.rpm | grep -vE '(\.src\.rpm$|-(debuginfo|debugsource)-)') + if [ "${#rpms[@]}" -gt 0 ]; then + echo "Installing dependency RPMs from ${DEPS_DIR}:" + printf ' - %s\n' "${rpms[@]}" + dnf -y install "${rpms[@]}" + dnf -y clean all + else + echo "NOTE: Only src/debug RPMs found in ${DEPS_DIR}; nothing installed." >&2 + fi +else + echo "NOTE: No RPMs found in ${DEPS_DIR}. If the build fails with missing python3dist(jinjaturtle)," >&2 + echo " mount your jinjaturtle RPM directory as -v :/deps" >&2 +fi + +mkdir -p "${WORKROOT}" "${OUT}" +WORK="${WORKROOT}/src" +rm -rf "${WORK}" +mkdir -p "${WORK}" + +rsync -a --delete \ + --exclude '.git' \ + --exclude '.venv' \ + --exclude 'dist' \ + --exclude 'build' \ + --exclude '__pycache__' \ + --exclude '.pytest_cache' \ + --exclude '.mypy_cache' \ + "${SRC}/" "${WORK}/" + +cd "${WORK}" + +# Determine version from pyproject.toml unless provided +if [ -n "${VERSION:-}" ]; then + ver="${VERSION}" +else + ver="$(grep -m1 '^version = ' pyproject.toml | sed -E 's/version = "([^"]+)".*/\1/')" +fi + +TOPDIR="${WORKROOT}/rpmbuild" +mkdir -p "${TOPDIR}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} + +tarball="${TOPDIR}/SOURCES/enroll-${ver}.tar.gz" +tar -czf "${tarball}" --transform "s#^#enroll/#" . + +spec_src="rpm/enroll.spec" + +cp -v "${spec_src}" "${TOPDIR}/SPECS/enroll.spec" + +rpmbuild -ba "${TOPDIR}/SPECS/enroll.spec" \ + --define "_topdir ${TOPDIR}" \ + --define "upstream_version ${ver}" + +shopt -s nullglob +cp -v "${TOPDIR}"/RPMS/*/*.rpm "${OUT}/" || true +cp -v "${TOPDIR}"/SRPMS/*.src.rpm "${OUT}/" || true +echo "Artifacts copied to ${OUT}" +EOF + +RUN chmod +x /usr/local/bin/build-rpm + +WORKDIR /work +ENTRYPOINT ["/usr/local/bin/build-rpm"] diff --git a/README.md b/README.md index 00f9d98..5a0db91 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ **enroll** inspects a Linux machine (currently Debian-only) and generates Ansible roles/playbooks (and optionally inventory) for what it finds. -It aims to be **optimistic and noninteractive**: - Detects packages that have been installed. - Detects Debian package ownership of `/etc` files using dpkg’s local database. - Captures config that has **changed from packaged defaults** (dpkg conffile hashes + package md5sums when available). @@ -26,9 +25,10 @@ It aims to be **optimistic and noninteractive**: 1) **Harvest**: collect host facts + relevant files into a harvest bundle (`state.json` + harvested artifacts) 2) **Manifest**: turn that harvest into Ansible roles/playbooks (and optionally inventory) -Additionally: +Additionally, some other functionalities exist: - **Diff**: compare two harvests and report what changed (packages/services/users/files) since the previous snapshot. +- **Single-shot mode**: run both harvest and manifest at once. --- diff --git a/poetry.lock b/poetry.lock index 1f2948d..0a90711 100644 --- a/poetry.lock +++ b/poetry.lock @@ -923,4 +923,4 @@ zstd = ["backports-zstd (>=1.0.0)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "c3466a6595a9822763431a6dff0c7f835407a2591b92d5995592f8e6802c774a" +content-hash = "20623104a1a5f4c6d4aaa759f25b2591d5de345d1464e727eb4140a6ef9a5b6e" diff --git a/pyproject.toml b/pyproject.toml index 541eded..3079404 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,8 +10,8 @@ repository = "https://git.mig5.net/mig5/enroll" [tool.poetry.dependencies] python = "^3.10" -pyyaml = "^6.0.3" -paramiko = "^4.0.0" +pyyaml = "^6" +paramiko = ">=3.5" [tool.poetry.scripts] enroll = "enroll.cli:main" diff --git a/release.sh b/release.sh index fe99a52..fdbe771 100755 --- a/release.sh +++ b/release.sh @@ -42,3 +42,34 @@ for dist in ${DISTS[@]}; do debfile=$(ls -1 dist/${release}/*.deb) reprepro -b /home/user/git/repo includedeb "${release}" "${debfile}" done + +# RPM +sudo apt-get -y install createrepo-c rpm +docker build -f Dockerfile.rpmbuild -t enroll:f42 --progress=plain . +docker run --rm -v "$PWD":/src -v "$PWD/dist/rpm":/out -v "$HOME/git/jinjaturtle/dist/rpm":/deps:ro enroll:f42 +sudo chown -R "${USER}" "$PWD/dist" + +REPO_ROOT="${HOME}/git/repo_rpm" +RPM_REPO="${REPO_ROOT}/rpm/x86_64" +BUILD_OUTPUT="${HOME}/git/enroll/dist" +REMOTE="letessier.mig5.net:/opt/repo_rpm" +KEYID="00AE817C24A10C2540461A9C1D7CDE0234DB458D" + +echo "==> Updating RPM repo..." +mkdir -p "$RPM_REPO" + +for file in `ls -1 "${BUILD_OUTPUT}/rpm"`; do + rpmsign --addsign "${BUILD_OUTPUT}/rpm/$file" +done + +cp "${BUILD_OUTPUT}/rpm/"*.rpm "$RPM_REPO/" + +createrepo_c "$RPM_REPO" + +echo "==> Signing repomd.xml..." +qubes-gpg-client --local-user "$KEYID" --detach-sign --armor "$RPM_REPO/repodata/repomd.xml" > "$RPM_REPO/repodata/repomd.xml.asc" + +echo "==> Syncing repo to server..." +rsync -aHPvz --exclude=.git --delete "$REPO_ROOT/" "$REMOTE/" + +echo "Done!" diff --git a/rpm/enroll.spec b/rpm/enroll.spec new file mode 100644 index 0000000..403d6da --- /dev/null +++ b/rpm/enroll.spec @@ -0,0 +1,47 @@ +%global upstream_version 0.1.3 + +Name: enroll +Version: %{upstream_version} +Release: 1%{?dist}.enroll1 +Summary: Enroll a server's running state retrospectively into Ansible. + +License: GPL-3.0-or-later +URL: https://git.mig5.net/mig5/enroll +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch + +BuildRequires: pyproject-rpm-macros +BuildRequires: python3-devel +BuildRequires: python3-poetry-core + +Requires: python3-yaml +Requires: python3-paramiko + +# Make sure private repo dependency is pulled in by package name as well. +Recommends: jinjaturtle + +%description +Enroll a server's running state retrospectively into Ansible. + +%prep +%autosetup -n enroll + +%generate_buildrequires +%pyproject_buildrequires + +%build +%pyproject_wheel + +%install +%pyproject_install +%pyproject_save_files enroll + +%files -f %{pyproject_files} +%license LICENSE +%doc README.md CHANGELOG.md +%{_bindir}/enroll + +%changelog +* Sat Dec 27 2025 Miguel Jacq - %{version}-%{release} +- Initial RPM packaging for Fedora 42