toolbox trips over the existing ubuntu home dir and its uid 1000.
notify-send "Backup started"
mkdir -p $(dirname $LOG)
+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
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 .cache/backup-passphrase --repo backup/restic forget --prune --keep-within-hourly 24h --keep-within-daily 7d --keep-within-weekly 30d --keep-within-monthly 12m
+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 .cache/backup-passphrase --repo backup/restic check || fail "BACKUP CHECK FAILED!"
+ssh piware.de restic --password-file backup/backup-passphrase --repo backup/restic check || fail "BACKUP CHECK FAILED!"
notify-send "Backup checked successfully"
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 '
sudo sed -i s/nodocs// /etc/dnf/dnf.conf
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 python3-pytest-asyncio libnotify \
- python3-mypy \
+ python3-boto python3-boto3 python3-openstacksdk libnotify \
simple-scan /usr/bin/scanimage /usr/bin/pngquant /usr/bin/convert pdfmerge \
cargo rustfmt clippy \
calibre qt5-qtwayland \
- dbus-daemon tmt tmt-provision-virtual flatpak-builder flatpak-spawn \
+ tmt python3-testcloud flatpak-builder flatpak-spawn qemu-ui-sdl \
fedpkg centpkg
# commands to forward to the host
-for cmd in podman toolbox nmcli eog evince; do
+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
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 '
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
-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 toolbox; do
+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
'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', re.I),
+ '(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('DB Vertrieb|Deutsche Bahn|Nextbike|Carsharing', re.I),
- 'Lebensmittel': re.compile('BIOS|Bäcker|Baecker|Ruta|Rewe', re.I),
- 'Eigentumswohnungen': re.compile('Rechnung Darl.-Leistung|Semmelweis', 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', 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', re.I),
+ 'campact|Amnesty|BN Landesverband', re.I),
}
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
return filter_re.search(entry.date)
with path.open() as f:
- reader = csv.reader(f)
+ reader = csv.reader(f, delimiter=';')
next(reader) # skip header
# first line is the column headers, chop it off
entries = map(parse_entry, reader)
#!/bin/sh
+DATA=$HOME/.cache/sunrise.json
set -eu
if [ "${1:-}" = "night" ]; then
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
(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 rhel-packager
-
-# coverity
-sudo dnf copr enable -y copr.devel.redhat.com/kdudka/covscan
-sudo dnf install -y covscan-client
--- /dev/null
+#!/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"))
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
- 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
# fix fonts between Fedora releases
rm -rf ~martin/.cache/fontconfig
-su -c 'flatpak -y update' martin
-
fstrim -av
+
+echo 'Run this now: flatpak -y update'
#!/bin/sh
set -eu
PACKAGE=${1:-cockpit}
-TARGET=${2:-jammy}
+TARGET=${2:-noble}
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*}
rm -rf /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
+ oracular) VER=24.10 ;;
noble) VER=24.04 ;;
- mantic) VER=23.10 ;;
- lunar) VER=23.04 ;;
- kinetic) VER=22.10 ;;
jammy) VER=22.04 ;;
*) echo "Unknown target $TARGET" >&2; exit 1 ;;
esac
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 "$@"