]> piware.de Git - bin.git/commitdiff
build-debian-toolbox: Fix for Ubuntu containers master
authorMartin Pitt <martin@piware.de>
Sun, 29 Jun 2025 08:24:46 +0000 (10:24 +0200)
committerMartin Pitt <martin@piware.de>
Sun, 29 Jun 2025 08:24:46 +0000 (10:24 +0200)
toolbox trips over the existing ubuntu home dir and its uid 1000.

15 files changed:
backup
build-cockpit-toolbox
build-debian-toolbox
consors-report.py [new file with mode: 0755]
daytime-term
debian-backport-cockpit
gomuks [new file with mode: 0755]
install-rhel-tools
install-tex [new file with mode: 0755]
process-photos
recipekeeper-split [new file with mode: 0755]
scandoc
system-maintenance
ubuntu-backport-cockpit
vm

diff --git a/backup b/backup
index 7a424862cc100015608c6cee15d85a5e0d62c5ae..79dc369ed05699cbfd160abf0fac455d5fd3210a 100755 (executable)
--- a/backup
+++ b/backup
@@ -1,8 +1,14 @@
 #!/bin/sh
 set -eu
 cd $HOME
 #!/bin/sh
 set -eu
 cd $HOME
-LOG=.cache/duplicity/log
+LOG=.cache/backup/log
 PATH=$PATH:/sbin:/usr/sbin
 PATH=$PATH:/sbin:/usr/sbin
+RESTIC="restic --password-file $HOME/.config/backup-passphrase --repo sftp:piware.de:backup/restic"
+
+fail() {
+    notify-send -i /usr/share/icons/Adwaita/48x48/status/network-error-symbolic.symbolic.png -u critical -t 180000 "${1:-BACKUP FAILED!}"
+    exit 1
+}
 
 # do backup every day
 if [ -e "$LOG" ] && [ $(( `date +%s` - `stat -c %Y $LOG` )) -lt 86300 ]; then
 
 # do backup every day
 if [ -e "$LOG" ] && [ $(( `date +%s` - `stat -c %Y $LOG` )) -lt 86300 ]; then
@@ -27,6 +33,18 @@ fi
 
 notify-send "Backup started"
 mkdir -p $(dirname $LOG)
 
 notify-send "Backup started"
 mkdir -p $(dirname $LOG)
-env PASSPHRASE="$(cat ~/.backup-passphrase)" duplicity --allow-source-mismatch --full-if-older-than 1M --exclude-filelist .duplicity-ignore . rsync://piware.de/backup/laptop >> $LOG || { notify-send "BACKUP FAILED!"; exit 1; }
+date >> $LOG
+
+$RESTIC backup --exclude-file=$HOME/.config/backup-ignore $HOME >> $LOG 2>&1 || fail
+# TODO: forget --prune policy: https://restic.readthedocs.io/en/stable/060_forget.html
 notify-send "Backup finished successfully"
 notify-send "Backup finished successfully"
-duplicity remove-all-but-n-full 6 --force rsync://piware.de/backup/laptop
+
+scp .config/backup-passphrase piware.de:.cache/
+ssh piware.de chmod u+w .cache/backup-passphrase
+trap "ssh piware.de shred -u .cache/backup-passphrase" EXIT INT QUIT PIPE
+
+ssh piware.de restic --password-file backup/backup-passphrase --repo backup/restic forget --prune --keep-within-hourly 24h --keep-within-daily 7d --keep-within-weekly 30d --keep-within-monthly 12m
+notify-send "Backup pruned successfully"
+
+ssh piware.de restic --password-file backup/backup-passphrase --repo backup/restic check || fail "BACKUP CHECK FAILED!"
+notify-send "Backup checked successfully"
index 3b0ae8613b66a6b934eb429348bc89d9473cf228..868a9d2ff122d7163971514d9bbb1bbe50b664d0 100755 (executable)
@@ -4,8 +4,8 @@ NAME=${NAME:-cockpit}
 TAG=${TAG:-latest}
 
 toolbox rm --force $NAME || true
 TAG=${TAG:-latest}
 
 toolbox rm --force $NAME || true
