diff --git a/cron.daily b/cron.daily new file mode 100644 index 0000000..eb74401 --- /dev/null +++ b/cron.daily @@ -0,0 +1,8 @@ +#!/bin/sh +set -e +if [ -e /etc/etckeeper/daily ] && [ -e /etc/etckeeper/etckeeper.conf ]; then + . /etc/etckeeper/etckeeper.conf + if [ "$AVOID_DAILY_AUTOCOMMITS" != "1" ]; then + /etc/etckeeper/daily + fi +fi diff --git a/etckeeper-1.18.12-fix-output-for-ansible.patch b/etckeeper-1.18.12-fix-output-for-ansible.patch new file mode 100644 index 0000000..7ca4b4f --- /dev/null +++ b/etckeeper-1.18.12-fix-output-for-ansible.patch @@ -0,0 +1,162 @@ +From 8266a4fb45621e08085c58537a531f33fc7eca74 Mon Sep 17 00:00:00 2001 +From: Alan Jenkins +Date: Wed, 10 Apr 2019 00:19:14 +0100 +Subject: [PATCH 1/2] DNF: stderr is not ours to log to (and doing so breaks + Ansible) + +stderr does not belong to etckeeper-dnf +--------------------------------------- + +The Ansible dnf module uses the python dnf bindings. In contexts like +these, stdout/stderr is owned by the host app (Ansible). dnf should not +mess with stdout/stderr, unless the host app asks it to log there. + +Specifically, we were breaking the JSON output of the Ansible dnf module. +This was only noticeable when the etckeeper message began with a "[" +character. Ansible has a mechanism to try and skip header messages like +login banners. However, "[" is a valid character to start a JSON document. + +https://unix.stackexchange.com/questions/511210/ansible-dnf-module-module-failure/ + +Solution +-------- + +Instead, log any non-zero exit status through the dnf logger. + +For the pre-transaction etckeeper run, this message replaces an exception. +So we now avoid logging a traceback, which did not appear to add anything +useful. (In my testing, dnf was continued to install after the exception +was logged). + +I have also added a warning message for the post-transaction etckeeper run. + +I switched from os.system() to subprocess.call(). The latter interface +supports better error reporting. (When I tested os.system(), it returned +an errno number which was indistinguishable from an exit code). + +etckeeper >/dev/null 2>&1 +-------------------------- + +Any specific error messages from etckeeper are now discarded. Because +unfortunately, python conventions about passing text strings have been +somewhat unclear. It is an error to log strings if the encoding settings +of a registered logger cannot handle them. Because a "bad" character +causes the entire string to be discarded, and optionally an exception +printed to stderr. In a previous proposal I showed code that should be +correct in all expected cases. However, the length of comment required to +define "all expected cases" was not very reassuring. + +That was on top of explaining that dnf has a workaround that will avoid +raising exceptions when arbitrary unicode is logged to stdout. My proposal +had to include that explanation, to show that we were not breaking a +system package manager. + +It might sound strange to talk about error messages with characters +we cannot encode. But I have one word for you: "filenames". + +(After the recent python PR #14008[1], logging.basicConfig() defaults +to errors='backslashreplace'. Also, errors= can be manually specified when +creating a lower-level logging "Handler". I think this will resolve the +problem in the future. It means that when programs are written for this +new version of python, it should be the responsibility of the log setup +code to prevent UnicodeEncodeError.) + +[1] https://github.com/python/cpython/pull/14008 +--- + etckeeper-dnf/etckeeper.py | 27 ++++++++++++++++----------- + 1 file changed, 16 insertions(+), 11 deletions(-) + +diff --git a/etckeeper-dnf/etckeeper.py b/etckeeper-dnf/etckeeper.py +index e8a1a51..69edd88 100644 +--- a/etckeeper-dnf/etckeeper.py ++++ b/etckeeper-dnf/etckeeper.py +@@ -9,7 +9,7 @@ + + from dnfpluginscore import logger + +-import os ++import subprocess + import dnf + + +@@ -17,20 +17,25 @@ class Etckeeper(dnf.Plugin): + + name = 'etckeeper' + +- def _out(self, msg): +- logger.debug('Etckeeper plugin: %s', msg) ++ def _run_command(self, command): ++ logger.debug('Etckeeper plugin: %s', command) ++ try: ++ with open("/dev/null", "wb") as devnull: ++ ret = subprocess.call(("etckeeper", command), ++ stdout=devnull, stderr=devnull, ++ close_fds=True) ++ if ret > 0: ++ logger.warning('"etckeeper %s" failed (exit code %d)' % (command, ret)) ++ if ret < 0: ++ logger.warning('"etckeeper %s" died (signal %d)' % (command, -ret)) ++ except OSError as err: ++ logger.warning('Failed to run "etckeeper %s": %s' % (command, err)) + + def resolved(self): +- self._out('pre transaction commit') +- command = '%s %s' % ('etckeeper', " pre-install") +- ret = os.system(command) +- if ret != 0: +- raise dnf.exceptions.Error('etckeeper returned %d' % (ret >> 8)) ++ self._run_command("pre-install") + + def transaction(self): +- self._out('post transaction commit') +- command = '%s %s > /dev/null' % ('etckeeper', "post-install") +- os.system(command) ++ self._run_command("post-install") + + if __name__ == "__main__": + from distutils.core import setup +-- +2.23.0 + + +From 7cda9678b1e60c0495a2a522721b319843d5fae0 Mon Sep 17 00:00:00 2001 +From: Alan Jenkins +Date: Tue, 23 Apr 2019 20:15:01 +0100 +Subject: [PATCH 2/2] Do not use dnfpluginscore.logger + +dnfpluginscore is not core support for dnf plugins, it is just a +collection of "core plugins" for dnf. There is equally +dnfpluginsextras and dnfpluginsextras.logger. + +A comment in dnf.logger comment says the logger name 'dnf.plugin' is "api". +So we can safely rely on that name. + +Technically you can install etckeeper without the dnfpluginscore package +(at least I managed to run into this for python2, on a Fedora 29 system +which uses python3 for dnf by default). +--- + etckeeper-dnf/etckeeper.py | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/etckeeper-dnf/etckeeper.py b/etckeeper-dnf/etckeeper.py +index 69edd88..d9dd2c3 100644 +--- a/etckeeper-dnf/etckeeper.py ++++ b/etckeeper-dnf/etckeeper.py +@@ -7,11 +7,12 @@ + # Distutils code below was copied from etckeeper-bzr distributed with v1.15 + # + +-from dnfpluginscore import logger +- ++import logging + import subprocess + import dnf + ++logger = logging.getLogger('dnf.plugin') ++ + + class Etckeeper(dnf.Plugin): + +-- +2.23.0 + diff --git a/etckeeper-1.18.16.tar.gz b/etckeeper-1.18.16.tar.gz new file mode 100644 index 0000000..8d21658 Binary files /dev/null and b/etckeeper-1.18.16.tar.gz differ diff --git a/etckeeper-1.18.7-fix-hg-warnings.patch b/etckeeper-1.18.7-fix-hg-warnings.patch new file mode 100644 index 0000000..90c6818 --- /dev/null +++ b/etckeeper-1.18.7-fix-hg-warnings.patch @@ -0,0 +1,34 @@ +diff --git a/pre-commit.d/20warn-problem-files b/pre-commit.d/20warn-problem-files +index 6bd5c2b..7899268 100755 +--- a/pre-commit.d/20warn-problem-files ++++ b/pre-commit.d/20warn-problem-files +@@ -1,19 +1,20 @@ + #!/bin/sh + set -e + +-exclude_internal () { +- egrep -v '(^|/)(\.git|\.hg|\.bzr|_darcs)/' +-} ++# (Note that when using this, the find expression must end with ++# -print or -exec, else the excluded directories will actually be ++# printed!) ++NOVCS='. -path ./.git -prune -o -path ./.bzr -prune -o -path ./.hg -prune -o -path ./_darcs -prune -o' + + if [ "$VCS" = bzr ] || [ "$VCS" = darcs ]; then +- special=$(find . ! -type d ! -type f ! -type l | exclude_internal) || true +- hardlinks=$(find . -type f ! -links 1 | exclude_internal ) || true ++ special=$(find $NOVCS ! -type d ! -type f ! -type l -print) || true ++ hardlinks=$(find $NOVCS -type f ! -links 1 -print) || true + elif [ "$VCS" = hg ]; then +- special=$(find . ! -type d ! -type f ! -type l | exclude_internal) || true +- hardlinks=$(find . -type f ! -links 1 -exec hg status {} \; | exclude_internal ) || true ++ special=$(find $NOVCS ! -type d ! -type f ! -type l -print) || true ++ hardlinks=$(find $NOVCS -type f ! -links 1 -exec hg status {} \; -print) || true + elif [ "$VCS" = git ]; then +- special=$(find . ! -type d ! -type f ! -type l -exec git ls-files --exclude-standard --cached --others {} + | exclude_internal) || true +- hardlinks=$(find . -type f ! -links 1 -exec git ls-files --exclude-standard --cached --others {} + | exclude_internal) || true ++ special=$(find $NOVCS ! -type d ! -type f ! -type l -exec git ls-files --exclude-standard --cached --others {} + -print) || true ++ hardlinks=$(find $NOVCS -type f ! -links 1 -exec git ls-files --exclude-standard --cached --others {} + -print) || true + else + special="" + fi diff --git a/etckeeper-1.18.7-fix-rpm-ignores.patch b/etckeeper-1.18.7-fix-rpm-ignores.patch new file mode 100644 index 0000000..1cb5fed --- /dev/null +++ b/etckeeper-1.18.7-fix-rpm-ignores.patch @@ -0,0 +1,14 @@ +diff -up etckeeper-1.18.7/update-ignore.d/01update-ignore.orig etckeeper-1.18.7/update-ignore.d/01update-ignore +--- etckeeper-1.18.7/update-ignore.d/01update-ignore.orig 2017-08-13 12:31:57.360734395 +0200 ++++ etckeeper-1.18.7/update-ignore.d/01update-ignore 2017-08-13 12:32:45.246242478 +0200 +@@ -91,7 +91,9 @@ writefile () { + nl + elif [ "$LOWLEVEL_PACKAGE_MANAGER" = "rpm" ]; then + comment "new and old versions of conffiles, stored by apt/rpm" +- ignore "*.rpm*" ++ ignore "*.rpmnew" ++ ignore "*.rpmorig" ++ ignore "*.rpmsave" + nl + elif [ "$LOWLEVEL_PACKAGE_MANAGER" = "pacman-g2" -o "$LOWLEVEL_PACKAGE_MANAGER" = "pacman" -o "$LOWLEVEL_PACKAGE_MANAGER" = "pacmatic" ]; then + comment "new and old versions of conffiles, stored by pacman" diff --git a/etckeeper-add-breezy-python3-plugin.patch b/etckeeper-add-breezy-python3-plugin.patch new file mode 100644 index 0000000..d234bef --- /dev/null +++ b/etckeeper-add-breezy-python3-plugin.patch @@ -0,0 +1,52 @@ +From b5919d7919dda614c3c3c76ba126f45e205494bd Mon Sep 17 00:00:00 2001 +From: Dimitri John Ledkov +Date: Mon, 29 Apr 2019 14:11:09 +0100 +Subject: [PATCH 1/3] Add breezy python3 plugin + +--- + Makefile | 3 +++ + debian/changelog | 6 ++++++ + debian/control | 6 +++--- + etckeeper-brz/__init__.py | 34 ++++++++++++++++++++++++++++++++++ + 4 files changed, 46 insertions(+), 3 deletions(-) + create mode 100644 etckeeper-brz/__init__.py + +Index: etckeeper-1.18.10/etckeeper-brz/__init__.py +=================================================================== +--- /dev/null ++++ etckeeper-1.18.10/etckeeper-brz/__init__.py +@@ -0,0 +1,34 @@ ++# ++# Breezy plugin that runs etckeeper pre-commit when necessary ++ ++"""Runs etckeeper pre-commit when necessary.""" ++ ++from breezy.errors import BzrError ++import os ++ ++def etckeeper_startcommit_hook(tree): ++ abspath = getattr(tree, "abspath", None) ++ if abspath is None or not os.path.exists(abspath(".etckeeper")): ++ # Only run the commit hook when this is an etckeeper branch ++ return ++ import subprocess ++ ret = subprocess.call(["etckeeper", "pre-commit", abspath(".")]) ++ if ret != 0: ++ raise BzrError("etckeeper pre-commit failed") ++ ++try: ++ from breezy.hooks import install_lazy_named_hook ++except ImportError: ++ from breezy.mutabletree import MutableTree ++ MutableTree.hooks.install_named_hook('start_commit', ++ etckeeper_startcommit_hook, 'etckeeper') ++else: ++ install_lazy_named_hook( ++ "breezy.mutabletree", "MutableTree.hooks", ++ 'start_commit', etckeeper_startcommit_hook, 'etckeeper') ++ ++if __name__ == "__main__": ++ from distutils.core import setup ++ setup(name="brz-etckeeper", ++ packages=["breezy.plugins.etckeeper"], ++ package_dir={"breezy.plugins.etckeeper":"etckeeper-brz"}) diff --git a/etckeeper-makefile-remove-python-plugins.patch b/etckeeper-makefile-remove-python-plugins.patch new file mode 100644 index 0000000..7ed0de8 --- /dev/null +++ b/etckeeper-makefile-remove-python-plugins.patch @@ -0,0 +1,32 @@ +diff -up etckeeper-1.18.7/Makefile.orig etckeeper-1.18.7/Makefile +--- etckeeper-1.18.7/Makefile.orig 2017-06-08 18:59:32.000000000 +0200 ++++ etckeeper-1.18.7/Makefile 2017-06-12 22:58:50.802133039 +0200 +@@ -15,13 +15,10 @@ CP=cp -R + INSTALL=install + INSTALL_EXE=${INSTALL} + INSTALL_DATA=${INSTALL} -m 0644 +-PYTHON=python + FAKEROOT := $(shell command -v fakeroot 2> /dev/null) + TESTDIR := $(shell mktemp -u -d) + + build: etckeeper.spec etckeeper.version +- -$(PYTHON) ./etckeeper-bzr/__init__.py build || echo "** bzr support not built" +- -$(PYTHON) ./etckeeper-dnf/etckeeper.py build || echo "** DNF support not built" + + install: etckeeper.version + mkdir -p $(DESTDIR)$(etcdir)/etckeeper/ $(DESTDIR)$(vardir)/cache/etckeeper/ +@@ -58,14 +55,10 @@ ifeq ($(HIGHLEVEL_PACKAGE_MANAGER),yum) + mkdir -p $(DESTDIR)$(etcdir)/yum/pluginconf.d + $(INSTALL_DATA) yum-etckeeper.conf $(DESTDIR)$(etcdir)/yum/pluginconf.d/etckeeper.conf + endif +-ifeq ($(HIGHLEVEL_PACKAGE_MANAGER),dnf) +- -$(PYTHON) ./etckeeper-dnf/etckeeper.py install --root=$(DESTDIR) ${PYTHON_INSTALL_OPTS} || echo "** DNF support not installed" +-endif + ifeq ($(HIGHLEVEL_PACKAGE_MANAGER),zypper) + mkdir -p $(DESTDIR)$(prefix)/lib/zypp/plugins/commit + $(INSTALL) zypper-etckeeper.py $(DESTDIR)$(prefix)/lib/zypp/plugins/commit/zypper-etckeeper.py + endif +- -$(PYTHON) ./etckeeper-bzr/__init__.py install --root=$(DESTDIR) ${PYTHON_INSTALL_OPTS} || echo "** bzr support not installed" + echo "** installation successful" + + clean: etckeeper.spec etckeeper.version diff --git a/etckeeper.spec b/etckeeper.spec new file mode 100644 index 0000000..d4cecf3 --- /dev/null +++ b/etckeeper.spec @@ -0,0 +1,170 @@ +%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}-%{version}} +%global dnf_is_mandatory 1 +Name: etckeeper +Version: 1.18.16 +Release: 1 +Summary: Store /etc in a SCM system (git, mercurial, bzr or darcs) +License: GPLv2+ +URL: https://etckeeper.branchable.com/ +Source0: https://git.joeyh.name/index.cgi/etckeeper.git/snapshot/%{name}-%{version}.tar.gz +Source2: cron.daily +Patch0: etckeeper-makefile-remove-python-plugins.patch +Patch1: etckeeper-1.18.7-fix-rpm-ignores.patch +Patch2: etckeeper-1.18.7-fix-hg-warnings.patch +Patch3: etckeeper-add-breezy-python3-plugin.patch +Patch4: etckeeper-1.18.12-fix-output-for-ansible.patch +BuildArch: noarch +BuildRequires: python3-markdown +Requires: git >= 1.6.1 perl-interpreter crontabs findutils hostname which +%if 0%{?dnf_is_mandatory} +Requires: %{name}-dnf = %{version}-%{release} +%endif +BuildRequires: systemd +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +%description +The etckeeper program is a tool to let /etc be stored in a git, +mercurial, bzr or darcs repository. It hooks into yum to automatically +commit changes made to /etc during package upgrades. It tracks file +metadata that version control systems do not normally support, but that +is important for /etc, such as the permissions of /etc/shadow. It's +quite modular and configurable, while also being simple to use if you +understand the basics of working with version control. +The default backend is git, if want to use a another backend please +install the appropriate tool (mercurial, darcs or bzr). +%{?bazaar: To use bazaar/breezy as backend, please also install the %{name}-%{bazaar} package.} +To start using the package please read %{_pkgdocdir}/README. +%if 0%{?with_brz} + +%package brz +Summary: Support for bzr with etckeeper (via breezy) +BuildRequires: python3-devel brz +Requires: %{name} = %{version}-%{release} brz +%description brz +This package provides a brz (breezy) backend for etckeeper, if you want to use +etckeeper with (bzr) bazaar repositories, install this package. +%endif + +%package dnf +Summary: DNF plugin for etckeeper support +BuildRequires: python3-devel +BuildRequires: dnf dnf-plugins-core make +Requires: %{name} = %{version}-%{release} dnf dnf-plugins-core +%description dnf +This package provides a DNF plugin for etckeeper. If you want to use +etckeeper with DNF, install this package. + +%prep +%autosetup -p1 +%if 0%{?with_yum} +sed -e 's|HIGHLEVEL_PACKAGE_MANAGER=.*|HIGHLEVEL_PACKAGE_MANAGER=yum|' \ +%else +sed -e 's|HIGHLEVEL_PACKAGE_MANAGER=.*|HIGHLEVEL_PACKAGE_MANAGER=dnf|' \ +%endif + -e 's|LOWLEVEL_PACKAGE_MANAGER=.*|LOWLEVEL_PACKAGE_MANAGER=rpm|' \ + -i etckeeper.conf +sed -e 's|^prefix=.*|prefix=%{_prefix}|' \ + -e 's|^bindir=.*|bindir=%{_bindir}|' \ + -e 's|^etcdir=.*|etcdir=%{_sysconfdir}|' \ + -e 's|^mandir=.*|mandir=%{_mandir}|' \ + -e 's|^vardir=.*|vardir=%{_localstatedir}|' \ + -e 's|^INSTALL=.*|INSTALL=install -p|' \ + -e 's|^CP=.*|CP=cp -pR|' \ + -e 's|^systemddir=.*|systemddir=%{_unitdir}|' \ + -i Makefile +mkdir brz-plugin ; mv etckeeper-brz brz-plugin +mkdir dnf-plugin ; mv etckeeper-dnf dnf-plugin + +%build +%make_build +%if 0%{?with_brz} +pushd brz-plugin +%define py_setup etckeeper-brz/__init__.py +%py3_build +popd +%endif +pushd dnf-plugin +%define py_setup etckeeper-dnf/etckeeper.py build +%py3_build +popd +`ls /usr/bin/ |grep "markdown"` -f README.html README.md + +%install +%make_install +%if 0%{?with_brz} +pushd brz-plugin +%define py_setup etckeeper-brz/__init__.py +%py3_install +popd +%endif +pushd dnf-plugin +%define py_setup etckeeper-dnf/etckeeper.py build +%py3_install +popd +%if 0%{?dnf_is_mandatory} +sed -e 's|HIGHLEVEL_PACKAGE_MANAGER=.*|HIGHLEVEL_PACKAGE_MANAGER=dnf|' \ + -i %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf +%endif +install -D -p %{SOURCE2} %{buildroot}%{_sysconfdir}/cron.daily/%{name} +install -d %{buildroot}%{_localstatedir}/cache/%{name} + +%post +if [ $1 -gt 1 ] ; then + %{_bindir}/%{name} update-ignore +fi +%systemd_post %{name}.service +%systemd_post %{name}.timer + +%preun +%systemd_preun %{name}.service +%systemd_preun %{name}.timer + +%postun +%systemd_postun %{name}.service +%systemd_postun %{name}.timer + +%files +%doc README.html README.md +%license GPL +%{_bindir}/%{name} +%{_mandir}/man8/%{name}.8* +%dir %{_sysconfdir}/%{name} +%{_sysconfdir}/%{name}/*.d +%{_sysconfdir}/%{name}/daily +%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf +%config(noreplace) %{_sysconfdir}/cron.daily/%{name} +%dir %{_datadir}/bash-completion +%dir %{_datadir}/bash-completion/completions +%{_datadir}/bash-completion/completions/%{name} +%dir %{_datadir}/zsh +%dir %{_datadir}/zsh/vendor-completions +%{_datadir}/zsh/vendor-completions/_%{name} +%if 0%{?with_yum} +%dir %{_prefix}/lib/yum-plugins +%{_prefix}/lib/yum-plugins/%{name}.* +%dir %{_sysconfdir}/yum/pluginconf.d +%config(noreplace) %{_sysconfdir}/yum/pluginconf.d/%{name}.conf +%endif +%{_localstatedir}/cache/%{name} +%{_unitdir}/%{name}.service +%{_unitdir}/%{name}.timer +%if 0%{?with_brz} + +%files brz +%dir %{python3_sitelib}/breezy/ +%dir %{python3_sitelib}/breezy/plugins/ +%{python3_sitelib}/breezy/plugins/%{name}/ +%{python3_sitelib}/brz_%{name}-*.egg-info +%endif + +%files dnf +%{python3_sitelib}/dnf-plugins/%{name}.py +%exclude %{python3_sitelib}/dnf-plugins/__init__.py +%{python3_sitelib}/dnf-plugins/__pycache__/%{name}.* +%exclude %{python3_sitelib}/dnf-plugins/__pycache__/__init__.* +%{python3_sitelib}/dnf_%{name}-*.egg-info + +%changelog +* Tue Sep 7 2021 zhengyaohui - 1.18.16-1 +- package init