2 Commits

10 changed files with 178 additions and 25 deletions
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env python3
import urllib.request
import urllib.parse
import json
import subprocess
import os
import re
import tempfile
import sys
import shutil
def run_cmd(cmd, cwd=None):
return subprocess.run(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def get_local_foreign_packages():
res = run_cmd(['pacman', '-Qm'])
if res.returncode != 0:
return {}
pkgs = {}
for line in res.stdout.strip().split('\n'):
if line:
parts = line.split()
if len(parts) == 2:
pkgs[parts[0]] = parts[1]
return pkgs
def get_aur_info(pkgnames):
if not pkgnames:
return {}
results = {}
chunk_size = 100
pkgs = list(pkgnames)
for i in range(0, len(pkgs), chunk_size):
chunk = pkgs[i:i+chunk_size]
url = "https://aur.archlinux.org/rpc/v5/info?" + "&".join([f"arg[]={urllib.parse.quote(p)}" for p in chunk])
try:
req = urllib.request.urlopen(url, timeout=10)
data = json.loads(req.read().decode('utf-8'))
if data.get('type') == 'multiinfo':
for item in data.get('results', []):
results[item['Name']] = item
except Exception:
pass
return results
def needs_upgrade(local_ver, aur_ver):
res = run_cmd(['vercmp', local_ver, aur_ver])
try:
return int(res.stdout.strip()) < 0
except Exception:
return False
def extract_ver(content):
pkgver, pkgrel = None, None
for line in content.split('\n'):
line = line.strip()
if line.startswith('pkgver='):
pkgver = line.split('=', 1)[1].strip('"\'')
elif line.startswith('pkgrel='):
pkgrel = line.split('=', 1)[1].strip('"\'')
return pkgver, pkgrel
def strip_volatile_fields(content):
content = re.sub(r'^pkgver=.*$', '', content, flags=re.MULTILINE)
content = re.sub(r'^pkgrel=.*$', '', content, flags=re.MULTILINE)
sum_patterns = [
r'md5sums', r'sha1sums', r'sha224sums', r'sha256sums',
r'sha384sums', r'sha512sums', r'b2sums', r'cksums'
]
for sp in sum_patterns:
pattern = r'^' + sp + r'(?:_[a-zA-Z0-9_]+)?=\(.*?\)'
content = re.sub(pattern, '', content, flags=re.MULTILINE | re.DOTALL)
lines = [line.rstrip() for line in content.split('\n') if line.strip()]
return '\n'.join(lines)
def main():
local_pkgs = get_local_foreign_packages()
if not local_pkgs:
sys.exit(0)
aur_info = get_aur_info(list(local_pkgs.keys()))
upgrades = []
for pkg, local_ver in local_pkgs.items():
if pkg in aur_info:
aur_ver = aur_info[pkg]['Version']
if needs_upgrade(local_ver, aur_ver):
upgrades.append((pkg, local_ver, aur_ver))
if not upgrades:
sys.exit(0)
print(f"Checking PKGBUILD diffs for {len(upgrades)} upgrades...")
temp_dir = tempfile.mkdtemp()
try:
for pkg, local_ver, aur_ver in upgrades:
print(f"Checking {pkg} ({local_ver} -> {aur_ver})")
repo_dir = os.path.join(temp_dir, pkg)
res = run_cmd(['git', 'clone', '--quiet', f'https://aur.archlinux.org/{pkg}.git', repo_dir])
if res.returncode != 0:
print(f"Failed to clone {pkg}", file=sys.stderr)
sys.exit(1)
res = run_cmd(['git', 'log', '--pretty=format:%H'], cwd=repo_dir)
commits = res.stdout.strip().split('\n')
old_pkgbuild_content = None
new_pkgbuild_content = None
with open(os.path.join(repo_dir, 'PKGBUILD'), 'r', encoding='utf-8', errors='ignore') as f:
new_pkgbuild_content = f.read()
local_ver_no_epoch = local_ver.split(':', 1)[-1]
parts = local_ver_no_epoch.rsplit('-', 1)
expected_ver, expected_rel = parts if len(parts) == 2 else (parts[0], "")
for commit in commits:
res = run_cmd(['git', 'show', f"{commit}:PKGBUILD"], cwd=repo_dir)
if res.returncode == 0:
content = res.stdout
p_ver, p_rel = extract_ver(content)
if p_ver == expected_ver and p_rel == expected_rel:
old_pkgbuild_content = content
break
if old_pkgbuild_content is None:
print(f"ERROR: Could not find old PKGBUILD for {pkg} version {local_ver}. Cannot safely verify diff.", file=sys.stderr)
sys.exit(1)
old_stripped = strip_volatile_fields(old_pkgbuild_content)
new_stripped = strip_volatile_fields(new_pkgbuild_content)
if old_stripped != new_stripped:
print(f"ERROR: Unsafe changes detected in PKGBUILD for {pkg}!", file=sys.stderr)
with open(os.path.join(temp_dir, 'old'), 'w') as f:
f.write(old_stripped)
with open(os.path.join(temp_dir, 'new'), 'w') as f:
f.write(new_stripped)
subprocess.run(['diff', '-u', os.path.join(temp_dir, 'old'), os.path.join(temp_dir, 'new')])
sys.exit(1)
finally:
shutil.rmtree(temp_dir)
if __name__ == '__main__':
main()
+6 -3
View File
@@ -2,13 +2,16 @@
ansible.builtin.shell:
cmd:
comm -12 <(pactree -lrud1 {{ package_pattern }} | sort -u) <(pacman -Qqm | sort -u)
executable: /bin/bash
register: aur_packages
changed_when: false
failed_when: false
- name: Rebuild AUR Python packages
aur:
use: "{{ aur_helper }}"
name: '{{ item }}'
aur_only: true
extra_args: --rebuild
loop: '{{ aur_packages.stdout.split() }}'
loop: '{{ aur_packages.stdout_lines | default([]) }}'
when: aur_packages.stdout_lines | default([]) | length > 0
+4
View File
@@ -1,4 +1,8 @@
---
- name: Check AUR package diffs for poisoning
ansible.builtin.script: aur_diff_check.py
changed_when: false
- name: AUR upgrade
aur:
use: "{{ aur_helper }}"
+4 -6
View File
@@ -6,12 +6,10 @@
upgrade: true
extra_args: "--noconfirm"
register: arch_upgrade_result
#- name: Debug full Arch upgrade output
# ansible.builtin.debug:
# var: arch_upgrade_result
failed_when:
- arch_upgrade_result.failed == true
# We ignore the failure if it's just 'nothing to do',
# # but otherwise, we let it fail so you can step in.
- "'Nothing to upgrade' not in arch_upgrade_result.msg"
- "'there is nothing to do' not in (arch_upgrade_result.stdout | default('') | lower)"
- name: Debug full Arch upgrade output
ansible.builtin.debug:
var: arch_upgrade_result
@@ -1,5 +0,0 @@
- name: Full system upgrade
become: true
ansible.builtin.apt:
update_cache: true
upgrade: full
+6 -2
View File
@@ -1,2 +1,6 @@
- name: Perform official repository updates
ansible.builtin.import_tasks: apt_upgrade.yaml
---
- name: Full system upgrade
become: true
ansible.builtin.apt:
update_cache: true
upgrade: full
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env zsh
#!/usr/bin/env bash
# Exit code 0 = Reboot required
# Exit code 1 = System is up to date / No reboot needed
+1
View File
@@ -5,6 +5,7 @@
name: mollyguard.service
state: stopped
listen: Reboot system
failed_when: false
- name: Execute System Reboot
become: true
+1 -1
View File
@@ -1,6 +1,6 @@
---
- name: Check if kernel or microcode update requires reboot
ansible.builtin.script: check_reboot.zsh # Your script placed in files/
ansible.builtin.script: check_reboot.sh # Your script placed in files/
register: reboot_check
# Prevent Ansible from failing if the script returns false (exit code 1)
failed_when: false
+1 -1
View File
@@ -22,6 +22,6 @@
- name: Create override
ansible.builtin.shell:
cmd: |
printf {{ passphrase }} | (echo "[Service]"; systemd-creds encrypt --name={{ creds_name }} --pretty - -) >> /etc/systemd/system/shared.d/00-systemd-creds.conf
printf '%s' {{ passphrase | quote }} | (echo "[Service]"; systemd-creds encrypt --name={{ creds_name }} --pretty - -) >> /etc/systemd/system/shared.d/00-systemd-creds.conf
printf "Environment=%s=%%d/%s\n" {{ creds_var }} {{ creds_name }} >> /etc/systemd/system/shared.d/00-systemd-creds.conf
#creates: /etc/systemd/system/shared.d/00-systemd-creds.conf