-podman pull quay.io/cockpit/tasks:${TAG}
-yes | toolbox create --image quay.io/cockpit/tasks:${TAG} -c $NAME
+podman pull ghcr.io/cockpit-project/tasks:${TAG}
+yes | toolbox create --image ghcr.io/cockpit-project/tasks:${TAG} -c $NAME
 
 # install some extra development and desktop tools
 toolbox run -c "$NAME" sh -exc '
 
 # install some extra development and desktop tools
 toolbox run -c "$NAME" sh -exc '
@@ -16,42 +16,17 @@ sudo hostname -F /etc/hostname
 # enable manpages
 sudo sed -i s/nodocs// /etc/dnf/dnf.conf
 
 # enable manpages
 sudo sed -i s/nodocs// /etc/dnf/dnf.conf
 
-sudo dnf install -y ansible man-db man-pages moreutils fd-find ripgrep gh \
+sudo dnf install -y ansible bash-completion man-db man-pages moreutils fd-find ripgrep gh git-delta neovim \
     python3-boto python3-boto3 python3-openstacksdk libnotify \
     python3-boto python3-boto3 python3-openstacksdk libnotify \
-    pandoc texlive-ec texlive-pdfjam texlive-beamer \
-    /usr/bin/scanimage /usr/bin/pngquant /usr/bin/convert \
-    kpcli \
+    simple-scan /usr/bin/scanimage /usr/bin/pngquant /usr/bin/convert pdfmerge \
+    cargo rustfmt clippy \
     calibre qt5-qtwayland \
     calibre qt5-qtwayland \
-    chromium dbus-daemon flatpak-builder flatpak-spawn
-
-# HACK: tmt-provision-virtual has way too heavy dependencies: https://bugzilla.redhat.com/show_bug.cgi?id=2093717
-cat <<EOF > /tmp/spec
-Name: dummy-deps
-Summary: dummy dependencies
-Version: 999
-Release: 1%{?dist}
-# https://bugzilla.redhat.com/show_bug.cgi?id=2093717
-Provides: python3-libguestfs
-Provides: libguestfs-tools-c
-# also for tmt-provision-virtual
-Provides: libvirt
-License: BSD
-
-%description
-Dummy dependencies to avoid bloat.
-
-%files
-EOF
-rpmbuild --define "_topdir /tmp/rpmbuild-dummy" -bb /tmp/spec
-sudo rpm -i --verbose /tmp/rpmbuild-dummy/RPMS/x86_64/dummy-deps-999-1.fc*.x86_64.rpm
-rm -rf /tmp/rpmbuild-dummy /tmp/spec
-
-sudo dnf install -y --setopt=install_weak_deps=False tmt tmt-provision-virtual
-
-# run podman in toolbox
-printf "flatpak-spawn --host podman \42\$@\42\n" | sudo tee /usr/local/bin/podman >/dev/null
-sudo chmod a+x /usr/local/bin/podman
-
-# debugging cockpit crashes
-sudo dnf debuginfo-install -y glib2 glibc libssh gnutls
+    tmt python3-testcloud flatpak-builder flatpak-spawn qemu-ui-sdl \
+    fedpkg centpkg
+
+# commands to forward to the host
+for cmd in podman buildah toolbox nmcli eog evince flatpak; do
+    printf "#!/bin/sh\nexec flatpak-spawn --host $cmd \42\$@\42\n" | sudo tee /usr/local/bin/$cmd >/dev/null
+    sudo chmod a+x /usr/local/bin/$cmd
+done
 '
 '
index 82868761747be77a1cbcd88b91e5523d48b869b3..df36cd0cfb7b30b42fa5d96d7ad1b3eed8e1cbbe 100755 (executable)
@@ -8,6 +8,11 @@ toolbox rm -f $RELEASE || true
 podman pull docker.io/$DISTRO:$RELEASE
 toolbox -y create -c $RELEASE --image docker.io/$DISTRO:$RELEASE
 
 podman pull docker.io/$DISTRO:$RELEASE
 toolbox -y create -c $RELEASE --image docker.io/$DISTRO:$RELEASE
 
