aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <egor@tensin.name>2025-11-23 23:25:52 +0100
committerEgor Tensin <egor@tensin.name>2025-11-23 23:37:31 +0100
commit58d256ea6262dc6365d26d3abc807c88a12809b2 (patch)
treea9736c6ded91ae9192ec89c7ebf04a691f0d0227
parentconfig_links: fix a typo (diff)
downloadinfra-ansible-58d256ea6262dc6365d26d3abc807c88a12809b2.tar.gz
infra-ansible-58d256ea6262dc6365d26d3abc807c88a12809b2.zip
plugins: add etckeeper_commit
-rwxr-xr-xplugins/action/etckeeper_commit.py63
-rw-r--r--roles/etckeeper/handlers/commit.yml30
-rw-r--r--roles/etckeeper/handlers/main.yml13
-rw-r--r--roles/etckeeper_commit/defaults/main.yml2
-rw-r--r--roles/etckeeper_commit/tasks/main.yml45
5 files changed, 74 insertions, 79 deletions
diff --git a/plugins/action/etckeeper_commit.py b/plugins/action/etckeeper_commit.py
new file mode 100755
index 0000000..065b9eb
--- /dev/null
+++ b/plugins/action/etckeeper_commit.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+import re
+
+from ansible.errors import AnsibleActionFail
+from ansible.plugins.action import ActionBase
+
+
+class ActionModule(ActionBase):
+ def run(self, tmp=None, task_vars=None):
+ result = super().run(tmp, task_vars)
+
+ msg = self._task.args['msg']
+ allowlist = self._task.args.get('allowlist', [])
+ allowlist = [re.compile(pattern) for pattern in allowlist]
+ skip_no_changes = self._task.args.get('skip_no_changes', False)
+
+ dirty_files = self._get_dirty_file_list('/etc', task_vars)
+ if not dirty_files:
+ if skip_no_changes:
+ return result
+ raise AnsibleActionFail('Nothing to commit')
+
+ if allowlist:
+ unexpected = [
+ path for path in dirty_files
+ if not any([pattern.fullmatch(path) for pattern in allowlist])
+ ]
+ if unexpected:
+ raise AnsibleActionFail(f"Unexpected modifications in repository:\n{'\n'.join(unexpected)}")
+
+ return self._commit(msg, task_vars)
+
+ def _get_dirty_file_list(self, repo_dir, task_vars):
+ status = self._get_status(repo_dir, task_vars)
+ status = status.splitlines()
+ status = [line[3:] for line in status]
+ return status
+
+ def _get_status(self, repo_dir, task_vars):
+ cmd_result = self._execute_module(
+ module_name='ansible.builtin.command',
+ module_args=dict(
+ argv=['git', 'status', '--porcelain'],
+ chdir=repo_dir,
+ ),
+ task_vars=task_vars,
+ )
+ if cmd_result.get('failed'):
+ raise AnsibleActionFail(f"Couldn't get git status for {repo_dir}", result=cmd_result)
+ return cmd_result['stdout']
+
+ def _commit(self, msg, task_vars):
+ cmd_result = self._execute_module(
+ module_name='ansible.builtin.command',
+ module_args=dict(
+ argv=['etckeeper', 'commit', msg],
+ ),
+ task_vars=task_vars,
+ )
+ if cmd_result.get('failed'):
+ raise AnsibleActionFail('etckeeper commit failed', result=cmd_result)
+ return cmd_result
diff --git a/roles/etckeeper/handlers/commit.yml b/roles/etckeeper/handlers/commit.yml
deleted file mode 100644
index de64614..0000000
--- a/roles/etckeeper/handlers/commit.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-# This was copied to role etckeeper_commit.
-#
-# TODO: figure out how to dedupe this (handlers cannot use include_role).
-
-- name: Get list of modified files
- become: true
- ansible.builtin.shell: |
- set -o pipefail && \
- git status --porcelain=v1 \
- | cut -c 4- \
- | grep -G -v {{ ('^' + (paths | map("regex_replace", "^/", "") | list | join("\|^"))) | quote }}
- args:
- chdir: /etc
- vars:
- ansible_shell_executable: /bin/bash
- register: git_status
- changed_when: false
- failed_when: git_status.rc not in [0, 1]
-
-- name: Fail if unexpected files were modified
- ansible.builtin.fail:
- msg: |
- Unexpected files were modified:
- {{ git_status.stdout }}
- when: git_status.rc == 0
-
-- name: etckeeper commit
- become: true
- ansible.builtin.command: |
- etckeeper commit {{ commit_msg | quote }}
diff --git a/roles/etckeeper/handlers/main.yml b/roles/etckeeper/handlers/main.yml
index 76c225e..c67dc02 100644
--- a/roles/etckeeper/handlers/main.yml
+++ b/roles/etckeeper/handlers/main.yml
@@ -1,13 +1,10 @@
- name: Commit etckeeper.conf
- ansible.builtin.include_tasks: commit.yml
- vars:
- paths: [etckeeper/etckeeper.conf]
- commit_msg: configure etckeeper
+ tensin.infra.etckeeper_commit:
+ msg: configure etckeeper
+ allowlist: [etckeeper/etckeeper.conf]
listen: etckeeper_commit_conf
- name: Commit .gitignore
- ansible.builtin.include_tasks: commit.yml
- vars:
- paths: ['.*']
- commit_msg: configure ignored files
+ tensin.infra.etckeeper_commit:
+ msg: configure ignored files
listen: etckeeper_commit_gitignore
diff --git a/roles/etckeeper_commit/defaults/main.yml b/roles/etckeeper_commit/defaults/main.yml
index 4a75c85..205b501 100644
--- a/roles/etckeeper_commit/defaults/main.yml
+++ b/roles/etckeeper_commit/defaults/main.yml
@@ -1,2 +1,2 @@
-#etckeeper_commit_paths: []
+#etckeeper_commit_allowlist: []
#etckeeper_commit_msg: ''
diff --git a/roles/etckeeper_commit/tasks/main.yml b/roles/etckeeper_commit/tasks/main.yml
index 94370d0..061942e 100644
--- a/roles/etckeeper_commit/tasks/main.yml
+++ b/roles/etckeeper_commit/tasks/main.yml
@@ -1,41 +1,6 @@
-# This was copied from role etckeeper's handlers.
-#
-# TODO: figure out how to dedupe this (handlers cannot use include_role).
-
-- name: Get list of modified files
+- name: Run etckeeper commit
become: true
- ansible.builtin.shell: |
- set -o pipefail && \
- git status --porcelain=v1 | cut -c 4-
- args:
- chdir: /etc
- vars:
- ansible_shell_executable: /bin/bash
- register: git_status
- changed_when: false
-
-- name: Try to commit if there're modified files
- when: git_status.stdout_lines | length > 0
- block:
- - name: Filter out expected modifications
- ansible.builtin.shell: |
- set -o pipefail && \
- echo {{ git_status.stdout | quote }} \
- | grep -G -v {{ ('^' + (etckeeper_commit_paths | map("regex_replace", "^/", "") | list | join("\|^"))) | quote }}
- vars:
- ansible_shell_executable: /bin/bash
- register: unexpected_files
- changed_when: false
- failed_when: unexpected_files.rc not in [0, 1]
-
- - name: Fail if unexpected files were modified
- ansible.builtin.fail:
- msg: |
- Unexpected files were modified:
- {{ unexpected_files.stdout }}
- when: unexpected_files.stdout_lines | length > 0
-
- - name: etckeeper commit
- become: true
- ansible.builtin.command: |
- etckeeper commit {{ etckeeper_commit_msg | quote }}
+ tensin.infra.etckeeper_commit:
+ msg: '{{ etckeeper_commit_msg }}'
+ allowlist: '{{ etckeeper_commit_allowlist }}'
+ skip_no_changes: true