+# hack for ubuntu: need to remove existing home dir
+if [ "$DISTRO" = "ubuntu" ]; then
+    podman unshare sh -exc "H=\$(podman mount $RELEASE); rm -r \$H/home/ubuntu; sed -i '/^ubuntu/d' \$H/etc/passwd \$H/etc/shadow"
+fi
+
 # can't do that with toolbox run yet, as we need to install sudo first
 podman start $RELEASE
 podman exec -it $RELEASE sh -exc '
 # can't do that with toolbox run yet, as we need to install sudo first
 podman start $RELEASE
 podman exec -it $RELEASE sh -exc '
@@ -38,13 +43,19 @@ toolbox run --container $RELEASE sh -exc '
 echo "${ID}-${VERSION_ID:-sid}" | sudo tee /etc/hostname
 sudo hostname -F /etc/hostname
 
 echo "${ID}-${VERSION_ID:-sid}" | sudo tee /etc/hostname
 sudo hostname -F /etc/hostname
 
-sudo eatmydata apt-get -y dist-upgrade
+sudo DEBIAN_FRONTEND=noninteractive eatmydata apt-get -y dist-upgrade
 
 # development tools
 
 # development tools
-sudo eatmydata apt-get install -y --no-install-recommends build-essential git-buildpackage libwww-perl less vim lintian debhelper manpages-dev git dput pristine-tar bash-completion wget gnupg ubuntu-dev-tools python3-debian fakeroot libdistro-info-perl openssh-client
+sudo eatmydata apt-get install -y --no-install-recommends build-essential git-buildpackage libwww-perl less vim lintian debhelper manpages-dev git dput pristine-tar bash-completion wget gnupg ubuntu-dev-tools python3-debian fakeroot libdistro-info-perl openssh-client flatpak-xdg-utils
 
 # autopkgtest
 
 # autopkgtest
-sudo eatmydata apt-get install -y --no-install-recommends autopkgtest qemu-system-x86 qemu-utils genisoimage
+sudo eatmydata apt-get install -y --no-install-recommends autopkgtest qemu-system-x86 qemu-utils genisoimage uidmap
+
+# commands to forward to the host
+for cmd in podman buildah toolbox; do
+    printf "#!/bin/sh\n/usr/libexec/flatpak-xdg-utils/flatpak-spawn --host $cmd \42\$@\42\n" | sudo tee /usr/local/bin/$cmd >/dev/null
+    sudo chmod a+x /usr/local/bin/$cmd
+done
 '
 
 toolbox enter --container $RELEASE
 '
 
 toolbox enter --container $RELEASE
diff --git a/consors-report.py b/consors-report.py
new file mode 100755 (executable)
index 0000000..1241dbc
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/python3
+
+import argparse
+import collections
+import csv
+import itertools
+import re
+from collections import namedtuple
+from pathlib import Path
+from typing import Iterable
+
+Entry = namedtuple('Entry', ['category', 'date', 'valuta', 'who', 'iban', 'bic', 'type', 'desc', 'value'])
+
+CATEGORIES = {
+    'Gehalt/Steuern': re.compile('Gehalt/Rente|RED HAT|Finanzamt|FK Guenzburg', re.I),
+    'Wohnung': re.compile('Telekom|hands4home|Goetzfried|Green Planet Energy|Beitragsservice.*Rundfunk', re.I),
+    'Medizin': re.compile(r'Apotheke|MVZ|\bmed\b|ZAB Abrechnung|Caika|HNOeins|PVS|Dr\..*Sellier|'
+                          r'BFS Health|Streifeneder|Labor|Physio|(Drescher.*Lung)|Osteopath|'
+                          '(Dr.*Borchers)|(Debeka.*Überweisung)|(DKV.*Überweisung)|Beihilfe|Klinik|'
+                          'Niklas Hermann|Alena Hauser|Strizrep|Aerzte|Hallesche.*Ausz|Physico|'
+                          'Orthop|Pathologie', re.I),
+    'Versicherung': re.compile('((debeka|DKV|Hallesche|Versicherung|Alte Leipziger|ConceptIF|'
+                               'Baloise).*Lastschrift)|Hallesche.*Bonu', re.I),
+    'Transport': re.compile(r'DB Vertr\s*ieb|DB Fernverkehr|Deutsche Bahn|Nextbike|Carsharing|Radstation', re.I),
+    'Lebensmittel': re.compile('BIOS|Bäcker|Baecker|Baker|Schubert Bio|Ruta|Rewe|Edeka', re.I),
+    'Eigentumswohnungen': re.compile('Rechnung Darl.-Leistung|Semmelweis|BHP Buerkner|Dr. Schmitz', re.I),
+    'Hobby/Sport': re.compile('Holstein|Mrs. Sporty|Kieser|DJK|Dimaso', re.I),
+    'Sparen': re.compile('Bausparkasse Mainz AG|FIL Fondsbank|MIG (Fonds|GmbH)|Netfonds AG|Festgeld|'
+                         'MORGAN STANLEY', re.I),
+    'Spenden': re.compile('Spende|Signal Foundation|nebenan.de|(paypal.*Sabine.Hossenf)|'
+                          'Patreon|Umwelthilfe|Foerderer|Malteser|(Aktion Tier.*Mensch)|'
+                          'campact|Amnesty|BN Landesverband', re.I),
+}
+
+
+def parse_args() -> argparse.Namespace:
+    parser = argparse.ArgumentParser(description='Read Consorsbank CSV and generate report')
+    parser.add_argument('-d', '--date', metavar='REGEX', default='.', help='date filter regex')
+    parser.add_argument('-s', '--summary', action='store_true', help='only show category sums')
+    parser.add_argument('csv', type=Path, nargs='+', help='CSV files to parse')
+    return parser.parse_args()
+
+
+def get_category(item: str) -> str:
+    for category, pattern in CATEGORIES.items():
+        if pattern.search(item):
+            return category
+    return 'Sonstiges'
+
+
+def parse_entry(raw_fields: Iterable[str]) -> Entry:
+    fields = [f.strip() for f in raw_fields]
+    # format change in May 2024, adds a 9th field "Währung"; ignore
+    fields = fields[:8]
+    # last field is the value, parse as float
+    value = float(fields.pop().replace('.', '').replace(',', '.'))
+    # match on who, IBAN, type, or desc
+    category = get_category(fields[2] + fields[3] + fields[5] + fields[6])
+    return Entry._make([category, *fields, value])
+
+
+def parse_csv(path: Path, date_filter: str) -> Iterable[Entry]:
+    filter_re = re.compile(date_filter, re.I)
+
+    def filter_entry(entry: Entry):
+        # ignore from/to Tagesgeldkonto
+        if entry.iban == 'DE13760300800853589101':
+            return None
+        return filter_re.search(entry.date)
+
+    with path.open() as f:
+        reader = csv.reader(f, delimiter=';')
+        next(reader)  # skip header
+        # first line is the column headers, chop it off
+        entries = map(parse_entry, reader)
+        # do the actual iteration here, as the files get closed afterwards
+        return list(filter(filter_entry, entries))
+
+
+def main():
+    args = parse_args()
+
+    entries = itertools.chain.from_iterable(map(lambda p: parse_csv(p, args.date), args.csv))
+    by_category = collections.defaultdict(lambda: [])
+    category_sum = collections.defaultdict(lambda: 0.0)
+    balance = 0.0
+    for e in entries:
+        by_category[e.category].append(e)
+        category_sum[e.category] += e.value
+        balance += e.value
+
+    if args.summary:
+        for cat in sorted(by_category):
+            print(f'| {category_sum[cat]:>8.2f} | {cat:20} |')
+        print('|----------|----------------------|')
+        print(f'| {balance:>8.2f} | {"Saldo":20} |')
+
+    else:
+        for cat in sorted(by_category):
+            print(f'## {cat}: {category_sum[cat]:.2f}\n')
+            for entry in by_category[cat]:
+                print(f'| {entry.date:7} | {entry.value:>7.2f} | _{entry.type}_ - {entry.who} - {entry.desc} |')
+            print()
+
+        print(f'## Saldo: {balance:.2f}')
+
+
+if __name__ == '__main__':
+    main()
index 102e417c8ced4479a89f62eb3152a86e7ed5437b..51f9b827bc2d5fe84546bbf0146070b1a5e0dc02 100755 (executable)
@@ -1,4 +1,5 @@
 #!/bin/sh
 #!/bin/sh
+DATA=$HOME/.cache/sunrise.json
 set -eu
 
 if [ "${1:-}" = "night" ]; then
 set -eu
 
 if [ "${1:-}" = "night" ]; then
@@ -6,8 +7,17 @@ if [ "${1:-}" = "night" ]; then
     night=1
 elif [ "${1:-}" = "day" ]; then
     night=
     night=1
 elif [ "${1:-}" = "day" ]; then
     night=
-elif [ `date +%k` -ge 20 ] || [ `date +%k` -lt 5 ]; then
-    night=1
+else
+    # get daylight times at most once a day
+    if [ ! -e "$DATA" ] || [ $(( `date +%s` - `stat -c %Y $DATA` )) -gt 86400 ]; then
+        curl --silent --show-error -o "$DATA" 'https://api.sunrise-sunset.org/json?lat=48.22&lng=10.54&formatted=0'
+    fi
+    sunrise=$(jq -r .results.sunrise < ~/.cache/sunrise.json)
+    sunset=$(jq -r .results.sunset < ~/.cache/sunrise.json)
+    now=$(date '+%H%M')
+    if [ $now -le $(date -d "$sunrise" '+%H%M') ] || [ $now -ge $(date -d "$sunset" '+%H%M') ]; then
+        night=1
+    fi
 fi
 
 if [ -n "${night:-}" ]; then
 fi
 
 if [ -n "${night:-}" ]; then
index 109f8482f02014a7e605632247899ae836362d09..a0284ed323fa4bb24d7355e29873a4a74998c7f5 100755 (executable)
@@ -20,14 +20,14 @@ upload() {
     rm -r ${PACKAGE}-*
 }
 
     rm -r ${PACKAGE}-*
 }
 
+rm -rf /tmp/backport
 mkdir /tmp/backport
 cd  /tmp/backport
 apt-get source $PACKAGE
 cd ${PACKAGE}-*
 
 mkdir /tmp/backport
 cd  /tmp/backport
 apt-get source $PACKAGE
 cd ${PACKAGE}-*
 
-# bullseye
-series_ver bullseye-backports
-[ ! -x debian/adjust-for-release ] || debian/adjust-for-release bullseye
-dch --local ~bpo11+ --distribution bullseye-backports --force-distribution "No-change backport to Debian Bullseye"
-dpkg-buildpackage -S -sd -nc -v$ver
+# bookworm
+series_ver bookworm-backports
+dch --local ~bpo12+ --distribution bookworm-backports --force-distribution "No-change backport to Debian Bookworm"
+debuild -S -sd -nc -v$ver
 upload
 upload
diff --git a/gomuks b/gomuks
new file mode 100755 (executable)
index 0000000..ebe585d
--- /dev/null
+++ b/gomuks
@@ -0,0 +1,3 @@
+#!/bin/sh
+printf '\033]2;gomuks\a'
+TERM=xterm ~/.local/bin/gomuks
index 7953c726b55162c5d2958276a39b0852baede522..4fc6d7b7e0a0df2bbf70c72e123b20731b2b3875 100755 (executable)
@@ -2,23 +2,10 @@
 # Install RHEL development tools into a toolbox
 set -eux
 
 # Install RHEL development tools into a toolbox
 set -eux
 
-sudo curl -o /etc/pki/ca-trust/source/anchors/RH-IT-Root-CA.crt https://password.corp.redhat.com/RH-IT-Root-CA.crt
+sudo curl -o /etc/pki/ca-trust/source/anchors/2015-RH-IT-Root-CA.crt https://certs.corp.redhat.com/certs/2015-IT-Root-CA.pem
+sudo curl -o /etc/pki/ca-trust/source/anchors/2022-RH-IT-Root-CA.crt https://certs.corp.redhat.com/certs/2022-IT-Root-CA.pem
+
 sudo update-ca-trust
 
 (cd /etc/yum.repos.d; sudo curl -O --location https://download.devel.redhat.com/rel-eng/RCMTOOLS/rcm-tools-fedora.repo)
 sudo update-ca-trust
 
 (cd /etc/yum.repos.d; sudo curl -O --location https://download.devel.redhat.com/rel-eng/RCMTOOLS/rcm-tools-fedora.repo)
-sudo dnf install -y rhpkg
-
-# CentOS stream
-sudo dnf copr enable -y james/centpkg
-sudo dnf install -y centpkg streamkoji
-
-# coverity
-sudo dnf copr enable -y copr.devel.redhat.com/kdudka/covscan
-sudo dnf install -y covscan-client
-
-# 1minutetip: https://wiki.test.redhat.com/BaseOs/Projects/1minuteTIP
-if mountpoint /mnt; then sudo umount /mnt; fi # HACK: rhts-test-env-5.0-2.fc32eng.noarch wants to fiddle with it
-. /etc/os-release
-sudo wget -O /etc/yum.repos.d/qa-tools.repo https://copr.devel.redhat.com/coprs/lpol/qa-tools/repo/fedora-36/lpol-qa-tools-fedora-${VERSION_ID}.repo
-sudo wget -O /etc/yum.repos.d/beaker-client.repo http://download.lab.bos.redhat.com/beakerrepos/beaker-client-Fedora.repo
-sudo dnf install -y qa-tools-workstation-1minutetip tmt-redhat-provision-minute
+sudo dnf install -y rhel-packager
diff --git a/install-tex b/install-tex
new file mode 100755 (executable)
index 0000000..62d2a6f
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+if command -v apt; then
+    sudo apt install -y texlive-lang-german  texlive-latex-base texlive-latex-extra
+else
+    sudo dnf install texlive-german texlive-german texlive-a4wide texlive-dinbrief texlive-latex texlive-hyphen-german pandoc texlive-ec texlive-pdfjam texlive-beamer
+fi
index 14ee5b3eb6781e554135c7e1d2627845e413d295..219edf3e3521feeabff3374d1545f471e392601a 100755 (executable)
@@ -1,7 +1,11 @@
 #!/bin/sh
 set -eu
 
 #!/bin/sh
 set -eu
 
-for f in *.jpg *.JPG; do
+for f in *.JPG; do
+    mv "$f" "${f%JPG}jpg"
+done
+
+for f in *.jpg; do
     echo "$f"
     chmod 644 "$f"
     info="$(exiv2 -Pnv "$f")"
     echo "$f"
     chmod 644 "$f"
     info="$(exiv2 -Pnv "$f")"
diff --git a/recipekeeper-split b/recipekeeper-split
new file mode 100755 (executable)
index 0000000..a56f52e
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/python3
+import html
+import os
+import re
+import shutil
+import sys
+import xml.etree.ElementTree as ET
+import zipfile
+
+(recipe_zip, destdir) = sys.argv[1:]
+
+# init destdir
+if os.path.exists(destdir):
+    shutil.rmtree(destdir)
+os.makedirs(destdir)
+
+with zipfile.ZipFile(recipe_zip, 'r') as archive:
+    archive.extractall(path=destdir)
+
+
+def safe_filename(name):
+    name = re.sub(r"[^\w\s-]", '', name).strip().replace(' ', '_')
+    return name[:40]
+
+
+with open(os.path.join(destdir, "recipes.html"), "r") as f:
+    content = f.read()
+
+# 1. Fix <meta ...> tags: make them self-closing
+content_fixed = re.sub(r'(<meta\b[^>]*)(?<!/)>', r'\1 />', content)
+
+# 2. Fix unquoted itemprop attribute values (itemprop=foo -> itemprop="foo")
+# Only match when the value is not already quoted
+content_fixed = re.sub(r'itemprop=([^\s">]+)', r'itemprop="\1"', content_fixed)
+
+# Parse as XML
+tree = ET.ElementTree(ET.fromstring(content_fixed))
+root = tree.getroot()
+
+head = root.find('head')
+body = root.find('body')
+assert head is not None
+assert body is not None
+recipes = [div for div in body.findall('div') if div.get('class') == 'recipe-details']
+
+# Prepare the <meta http-equiv="content-type"...> tag string
+meta_tag = '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'
+
+for recipe in recipes:
+    h2 = recipe.find('.//h2[@itemprop="name"]')
+    meta_id = recipe.find('.//meta[@itemprop="recipeId"]')
+    if h2 is not None and h2.text and h2.text.strip():
+        base = safe_filename(html.unescape(h2.text))
+    elif meta_id is not None:
+        base = meta_id.attrib['content']
+    else:
+        base = "recipe"
+
+    filename = f"{base}.html"
+
+    # Convert head to string and insert the meta tag after <head>
+    head_str = ET.tostring(head, encoding="unicode")
+    head_str = re.sub(
+        r'(<head.*?>)',  # Match opening <head> tag (with possible attributes)
+        r'\1\n' + meta_tag,  # Insert meta tag right after opening tag
+        head_str,
+        count=1,
+        flags=re.IGNORECASE | re.DOTALL
+    )
+    # fix too small font sizes
+    head_str = re.sub(r'14px', '18pt', head_str)
+    head_str = re.sub(r'24px', '24pt', head_str)
+
+    out_html = (
+        '<!DOCTYPE html>\n<html>\n' +
+        head_str + '\n' +
+        '<body>\n' +
+        ET.tostring(recipe, encoding="unicode") + '\n' +
+        '</body>\n</html>'
+    )
+    with open(os.path.join(destdir, filename), "w", encoding="utf-8") as out:
+        out.write(out_html)
+
+    print(f"Wrote: {filename}")
+
+os.remove(os.path.join(destdir, "recipes.html"))
diff --git a/scandoc b/scandoc
index f62373cb379c4a795723e2fa57175caca7cd215f..3844e5c90939b36640960a159cf06bcc1659e95b 100755 (executable)
--- a/scandoc
+++ b/scandoc
@@ -13,15 +13,20 @@ echo "Output file directory: $D"
 PAGE=0
 while true; do
     PAGE=$((PAGE+1))
 PAGE=0
 while true; do
     PAGE=$((PAGE+1))
-    scanimage --device-name=genesys --format=png --progress --resolution 150 --mode Gray -x 210 -y 297 | convert -rotate 180 -modulate 120 -level 30,60% - cur.png
+    scanimage --device-name=genesys --format=png --progress --resolution 150 --mode Gray -x 210 -y 297 | magick - -rotate 180 -modulate 120 -level 30,60% cur.png
     FNAME=page`printf '%02i' $PAGE`
     pngquant 4 < cur.png > $FNAME.png
     FNAME=page`printf '%02i' $PAGE`
     pngquant 4 < cur.png > $FNAME.png
-    convert $FNAME.png $FNAME.pdf
+    magick $FNAME.png $FNAME.pdf
     echo "Insert next page and press Enter; q to stop"
     read x
     [ "$x" = q ] && break || true
 done
 
     echo "Insert next page and press Enter; q to stop"
     read x
     [ "$x" = q ] && break || true
 done
 
-pdfjam --outfile "$name" --fitpaper true --rotateoversize false page*.pdf
+# pdfmerge does not get along with just one input file
+if [ $PAGE -eq 1 ]; then
+    mv page*.pdf "$name"
+else
+    pdfmerge page*.pdf "$name"
+fi
 
 rm -rf "$D"
 
 rm -rf "$D"
index 8db4659d343a2d3a3b06fa12f232485d27febd43..4b3557c9d923d620ef828ed3050831898342daf8 100755 (executable)
@@ -11,6 +11,6 @@ rpm-ostree upgrade
 # fix fonts between Fedora releases
 rm -rf ~martin/.cache/fontconfig
 
 # fix fonts between Fedora releases
 rm -rf ~martin/.cache/fontconfig
 
-su -c 'flatpak -y update' martin
-
 fstrim -av
 fstrim -av
+
+echo 'Run this now: flatpak -y update'
index a8cf5f491e28ed81cd6d84fd171e065419260f2a..b1d182893b82113be8cf665adc8fb21217f5bd72 100755 (executable)
@@ -1,9 +1,11 @@
 #!/bin/sh
 set -eu
 PACKAGE=${1:-cockpit}
 #!/bin/sh
 set -eu
 PACKAGE=${1:-cockpit}
-TARGET=${2:-jammy}
+TARGET=${2:-noble}
 POCKET="${TARGET}-backports"
 
 POCKET="${TARGET}-backports"
 
+[ -z "${3:-}" ] || DSC=$(realpath "$3")
+
 series_ver() {
     ver=$(wget -q -O- http://de.archive.ubuntu.com/ubuntu/dists/$1/universe/source/Sources.xz | xz -d | grep-dctrl -n -sVersion -XP $PACKAGE | sort -u | tail -n1)
     ver=${ver%~bpo*}
 series_ver() {
     ver=$(wget -q -O- http://de.archive.ubuntu.com/ubuntu/dists/$1/universe/source/Sources.xz | xz -d | grep-dctrl -n -sVersion -XP $PACKAGE | sort -u | tail -n1)
     ver=${ver%~bpo*}
@@ -25,17 +27,20 @@ upload() {
     rm -r ${PACKAGE}-*
 }
 
     rm -r ${PACKAGE}-*
 }
 
+rm -rf /tmp/backport
 mkdir /tmp/backport
 cd  /tmp/backport
 mkdir /tmp/backport
 cd  /tmp/backport
-apt-get source $PACKAGE
+if [ -n "${DSC:-}" ]; then
+    dpkg-source -x "$DSC"
+else
+    apt-get source $PACKAGE
+fi
 cd ${PACKAGE}-*
 
 case "$TARGET" in
 cd ${PACKAGE}-*
 
 case "$TARGET" in
-    lunar) VER=23.04 ;;
-    kinetic) VER=22.10 ;;
+    oracular) VER=24.10 ;;
+    noble) VER=24.04 ;;
     jammy) VER=22.04 ;;
     jammy) VER=22.04 ;;
-    impish) VER=21.10 ;;
-    focal) VER=20.04 ;;
     *) echo "Unknown target $TARGET" >&2; exit 1 ;;
 esac
 
     *) echo "Unknown target $TARGET" >&2; exit 1 ;;
 esac
 
diff --git a/vm b/vm
index 174aedf385883e769757df714fe576db0c6bdbef..f403cb2860f5f9251da9bf3cc1c302e92fc5092b 100755 (executable)
--- a/vm
+++ b/vm
@@ -9,7 +9,7 @@ until echo '' | nc localhost $ssh_port 2>&1 >/dev/null | grep -q 'Connection ref
     ssh_port=$((ssh_port+1))
 done
 echo "Host ssh port: $ssh_port"
     ssh_port=$((ssh_port+1))
 done
 echo "Host ssh port: $ssh_port"
-qemu-system-x86_64 -enable-kvm -display sdl -m 2048 -device virtio-rng-pci \
+qemu-system-x86_64 -enable-kvm -cpu host -display sdl -m 2048 -device virtio-rng-pci \
     -drive file="$image",if=virtio \
     -virtfs local,id=src,path=$HOME,security_model=none,mount_tag=home,readonly=on \
     -net nic,model=virtio -net user,hostfwd=tcp::${ssh_port}-:22 "$@"
     -drive file="$image",if=virtio \
     -virtfs local,id=src,path=$HOME,security_model=none,mount_tag=home,readonly=on \
     -net nic,model=virtio -net user,hostfwd=tcp::${ssh_port}-:22 "$@"