Card Holder Notes
This morning I had a machine crash on me and came back to an error.
Error
zsh: corrupt history file /home/u_walkews/.zsh_history
Dammit I don’t want to redo my shell history, I checked with a clanker and they
came up with this solution using strings that only prints printable
characters.
cp ~/.zsh_history ~/.zsh_history.bak
mv ~/.zsh_history ~/.zsh_history.corrupt
touch ~/.zsh_history
chmod 600 ~/.zsh_history
strings ~/.zsh_history.corrupt > ~/.zsh_history
chmod 600 ~/.zsh_history
hmmm.
Does codex change throughout the token usage window?
T Handle Wire Puller
Keys.waylonwalker.com
Malicious Aur Packages Jun 2026
Prompt Seagulls
Mythos gone already
Just give em the ol one eyed fighting Kirby
This is a vim substitution technique to capture the rest of the line as a capture group.
:'<,'>s/longhorn\(.*\)/longhorn\1-rwx
This one captures pesky optional " and places it back at the end if it found
one.
:'<,'>s/longhorn\("\?\)\(.*\)/longhorn\2-rwx\1
!!! see-also
<a href="/thought-200/" class="wikilink" data-title="The One Eyed Fighting Kirby" data-description="!https://www.youtube.com/watch?v=9_Ekt1PZBzQ&t=351s" data-date="2024-01-26">The One Eyed Fighting Kirby</a>
just good enough
I’ve been deploying my site old school for most of this year, rsync to a volume mounted to nginx. I ran into an issue today where I updated my site and all of the pages updated first, followed by upload. The issue this created was that the new cache busted css files were not up yet and the site had no styles for a brief period during upload.
I found that delaying updates and delaying deletes until the new content exists first solves this problem pretty well. Theres still possiblility of jank while uploading to a live directory and not doing some sort of hot swap, but I’m good with this low budget option for now.
sync:
rsync -rlt --delete --omit-dir-times \
--info=progress2 \
--delay-updates \
--delete-delay \
./output/ \
server:/mnt/mysite
Flowing Thoughts Ai To Help Blast Radius
To ignore commands that start with a space character, use the
HIST_IGNORE_SPACE option in bash or zsh.
setopt HIST_IGNORE_SPACE
Ping 59
remember rule 4
agents are never done
I just learned that forgejo has a push to create repo feature and it is a gamechanger. Upon first try it didn’t work, with just a couple of environment variables I was up and running with push to create.
notify.wayl.one on main is 📦 v0.1.62 v3.14.4 NO PYTHON VENV SET USING SYSTEM NVIM
❯ git remote add origin https://git.waylonwalker.com/waylon/notify.wayl.one
notify.wayl.one on main is 📦 v0.1.62 v3.14.4 NO PYTHON VENV SET USING SYSTEM NVIM
❯ git push
remote: Push to create is not enabled for users.
fatal: unable to access 'https://git.waylonwalker.com/waylon/notify.wayl.one/': The requested URL returned error: 403
So I added the following environment variables.
Author: Waylon S. Walker <[email protected]>
Date: Wed May 6 21:56:53 2026 -0500
enable push to create
diff --git a/k8s/forgejo/deployment.yaml b/k8s/forgejo/deployment.yaml
index d77daab..9346763 100644
--- a/k8s/forgejo/deployment.yaml
+++ b/k8s/forgejo/deployment.yaml
@@ -91,6 +91,10 @@ spec:
value: "0.0.0.0"
- name: FORGEJO__server__HTTP_PORT
value: "3000"
+ - name: FORGEJO__repository__ENABLE_PUSH_CREATE_USER
+ value: "true"
+ - name: FORGEJO__repository__ENABLE_PUSH_CREATE_ORG
+ value: "true"
- name: FORGEJO__database__DB_TYPE
value: postgres
- name: FORGEJO__database__HOST
https://github.com/WaylonWalker/homelab-argo/commit/b2e953bc12
Tried again, and it just worked!
notify.wayl.one on main is 📦 v0.1.62 v3.14.4 NO PYTHON VENV SET USING SYSTEM NVIM
❯ git push
Enumerating objects: 171, done.
Counting objects: 100% (171/171), done.
Delta compression using up to 12 threads
Compressing objects: 100% (169/169), done.
Writing objects: 100% (171/171), 176.22 KiB | 16.02 MiB/s, done.
Total 171 (delta 99), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (99/99), done.
To https://git.waylonwalker.com/waylon/notify.wayl.one
* [new branch] main -> main
nless is a seriously sick tui for exploring streaming data. It makes it seriously simple to pivot (U), drill in (Enter), sort (s). It leave breadcrumbs as you go and you can press q to back out.
Play with your kubernetes events. Ya, my homelab is far from perfect, dont judge.
kubectl get events -A -w | uvx --from nothing-less nless
almost left tokens on the table
markata-go now has web awesome integration for image compare. It renders a
nice web component with a slider to compare two images.


It’s done with a class wrapper around the image components.
::: wa-comparison


:::
Without markata-go’s web awesome integration, the above would look like:
<script type="module">
import 'https://ka-f.webawesome.com/[email protected]/components/comparison/comparison.js';
</script>
<wa-comparison>
<img
slot="before"
src="https://dropper.wayl.one/file/d628ffba-de18-4fff-91a8-700f037df119.webp"
alt="Grayscale version of kittens in a basket looking around."
/>
<img
slot="after"
src="https://dropper.waylonwalker.com/file/ca30665f-1a15-453e-aab8-221901c7df99.webp"
alt="Color version of kittens in a basket looking around."
/>
</wa-comparison>
Today I found a way to test model syntax, cause the clankers always get the exact model name that copilot wants wrong.
copilot --model claude-sonnet-4.5 -p "Reply with OK" --allow-all --no-ask-user -s
copilot --model gpt-5.4 -p "Reply with OK" --allow-all --no-ask-user -s
Can You Feel the Slop
approve rm
Ping 54
Tokens Just don't go as far as they used to
Stow comes with a local and global ignore list that you can use to ignore certain files or directories.
If you put Perl regular expressions, one per line, in a .stow-local-ignore file within any top level package directory, in which case any file or directory within that package matching any of these regular expressions will be ignored. In the absence of this package-specific ignore list, Stow will instead use the contents of ~/.stow-global-ignore, if it exists. If neither the package-local or global ignore list exist, Stow will use its own built-in default ignore list, which serves as a useful example of the format of these ignore list files:
Example given from the docs
RCS
.+,v
CVS
\.\#.+ # CVS conflict files / emacs lock files
\.cvsignore
\.svn
_darcs
\.hg
\.git
\.gitignore
\.gitmodules
.+~ # emacs backup files
\#.*\# # emacs autosave files
^/README.*
^/LICENSE.*
^/COPYING
Reference #
https://www.gnu.org/software/stow/manual/html_node/Types-And-Syntax-Of-Ignore-Lists.html
Ping 52
Ping 50 A
Prove Yourself Agent
What happens when the 0 days are exposed?
Ping 48
2026-04-06 Notes
https://youtu.be/cbgSkrQ3HNg?si=VJ2kqj8XD0P6ukI_
What is this job anymore
Ping 47
The year of the supply chain attacks
The final nail for Windows?
Ping 43
Social Media is dead
/carry/
Where Is The Tech Industry Going
Is Compaction The Issue
Lets Land The Plane
I’ve been having issue with my keyboard disconnecting to my main desktop for awhile. Today I got a cheap bluetooh dongle in and am giving it a run this week to see how things go. The first step was to move it to the new adapter. I’ve never had multiple adapters installed so this was a new to me process.
I was able to do it all with the same keyboard, It did require some juggling between usb and bluetooth modes pluging and unplugging, two keyboards would be simpler to reason about.
I can’t be bothered to change my brain to think about this machine on a different zmk profile it is of absolute importance for it to remain on the same profile, otherwise this would be a simple bind to another empty profile.
Why not use a cable on desktop?
I dont mind cable, and have used one on this setup for years, but I have actually been picking up and moving this keyboard and using it with different devices.
I’ve got a big battery and performace cranked up, unless my machine is under load I do not notice any key lag.
I did it with bluetoothctl, I’m sure it could have been done with a gui like
blueberry or blueman.
bluetoothctl
# list adapters
list
select <old-adapter>
devices
# fin the MAC address of the device 42BLOCK
remove <42BLOCK_MAC>
Now I plugged into usb. And importantly cleared out the zmk profile. If you do not clear the profile your board does not go into pairing mode.
bluetoothctl
# switch adapters
select <new-adapter>
power on
agent on
default-agent
scan on
Put 42Block in pairing mode, then:
pair <42BLOCK_MAC>
trust <42BLOCK_MAC>
At this pint I saw this show up in the logs, I think there was some masking issues or something in zmk, output kept going out usb no matter what so I disconnected the keyboard and typed the passkey in, and it worked.
[agent] Passkey: 540044
Boom, it just started working right away.
bluetoothctl
connect <42BLOCK_MAC>
info <42BLOCK_MAC>
scan off
exit
I don't want someone else running my agents
Ping 38
Learning to agent
Studio Ghibli Images in the Wild
Ping 36
Research, Plan, Implement
Today I learned that docker creates an empty /.dockerenv file to indicate that
you are running in a docker container. Other runtimes like podman commonly use
/run/.containerenv. kubernetes uses neither of these, the most common way to
detect if you are running in kubernetes is to check for the presence of the
KUBERNETES_SERVICE_HOST environment variable. There will also be a directory
at /var/run/secrets/kubernetes.io/serviceaccount that contains the service
account credentials if you are running in kubernetes.
Context Poisoning Was There All Along
Agents cannot replace the thinking, they only amplify it
Thinking about ai productivity again
Did you even like to code?
The only thing that seems interesting is AI right now
It's all moving so fast
I Built A Tmux Session Switcher
Is gpt-5.4 slow?
We are the Grey Beards
Clankers got me tired
In the age of agents sometimes work gets done on so many different worktrees
and branches its hard to tell if there is already a PR or any of them or not,
the great gh cli has us covered.
gh pr list --head fix/markata-go-connections-graph
The Ghostty Guy
/verify
I keep forgetting about the double gutter problem with nested containers. When you put padding on a parent and the child also has padding, you get twice the spacing you wanted.
The Problem #
.container {
padding: 2rem;
}
.child {
padding: 2rem;
}
Now your content is 4rem from the edge. Not what I meant at all.
The Fix #
Either remove padding from the parent or use box-sizing: border-box and plan
for it. I usually just drop the parent padding when I realize what I have done.
First W In Brotato
/top4
/yep
/nope
Pm Not Babysitter
Like a dufus this morning I did a hard reset on a git repo for getting I was working on a manifest for. You see I generally use argo, but occasionally I have no idea what I am doing or want yet and I start raw doggin it, fully aware that I’m going to just nuke this namespace before getting it into a proper argocd.
I was overjoyed when I found out that you can diff your manifests with live
production using the kubectl diff command. It uses standard diff so you can
bring all your fancy diff viewers you like.
# regular manifest
kubectl diff -f k8s/shots -n shot
# kustomize
kubectl diff -k k8s -n go-waylonwalker-com
# using a fancy diff viewer
kubectl diff -f k8s/shots -n shot | delta
# using an even fancier diff viewer
# pinkies out for this one
kubectl diff -f k8s/shots -n shot | delta --diff-so-fancy
Now I can get those changes back that I thought I lost, and apply updates with confidence knowing what is about to change.
Ping 23
Ping 21
Agent Management Is Exhausting
Stop Using Boomer Ai
Mentions
2026-01-23 Notes
Next for markata go
What Your Coding Tool Says About You
Quick Tip To Get Agents Running Longer
I really wish I would have got this right a few years ago. Theres a couple of flags I had to use to get mdformat to do hard wraps at 80 characters and not wreck tables. This mix of flags and plugins is workign really well for me so far.
mdfmt() {
uvx \
--with "mdformat-ruff" \
--with "mdformat-beautysh" \
--with "mdformat-web" \
--with "mdformat-config" \
--with "mdformat-gfm" \
--with "mdformat-front-matters" \
--with "mdformat-wikilink" \
--with "mdformat-simple-breaks" \
mdformat \
--wrap 80 \
--end-of-line lf \
--codeformatters python \
--codeformatters bash \
"$@"
}
And as pre-commmit.
repos
- repo: https://github.com/hukkin/mdformat
rev: 1.0.0 # pin to the version you want
hooks:
- id: mdformat
args:
- --wrap
- "80"
- --end-of-line
- lf
- --codeformatters
- python
- --codeformatters
- bash
additional_dependencies:
- mdformat-ruff
- mdformat-beautysh
- mdformat-web
- mdformat-config
- mdformat-gfm
- mdformat-front-matters
- mdformat-wikilink
- mdformat-simple-breaks
Ping 19
What is that
Dont Trust Users Tokens
Opencode is changing on the daily right now, today I noticed the word low pop
up in Orange text in my opencode window. Looking into this they are exposing
variants to the user. This allows
you to change between fast or slow and thinking, the later taking more time to
prepare before doing an action.
It looks like this toggle may have been here for awhile and I’m just now discovering it. Potentially because its a new feature of the free Zen provider.
Variants Many models support multiple variants with different configurations. OpenCode ships with built-in default variants for popular providers.
Built-in variants OpenCode ships with default variants for many providers:
Anthropic:
high - High thinking budget (default) max - Maximum thinking budget OpenAI:
Varies by model but roughly:
none - No reasoning minimal - Minimal reasoning effort low - Low reasoning effort medium - Medium reasoning effort high - High reasoning effort xhigh - Extra high reasoning effort Google:
low - Lower effort/token budget high - Higher effort/token budget
Ping 17
Ai
This is opencode’s init prompt.
Please analyze this codebase and create an AGENTS.md file containing:
1. Build/lint/test commands - especially for running a single test
2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 150 lines long.
If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them.
If there's already an AGENTS.md, improve it if it's located in <dir>
Og-Sample
2026 Resolutions
Today I discovered vim-speeddating
by tpope. I’m sure I’ve seen years ago but it did not click for my workflow
until today. I often go through pictures from my phone for the past few days
and make Posts tagged: shots posts, but I want to date them to about when the image was
taken most of the time. This allows me to quickly bump days up and down using
c-a and c-x even around the new year.
Sound on, listen to those new switches.
new keeb so good
Yesterday I wrote about a way to do light mode screen recording to convert to light mode from dark mode with ffmpeg. I was wondering if it could be done entirely on the front end for web applications. Turns out you can. I’m sure there are limited wikis and site builders that don’t allow adding style like this, but it works if you can.
<video
src="https://dropper.waylonwalker.com/file/1c53dbcb-4b84-4e94-9f04-a42986ab3fa1.mp4?width=800"
controls
style="filter: invert(1) hue-rotate(180deg) contrast(1.2) saturate(1.1);"
>
<!--markata-attribution-->
</video>
0 deg hue rotate
90 deg hue rotate
180 deg hue rotate
270 deg hue rotate
I saw this tip from Cassidoo and had to try it out for myself. I kicked on a screen recording right from where my terminal was, converted it, and it actually looks pretty good.
ffmpeg \
-i screenrecording-2026-01-01_10-10-49.mp4 \
-vf "negate,hue=h=180,eq=contrast=1.2:saturation=1.1" \
screenrecording-2026-01-01_10-10-49-light.mp4
Dark Mode
Light Mode
There are a few unsettling things about it, but overall I feel like it was a success.
I’ve found Gemini to be very useful lately, especially for finding information within long form content.
When writing thought-896, I wanted to use a direct quote from Jeff Dickey, Gemini popped it out very quickly.
give me a quote from jeff just before the timestamp I'm at the interviewer
asked what makes a good cli and he started talking about stdout/stderr
In another case, my wife and I are huge Good Eats fans. Alton Brown taught us how to cook during college and on. We watched every single good eats episode nearly 10 years after they aired. He is back with some updates to those those shows on his Youtube. Gemini gives very good detailed responses with timestamps.
Alton Brown had a recent YouTube video for cooking turkey. Can you get the
instructions from the video?
Ping 15
Mise looks promising
Gross phone
Developer Vs Artist Ai
Ping 12
Ping 11
I'm being gaslit by the ai
I’ve been using this one for awhile now, I have a post type that I only edit from my phone, but I have all the post numbered. I set up a template in obsidian for using templater, the template goes right in the static site repo, I point templater to the templates directory and this has been working pretty seamlessly for awhile.
---
date: <% tp.date.now("YYYY-MM-DD HH:mm:ss") %>
templateKey: myposttype
published: true
tags:
- myposttype
<%*
const folder = "pages/myposttype";
// get all files in the vault, keep only those inside the folder
const files = app.vault.getFiles().filter(f => f.path.startsWith(folder + "/"));
// extract numeric suffixes from filenames like myposttype-123.md
const nums = files.map(f => {
const m = f.basename.match(/^myposttype-(\d+)$/);
return m ? parseInt(m[1], 10) : null;
}).filter(n => n !== null);
// next number (start at 1 if none exist)
const next = (nums.length ? Math.max(...nums) : 0) + 1;
// include the .md extension when moving
const newPath = `${folder}/myposttype-${next}`;
await tp.file.move(newPath);
%>
---
notifications for static site builds
--name-status is a great way to see what files have changed in a git diff
alongside the status code. I recently used this in a script to create a report
of new and modified files during a build.
git diff --name-status
git diff --name-status origin/main
git diff --name-status --staged
git diff --name-status 'HEAD@{3 days ago}'
fast changing dev server today
The Right Reasons To Run Kubernetes In Your Homelab
I learned to today that setting MEMORY on your minecraft server causes the
JVM to egregiously allocate all of that memory. Not setting it causes slow
downs and potential crashes, but setting INIT_MEMORY and MAX_MEMORY gives
us the best of both worlds. It is allowed to use more, but does not gobble it
all up on startup.
In this economy we need to save all the memory we can!
Here is a non-working snippet for a minecraft server deployment in kubernetes.
containers:
- name: dungeon
image: itzg/minecraft-server
env:
- name: EULA
value: "true"
- name: INIT_MEMORY
value: "512M"
- name: MAX_MEMORY
value: "3G"
and in docker compose
dungeon:
image: itzg/minecraft-server
environment:
EULA: "true"
INIT_MEMORY: "512M"
MAX_MEMORY: "3G"
One Year Of Shots
I found snow-fall component from zachleat, and its beautiful… to me. I like the way it looks, its simple and whimsical.
Install #
There is an npm package <a href="https://zachleat.com" class="mention" data-name="Zach Leatherman" data-bio="A post by Zach Leatherman (zachleat)" data-avatar="https://www.zachleat.com/og/opengraph-default.png" data-handle="@zachleat">@zachleat</a>/snow-fall if that’s your thing. I like
vendoring in small things like this.
curl -o static/snow-fall.js https://raw.githubusercontent.com/zachleat/snow-fall/refs/heads/main/snow-fall.js
I generally save it in my justfile so that I remember how I got it and how to update…. yaya I could use npm, but I don’t for no build sites.
get-snowfall:
curl -o static/snow-fall.js https://raw.githubusercontent.com/zachleat/snow-fall/refs/heads/main/snow-fall.js
Usage #
Now add the component to your page.
<!-- This belongs somewhere inside <head> -->
<script type="module" src="snow-fall.js"></script> <!-- Adjust the src to your path -->
<!-- This belongs somewhere inside <body> -->
<!-- Anything before will be below the snow. -->
<snow-fall></snow-fall>
<!-- Anything after will show above the snow. -->
Today I learned an important lesson that you should periodically check on your kubeconfigs expiration date. It’s easy to do. You can ask for the client-certificate-data from your kubeconfig, decode it, and use openssl to get the expiration date.
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}' \
| base64 -d 2>/dev/null \
| openssl x509 -noout -dates
Note
This will only work for the first user, if you have more than one user or context defined in your kubeconfig you will need to adjust.
Not every print needs supports
reminder Include steps to reproduce
All I want for Christmas is, filliment
When using two GitHub accounts the gh cli gives very easy gh auth switch workflow from the cli.
from the docs
gh auth switch –help Switch the active account for a GitHub host.
This command changes the authentication configuration that will be used when running commands targeting the specified GitHub host.
If the specified host has two accounts, the active account will be switched
automatically. If there are more than two accounts, disambiguation will be
required either through the --user flag or an interactive prompt.
# list accounts
gh auth status
# switch accounds (interactive if more than 2, i've never seen this personally)
gh auth switch
gpus are awesome and I need one for Bambu Studio to be usable in a
distrobox. Adding the --nvidia flag to distrobox create bind mounts the
nvidia /dev/ devices and sets up the necessary environment variables. Once
we are in there are a couple of packages to install to make it work.
distrobox create --name bambu-studio --image archlinux:latest --nvidia
distrobox enter bambu-studio
sudo pacman -S nvidia-utils lib32-nvidia-utils vulkan-icd-loader
nvidia-smi
glxinfo | gprep OpenGL
sudo pacman -Syu --needed base-devel git
git clone https://aur.archlinux.org/paru-bin.git
cd paru-bin
makepkg -si
paru -S bambustudio-bin
bambu-studio
distrobox-export --app bambu-studio
git worktrees are needed
The Wrong Reasons To Run Kubernetes In Your Homelab
my home row
I got the kubernetes in my basement autism
The k3s system-upgrade controller is a fantastic tool for upgrading k3s automatically. It has done a fantastic job for me every time I’ve used it. Today I ran it on a cluster that needed to upgrade several minors and I learned that the controller does not pick up on changes to the channel url if you change from minor to minor.
The solution I came up with was to name the plan with the version it supports. Then on each patch upgrade, change both the plan name and the channel. I use gitops with argocd, it automcatically cleaned up old plans, created new plans, and the system-upgrade-controller picked up the plan and started applying immediately.
# Server plan
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: server-plan-v1.33 # <- This is important if you want to change the channel name
namespace: system-upgrade
spec:
concurrency: 1
cordon: true
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: In
values:
- "true"
serviceAccountName: system-upgrade
upgrade:
image: rancher/k3s-upgrade
channel: https://update.k3s.io/v1-release/channels/v1.33
---
# Agent plan
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: agent-plan-v1.33 # <- This is important if you want to change the channel name
namespace: system-upgrade
spec:
concurrency: 1
cordon: true
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
prepare:
args:
- prepare
- server-plan
image: rancher/k3s-upgrade
serviceAccountName: system-upgrade
upgrade:
image: rancher/k3s-upgrade
channel: https://update.k3s.io/v1-release/channels/v1.33
I’d love to see a better way if you have a way to upgrade through minors, or manually control the minor of your cluster let me know.
gpus are awesome
setting COLUMNS env var to a number greater than 0 will make the terminal resize to that number of columns.
COLUMNS=80 uvx --from rich-cli rich myscript.py
Note
Not all programs respct the COLUMNS env var, but rich does, and a lot of
stuff I’m building uses rich.
I discovered this when I was trying to make a low effort readme generated from the code, but did not depend on the size of terminal it was ran on.
# justfile
readme:
echo "# Workspaces" > README.md
echo "" >> README.md
echo '``` bash' >> README.md
COLUMNS=80 ./workspaces.py --help >> README.md
echo '```' >> README.md
The tea command for gitea (used by forgejo) has a flag for login. With gitea
you can have multiple accounts logged in. When you try to run a command such
as repo create it will prompt you which login to use, but I learned that you
can bake it in to all of them with --login <login-name>
❯ tea repo create --name deleteme --description 'for example'
┃ NOTE: no gitea login detected, whether falling back to login 'git.waylonwalker.com'?
tea repo create --name deleteme --description 'for example' --login git.wayl.one
2025-11-21 Notes
Learned about nginx_auth today. Feels good to unlock a new skill that I did not quite understand before. I don't think I grasped that there is a backend...
2025-11-19 Notes
Making progress on dropper this week.
Another Big Cloud Outage Nov 2025
Today I learned how to use AliasChoices with pydantic settings to setup common aliases for the same field. I’m bad about remembering these things, and hate looking up the docs. I like things to be intuitive and just do the thing I want it to do. Especially when they get configured through something like yaml and do not have a direct lsp look up right from my editor. I figured out how to support what might be common aliases for a storage directory. These can be set up as environment variables and used by config.
from pathlib import Path
from pydantic import Field
from pydantic import AliasChoices
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
storage_dir: Path | None = Field(
default=None,
validation_alias=AliasChoices(
"STORAGE_DIR", "STORAGE_DIRECTORY", "STORAGE_PATH", "STORAGE_PATHNAME",
"DROPPER_STORAGE_DIR", "DROPPER_STORAGE_DIRECTORY", "DROPPER_STORAGE_PATH", "DROPPER_STORAGE_PATHNAME",
),
description="Directory for stored files",
)
3d-Printed Corner Clamp
Techbrophobic
Mcat Anything
Missing Thoughts
2025-11-04 Notes
Today I gave mcat a try and it's so sick. It can anything right in the terminal, pdf, image, even video. It even works inside tmux unlike almost anything...
Rules
I often want to run an s3 sync in an isolated environment, I don’t want to set
any environment variables, I don’t want anything secret in my history, and I
don’t want to change my dotenv into something that exports variables, I just
want s3 sync to work. dotenv run is the tool that I’ve been using for this,
and this uv one liner lets it run fully isolated from the project.
one liner #
uv tool run --from 'python-dotenv[cli]' dotenv run -- uv tool run --from awscli aws s3 sync s3://bucket data
multi-line #
same thing formatted for readability
uv tool run \
--from 'python-dotenv[cli]' \
dotenv run -- \
uv tool run \
--from awscli \
aws s3 sync s3://dropper data
There are probably 10 ways to skin this cat, but this is what I did, if you have a better way let me know, I’ll link you below.
First 3d Printed Threads
FastAPI.">Starlette has a head request that works right along side your get requests.
This morning I fiddled around with custom routes for GET and HEAD, but had
to manually set some things about the file, and was still missing e-tag in
the end. Turns out as a developer you can just add a head route to
your get routes and starlette will strip the content for you, while
preserving all of those good headers that fastapi FileResponse created
automatically for you.
from fastapi import APIRouter
from fastapi.response import FileResponse
from fastapi import Request
from pathlib import Path
router = APIRouter()
@router.get("/file/{filename}")
@router.head("/file/{filename}")
async def get_file(filename: str, request: Request,):
headers = {
"Cache-Control": "no-cache, no-store, must-revalidate",
}
from pathlib import Path
filename = Path(f"data/{filename}")
if not filename.exists():
raise HTTPException(status_code=404, detail="File not found")
return FileResponse(filename, headers=headers)
Here is an example of the response with curl.
❯ curl -I -L "http://localhost:8100/api/file/e5523925-1565-454c-bab3-c70c4deabc83.webp?width=250"
HTTP/1.1 200 OK
date: Wed, 22 Oct 2025 14:16:03 GMT
server: uvicorn
cache-control: no-cache, no-store, must-revalidate
content-type: image/webp
content-length: 17206
last-modified: Tue, 23 Sep 2025 14:03:20 GMT
etag: f891660c1543feb1af7564f08abdd511
❯ curl -I -L "http://localhost:8100/api/file/unknown-file.webp?width=250"
HTTP/1.1 404 Not Found
date: Wed, 22 Oct 2025 14:16:11 GMT
server: uvicorn
content-length: 27
content-type: application/json
Today I learned that while .stignore and .gitignore look very similar they
are not. My obsidian directory had been locked up for a few weeks and I had no
idea why until I logged into the web ui and saw errors. The errors were some
confusing regex validator not matching. I don’t know what the exact error was,
but I went in and only ignored the files I cared about instead of the entire
gitignore. Primarily I was getting conflicts in my .git directory.
3d Printing Dovetails Experiment
python extras are for shipping
Kraft-Coordinates
Shots Get An Upgrade To the Main Feed
zmk two hand hold down
Why Make a Website in 2025
I needed to display some hover text in a web app that I am using tailwind and jinja on. It has no js, and no build other than the tailwind. I want this to remain simple. Turns out that you can use a span with a title attribute to get hover text in HTML.
<p>
I needed to display some hover text in a web app that I am using tailwind and
jinja on. It has no js, and no build other than the tailwind. I want this to
remain <span style='cursor: help; color:yellow;' title='respective to the
python developer I am and the team it is used for'>simple</span>.
</p>
Today I learned how to use tar over ssh to save hours in file transfers. I keep all of my projects in ~/git (very creative I know, I’ve done it for years and haven’t changed). I just swapped out my main desktop from bazzite to hyprland, and wanted to get all of my projects back. Before killing my bazzite install I moved everything over (16GB of many small files), it took over 14 hours, maybe longer. I had started in the morning and just let it churn.
This was not going to happen for re-seeding all of my projects on my new system, I knew there had to be a better way, I looked at rsync, but for seeding I ran into this tar over ssh technique and it only took me 6m51s to pull all of my projects off of my remote server.
ssh [email protected] 'tar -C /tank/git -cpf - .' \
| tar -C "$HOME/git" -xpf -
store
I’ve been leaning on lazy-self-installing-python-scripts more and more, but I did not realize how much tooling that uv gives you to help manage your scripts.
uv init --script up
uv add --script up typer rich
uv remove --script up rich
sed -i '1i #!/usr/bin/env -S uv run --script' up
chmod +x up
./up
The result is a script that looks like this, its executable as what looks like regular command in your shell.
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "typer",
# ]
# ///
def main() -> None:
print("Hello from up!")
if __name__ == "__main__":
main()
When To Vibe Code
This one is one that I’ve been using quite often, I did’t have a hotkey for it,
I just used the rm shell command.
!!rm %<TAB><CR>
When you type !! from normal mode it will automatically put you in command
mode with .! pre-filled, then you just type rm and <TAB> to
auto-complete the current file name, and <CR> to execute the command.
:.!rm %<TAB><CR>
Making it better #
The one quirk that I don’t like about this is that the buffer remains open
after deleting, and sometimes I forget to close it and end up re-creating it by
mistake when running :wall or :xall.
Create a DeleteFile command with vim command.
:command! DeleteFile execute "!rm %" | bdelete!
Create a DeleteFile command with lua.
vim.api.nvim_create_user_command(
'DeleteFile',
function()
-- Delete the current file from disk
vim.cmd('!rm %')
-- Close the buffer without saving
vim.cmd('bdelete!')
end,
{}
)
Keycap Cut Away
Knife Sharpener Small Upgrade
Vim :noa is a command that runs what you call without autocommands on. This
is typically used when you have some BufWritePre commands for formatting,
most auto formatters are implemented this way in vim. It can be super useful
if you have something like a yaml/json file that you have crafted perfectly how
you want it, maybe it has some source code for a small script or sql embeded
and your formatter wants to turn it into one line. You could get a better
formatter, but for these one off cases that aren’t a big bother to me I run
:noa w.
:noa w
I'm Out On Agents
Today I gave modd a try, and it seems like a good file watcher executor. I tried using libnotify to send desktop notifications, but all I got was modd, I might not have notifications setup right on the awesomewm machine.
config goes in modd.conf
**/*.py {
# check formatting via ruff
prep: ruff format --check .
# check docstring formatting
prep: pydocstyle .
#
# # check type hints via ty
prep: ty check .
#
# # run linter via ruff
prep: ruff check .
}
I installed it using installer from jpillora, pulling pre-built binaries right out of the github repo.
curl https://i.jpillora.com/cortesi/modd | bash
Then you can install it, and on file change it will run the commands you configured.
modd
2025-08-25 Notes
Gave modd a try and it seems pretty good, will likely be slotting it in next to my justfile usage.
2025-08-23 Notes
Today, some great work on the knife sharpener re-design. I've been using the same one since I first got my ender 3 3d printer, and have wanted to make some...
Today I needed to make a backup of some config. I wanted to add a timestamp so that I knew when the backup was made. This would make unique backups easy, and I could tell when they were made.
cp configfile configfile.backup.$(date %s)
If you want to decrypt the timestamp into something more human readable. You can list backup files, strip out the timestamp, and then convert it to a human readable date.
/bin/ls | grep backup | sed 's/configfile.backup.//' | xargs -I {} date -d @{}
or just throw it to the date command by hand.
date -d @1755895402
2025-08-21 Notes
https://youtu.be/-EYRzF0zp3U?si=mKCPlMDecrqzvjuF
2025-08-17 Notes
After a long time I begrudgingly setup window rules for hyprland. What I wanted was the ability to log out and jump back into work with a freshened work...
2025-08-16 Notes
[x] remove nextcloud - [x] pause photoprism - [x] pause syncthing - [x] move data to walkershare - [x] filepermissions 1000:1000 all of it - [x] organize how...
2025-08-14 Notes
Huge progress on shots not to be confused with shots inspired by . I'm building out my own instagram grid design, right now its 4 wide, but I wonder if it...
2025-08-13 Notes
Inspired by Justin Searls Shots, I made started my own shots feed for self hosted Instagram style photos. The layout could use a lot of work, the feed seems...
2025-08-12 Notes
Sad day yesterday. We discovered that our freezer was left cracked over a day or so. Kids self serviced themselves to some sausage dogs at some point and...
trying forgejo
2025-08-08 Notes
I found this post from miriam.codes while reading dbushell's notes. I kinda agree with Miriam and David here. AI is really making me feel like an old...
2025-08-05 Notes
Yesterday I started building out some qrcode tooling for myself starting with qrcode.waylonwalker.com. This is part of my tinyapps project.
I have a couple of use cases for simple qr codes in python coming up. One is
for blog posts, the other is for auth into a new server application logged to a
terminal. I tried the qrcode library
and it does not look as nice to me and I found
pyqrcode to be quite nice.
import pyqrcode
url = pyqrcode.create('https://waylonwalker.com/qr-codes-in-python')
url.svg('qr-codes-in-python.svg', scale=8)
print(url.terminal(quiet_zone=1))
url.svg('qr-codes-in-python.svg', scale=12)
url.svg('qr-codes-in-python.svg', omithw=True) # width is controlled by the container
url.svg('qr-codes-in-python.svg', omithw=True, module_color='#ffd119')
url.svg('qr-codes-in-python.svg', omithw=True, module_color='#ff69b4', background='#2b034c')
result #
Here is the final svg result.
Here is what it looks like in the terminal.
If you want fancier qrcodes check out https://mydigitalharbor.com/
I’ve got a few samba shares going in my homelab, and I’m struggling finding a great app to scroll through vacation photos with my wife. I want something intuitive, non intimidating, and just works. Turns out that the default file browser application for hyprland works great, but you need to enable previews for remote storage for it to work for my use case here.
2025-07-27 Notes
Today Wyatt and I conquered cathulu in Cat quest III.
blender donut tutorial 2025
2025-07-24 Notes
Completed the 8-bit dungeon in Cat Quest III with wyatt today.
2025-07-23 Notes
Today I set up hypridle on my hyprland desktop build. It was not too bad, I'm probably going to tweak it as it I go if it bothers me, for now I'm giving it a...
2025-07-22 Notes
Next I ripped a calendar from the docs. When hovered I get a full year calendar, right click gives me only the current month.
2025-07-21 Notes
In my adventure to rice out hyprland I set up a sddm theme today, and it looks sick by only changing the wallpaper. I tried out one theme and was pretty...
2025-07-19 Notes
Progress on markata themes, I'm able to get feedback in the terminal. I'm building out a custom color model that will be able to mix and blend colors with...
2025-07-18 Notes
fc is pretty great.
I am a linux user through and through. Desktop, server, vms, containers,
everything except my phone is linux. With this I spend a lot of time in the
terminal, and have been a long time user of !! to rerun the last command, but
with the ability to tack something on at the beginning or end.
TIL about fc, which opens the last command in your shell history in your
$EDITOR or pass in your editor -e nvim.
Rcap of how !! works #
!! pronounces bang bang and will run the last command in your history.
ls -l
!! | wc -l
# ls -l | wc -l
sudo !!
# sudo ls -l | wc -l
!!:s/-l/-l \/tmp
# sudo ls -l /tmp | wc -l
fc enters the chat #
Now making complex edits in your shell can be a bit of a chore, so fc moves
this work to your $EDITOR.
fc
This pops open your $EDITOR with the last command in your history.
sudo ls -l | wc -l
Shell History #
fc shows up in shell history, but !! does not, !! gets replaced by the
command that it becomes.
Up Arrow #
yaya yaya, I know you can also up-arrow c-e, but what fun is that, it’s barely a
flex. fc just looks big brained and like you really know what you are doing.
2025-07-17 Notes
TIL pygments-htmlformatter is a big win for markata, didn't have a chance to take notes yesterday, but I'm really working on templates and themes. It's...
I’ve been a long user of pygments, it’s been the thing that injects <spans>
with funny little class names like sc and si into the code blocks of my
website. I’ve even gone as far as implementing a plugin for
md-it,
but I had no idea how to re-style it. I long ago got a theme that looked good
enough from somewhere and just used it, maybe I pulled something from their
docs site and forgot. Today I learned you can list all the themes easily from
the library itself, and render out new css.
from pygments.styles import get_all_styles
list(get_all_styles())
# [
# 'abap',
# 'algol_nu',
# 'algol',
# 'arduino',
# 'autumn',
# 'borland',
# 'bw',
# 'colorful',
# 'default',
# 'dracula',
# 'emacs',
# 'friendly_grayscale',
# 'friendly',
# 'fruity',
# 'github-dark',
# 'gruvbox-dark',
# 'gruvbox-light',
# 'igor',
# 'inkpot',
# 'lightbulb',
# 'lilypond',
# 'lovelace',
# 'manni',
# 'material',
# 'monokai',
# 'murphy',
# 'native',
# 'nord-darker',
# 'nord',
# 'one-dark',
# 'paraiso-dark',
# 'paraiso-light',
# 'pastie',
# 'perldoc',
# 'rainbow_dash',
# 'rrt',
# 'sas',
# 'solarized-dark',
# 'solarized-light',
# 'staroffice',
# 'stata-dark',
# 'stata-light',
# 'stata',
# 'tango',
# 'trac',
# 'vim',
# 'vs',
# 'xcode',
# 'zenburn'
# ]
from pygments.formatters import HtmlFormatter
from pygments.styles import get_style_by_name
style = get_style_by_name("monokai")
formatter = HtmlFormatter(style=style)
print(formatter.get_style_defs('.highlight'))
And now you get styles that you can add to your css and be any theme from the list above.
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #49483e }
.highlight { background: #272822; color: #f8f8f2 }
.highlight .c { color: #959077 } /* Comment */
.highlight .err { color: #ed007e; background-color: #1e0010 } /* Error */
.highlight .esc { color: #f8f8f2 } /* Escape */
.highlight .g { color: #f8f8f2 } /* Generic */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #ff4689 } /* Operator */
.highlight .x { color: #f8f8f2 } /* Other */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #959077 } /* Comment.Hashbang */
.highlight .cm { color: #959077 } /* Comment.Multiline */
.highlight .cp { color: #959077 } /* Comment.Preproc */
.highlight .cpf { color: #959077 } /* Comment.PreprocFile */
.highlight .c1 { color: #959077 } /* Comment.Single */
.highlight .cs { color: #959077 } /* Comment.Special */
.highlight .gd { color: #ff4689 } /* Generic.Deleted */
.highlight .ge { color: #f8f8f2; font-style: italic } /* Generic.Emph */
.highlight .ges { color: #f8f8f2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #f8f8f2 } /* Generic.Error */
.highlight .gh { color: #f8f8f2 } /* Generic.Heading */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .go { color: #66d9ef } /* Generic.Output */
.highlight .gp { color: #ff4689; font-weight: bold } /* Generic.Prompt */
.highlight .gs { color: #f8f8f2; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #959077 } /* Generic.Subheading */
.highlight .gt { color: #f8f8f2 } /* Generic.Traceback */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #ff4689 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #ff4689 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #ff4689 } /* Operator.Word */
.highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
2025-07-15 Notes
I'm working on hyprland, one thing I am missing from my awesomewm setup is rofi list all running windows. Sometimes I put a browser instance in a different...
2025-07-12 Notes
Starting the polkit agent for hyperland arch to handle permission elevation for desktop applications.
2025-07-11 Notes
Continuing my exploration of Searchcraft I loaded all of my blog into it. It fully posted, indexed, and had my content ready for search in a literal blink....
2025-07-10 Notes
[](https://www.youtube.com/watch?v=VFlLRH3ifcM)
2025-07-09 Notes
What a nightmare this glossary plugin turns out to be, it broke my site. I had it really close, then one more change asked for to the llm, an LGTM and did...
2025-07-08 Notes
Setting up the hyprland run launcher to not be case sensitive.
2025-07-07 Notes
leaning into the rediculous rainbow borders instead of getting rid of them in hyprland.
Smooth clipboard settings for tmux is critical for my workflow. I’m often
grabbing snippets of terminal output to paste into team chats, blog posts, or
llm prompts. Admittedly, I’m often doing this with the mouse, unless it’s
coming from neovim, which I generally do with motions. Moving from an xorg
based setup to hyprland has required me to reconfigure my tmux clipboard
settings. This is what I did.
First install wl-clipboard with AUR.">paru.
paru -S wl-clipboard
Next add this to your tmux config. I’ve long had this config, but with only
the xorg/xclip setup, now this checks for wl-copy, uses it, or falls back to
my old xclip setup.
bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "bash -c 'command -v wl-copy >/dev/null && wl-copy || xclip -i -f -selection primary | xclip -i -selection clipboard'"
set-option -s set-clipboard off
bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "bash -c 'command -v wl-copy >/dev/null && wl-copy || xclip -i -f -selection primary | xclip -i -selection clipboard'"
command palettes are overrated
2025-07-06 Notes
hyprland volume control, I wasn't sure if I needed something specific for wayland/pipewire, nope pavucontrol just works.
2025-07-05 Notes
I might have gpg setup right for kdewallet on hyprland, and I just timed out the request before.
2025-07-04 Notes
Failed to gpg-setup-for-kdewallet correctly on hyprland, brave still complains. Maybe someday I'll figure it out and complete the post.
just fucking use kubernetes
markata parallel render
principal-engineer-at-meta
I got virtual machine manager running on two Bazzite machines today. It was a
bit tricky, more than I thought actually. I ran into all sorts of
virtualisation not setup issues when I tried the flatpak. Then I found that
Bazzite comes with a ujust setup-virtualization command that does all the
work for me. I tried that and again virtual machine manager was here, but not
working, this time it feels like flatpak issues.
In a Hail Mary attempt I got it working by using an ubuntu distrobox container to run the UI. And it worked!
from the host #
From the host we create the container to use from distrobox. This is an ubuntu machine, it can be any os of your choosing, preferably one that you are familiar with and contains virt-manager in its package repos.
distrobox create -i ubuntu
distrobox enter ubuntu
from inside the distrobox container #
Now that we are in the distrobox we are no longer in an immutable distro and we can easily install anything we want. I actually like this process. I might have shit like this that I use for a month or a few months, on a normal distro, this is fully installed on the os, raises the potential of package conflicts and lengthens the update time.
sudo apt update
sudo apt upgrade
sudo apt install virtinst virt-manager
virt-manager
Along the way #
I tried adding myself to the libvirt group, and expicitly setting the socket path. After setting up another machine I realized these steps were unnecessary.
sudo usermod -aG libvirt $USER
export LIBVIRT_DEFAULT_URI="qemu+unix:///system"
ls -l /run/libvirt/libvirt-sock
virt-install --version
Note
After fully reading through `ujust setup-virtualization` I realized that it
adds me to the libvirt group, so that’s why it is not needed.
An even Better Guide #
I later found [[ thoughts-723 ]] there is documentation for setting up virtual machine manager in distrobox in the distrobox docs. It even calls out running on an immutable distro like bazzite like it knew I was coming.
full log #
waylon@razorcrest:~$ distrobox create -i ubuntu
Image ubuntu not found.
Do you want to pull the image now? [Y/n]: y
Resolved "ubuntu" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/ubuntu:latest...
Getting image source signatures
Copying blob d9d352c11bbd done |
Copying config bf16bdcff9 done |
Writing manifest to image destination
bf16bdcff9c96b76a6d417bd8f0a3abe0e55c0ed9bdb3549e906834e2592fd5f
Creating 'ubuntu' using image ubuntu [ OK ]
Distrobox 'ubuntu' successfully created.
To enter, run:
distrobox enter ubuntu
waylon@razorcrest:~$ distrobox enter ubuntu
Starting container... [ OK ]
Installing basic packages... [ OK ]
Setting up devpts mounts... [ OK ]
Setting up read-only mounts... [ OK ]
Setting up read-write mounts... [ OK ]
Setting up host's sockets integration... [ OK ]
Integrating host's themes, icons, fonts... [ OK ]
Setting up distrobox profile... [ OK ]
Setting up sudo... [ OK ]
Setting up user groups... [ OK ]
Setting up user's group list... [ OK ]
Setting up existing user... [ OK ]
Ensuring user's access... [ OK ]
Container Setup Complete!
📦[waylon@ubuntu ~]$ sudo apt update
sudo apt install virtinst
Hit:1 http://security.ubuntu.com/ubuntu noble-security InRelease
Hit:2 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:3 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:4 http://archive.ubuntu.com/ubuntu noble-backports InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
4 packages can be upgraded. Run 'apt list --upgradable' to see them.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
gir1.2-freedesktop gir1.2-libosinfo-1.0 glib-networking glib-networking-common glib-networking-services
gstreamer1.0-plugins-base i965-va-driver intel-media-va-driver iso-codes libaio1t64 libasyncns0
libboost-iostreams1.83.0 libboost-thread1.83.0 libburn4t64 libcacard0 libcdparanoia0 libcurl3t64-gnutls
libdaxctl1 libduktape207 libdw1t64 libflac12t64 libfuse3-3 libgstreamer-plugins-base1.0-0 libgstreamer1.0-0
libgtk-vnc-2.0-0 libgvnc-1.0-0 libigdgmm12 libiscsi7 libisoburn1t64 libisofs6t64 libjson-glib-1.0-0
libjson-glib-1.0-common libmp3lame0 libmpg123-0t64 libndctl6 libnfs14 libnspr4 libnss3 libnuma1 libogg0
libopus0 liborc-0.4-0t64 libosinfo-1.0-0 libosinfo-l10n libpcsclite1 libphodav-3.0-0 libphodav-3.0-common
libpmem1 libpmemobj1 libpolkit-gobject-1-0 libproxy1v5 libpulse0 librados2 librbd1 librdmacm1t64 libsndfile1
libsoup-3.0-0 libsoup-3.0-common libspice-client-glib-2.0-8 libspice-client-gtk-3.0-5 libtheora0 libunwind8
liburing2 libusb-1.0-0 libusbredirhost1t64 libusbredirparser1t64 libva-x11-2 libva2 libvirt-clients
libvirt-glib-1.0-0 libvirt-glib-1.0-data libvirt-l10n libvirt0 libvisual-0.4-0 libvorbis0a libvorbisenc2
libvte-2.91-0 libxslt1.1 libyajl2 mesa-va-drivers osinfo-db pci.ids python3-certifi python3-chardet
python3-idna python3-libvirt python3-libxml2 python3-pkg-resources python3-requests python3-urllib3
qemu-block-extra qemu-utils spice-client-glib-usb-acl-helper usb.ids va-driver-all virt-viewer xorriso
Suggested packages:
gvfs i965-va-driver-shaders isoquery fuse3 libvisual-0.4-plugins gstreamer1.0-tools opus-tools pcscd pulseaudio
gstreamer1.0-libav gstreamer1.0-plugins-bad gstreamer1.0-plugins-good libvirt-clients-qemu libvirt-daemon
libvirt-login-shell python3-setuptools python3-cryptography python3-openssl python3-socks python-requests-doc
python3-brotli netcat python3-argcomplete xorriso-tcltk jigit cdck
The following NEW packages will be installed:
gir1.2-freedesktop gir1.2-libosinfo-1.0 glib-networking glib-networking-common glib-networking-services
gstreamer1.0-plugins-base i965-va-driver intel-media-va-driver iso-codes libaio1t64 libasyncns0
libboost-iostreams1.83.0 libboost-thread1.83.0 libburn4t64 libcacard0 libcdparanoia0 libcurl3t64-gnutls
libdaxctl1 libduktape207 libdw1t64 libflac12t64 libfuse3-3 libgstreamer-plugins-base1.0-0 libgstreamer1.0-0
libgtk-vnc-2.0-0 libgvnc-1.0-0 libigdgmm12 libiscsi7 libisoburn1t64 libisofs6t64 libjson-glib-1.0-0
libjson-glib-1.0-common libmp3lame0 libmpg123-0t64 libndctl6 libnfs14 libnspr4 libnss3 libnuma1 libogg0
libopus0 liborc-0.4-0t64 libosinfo-1.0-0 libosinfo-l10n libpcsclite1 libphodav-3.0-0 libphodav-3.0-common
libpmem1 libpmemobj1 libpolkit-gobject-1-0 libproxy1v5 libpulse0 librados2 librbd1 librdmacm1t64 libsndfile1
libsoup-3.0-0 libsoup-3.0-common libspice-client-glib-2.0-8 libspice-client-gtk-3.0-5 libtheora0 libunwind8
liburing2 libusb-1.0-0 libusbredirhost1t64 libusbredirparser1t64 libva-x11-2 libva2 libvirt-clients
libvirt-glib-1.0-0 libvirt-glib-1.0-data libvirt-l10n libvirt0 libvisual-0.4-0 libvorbis0a libvorbisenc2
libvte-2.91-0 libxslt1.1 libyajl2 mesa-va-drivers osinfo-db pci.ids python3-certifi python3-chardet
python3-idna python3-libvirt python3-libxml2 python3-pkg-resources python3-requests python3-urllib3
qemu-block-extra qemu-utils spice-client-glib-usb-acl-helper usb.ids va-driver-all virt-viewer virtinst xorriso
0 upgraded, 98 newly installed, 0 to remove and 4 not upgraded.
Need to get 34.2 MB of archives.
After this operation, 143 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://archive.ubuntu.com/ubuntu noble/main amd64 iso-codes all 4.16.0-1 [3,492 kB]
Get:2 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 python3-pkg-resources all 68.1.2-2ubuntu1.2 [168 kB]
Get:3 http://archive.ubuntu.com/ubuntu noble/main amd64 libfuse3-3 amd64 3.14.0-5build1 [83.1 kB]
Get:4 http://archive.ubuntu.com/ubuntu noble/main amd64 libnuma1 amd64 2.0.18-1build1 [23.3 kB]
Get:5 http://archive.ubuntu.com/ubuntu noble/main amd64 libusb-1.0-0 amd64 2:1.0.27-1 [54.0 kB]
Get:6 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 pci.ids all 0.0~2024.03.31-1ubuntu0.1 [275 kB]
Get:7 http://archive.ubuntu.com/ubuntu noble/main amd64 usb.ids all 2024.03.18-1 [223 kB]
Get:8 http://archive.ubuntu.com/ubuntu noble/main amd64 gir1.2-freedesktop amd64 1.80.1-1 [49.7 kB]
Get:9 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libcurl3t64-gnutls amd64 8.5.0-2ubuntu10.6 [333 kB]
Get:10 http://archive.ubuntu.com/ubuntu noble/main amd64 libduktape207 amd64 2.7.0+tests-0ubuntu3 [143 kB]
Get:11 http://archive.ubuntu.com/ubuntu noble/main amd64 libproxy1v5 amd64 0.5.4-4build1 [26.5 kB]
Get:12 http://archive.ubuntu.com/ubuntu noble/main amd64 glib-networking-common all 2.80.0-1build1 [6,702 B]
Get:13 http://archive.ubuntu.com/ubuntu noble/main amd64 glib-networking-services amd64 2.80.0-1build1 [12.8 kB]
Get:14 http://archive.ubuntu.com/ubuntu noble/main amd64 glib-networking amd64 2.80.0-1build1 [64.1 kB]
Get:15 http://archive.ubuntu.com/ubuntu noble/main amd64 libcdparanoia0 amd64 3.10.2+debian-14build3 [48.5 kB]
Get:16 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libdw1t64 amd64 0.190-1.1ubuntu0.1 [261 kB]
Get:17 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libunwind8 amd64 1.6.2-3build1.1 [55.3 kB]
Get:18 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libgstreamer1.0-0 amd64 1.24.2-1ubuntu0.1 [1,165 kB]
Get:19 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 liborc-0.4-0t64 amd64 1:0.4.38-1ubuntu0.1 [207 kB]
Get:20 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libgstreamer-plugins-base1.0-0 amd64 1.24.2-1ubuntu0.2 [862 kB]
Get:21 http://archive.ubuntu.com/ubuntu noble/main amd64 libogg0 amd64 1.3.5-3build1 [22.7 kB]
Get:22 http://archive.ubuntu.com/ubuntu noble/main amd64 libopus0 amd64 1.4-1build1 [208 kB]
Get:23 http://archive.ubuntu.com/ubuntu noble/main amd64 libtheora0 amd64 1.1.1+dfsg.1-16.1build3 [211 kB]
Get:24 http://archive.ubuntu.com/ubuntu noble/main amd64 libvisual-0.4-0 amd64 0.4.2-2build1 [115 kB]
Get:25 http://archive.ubuntu.com/ubuntu noble/main amd64 libvorbis0a amd64 1.3.7-1build3 [97.6 kB]
Get:26 http://archive.ubuntu.com/ubuntu noble/main amd64 libvorbisenc2 amd64 1.3.7-1build3 [80.8 kB]
Get:27 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gstreamer1.0-plugins-base amd64 1.24.2-1ubuntu0.2 [721 kB]
Get:28 http://archive.ubuntu.com/ubuntu noble/universe amd64 libva2 amd64 2.20.0-2build1 [66.2 kB]
Get:29 http://archive.ubuntu.com/ubuntu noble/universe amd64 libigdgmm12 amd64 22.3.17+ds1-1 [145 kB]
Get:30 http://archive.ubuntu.com/ubuntu noble/universe amd64 intel-media-va-driver amd64 24.1.0+dfsg1-1 [4,022 kB]
Get:31 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libaio1t64 amd64 0.3.113-6build1.1 [7,210 B]
Get:32 http://archive.ubuntu.com/ubuntu noble/main amd64 libasyncns0 amd64 0.8-6build4 [11.3 kB]
Get:33 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libboost-iostreams1.83.0 amd64 1.83.0-2.1ubuntu3.1 [259 kB]
Get:34 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libboost-thread1.83.0 amd64 1.83.0-2.1ubuntu3.1 [276 kB]
Get:35 http://archive.ubuntu.com/ubuntu noble/main amd64 libburn4t64 amd64 1.5.6-1.1build1 [158 kB]
Get:36 http://archive.ubuntu.com/ubuntu noble/main amd64 libnspr4 amd64 2:4.35-1.1build1 [117 kB]
Get:37 http://archive.ubuntu.com/ubuntu noble/main amd64 libnss3 amd64 2:3.98-1build1 [1,445 kB]
Get:38 http://archive.ubuntu.com/ubuntu noble/main amd64 libpcsclite1 amd64 2.0.3-1build1 [21.4 kB]
Get:39 http://archive.ubuntu.com/ubuntu noble/main amd64 libcacard0 amd64 1:2.8.0-3build4 [36.5 kB]
Get:40 http://archive.ubuntu.com/ubuntu noble/main amd64 libdaxctl1 amd64 77-2ubuntu2 [21.4 kB]
Get:41 http://archive.ubuntu.com/ubuntu noble/main amd64 libflac12t64 amd64 1.4.3+ds-2.1ubuntu2 [197 kB]
Get:42 http://archive.ubuntu.com/ubuntu noble/main amd64 libmp3lame0 amd64 3.100-6build1 [142 kB]
Get:43 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libmpg123-0t64 amd64 1.32.5-1ubuntu1.1 [169 kB]
Get:44 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libsndfile1 amd64 1.2.2-1ubuntu5.24.04.1 [209 kB]
Get:45 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libpulse0 amd64 1:16.1+dfsg1-2ubuntu10.1 [292 kB]
Get:46 http://archive.ubuntu.com/ubuntu noble/universe amd64 libgvnc-1.0-0 amd64 1.3.1-1build2 [67.1 kB]
Get:47 http://archive.ubuntu.com/ubuntu noble/universe amd64 libgtk-vnc-2.0-0 amd64 1.3.1-1build2 [28.9 kB]
Get:48 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 librdmacm1t64 amd64 50.0-2ubuntu0.2 [70.7 kB]
Get:49 http://archive.ubuntu.com/ubuntu noble/main amd64 libiscsi7 amd64 1.19.0-3build4 [68.7 kB]
Get:50 http://archive.ubuntu.com/ubuntu noble/main amd64 libisofs6t64 amd64 1.5.6.pl01-1.1ubuntu2 [220 kB]
Get:51 http://archive.ubuntu.com/ubuntu noble/main amd64 libisoburn1t64 amd64 1:1.5.6-1.1ubuntu3 [405 kB]
Get:52 http://archive.ubuntu.com/ubuntu noble/main amd64 libjson-glib-1.0-common all 1.8.0-2build2 [4,244 B]
Get:53 http://archive.ubuntu.com/ubuntu noble/main amd64 libjson-glib-1.0-0 amd64 1.8.0-2build2 [68.0 kB]
Get:54 http://archive.ubuntu.com/ubuntu noble/main amd64 libndctl6 amd64 77-2ubuntu2 [62.8 kB]
Get:55 http://archive.ubuntu.com/ubuntu noble/main amd64 libnfs14 amd64 5.0.2-1build1 [109 kB]
Get:56 http://archive.ubuntu.com/ubuntu noble/universe amd64 libphodav-3.0-common all 3.0-8build3 [14.9 kB]
Get:57 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libsoup-3.0-common all 3.4.4-5ubuntu0.4 [11.1 kB]
Get:58 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libsoup-3.0-0 amd64 3.4.4-5ubuntu0.4 [290 kB]
Get:59 http://archive.ubuntu.com/ubuntu noble/universe amd64 libphodav-3.0-0 amd64 3.0-8build3 [29.9 kB]
Get:60 http://archive.ubuntu.com/ubuntu noble/main amd64 libpmem1 amd64 1.13.1-1.1ubuntu2 [84.8 kB]
Get:61 http://archive.ubuntu.com/ubuntu noble/main amd64 libpmemobj1 amd64 1.13.1-1.1ubuntu2 [116 kB]
Get:62 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libpolkit-gobject-1-0 amd64 124-2ubuntu1.24.04.2 [49.1 kB]
Get:63 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 librados2 amd64 19.2.0-0ubuntu0.24.04.2 [3,972 kB]
Get:64 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 librbd1 amd64 19.2.0-0ubuntu0.24.04.2 [3,351 kB]
Get:65 http://archive.ubuntu.com/ubuntu noble/universe amd64 spice-client-glib-usb-acl-helper amd64 0.42-2ubuntu2 [12.5 kB]
Get:66 http://archive.ubuntu.com/ubuntu noble/main amd64 libusbredirparser1t64 amd64 0.13.0-2.1build1 [16.5 kB]
Get:67 http://archive.ubuntu.com/ubuntu noble/main amd64 libusbredirhost1t64 amd64 0.13.0-2.1build1 [20.0 kB]
Get:68 http://archive.ubuntu.com/ubuntu noble/universe amd64 libspice-client-glib-2.0-8 amd64 0.42-2ubuntu2 [314 kB]
Get:69 http://archive.ubuntu.com/ubuntu noble/universe amd64 libva-x11-2 amd64 2.20.0-2build1 [12.0 kB]
Get:70 http://archive.ubuntu.com/ubuntu noble/universe amd64 libspice-client-gtk-3.0-5 amd64 0.42-2ubuntu2 [56.6 kB]
Get:71 http://archive.ubuntu.com/ubuntu noble/main amd64 liburing2 amd64 2.5-1build1 [21.1 kB]
Get:72 http://archive.ubuntu.com/ubuntu noble/main amd64 libyajl2 amd64 2.1.0-5build1 [20.2 kB]
Get:73 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt0 amd64 10.0.0-2ubuntu8.7 [1,826 kB]
Get:74 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-clients amd64 10.0.0-2ubuntu8.7 [438 kB]
Get:75 http://archive.ubuntu.com/ubuntu noble/universe amd64 libvirt-glib-1.0-data all 5.0.0-2build3 [17.3 kB]
Get:76 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-l10n all 10.0.0-2ubuntu8.7 [1,150 B]
Get:77 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvte-2.91-0 amd64 0.76.0-1ubuntu0.1 [230 kB]
Get:78 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libxslt1.1 amd64 1.1.39-0exp1ubuntu0.24.04.2 [167 kB]
Get:79 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 mesa-va-drivers amd64 24.2.8-1ubuntu1~24.04.1 [19.5 kB]
Get:80 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 osinfo-db all 0.20250124-0ubuntu0.24.04.1 [176 kB]
Get:81 http://archive.ubuntu.com/ubuntu noble/main amd64 python3-certifi all 2023.11.17-1 [165 kB]
Get:82 http://archive.ubuntu.com/ubuntu noble/main amd64 python3-chardet all 5.2.0+dfsg-1 [117 kB]
Get:83 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 python3-idna all 3.6-2ubuntu0.1 [49.0 kB]
Get:84 http://archive.ubuntu.com/ubuntu noble/main amd64 python3-libvirt amd64 10.0.0-1build1 [164 kB]
Get:85 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 python3-libxml2 amd64 2.9.14+dfsg-1.3ubuntu3.3 [140 kB]
Get:86 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 python3-urllib3 all 2.0.7-1ubuntu0.2 [93.3 kB]
Get:87 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 python3-requests all 2.31.0+dfsg-1ubuntu1.1 [50.8 kB]
Get:88 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-utils amd64 1:8.2.2+ds-0ubuntu1.7 [2,220 kB]
Get:89 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-block-extra amd64 1:8.2.2+ds-0ubuntu1.7 [111 kB]
Get:90 http://archive.ubuntu.com/ubuntu noble/universe amd64 i965-va-driver amd64 2.4.1+dfsg1-1build2 [332 kB]
Get:91 http://archive.ubuntu.com/ubuntu noble/universe amd64 va-driver-all amd64 2.20.0-2build1 [4,844 B]
Get:92 http://archive.ubuntu.com/ubuntu noble/universe amd64 libvirt-glib-1.0-0 amd64 5.0.0-2build3 [121 kB]
Get:93 http://archive.ubuntu.com/ubuntu noble/universe amd64 virt-viewer amd64 11.0-3build2 [285 kB]
Get:94 http://archive.ubuntu.com/ubuntu noble/main amd64 xorriso amd64 1:1.5.6-1.1ubuntu3 [297 kB]
Get:95 http://archive.ubuntu.com/ubuntu noble/universe amd64 libosinfo-l10n all 1.11.0-2build3 [50.6 kB]
Get:96 http://archive.ubuntu.com/ubuntu noble/universe amd64 libosinfo-1.0-0 amd64 1.11.0-2build3 [91.5 kB]
Get:97 http://archive.ubuntu.com/ubuntu noble/universe amd64 gir1.2-libosinfo-1.0 amd64 1.11.0-2build3 [18.6 kB]
Get:98 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 virtinst all 1:4.1.0-3ubuntu0.1 [891 kB]
Fetched 34.2 MB in 3s (10.8 MB/s)
Extracting templates from packages: 100%
Selecting previously unselected package iso-codes.
(Reading database ... 26588 files and directories currently installed.)
Preparing to unpack .../00-iso-codes_4.16.0-1_all.deb ...
Unpacking iso-codes (4.16.0-1) ...
Selecting previously unselected package python3-pkg-resources.
Preparing to unpack .../01-python3-pkg-resources_68.1.2-2ubuntu1.2_all.deb ...
Unpacking python3-pkg-resources (68.1.2-2ubuntu1.2) ...
Selecting previously unselected package libfuse3-3:amd64.
Preparing to unpack .../02-libfuse3-3_3.14.0-5build1_amd64.deb ...
Unpacking libfuse3-3:amd64 (3.14.0-5build1) ...
Selecting previously unselected package libnuma1:amd64.
Preparing to unpack .../03-libnuma1_2.0.18-1build1_amd64.deb ...
Unpacking libnuma1:amd64 (2.0.18-1build1) ...
Selecting previously unselected package libusb-1.0-0:amd64.
Preparing to unpack .../04-libusb-1.0-0_2%3a1.0.27-1_amd64.deb ...
Unpacking libusb-1.0-0:amd64 (2:1.0.27-1) ...
Selecting previously unselected package pci.ids.
Preparing to unpack .../05-pci.ids_0.0~2024.03.31-1ubuntu0.1_all.deb ...
Unpacking pci.ids (0.0~2024.03.31-1ubuntu0.1) ...
Selecting previously unselected package usb.ids.
Preparing to unpack .../06-usb.ids_2024.03.18-1_all.deb ...
Unpacking usb.ids (2024.03.18-1) ...
Selecting previously unselected package gir1.2-freedesktop:amd64.
Preparing to unpack .../07-gir1.2-freedesktop_1.80.1-1_amd64.deb ...
Unpacking gir1.2-freedesktop:amd64 (1.80.1-1) ...
Selecting previously unselected package libcurl3t64-gnutls:amd64.
Preparing to unpack .../08-libcurl3t64-gnutls_8.5.0-2ubuntu10.6_amd64.deb ...
Unpacking libcurl3t64-gnutls:amd64 (8.5.0-2ubuntu10.6) ...
Selecting previously unselected package libduktape207:amd64.
Preparing to unpack .../09-libduktape207_2.7.0+tests-0ubuntu3_amd64.deb ...
Unpacking libduktape207:amd64 (2.7.0+tests-0ubuntu3) ...
Selecting previously unselected package libproxy1v5:amd64.
Preparing to unpack .../10-libproxy1v5_0.5.4-4build1_amd64.deb ...
Unpacking libproxy1v5:amd64 (0.5.4-4build1) ...
Selecting previously unselected package glib-networking-common.
Preparing to unpack .../11-glib-networking-common_2.80.0-1build1_all.deb ...
Unpacking glib-networking-common (2.80.0-1build1) ...
Selecting previously unselected package glib-networking-services.
Preparing to unpack .../12-glib-networking-services_2.80.0-1build1_amd64.deb ...
Unpacking glib-networking-services (2.80.0-1build1) ...
Selecting previously unselected package glib-networking:amd64.
Preparing to unpack .../13-glib-networking_2.80.0-1build1_amd64.deb ...
Unpacking glib-networking:amd64 (2.80.0-1build1) ...
Selecting previously unselected package libcdparanoia0:amd64.
Preparing to unpack .../14-libcdparanoia0_3.10.2+debian-14build3_amd64.deb ...
Unpacking libcdparanoia0:amd64 (3.10.2+debian-14build3) ...
Selecting previously unselected package libdw1t64:amd64.
Preparing to unpack .../15-libdw1t64_0.190-1.1ubuntu0.1_amd64.deb ...
Unpacking libdw1t64:amd64 (0.190-1.1ubuntu0.1) ...
Selecting previously unselected package libunwind8:amd64.
Preparing to unpack .../16-libunwind8_1.6.2-3build1.1_amd64.deb ...
Unpacking libunwind8:amd64 (1.6.2-3build1.1) ...
Selecting previously unselected package libgstreamer1.0-0:amd64.
Preparing to unpack .../17-libgstreamer1.0-0_1.24.2-1ubuntu0.1_amd64.deb ...
Unpacking libgstreamer1.0-0:amd64 (1.24.2-1ubuntu0.1) ...
Selecting previously unselected package liborc-0.4-0t64:amd64.
Preparing to unpack .../18-liborc-0.4-0t64_1%3a0.4.38-1ubuntu0.1_amd64.deb ...
Unpacking liborc-0.4-0t64:amd64 (1:0.4.38-1ubuntu0.1) ...
Selecting previously unselected package libgstreamer-plugins-base1.0-0:amd64.
Preparing to unpack .../19-libgstreamer-plugins-base1.0-0_1.24.2-1ubuntu0.2_amd64.deb ...
Unpacking libgstreamer-plugins-base1.0-0:amd64 (1.24.2-1ubuntu0.2) ...
Selecting previously unselected package libogg0:amd64.
Preparing to unpack .../20-libogg0_1.3.5-3build1_amd64.deb ...
Unpacking libogg0:amd64 (1.3.5-3build1) ...
Selecting previously unselected package libopus0:amd64.
Preparing to unpack .../21-libopus0_1.4-1build1_amd64.deb ...
Unpacking libopus0:amd64 (1.4-1build1) ...
Selecting previously unselected package libtheora0:amd64.
Preparing to unpack .../22-libtheora0_1.1.1+dfsg.1-16.1build3_amd64.deb ...
Unpacking libtheora0:amd64 (1.1.1+dfsg.1-16.1build3) ...
Selecting previously unselected package libvisual-0.4-0:amd64.
Preparing to unpack .../23-libvisual-0.4-0_0.4.2-2build1_amd64.deb ...
Unpacking libvisual-0.4-0:amd64 (0.4.2-2build1) ...
Selecting previously unselected package libvorbis0a:amd64.
Preparing to unpack .../24-libvorbis0a_1.3.7-1build3_amd64.deb ...
Unpacking libvorbis0a:amd64 (1.3.7-1build3) ...
Selecting previously unselected package libvorbisenc2:amd64.
Preparing to unpack .../25-libvorbisenc2_1.3.7-1build3_amd64.deb ...
Unpacking libvorbisenc2:amd64 (1.3.7-1build3) ...
Selecting previously unselected package gstreamer1.0-plugins-base:amd64.
Preparing to unpack .../26-gstreamer1.0-plugins-base_1.24.2-1ubuntu0.2_amd64.deb ...
Unpacking gstreamer1.0-plugins-base:amd64 (1.24.2-1ubuntu0.2) ...
Selecting previously unselected package libva2:amd64.
Preparing to unpack .../27-libva2_2.20.0-2build1_amd64.deb ...
Unpacking libva2:amd64 (2.20.0-2build1) ...
Selecting previously unselected package libigdgmm12:amd64.
Preparing to unpack .../28-libigdgmm12_22.3.17+ds1-1_amd64.deb ...
Unpacking libigdgmm12:amd64 (22.3.17+ds1-1) ...
Selecting previously unselected package intel-media-va-driver:amd64.
Preparing to unpack .../29-intel-media-va-driver_24.1.0+dfsg1-1_amd64.deb ...
Unpacking intel-media-va-driver:amd64 (24.1.0+dfsg1-1) ...
Selecting previously unselected package libaio1t64:amd64.
Preparing to unpack .../30-libaio1t64_0.3.113-6build1.1_amd64.deb ...
Unpacking libaio1t64:amd64 (0.3.113-6build1.1) ...
Selecting previously unselected package libasyncns0:amd64.
Preparing to unpack .../31-libasyncns0_0.8-6build4_amd64.deb ...
Unpacking libasyncns0:amd64 (0.8-6build4) ...
Selecting previously unselected package libboost-iostreams1.83.0:amd64.
Preparing to unpack .../32-libboost-iostreams1.83.0_1.83.0-2.1ubuntu3.1_amd64.deb ...
Unpacking libboost-iostreams1.83.0:amd64 (1.83.0-2.1ubuntu3.1) ...
Selecting previously unselected package libboost-thread1.83.0:amd64.
Preparing to unpack .../33-libboost-thread1.83.0_1.83.0-2.1ubuntu3.1_amd64.deb ...
Unpacking libboost-thread1.83.0:amd64 (1.83.0-2.1ubuntu3.1) ...
Selecting previously unselected package libburn4t64:amd64.
Preparing to unpack .../34-libburn4t64_1.5.6-1.1build1_amd64.deb ...
Unpacking libburn4t64:amd64 (1.5.6-1.1build1) ...
Selecting previously unselected package libnspr4:amd64.
Preparing to unpack .../35-libnspr4_2%3a4.35-1.1build1_amd64.deb ...
Unpacking libnspr4:amd64 (2:4.35-1.1build1) ...
Selecting previously unselected package libnss3:amd64.
Preparing to unpack .../36-libnss3_2%3a3.98-1build1_amd64.deb ...
Unpacking libnss3:amd64 (2:3.98-1build1) ...
Selecting previously unselected package libpcsclite1:amd64.
Preparing to unpack .../37-libpcsclite1_2.0.3-1build1_amd64.deb ...
Unpacking libpcsclite1:amd64 (2.0.3-1build1) ...
Selecting previously unselected package libcacard0:amd64.
Preparing to unpack .../38-libcacard0_1%3a2.8.0-3build4_amd64.deb ...
Unpacking libcacard0:amd64 (1:2.8.0-3build4) ...
Selecting previously unselected package libdaxctl1:amd64.
Preparing to unpack .../39-libdaxctl1_77-2ubuntu2_amd64.deb ...
Unpacking libdaxctl1:amd64 (77-2ubuntu2) ...
Selecting previously unselected package libflac12t64:amd64.
Preparing to unpack .../40-libflac12t64_1.4.3+ds-2.1ubuntu2_amd64.deb ...
Unpacking libflac12t64:amd64 (1.4.3+ds-2.1ubuntu2) ...
Selecting previously unselected package libmp3lame0:amd64.
Preparing to unpack .../41-libmp3lame0_3.100-6build1_amd64.deb ...
Unpacking libmp3lame0:amd64 (3.100-6build1) ...
Selecting previously unselected package libmpg123-0t64:amd64.
Preparing to unpack .../42-libmpg123-0t64_1.32.5-1ubuntu1.1_amd64.deb ...
Unpacking libmpg123-0t64:amd64 (1.32.5-1ubuntu1.1) ...
Selecting previously unselected package libsndfile1:amd64.
Preparing to unpack .../43-libsndfile1_1.2.2-1ubuntu5.24.04.1_amd64.deb ...
Unpacking libsndfile1:amd64 (1.2.2-1ubuntu5.24.04.1) ...
Selecting previously unselected package libpulse0:amd64.
Preparing to unpack .../44-libpulse0_1%3a16.1+dfsg1-2ubuntu10.1_amd64.deb ...
Unpacking libpulse0:amd64 (1:16.1+dfsg1-2ubuntu10.1) ...
Selecting previously unselected package libgvnc-1.0-0:amd64.
Preparing to unpack .../45-libgvnc-1.0-0_1.3.1-1build2_amd64.deb ...
Unpacking libgvnc-1.0-0:amd64 (1.3.1-1build2) ...
Selecting previously unselected package libgtk-vnc-2.0-0:amd64.
Preparing to unpack .../46-libgtk-vnc-2.0-0_1.3.1-1build2_amd64.deb ...
Unpacking libgtk-vnc-2.0-0:amd64 (1.3.1-1build2) ...
Selecting previously unselected package librdmacm1t64:amd64.
Preparing to unpack .../47-librdmacm1t64_50.0-2ubuntu0.2_amd64.deb ...
Unpacking librdmacm1t64:amd64 (50.0-2ubuntu0.2) ...
Selecting previously unselected package libiscsi7:amd64.
Preparing to unpack .../48-libiscsi7_1.19.0-3build4_amd64.deb ...
Unpacking libiscsi7:amd64 (1.19.0-3build4) ...
Selecting previously unselected package libisofs6t64:amd64.
Preparing to unpack .../49-libisofs6t64_1.5.6.pl01-1.1ubuntu2_amd64.deb ...
Unpacking libisofs6t64:amd64 (1.5.6.pl01-1.1ubuntu2) ...
Selecting previously unselected package libisoburn1t64:amd64.
Preparing to unpack .../50-libisoburn1t64_1%3a1.5.6-1.1ubuntu3_amd64.deb ...
Unpacking libisoburn1t64:amd64 (1:1.5.6-1.1ubuntu3) ...
Selecting previously unselected package libjson-glib-1.0-common.
Preparing to unpack .../51-libjson-glib-1.0-common_1.8.0-2build2_all.deb ...
Unpacking libjson-glib-1.0-common (1.8.0-2build2) ...
Selecting previously unselected package libjson-glib-1.0-0:amd64.
Preparing to unpack .../52-libjson-glib-1.0-0_1.8.0-2build2_amd64.deb ...
Unpacking libjson-glib-1.0-0:amd64 (1.8.0-2build2) ...
Selecting previously unselected package libndctl6:amd64.
Preparing to unpack .../53-libndctl6_77-2ubuntu2_amd64.deb ...
Unpacking libndctl6:amd64 (77-2ubuntu2) ...
Selecting previously unselected package libnfs14:amd64.
Preparing to unpack .../54-libnfs14_5.0.2-1build1_amd64.deb ...
Unpacking libnfs14:amd64 (5.0.2-1build1) ...
Selecting previously unselected package libphodav-3.0-common.
Preparing to unpack .../55-libphodav-3.0-common_3.0-8build3_all.deb ...
Unpacking libphodav-3.0-common (3.0-8build3) ...
Selecting previously unselected package libsoup-3.0-common.
Preparing to unpack .../56-libsoup-3.0-common_3.4.4-5ubuntu0.4_all.deb ...
Unpacking libsoup-3.0-common (3.4.4-5ubuntu0.4) ...
Selecting previously unselected package libsoup-3.0-0:amd64.
Preparing to unpack .../57-libsoup-3.0-0_3.4.4-5ubuntu0.4_amd64.deb ...
Unpacking libsoup-3.0-0:amd64 (3.4.4-5ubuntu0.4) ...
Selecting previously unselected package libphodav-3.0-0:amd64.
Preparing to unpack .../58-libphodav-3.0-0_3.0-8build3_amd64.deb ...
Unpacking libphodav-3.0-0:amd64 (3.0-8build3) ...
Selecting previously unselected package libpmem1:amd64.
Preparing to unpack .../59-libpmem1_1.13.1-1.1ubuntu2_amd64.deb ...
Unpacking libpmem1:amd64 (1.13.1-1.1ubuntu2) ...
Selecting previously unselected package libpmemobj1:amd64.
Preparing to unpack .../60-libpmemobj1_1.13.1-1.1ubuntu2_amd64.deb ...
Unpacking libpmemobj1:amd64 (1.13.1-1.1ubuntu2) ...
Selecting previously unselected package libpolkit-gobject-1-0:amd64.
Preparing to unpack .../61-libpolkit-gobject-1-0_124-2ubuntu1.24.04.2_amd64.deb ...
Unpacking libpolkit-gobject-1-0:amd64 (124-2ubuntu1.24.04.2) ...
Selecting previously unselected package librados2.
Preparing to unpack .../62-librados2_19.2.0-0ubuntu0.24.04.2_amd64.deb ...
Unpacking librados2 (19.2.0-0ubuntu0.24.04.2) ...
Selecting previously unselected package librbd1.
Preparing to unpack .../63-librbd1_19.2.0-0ubuntu0.24.04.2_amd64.deb ...
Unpacking librbd1 (19.2.0-0ubuntu0.24.04.2) ...
Selecting previously unselected package spice-client-glib-usb-acl-helper.
Preparing to unpack .../64-spice-client-glib-usb-acl-helper_0.42-2ubuntu2_amd64.deb ...
Unpacking spice-client-glib-usb-acl-helper (0.42-2ubuntu2) ...
Selecting previously unselected package libusbredirparser1t64:amd64.
Preparing to unpack .../65-libusbredirparser1t64_0.13.0-2.1build1_amd64.deb ...
Unpacking libusbredirparser1t64:amd64 (0.13.0-2.1build1) ...
Selecting previously unselected package libusbredirhost1t64:amd64.
Preparing to unpack .../66-libusbredirhost1t64_0.13.0-2.1build1_amd64.deb ...
Unpacking libusbredirhost1t64:amd64 (0.13.0-2.1build1) ...
Selecting previously unselected package libspice-client-glib-2.0-8:amd64.
Preparing to unpack .../67-libspice-client-glib-2.0-8_0.42-2ubuntu2_amd64.deb ...
Unpacking libspice-client-glib-2.0-8:amd64 (0.42-2ubuntu2) ...
Selecting previously unselected package libva-x11-2:amd64.
Preparing to unpack .../68-libva-x11-2_2.20.0-2build1_amd64.deb ...
Unpacking libva-x11-2:amd64 (2.20.0-2build1) ...
Selecting previously unselected package libspice-client-gtk-3.0-5:amd64.
Preparing to unpack .../69-libspice-client-gtk-3.0-5_0.42-2ubuntu2_amd64.deb ...
Unpacking libspice-client-gtk-3.0-5:amd64 (0.42-2ubuntu2) ...
Selecting previously unselected package liburing2:amd64.
Preparing to unpack .../70-liburing2_2.5-1build1_amd64.deb ...
Unpacking liburing2:amd64 (2.5-1build1) ...
Selecting previously unselected package libyajl2:amd64.
Preparing to unpack .../71-libyajl2_2.1.0-5build1_amd64.deb ...
Unpacking libyajl2:amd64 (2.1.0-5build1) ...
Selecting previously unselected package libvirt0:amd64.
Preparing to unpack .../72-libvirt0_10.0.0-2ubuntu8.7_amd64.deb ...
Unpacking libvirt0:amd64 (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libvirt-clients.
Preparing to unpack .../73-libvirt-clients_10.0.0-2ubuntu8.7_amd64.deb ...
Unpacking libvirt-clients (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libvirt-glib-1.0-data.
Preparing to unpack .../74-libvirt-glib-1.0-data_5.0.0-2build3_all.deb ...
Unpacking libvirt-glib-1.0-data (5.0.0-2build3) ...
Selecting previously unselected package libvirt-l10n.
Preparing to unpack .../75-libvirt-l10n_10.0.0-2ubuntu8.7_all.deb ...
Unpacking libvirt-l10n (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libvte-2.91-0:amd64.
Preparing to unpack .../76-libvte-2.91-0_0.76.0-1ubuntu0.1_amd64.deb ...
Unpacking libvte-2.91-0:amd64 (0.76.0-1ubuntu0.1) ...
Selecting previously unselected package libxslt1.1:amd64.
Preparing to unpack .../77-libxslt1.1_1.1.39-0exp1ubuntu0.24.04.2_amd64.deb ...
Unpacking libxslt1.1:amd64 (1.1.39-0exp1ubuntu0.24.04.2) ...
Selecting previously unselected package mesa-va-drivers:amd64.
Preparing to unpack .../78-mesa-va-drivers_24.2.8-1ubuntu1~24.04.1_amd64.deb ...
Unpacking mesa-va-drivers:amd64 (24.2.8-1ubuntu1~24.04.1) ...
Selecting previously unselected package osinfo-db.
Preparing to unpack .../79-osinfo-db_0.20250124-0ubuntu0.24.04.1_all.deb ...
Unpacking osinfo-db (0.20250124-0ubuntu0.24.04.1) ...
Selecting previously unselected package python3-certifi.
Preparing to unpack .../80-python3-certifi_2023.11.17-1_all.deb ...
Unpacking python3-certifi (2023.11.17-1) ...
Selecting previously unselected package python3-chardet.
Preparing to unpack .../81-python3-chardet_5.2.0+dfsg-1_all.deb ...
Unpacking python3-chardet (5.2.0+dfsg-1) ...
Selecting previously unselected package python3-idna.
Preparing to unpack .../82-python3-idna_3.6-2ubuntu0.1_all.deb ...
Unpacking python3-idna (3.6-2ubuntu0.1) ...
Selecting previously unselected package python3-libvirt.
Preparing to unpack .../83-python3-libvirt_10.0.0-1build1_amd64.deb ...
Unpacking python3-libvirt (10.0.0-1build1) ...
Selecting previously unselected package python3-libxml2:amd64.
Preparing to unpack .../84-python3-libxml2_2.9.14+dfsg-1.3ubuntu3.3_amd64.deb ...
Unpacking python3-libxml2:amd64 (2.9.14+dfsg-1.3ubuntu3.3) ...
Selecting previously unselected package python3-urllib3.
Preparing to unpack .../85-python3-urllib3_2.0.7-1ubuntu0.2_all.deb ...
Unpacking python3-urllib3 (2.0.7-1ubuntu0.2) ...
Selecting previously unselected package python3-requests.
Preparing to unpack .../86-python3-requests_2.31.0+dfsg-1ubuntu1.1_all.deb ...
Unpacking python3-requests (2.31.0+dfsg-1ubuntu1.1) ...
Selecting previously unselected package qemu-utils.
Preparing to unpack .../87-qemu-utils_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-utils (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package qemu-block-extra.
Preparing to unpack .../88-qemu-block-extra_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-block-extra (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package i965-va-driver:amd64.
Preparing to unpack .../89-i965-va-driver_2.4.1+dfsg1-1build2_amd64.deb ...
Unpacking i965-va-driver:amd64 (2.4.1+dfsg1-1build2) ...
Selecting previously unselected package va-driver-all:amd64.
Preparing to unpack .../90-va-driver-all_2.20.0-2build1_amd64.deb ...
Unpacking va-driver-all:amd64 (2.20.0-2build1) ...
Selecting previously unselected package libvirt-glib-1.0-0:amd64.
Preparing to unpack .../91-libvirt-glib-1.0-0_5.0.0-2build3_amd64.deb ...
Unpacking libvirt-glib-1.0-0:amd64 (5.0.0-2build3) ...
Selecting previously unselected package virt-viewer.
Preparing to unpack .../92-virt-viewer_11.0-3build2_amd64.deb ...
Unpacking virt-viewer (11.0-3build2) ...
Selecting previously unselected package xorriso.
Preparing to unpack .../93-xorriso_1%3a1.5.6-1.1ubuntu3_amd64.deb ...
Unpacking xorriso (1:1.5.6-1.1ubuntu3) ...
Selecting previously unselected package libosinfo-l10n.
Preparing to unpack .../94-libosinfo-l10n_1.11.0-2build3_all.deb ...
Unpacking libosinfo-l10n (1.11.0-2build3) ...
Selecting previously unselected package libosinfo-1.0-0:amd64.
Preparing to unpack .../95-libosinfo-1.0-0_1.11.0-2build3_amd64.deb ...
Unpacking libosinfo-1.0-0:amd64 (1.11.0-2build3) ...
Selecting previously unselected package gir1.2-libosinfo-1.0:amd64.
Preparing to unpack .../96-gir1.2-libosinfo-1.0_1.11.0-2build3_amd64.deb ...
Unpacking gir1.2-libosinfo-1.0:amd64 (1.11.0-2build3) ...
Selecting previously unselected package virtinst.
Preparing to unpack .../97-virtinst_1%3a4.1.0-3ubuntu0.1_all.deb ...
Unpacking virtinst (1:4.1.0-3ubuntu0.1) ...
Setting up python3-pkg-resources (68.1.2-2ubuntu1.2) ...
Setting up libcdparanoia0:amd64 (3.10.2+debian-14build3) ...
Setting up pci.ids (0.0~2024.03.31-1ubuntu0.1) ...
Setting up gir1.2-freedesktop:amd64 (1.80.1-1) ...
Setting up libogg0:amd64 (1.3.5-3build1) ...
Setting up libphodav-3.0-common (3.0-8build3) ...
Setting up libvisual-0.4-0:amd64 (0.4.2-2build1) ...
Setting up libyajl2:amd64 (2.1.0-5build1) ...
Setting up libcurl3t64-gnutls:amd64 (8.5.0-2ubuntu10.6) ...
Setting up libboost-thread1.83.0:amd64 (1.83.0-2.1ubuntu3.1) ...
Setting up libigdgmm12:amd64 (22.3.17+ds1-1) ...
Setting up libsoup-3.0-common (3.4.4-5ubuntu0.4) ...
Setting up libmpg123-0t64:amd64 (1.32.5-1ubuntu1.1) ...
Setting up libvte-2.91-0:amd64 (0.76.0-1ubuntu0.1) ...
Setting up libunwind8:amd64 (1.6.2-3build1.1) ...
Setting up libnfs14:amd64 (5.0.2-1build1) ...
Setting up liborc-0.4-0t64:amd64 (1:0.4.38-1ubuntu0.1) ...
Setting up libdw1t64:amd64 (0.190-1.1ubuntu0.1) ...
Setting up python3-libxml2:amd64 (2.9.14+dfsg-1.3ubuntu3.3) ...
Setting up python3-chardet (5.2.0+dfsg-1) ...
Setting up libvirt-glib-1.0-data (5.0.0-2build3) ...
Setting up libva2:amd64 (2.20.0-2build1) ...
Setting up python3-certifi (2023.11.17-1) ...
Setting up libnspr4:amd64 (2:4.35-1.1build1) ...
Setting up libboost-iostreams1.83.0:amd64 (1.83.0-2.1ubuntu3.1) ...
Setting up libopus0:amd64 (1.4-1build1) ...
Setting up intel-media-va-driver:amd64 (24.1.0+dfsg1-1) ...
Setting up libvorbis0a:amd64 (1.3.7-1build3) ...
Setting up python3-idna (3.6-2ubuntu0.1) ...
Setting up usb.ids (2024.03.18-1) ...
Setting up osinfo-db (0.20250124-0ubuntu0.24.04.1) ...
Setting up libpcsclite1:amd64 (2.0.3-1build1) ...
Setting up libfuse3-3:amd64 (3.14.0-5build1) ...
Setting up libdaxctl1:amd64 (77-2ubuntu2) ...
Setting up python3-urllib3 (2.0.7-1ubuntu0.2) ...
Setting up libnuma1:amd64 (2.0.18-1build1) ...
Setting up libvirt0:amd64 (10.0.0-2ubuntu8.7) ...
Setting up libaio1t64:amd64 (0.3.113-6build1.1) ...
Setting up libvirt-glib-1.0-0:amd64 (5.0.0-2build3) ...
Setting up libisofs6t64:amd64 (1.5.6.pl01-1.1ubuntu2) ...
Setting up libduktape207:amd64 (2.7.0+tests-0ubuntu3) ...
Setting up libasyncns0:amd64 (0.8-6build4) ...
Setting up libusbredirparser1t64:amd64 (0.13.0-2.1build1) ...
Setting up libtheora0:amd64 (1.1.1+dfsg.1-16.1build3) ...
Setting up libxslt1.1:amd64 (1.1.39-0exp1ubuntu0.24.04.2) ...
Setting up libburn4t64:amd64 (1.5.6-1.1build1) ...
Setting up libndctl6:amd64 (77-2ubuntu2) ...
Setting up librdmacm1t64:amd64 (50.0-2ubuntu0.2) ...
Setting up libjson-glib-1.0-common (1.8.0-2build2) ...
Setting up libflac12t64:amd64 (1.4.3+ds-2.1ubuntu2) ...
Setting up libusb-1.0-0:amd64 (2:1.0.27-1) ...
Setting up mesa-va-drivers:amd64 (24.2.8-1ubuntu1~24.04.1) ...
Setting up glib-networking-common (2.80.0-1build1) ...
Setting up liburing2:amd64 (2.5-1build1) ...
Setting up libiscsi7:amd64 (1.19.0-3build4) ...
Setting up libisoburn1t64:amd64 (1:1.5.6-1.1ubuntu3) ...
Setting up xorriso (1:1.5.6-1.1ubuntu3) ...
Setting up libpmem1:amd64 (1.13.1-1.1ubuntu2) ...
Setting up libva-x11-2:amd64 (2.20.0-2build1) ...
Setting up iso-codes (4.16.0-1) ...
Setting up libpolkit-gobject-1-0:amd64 (124-2ubuntu1.24.04.2) ...
Setting up libgstreamer1.0-0:amd64 (1.24.2-1ubuntu0.1) ...
Setcap worked! gst-ptp-helper is not suid!
Setting up libmp3lame0:amd64 (3.100-6build1) ...
Setting up i965-va-driver:amd64 (2.4.1+dfsg1-1build2) ...
Setting up libosinfo-l10n (1.11.0-2build3) ...
Setting up libvorbisenc2:amd64 (1.3.7-1build3) ...
Setting up librados2 (19.2.0-0ubuntu0.24.04.2) ...
Setting up libproxy1v5:amd64 (0.5.4-4build1) ...
Setting up python3-libvirt (10.0.0-1build1) ...
Setting up libvirt-clients (10.0.0-2ubuntu8.7) ...
Setting up spice-client-glib-usb-acl-helper (0.42-2ubuntu2) ...
Setting up libgstreamer-plugins-base1.0-0:amd64 (1.24.2-1ubuntu0.2) ...
Setting up libnss3:amd64 (2:3.98-1build1) ...
Setting up libcacard0:amd64 (1:2.8.0-3build4) ...
Setting up libusbredirhost1t64:amd64 (0.13.0-2.1build1) ...
Setting up libjson-glib-1.0-0:amd64 (1.8.0-2build2) ...
Setting up gstreamer1.0-plugins-base:amd64 (1.24.2-1ubuntu0.2) ...
Setting up libvirt-l10n (10.0.0-2ubuntu8.7) ...
Setting up va-driver-all:amd64 (2.20.0-2build1) ...
Setting up python3-requests (2.31.0+dfsg-1ubuntu1.1) ...
Setting up libpmemobj1:amd64 (1.13.1-1.1ubuntu2) ...
Setting up librbd1 (19.2.0-0ubuntu0.24.04.2) ...
Setting up libsndfile1:amd64 (1.2.2-1ubuntu5.24.04.1) ...
Setting up qemu-utils (1:8.2.2+ds-0ubuntu1.7) ...
Setting up qemu-block-extra (1:8.2.2+ds-0ubuntu1.7) ...
Created symlink /etc/systemd/system/multi-user.target.wants/run-qemu.mount → /usr/lib/systemd/system/run-qemu.mount
.
Setting up glib-networking-services (2.80.0-1build1) ...
Setting up libpulse0:amd64 (1:16.1+dfsg1-2ubuntu10.1) ...
Setting up libgvnc-1.0-0:amd64 (1.3.1-1build2) ...
Setting up glib-networking:amd64 (2.80.0-1build1) ...
Setting up libgtk-vnc-2.0-0:amd64 (1.3.1-1build2) ...
Setting up libsoup-3.0-0:amd64 (3.4.4-5ubuntu0.4) ...
Setting up libphodav-3.0-0:amd64 (3.0-8build3) ...
Setting up libosinfo-1.0-0:amd64 (1.11.0-2build3) ...
Setting up gir1.2-libosinfo-1.0:amd64 (1.11.0-2build3) ...
Setting up libspice-client-glib-2.0-8:amd64 (0.42-2ubuntu2) ...
Setting up virtinst (1:4.1.0-3ubuntu0.1) ...
Setting up libspice-client-gtk-3.0-5:amd64 (0.42-2ubuntu2) ...
Setting up virt-viewer (11.0-3build2) ...
Processing triggers for libc-bin (2.39-0ubuntu8.4) ...
Processing triggers for man-db (2.12.0-4build2) ...
Processing triggers for libglib2.0-0t64:amd64 (2.80.0-6ubuntu3.4) ...
Processing triggers for shared-mime-info (2.4-4) ...
Processing triggers for hicolor-icon-theme (0.17-2) ...
📦[waylon@ubuntu ~]$ virt-install --version
virsh list --all
4.1.0
error: failed to connect to the hypervisor
error: binary '/usr/sbin/libvirtd' does not exist in $PATH: No such file or directory
📦[waylon@ubuntu ~]$ exit
logout
waylon@razorcrest:~$ distrobox enter --additional-flags "--device /dev/kvm" ubuntu
Error: unknown flag: --device
See 'podman exec --help'
waylon@razorcrest:~$ distrobox enter ubuntu
📦[waylon@ubuntu ~]$ ls -l /run/libvirt/
common/ libvirt-sock nodedev/ secrets/ virtlogd-admin-sock
hostdevmgr/ libvirt-sock-ro nwfilter/ storage/ virtlogd-sock
interface/ lxc/ nwfilter-binding/ virtlockd-admin-sock
libvirt-admin-sock network/ qemu/ virtlockd-sock
📦[waylon@ubuntu ~]$ ls -l /run/libvirt/libvirt-sock
srw-rw-rw-. 1 nobody nogroup 0 Jun 27 11:09 /run/libvirt/libvirt-sock
📦[waylon@ubuntu ~]$ sudo usermod -aG libvirt $USER
usermod: group 'libvirt' does not exist
📦[waylon@ubuntu ~]$ export LIBVIRT_DEFAULT_URI="qemu+unix:///system"
virsh list --all
Id Name State
--------------------
📦[waylon@ubuntu ~]$ ls -l /run/libvirt/libvirt-sock
srw-rw-rw-. 1 nobody nogroup 0 Jun 27 11:09 /run/libvirt/libvirt-sock
📦[waylon@ubuntu ~]$ virt-install --version
virsh list --all
4.1.0
Id Name State
--------------------
📦[waylon@ubuntu ~]$ sudo apt update
sudo apt install virt-manager -y
Hit:1 http://security.ubuntu.com/ubuntu noble-security InRelease
Hit:2 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:3 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:4 http://archive.ubuntu.com/ubuntu noble-backports InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
4 packages can be upgraded. Run 'apt list --upgradable' to see them.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
acl alsa-topology-conf alsa-ucm-conf cpu-checker dmeventd dmidecode dns-root-data dnsmasq-base gettext-base
gir1.2-atk-1.0 gir1.2-ayatanaappindicator3-0.1 gir1.2-gdkpixbuf-2.0 gir1.2-gstreamer-1.0 gir1.2-gtk-3.0
gir1.2-gtk-vnc-2.0 gir1.2-gtksource-4 gir1.2-harfbuzz-0.0 gir1.2-libvirt-glib-1.0 gir1.2-pango-1.0
gir1.2-spiceclientglib-2.0 gir1.2-spiceclientgtk-3.0 gir1.2-vte-2.91 gstreamer1.0-plugins-good gstreamer1.0-x
iptables ipxe-qemu ipxe-qemu-256k-compat-efi-roms libaa1 libasound2-data libasound2t64 libavc1394-0
libayatana-appindicator3-1 libayatana-ido3-0.4-0 libayatana-indicator3-7 libbrlapi0.8 libcaca0
libdbusmenu-glib4 libdbusmenu-gtk3-4 libdecor-0-0 libdecor-0-plugin-1-gtk libdevmapper-event1.02.1 libdv4t64
libfdt1 libgstreamer-plugins-good1.0-0 libgtksourceview-4-0 libgtksourceview-4-common libgudev-1.0-0
libharfbuzz-gobject0 libiec61883-0 libip4tc2 libip6tc2 libjack-jackd2-0 liblvm2cmd2.03 libnetfilter-conntrack3
libnfnetlink0 libnftables1 libnftnl11 libnss-mymachines libpangoxft-1.0-0 libparted2t64 libpipewire-0.3-0t64
libpipewire-0.3-common libpolkit-agent-1-0 libraw1394-11 libsamplerate0 libsdl2-2.0-0 libshout3 libslang2
libslirp0 libspa-0.2-modules libspeex1 libspice-server1 libtag1v5 libtag1v5-vanilla libtpms0 libtwolame0
libv4l-0t64 libv4lconvert0t64 libvirglrenderer1 libvirt-daemon libvirt-daemon-config-network
libvirt-daemon-config-nwfilter libvirt-daemon-driver-qemu libvirt-daemon-system libvirt-daemon-system-systemd
libvpx9 libwavpack1 libwebrtc-audio-processing1 libxft2 libxml2-utils libxss1 libxv1 logrotate lvm2 mdevctl
msr-tools netcat-openbsd nftables ovmf parted polkitd python3-cairo python3-gi-cairo qemu-system-common
qemu-system-data qemu-system-gui qemu-system-modules-opengl qemu-system-modules-spice qemu-system-x86 seabios
sgml-base swtpm swtpm-tools systemd-container systemd-hwe-hwdb thin-provisioning-tools udev xml-core
Suggested packages:
firewalld kmod alsa-utils libasound2-plugins libdv-bin oss-compat jackd2 libparted-dev libparted-i18n pipewire
libraw1394-doc xdg-utils speex gstreamer1.0-libav gstreamer1.0-plugins-ugly
libvirt-daemon-driver-storage-gluster libvirt-daemon-driver-storage-iscsi-direct
libvirt-daemon-driver-storage-rbd libvirt-daemon-driver-storage-zfs libvirt-daemon-driver-lxc
libvirt-daemon-driver-vbox libvirt-daemon-driver-xen numad passt apparmor auditd nfs-common open-iscsi pm-utils
systemtap zfsutils bsd-mailx | mailx parted-doc polkitd-pkla samba vde2 sgml-base-doc trousers gir1.2-secret-1
gnome-keyring python3-guestfs ssh-askpass debhelper
The following NEW packages will be installed:
acl alsa-topology-conf alsa-ucm-conf cpu-checker dmeventd dmidecode dns-root-data dnsmasq-base gettext-base
gir1.2-atk-1.0 gir1.2-ayatanaappindicator3-0.1 gir1.2-gdkpixbuf-2.0 gir1.2-gstreamer-1.0 gir1.2-gtk-3.0
gir1.2-gtk-vnc-2.0 gir1.2-gtksource-4 gir1.2-harfbuzz-0.0 gir1.2-libvirt-glib-1.0 gir1.2-pango-1.0
gir1.2-spiceclientglib-2.0 gir1.2-spiceclientgtk-3.0 gir1.2-vte-2.91 gstreamer1.0-plugins-good gstreamer1.0-x
iptables ipxe-qemu ipxe-qemu-256k-compat-efi-roms libaa1 libasound2-data libasound2t64 libavc1394-0
libayatana-appindicator3-1 libayatana-ido3-0.4-0 libayatana-indicator3-7 libbrlapi0.8 libcaca0
libdbusmenu-glib4 libdbusmenu-gtk3-4 libdecor-0-0 libdecor-0-plugin-1-gtk libdevmapper-event1.02.1 libdv4t64
libfdt1 libgstreamer-plugins-good1.0-0 libgtksourceview-4-0 libgtksourceview-4-common libgudev-1.0-0
libharfbuzz-gobject0 libiec61883-0 libip4tc2 libip6tc2 libjack-jackd2-0 liblvm2cmd2.03 libnetfilter-conntrack3
libnfnetlink0 libnftables1 libnftnl11 libnss-mymachines libpangoxft-1.0-0 libparted2t64 libpipewire-0.3-0t64
libpipewire-0.3-common libpolkit-agent-1-0 libraw1394-11 libsamplerate0 libsdl2-2.0-0 libshout3 libslang2
libslirp0 libspa-0.2-modules libspeex1 libspice-server1 libtag1v5 libtag1v5-vanilla libtpms0 libtwolame0
libv4l-0t64 libv4lconvert0t64 libvirglrenderer1 libvirt-daemon libvirt-daemon-config-network
libvirt-daemon-config-nwfilter libvirt-daemon-driver-qemu libvirt-daemon-system libvirt-daemon-system-systemd
libvpx9 libwavpack1 libwebrtc-audio-processing1 libxft2 libxml2-utils libxss1 libxv1 logrotate lvm2 mdevctl
msr-tools netcat-openbsd nftables ovmf parted polkitd python3-cairo python3-gi-cairo qemu-system-common
qemu-system-data qemu-system-gui qemu-system-modules-opengl qemu-system-modules-spice qemu-system-x86 seabios
sgml-base swtpm swtpm-tools systemd-container systemd-hwe-hwdb thin-provisioning-tools udev virt-manager
xml-core
0 upgraded, 119 newly installed, 0 to remove and 4 not upgraded.
Need to get 43.2 MB of archives.
After this operation, 180 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu noble/main amd64 sgml-base all 1.31 [11.4 kB]
Get:2 http://archive.ubuntu.com/ubuntu noble/main amd64 libslang2 amd64 2.3.3-3build2 [506 kB]
Get:3 http://archive.ubuntu.com/ubuntu noble/main amd64 logrotate amd64 3.21.0-2build1 [52.2 kB]
Get:4 http://archive.ubuntu.com/ubuntu noble/main amd64 netcat-openbsd amd64 1.226-1ubuntu2 [44.3 kB]
Get:5 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 udev amd64 255.4-1ubuntu8.8 [1,874 kB]
Get:6 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 systemd-hwe-hwdb all 255.1.4 [3,200 B]
Get:7 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 dmidecode amd64 3.5-3ubuntu0.1 [73.0 kB]
Get:8 http://archive.ubuntu.com/ubuntu noble/main amd64 gettext-base amd64 0.21-14ubuntu2 [38.4 kB]
Get:9 http://archive.ubuntu.com/ubuntu noble/main amd64 libip4tc2 amd64 1.8.10-3ubuntu2 [23.3 kB]
Get:10 http://archive.ubuntu.com/ubuntu noble/main amd64 libip6tc2 amd64 1.8.10-3ubuntu2 [23.7 kB]
Get:11 http://archive.ubuntu.com/ubuntu noble/main amd64 libnfnetlink0 amd64 1.0.2-2build1 [14.8 kB]
Get:12 http://archive.ubuntu.com/ubuntu noble/main amd64 libnetfilter-conntrack3 amd64 1.0.9-6build1 [45.2 kB]
Get:13 http://archive.ubuntu.com/ubuntu noble/main amd64 libnftnl11 amd64 1.2.6-2build1 [66.0 kB]
Get:14 http://archive.ubuntu.com/ubuntu noble/main amd64 iptables amd64 1.8.10-3ubuntu2 [381 kB]
Get:15 http://archive.ubuntu.com/ubuntu noble/main amd64 libnftables1 amd64 1.0.9-1build1 [358 kB]
Get:16 http://archive.ubuntu.com/ubuntu noble/main amd64 libparted2t64 amd64 3.6-4build1 [152 kB]
Get:17 http://archive.ubuntu.com/ubuntu noble/main amd64 nftables amd64 1.0.9-1build1 [69.8 kB]
Get:18 http://archive.ubuntu.com/ubuntu noble/main amd64 parted amd64 3.6-4build1 [43.3 kB]
Get:19 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 acl amd64 2.3.2-1build1.1 [39.4 kB]
Get:20 http://archive.ubuntu.com/ubuntu noble/main amd64 alsa-topology-conf all 1.2.5.1-2 [15.5 kB]
Get:21 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libasound2-data all 1.2.11-1ubuntu0.1 [21.1 kB]
Get:22 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libasound2t64 amd64 1.2.11-1ubuntu0.1 [399 kB]
Get:23 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 alsa-ucm-conf all 1.2.10-1ubuntu5.7 [66.4 kB]
Get:24 http://archive.ubuntu.com/ubuntu noble/main amd64 msr-tools amd64 1.3-5build1 [9,610 B]
Get:25 http://archive.ubuntu.com/ubuntu noble/main amd64 cpu-checker amd64 0.7-1.3build2 [6,148 B]
Get:26 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libdevmapper-event1.02.1 amd64 2:1.02.185-3ubuntu3.2 [12.6 kB]
Get:27 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 liblvm2cmd2.03 amd64 2.03.16-3ubuntu3.2 [797 kB]
Get:28 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 dmeventd amd64 2:1.02.185-3ubuntu3.2 [38.0 kB]
Get:29 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 dns-root-data all 2024071801~ubuntu0.24.04.1 [5,918 B]
Get:30 http://archive.ubuntu.com/ubuntu noble/main amd64 dnsmasq-base amd64 2.90-2build2 [375 kB]
Get:31 http://archive.ubuntu.com/ubuntu noble/main amd64 gir1.2-atk-1.0 amd64 2.52.0-1build1 [23.1 kB]
Get:32 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gir1.2-gdkpixbuf-2.0 amd64 2.42.10+dfsg-3ubuntu3.1 [9,486 B]
Get:33 http://archive.ubuntu.com/ubuntu noble/main amd64 libharfbuzz-gobject0 amd64 8.3.0-2build2 [34.3 kB]
Get:34 http://archive.ubuntu.com/ubuntu noble/main amd64 gir1.2-harfbuzz-0.0 amd64 8.3.0-2build2 [44.5 kB]
Get:35 http://archive.ubuntu.com/ubuntu noble/main amd64 libxft2 amd64 2.3.6-1build1 [45.3 kB]
Get:36 http://archive.ubuntu.com/ubuntu noble/main amd64 libpangoxft-1.0-0 amd64 1.52.1+ds-1build1 [20.3 kB]
Get:37 http://archive.ubuntu.com/ubuntu noble/main amd64 gir1.2-pango-1.0 amd64 1.52.1+ds-1build1 [34.8 kB]
Get:38 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gir1.2-gtk-3.0 amd64 3.24.41-4ubuntu1.3 [245 kB]
Get:39 http://archive.ubuntu.com/ubuntu noble/main amd64 libayatana-ido3-0.4-0 amd64 0.10.1-1build2 [56.6 kB]
Get:40 http://archive.ubuntu.com/ubuntu noble/main amd64 libayatana-indicator3-7 amd64 0.9.4-1build1 [31.7 kB]
Get:41 http://archive.ubuntu.com/ubuntu noble/main amd64 libdbusmenu-glib4 amd64 18.10.20180917~bzr492+repack1-3.1ubuntu5 [43.0 kB]
Get:42 http://archive.ubuntu.com/ubuntu noble/main amd64 libdbusmenu-gtk3-4 amd64 18.10.20180917~bzr492+repack1-3.1ubuntu5 [27.6 kB]
Get:43 http://archive.ubuntu.com/ubuntu noble/main amd64 libayatana-appindicator3-1 amd64 0.5.93-1build3 [24.7 kB]
Get:44 http://archive.ubuntu.com/ubuntu noble/main amd64 gir1.2-ayatanaappindicator3-0.1 amd64 0.5.93-1build3 [5,838 B]
Get:45 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gir1.2-gstreamer-1.0 amd64 1.24.2-1ubuntu0.1 [88.4 kB]
Get:46 http://archive.ubuntu.com/ubuntu noble/universe amd64 gir1.2-gtk-vnc-2.0 amd64 1.3.1-1build2 [12.1 kB]
Get:47 http://archive.ubuntu.com/ubuntu noble/universe amd64 libgtksourceview-4-common all 4.8.4-5build4 [590 kB]
Get:48 http://archive.ubuntu.com/ubuntu noble/universe amd64 libgtksourceview-4-0 amd64 4.8.4-5build4 [233 kB]
Get:49 http://archive.ubuntu.com/ubuntu noble/universe amd64 gir1.2-gtksource-4 amd64 4.8.4-5build4 [20.3 kB]
Get:50 http://archive.ubuntu.com/ubuntu noble/universe amd64 gir1.2-spiceclientglib-2.0 amd64 0.42-2ubuntu2 [14.4 kB]
Get:51 http://archive.ubuntu.com/ubuntu noble/universe amd64 gir1.2-spiceclientgtk-3.0 amd64 0.42-2ubuntu2 [5,610 B]
Get:52 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gir1.2-vte-2.91 amd64 0.76.0-1ubuntu0.1 [11.5 kB]
Get:53 http://archive.ubuntu.com/ubuntu noble/main amd64 libaa1 amd64 1.4p5-51.1 [49.9 kB]
Get:54 http://archive.ubuntu.com/ubuntu noble/main amd64 libraw1394-11 amd64 2.1.2-2build3 [26.2 kB]
Get:55 http://archive.ubuntu.com/ubuntu noble/main amd64 libavc1394-0 amd64 0.5.4-5build3 [15.4 kB]
Get:56 http://archive.ubuntu.com/ubuntu noble/main amd64 libcaca0 amd64 0.99.beta20-4build2 [208 kB]
Get:57 http://archive.ubuntu.com/ubuntu noble/main amd64 libdv4t64 amd64 1.0.0-17.1build1 [63.2 kB]
Get:58 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libgstreamer-plugins-good1.0-0 amd64 1.24.2-1ubuntu1.1 [32.9 kB]
Get:59 http://archive.ubuntu.com/ubuntu noble/main amd64 libgudev-1.0-0 amd64 1:238-5ubuntu1 [15.9 kB]
Get:60 http://archive.ubuntu.com/ubuntu noble/main amd64 libiec61883-0 amd64 1.2.0-6build1 [24.5 kB]
Get:61 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libspeex1 amd64 1.2.1-2ubuntu2.24.04.1 [59.6 kB]
Get:62 http://archive.ubuntu.com/ubuntu noble/main amd64 libshout3 amd64 2.4.6-1build2 [50.3 kB]
Get:63 http://archive.ubuntu.com/ubuntu noble/main amd64 libtag1v5-vanilla amd64 1.13.1-1build1 [326 kB]
Get:64 http://archive.ubuntu.com/ubuntu noble/main amd64 libtag1v5 amd64 1.13.1-1build1 [11.7 kB]
Get:65 http://archive.ubuntu.com/ubuntu noble/main amd64 libtwolame0 amd64 0.4.0-2build3 [52.3 kB]
Get:66 http://archive.ubuntu.com/ubuntu noble/main amd64 libv4lconvert0t64 amd64 1.26.1-4build3 [87.6 kB]
Get:67 http://archive.ubuntu.com/ubuntu noble/main amd64 libv4l-0t64 amd64 1.26.1-4build3 [46.9 kB]
Get:68 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvpx9 amd64 1.14.0-1ubuntu2.2 [1,143 kB]
Get:69 http://archive.ubuntu.com/ubuntu noble/main amd64 libwavpack1 amd64 5.6.0-1build1 [84.6 kB]
Get:70 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gstreamer1.0-plugins-good amd64 1.24.2-1ubuntu1.1 [2,238 kB]
Get:71 http://archive.ubuntu.com/ubuntu noble/main amd64 libxv1 amd64 2:1.0.11-1.1build1 [10.7 kB]
Get:72 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 gstreamer1.0-x amd64 1.24.2-1ubuntu0.2 [85.0 kB]
Get:73 http://archive.ubuntu.com/ubuntu noble/main amd64 ipxe-qemu all 1.21.1+git-20220113.fbbdc3926-0ubuntu2 [1,565 kB]
Get:74 http://archive.ubuntu.com/ubuntu noble/main amd64 ipxe-qemu-256k-compat-efi-roms all 1.0.0+git-20150424.a25a16d-0ubuntu5 [548 kB]
Get:75 http://archive.ubuntu.com/ubuntu noble/main amd64 libbrlapi0.8 amd64 6.6-4ubuntu5 [31.4 kB]
Get:76 http://archive.ubuntu.com/ubuntu noble/main amd64 libdecor-0-0 amd64 0.2.2-1build2 [16.5 kB]
Get:77 http://archive.ubuntu.com/ubuntu noble/main amd64 libdecor-0-plugin-1-gtk amd64 0.2.2-1build2 [22.2 kB]
Get:78 http://archive.ubuntu.com/ubuntu noble/main amd64 libsamplerate0 amd64 0.2.2-4build1 [1,344 kB]
Get:79 http://archive.ubuntu.com/ubuntu noble/main amd64 libjack-jackd2-0 amd64 1.9.21~dfsg-3ubuntu3 [289 kB]
Get:80 http://archive.ubuntu.com/ubuntu noble/main amd64 libwebrtc-audio-processing1 amd64 0.3.1-0ubuntu6 [290 kB]
Get:81 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libspa-0.2-modules amd64 1.0.5-1ubuntu3 [626 kB]
Get:82 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libpipewire-0.3-0t64 amd64 1.0.5-1ubuntu3 [252 kB]
Get:83 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libpipewire-0.3-common all 1.0.5-1ubuntu3 [18.8 kB]
Get:84 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libpolkit-agent-1-0 amd64 124-2ubuntu1.24.04.2 [17.4 kB]
Get:85 http://archive.ubuntu.com/ubuntu noble/main amd64 libxss1 amd64 1:1.2.3-1build3 [7,204 B]
Get:86 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libsdl2-2.0-0 amd64 2.30.0+dfsg-1ubuntu3.1 [686 kB]
Get:87 http://archive.ubuntu.com/ubuntu noble/main amd64 libslirp0 amd64 4.7.0-1ubuntu3 [63.8 kB]
Get:88 http://archive.ubuntu.com/ubuntu noble/main amd64 libspice-server1 amd64 0.15.1-1build2 [349 kB]
Get:89 http://archive.ubuntu.com/ubuntu noble/main amd64 libtpms0 amd64 0.9.3-0ubuntu4 [373 kB]
Get:90 http://archive.ubuntu.com/ubuntu noble/main amd64 libvirglrenderer1 amd64 1.0.0-1ubuntu2 [226 kB]
Get:91 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-daemon-driver-qemu amd64 10.0.0-2ubuntu8.7 [740 kB]
Get:92 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-daemon amd64 10.0.0-2ubuntu8.7 [431 kB]
Get:93 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-daemon-config-network all 10.0.0-2ubuntu8.7 [3,120 B]
Get:94 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-daemon-config-nwfilter all 10.0.0-2ubuntu8.7 [6,042 B]
Get:95 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 systemd-container amd64 255.4-1ubuntu8.8 [417 kB]
Get:96 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-daemon-system-systemd all 10.0.0-2ubuntu8.7 [1,376 B]
Get:97 http://archive.ubuntu.com/ubuntu noble/main amd64 xml-core all 0.19 [20.3 kB]
Get:98 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 polkitd amd64 124-2ubuntu1.24.04.2 [95.2 kB]
Get:99 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libvirt-daemon-system amd64 10.0.0-2ubuntu8.7 [49.6 kB]
Get:100 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libxml2-utils amd64 2.9.14+dfsg-1.3ubuntu3.3 [39.4 kB]
Get:101 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 lvm2 amd64 2.03.16-3ubuntu3.2 [1,186 kB]
Get:102 http://archive.ubuntu.com/ubuntu noble/main amd64 mdevctl amd64 1.3.0-1ubuntu2 [936 kB]
Get:103 http://archive.ubuntu.com/ubuntu noble/main amd64 python3-cairo amd64 1.25.1-2build2 [119 kB]
Get:104 http://archive.ubuntu.com/ubuntu noble/main amd64 python3-gi-cairo amd64 3.48.2-1 [8,132 B]
Get:105 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-system-common amd64 1:8.2.2+ds-0ubuntu1.7 [1,253 kB]
Get:106 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-system-data all 1:8.2.2+ds-0ubuntu1.7 [1,793 kB]
Get:107 http://archive.ubuntu.com/ubuntu noble/main amd64 libfdt1 amd64 1.7.0-2build1 [20.1 kB]
Get:108 http://archive.ubuntu.com/ubuntu noble/main amd64 seabios all 1.16.3-2 [175 kB]
Get:109 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-system-x86 amd64 1:8.2.2+ds-0ubuntu1.7 [11.2 MB]
Get:110 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-system-modules-opengl amd64 1:8.2.2+ds-0ubuntu1.7 [184 kB]
Get:111 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-system-gui amd64 1:8.2.2+ds-0ubuntu1.7 [314 kB]
Get:112 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 qemu-system-modules-spice amd64 1:8.2.2+ds-0ubuntu1.7 [70.2 kB]
Get:113 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 swtpm amd64 0.7.3-0ubuntu5.24.04.1 [53.1 kB]
Get:114 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 swtpm-tools amd64 0.7.3-0ubuntu5.24.04.1 [92.9 kB]
Get:115 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 thin-provisioning-tools amd64 0.9.0-2ubuntu5.1 [436 kB]
Get:116 http://archive.ubuntu.com/ubuntu noble/universe amd64 gir1.2-libvirt-glib-1.0 amd64 5.0.0-2build3 [32.6 kB]
Get:117 http://archive.ubuntu.com/ubuntu noble-updates/universe amd64 virt-manager all 1:4.1.0-3ubuntu0.1 [263 kB]
Get:118 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 libnss-mymachines amd64 255.4-1ubuntu8.8 [153 kB]
Get:119 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 ovmf all 2024.02-2ubuntu0.4 [4,571 kB]
Fetched 43.2 MB in 5s (9,539 kB/s)
Extracting templates from packages: 100%
Preconfiguring packages ...
Selecting previously unselected package sgml-base.
(Reading database ... 30439 files and directories currently installed.)
Preparing to unpack .../000-sgml-base_1.31_all.deb ...
Unpacking sgml-base (1.31) ...
Selecting previously unselected package libslang2:amd64.
Preparing to unpack .../001-libslang2_2.3.3-3build2_amd64.deb ...
Unpacking libslang2:amd64 (2.3.3-3build2) ...
Selecting previously unselected package logrotate.
Preparing to unpack .../002-logrotate_3.21.0-2build1_amd64.deb ...
Unpacking logrotate (3.21.0-2build1) ...
Selecting previously unselected package netcat-openbsd.
Preparing to unpack .../003-netcat-openbsd_1.226-1ubuntu2_amd64.deb ...
Unpacking netcat-openbsd (1.226-1ubuntu2) ...
Selecting previously unselected package udev.
Preparing to unpack .../004-udev_255.4-1ubuntu8.8_amd64.deb ...
Unpacking udev (255.4-1ubuntu8.8) ...
Selecting previously unselected package systemd-hwe-hwdb.
Preparing to unpack .../005-systemd-hwe-hwdb_255.1.4_all.deb ...
Unpacking systemd-hwe-hwdb (255.1.4) ...
Selecting previously unselected package dmidecode.
Preparing to unpack .../006-dmidecode_3.5-3ubuntu0.1_amd64.deb ...
Unpacking dmidecode (3.5-3ubuntu0.1) ...
Selecting previously unselected package gettext-base.
Preparing to unpack .../007-gettext-base_0.21-14ubuntu2_amd64.deb ...
Unpacking gettext-base (0.21-14ubuntu2) ...
Selecting previously unselected package libip4tc2:amd64.
Preparing to unpack .../008-libip4tc2_1.8.10-3ubuntu2_amd64.deb ...
Unpacking libip4tc2:amd64 (1.8.10-3ubuntu2) ...
Selecting previously unselected package libip6tc2:amd64.
Preparing to unpack .../009-libip6tc2_1.8.10-3ubuntu2_amd64.deb ...
Unpacking libip6tc2:amd64 (1.8.10-3ubuntu2) ...
Selecting previously unselected package libnfnetlink0:amd64.
Preparing to unpack .../010-libnfnetlink0_1.0.2-2build1_amd64.deb ...
Unpacking libnfnetlink0:amd64 (1.0.2-2build1) ...
Selecting previously unselected package libnetfilter-conntrack3:amd64.
Preparing to unpack .../011-libnetfilter-conntrack3_1.0.9-6build1_amd64.deb ...
Unpacking libnetfilter-conntrack3:amd64 (1.0.9-6build1) ...
Selecting previously unselected package libnftnl11:amd64.
Preparing to unpack .../012-libnftnl11_1.2.6-2build1_amd64.deb ...
Unpacking libnftnl11:amd64 (1.2.6-2build1) ...
Selecting previously unselected package iptables.
Preparing to unpack .../013-iptables_1.8.10-3ubuntu2_amd64.deb ...
Unpacking iptables (1.8.10-3ubuntu2) ...
Selecting previously unselected package libnftables1:amd64.
Preparing to unpack .../014-libnftables1_1.0.9-1build1_amd64.deb ...
Unpacking libnftables1:amd64 (1.0.9-1build1) ...
Selecting previously unselected package libparted2t64:amd64.
Preparing to unpack .../015-libparted2t64_3.6-4build1_amd64.deb ...
Adding 'diversion of /lib/x86_64-linux-gnu/libparted.so.2 to /lib/x86_64-linux-gnu/libparted.so.2.usr-is-merged by
libparted2t64'
Adding 'diversion of /lib/x86_64-linux-gnu/libparted.so.2.0.5 to /lib/x86_64-linux-gnu/libparted.so.2.0.5.usr-is-me
rged by libparted2t64'
Unpacking libparted2t64:amd64 (3.6-4build1) ...
Selecting previously unselected package nftables.
Preparing to unpack .../016-nftables_1.0.9-1build1_amd64.deb ...
Unpacking nftables (1.0.9-1build1) ...
Selecting previously unselected package parted.
Preparing to unpack .../017-parted_3.6-4build1_amd64.deb ...
Unpacking parted (3.6-4build1) ...
Selecting previously unselected package acl.
Preparing to unpack .../018-acl_2.3.2-1build1.1_amd64.deb ...
Unpacking acl (2.3.2-1build1.1) ...
Selecting previously unselected package alsa-topology-conf.
Preparing to unpack .../019-alsa-topology-conf_1.2.5.1-2_all.deb ...
Unpacking alsa-topology-conf (1.2.5.1-2) ...
Selecting previously unselected package libasound2-data.
Preparing to unpack .../020-libasound2-data_1.2.11-1ubuntu0.1_all.deb ...
Unpacking libasound2-data (1.2.11-1ubuntu0.1) ...
Selecting previously unselected package libasound2t64:amd64.
Preparing to unpack .../021-libasound2t64_1.2.11-1ubuntu0.1_amd64.deb ...
Unpacking libasound2t64:amd64 (1.2.11-1ubuntu0.1) ...
Selecting previously unselected package alsa-ucm-conf.
Preparing to unpack .../022-alsa-ucm-conf_1.2.10-1ubuntu5.7_all.deb ...
Unpacking alsa-ucm-conf (1.2.10-1ubuntu5.7) ...
Selecting previously unselected package msr-tools.
Preparing to unpack .../023-msr-tools_1.3-5build1_amd64.deb ...
Unpacking msr-tools (1.3-5build1) ...
Selecting previously unselected package cpu-checker.
Preparing to unpack .../024-cpu-checker_0.7-1.3build2_amd64.deb ...
Unpacking cpu-checker (0.7-1.3build2) ...
Selecting previously unselected package libdevmapper-event1.02.1:amd64.
Preparing to unpack .../025-libdevmapper-event1.02.1_2%3a1.02.185-3ubuntu3.2_amd64.deb ...
Unpacking libdevmapper-event1.02.1:amd64 (2:1.02.185-3ubuntu3.2) ...
Selecting previously unselected package liblvm2cmd2.03:amd64.
Preparing to unpack .../026-liblvm2cmd2.03_2.03.16-3ubuntu3.2_amd64.deb ...
Unpacking liblvm2cmd2.03:amd64 (2.03.16-3ubuntu3.2) ...
Selecting previously unselected package dmeventd.
Preparing to unpack .../027-dmeventd_2%3a1.02.185-3ubuntu3.2_amd64.deb ...
Unpacking dmeventd (2:1.02.185-3ubuntu3.2) ...
Selecting previously unselected package dns-root-data.
Preparing to unpack .../028-dns-root-data_2024071801~ubuntu0.24.04.1_all.deb ...
Unpacking dns-root-data (2024071801~ubuntu0.24.04.1) ...
Selecting previously unselected package dnsmasq-base.
Preparing to unpack .../029-dnsmasq-base_2.90-2build2_amd64.deb ...
Unpacking dnsmasq-base (2.90-2build2) ...
Selecting previously unselected package gir1.2-atk-1.0:amd64.
Preparing to unpack .../030-gir1.2-atk-1.0_2.52.0-1build1_amd64.deb ...
Unpacking gir1.2-atk-1.0:amd64 (2.52.0-1build1) ...
Selecting previously unselected package gir1.2-gdkpixbuf-2.0:amd64.
Preparing to unpack .../031-gir1.2-gdkpixbuf-2.0_2.42.10+dfsg-3ubuntu3.1_amd64.deb ...
Unpacking gir1.2-gdkpixbuf-2.0:amd64 (2.42.10+dfsg-3ubuntu3.1) ...
Selecting previously unselected package libharfbuzz-gobject0:amd64.
Preparing to unpack .../032-libharfbuzz-gobject0_8.3.0-2build2_amd64.deb ...
Unpacking libharfbuzz-gobject0:amd64 (8.3.0-2build2) ...
Selecting previously unselected package gir1.2-harfbuzz-0.0:amd64.
Preparing to unpack .../033-gir1.2-harfbuzz-0.0_8.3.0-2build2_amd64.deb ...
Unpacking gir1.2-harfbuzz-0.0:amd64 (8.3.0-2build2) ...
Selecting previously unselected package libxft2:amd64.
Preparing to unpack .../034-libxft2_2.3.6-1build1_amd64.deb ...
Unpacking libxft2:amd64 (2.3.6-1build1) ...
Selecting previously unselected package libpangoxft-1.0-0:amd64.
Preparing to unpack .../035-libpangoxft-1.0-0_1.52.1+ds-1build1_amd64.deb ...
Unpacking libpangoxft-1.0-0:amd64 (1.52.1+ds-1build1) ...
Selecting previously unselected package gir1.2-pango-1.0:amd64.
Preparing to unpack .../036-gir1.2-pango-1.0_1.52.1+ds-1build1_amd64.deb ...
Unpacking gir1.2-pango-1.0:amd64 (1.52.1+ds-1build1) ...
Selecting previously unselected package gir1.2-gtk-3.0:amd64.
Preparing to unpack .../037-gir1.2-gtk-3.0_3.24.41-4ubuntu1.3_amd64.deb ...
Unpacking gir1.2-gtk-3.0:amd64 (3.24.41-4ubuntu1.3) ...
Selecting previously unselected package libayatana-ido3-0.4-0:amd64.
Preparing to unpack .../038-libayatana-ido3-0.4-0_0.10.1-1build2_amd64.deb ...
Unpacking libayatana-ido3-0.4-0:amd64 (0.10.1-1build2) ...
Selecting previously unselected package libayatana-indicator3-7:amd64.
Preparing to unpack .../039-libayatana-indicator3-7_0.9.4-1build1_amd64.deb ...
Unpacking libayatana-indicator3-7:amd64 (0.9.4-1build1) ...
Selecting previously unselected package libdbusmenu-glib4:amd64.
Preparing to unpack .../040-libdbusmenu-glib4_18.10.20180917~bzr492+repack1-3.1ubuntu5_amd64.deb ...
Unpacking libdbusmenu-glib4:amd64 (18.10.20180917~bzr492+repack1-3.1ubuntu5) ...
Selecting previously unselected package libdbusmenu-gtk3-4:amd64.
Preparing to unpack .../041-libdbusmenu-gtk3-4_18.10.20180917~bzr492+repack1-3.1ubuntu5_amd64.deb ...
Unpacking libdbusmenu-gtk3-4:amd64 (18.10.20180917~bzr492+repack1-3.1ubuntu5) ...
Selecting previously unselected package libayatana-appindicator3-1.
Preparing to unpack .../042-libayatana-appindicator3-1_0.5.93-1build3_amd64.deb ...
Unpacking libayatana-appindicator3-1 (0.5.93-1build3) ...
Selecting previously unselected package gir1.2-ayatanaappindicator3-0.1.
Preparing to unpack .../043-gir1.2-ayatanaappindicator3-0.1_0.5.93-1build3_amd64.deb ...
Unpacking gir1.2-ayatanaappindicator3-0.1 (0.5.93-1build3) ...
Selecting previously unselected package gir1.2-gstreamer-1.0:amd64.
Preparing to unpack .../044-gir1.2-gstreamer-1.0_1.24.2-1ubuntu0.1_amd64.deb ...
Unpacking gir1.2-gstreamer-1.0:amd64 (1.24.2-1ubuntu0.1) ...
Selecting previously unselected package gir1.2-gtk-vnc-2.0:amd64.
Preparing to unpack .../045-gir1.2-gtk-vnc-2.0_1.3.1-1build2_amd64.deb ...
Unpacking gir1.2-gtk-vnc-2.0:amd64 (1.3.1-1build2) ...
Selecting previously unselected package libgtksourceview-4-common.
Preparing to unpack .../046-libgtksourceview-4-common_4.8.4-5build4_all.deb ...
Unpacking libgtksourceview-4-common (4.8.4-5build4) ...
Selecting previously unselected package libgtksourceview-4-0:amd64.
Preparing to unpack .../047-libgtksourceview-4-0_4.8.4-5build4_amd64.deb ...
Unpacking libgtksourceview-4-0:amd64 (4.8.4-5build4) ...
Selecting previously unselected package gir1.2-gtksource-4:amd64.
Preparing to unpack .../048-gir1.2-gtksource-4_4.8.4-5build4_amd64.deb ...
Unpacking gir1.2-gtksource-4:amd64 (4.8.4-5build4) ...
Selecting previously unselected package gir1.2-spiceclientglib-2.0:amd64.
Preparing to unpack .../049-gir1.2-spiceclientglib-2.0_0.42-2ubuntu2_amd64.deb ...
Unpacking gir1.2-spiceclientglib-2.0:amd64 (0.42-2ubuntu2) ...
Selecting previously unselected package gir1.2-spiceclientgtk-3.0:amd64.
Preparing to unpack .../050-gir1.2-spiceclientgtk-3.0_0.42-2ubuntu2_amd64.deb ...
Unpacking gir1.2-spiceclientgtk-3.0:amd64 (0.42-2ubuntu2) ...
Selecting previously unselected package gir1.2-vte-2.91:amd64.
Preparing to unpack .../051-gir1.2-vte-2.91_0.76.0-1ubuntu0.1_amd64.deb ...
Unpacking gir1.2-vte-2.91:amd64 (0.76.0-1ubuntu0.1) ...
Selecting previously unselected package libaa1:amd64.
Preparing to unpack .../052-libaa1_1.4p5-51.1_amd64.deb ...
Unpacking libaa1:amd64 (1.4p5-51.1) ...
Selecting previously unselected package libraw1394-11:amd64.
Preparing to unpack .../053-libraw1394-11_2.1.2-2build3_amd64.deb ...
Unpacking libraw1394-11:amd64 (2.1.2-2build3) ...
Selecting previously unselected package libavc1394-0:amd64.
Preparing to unpack .../054-libavc1394-0_0.5.4-5build3_amd64.deb ...
Unpacking libavc1394-0:amd64 (0.5.4-5build3) ...
Selecting previously unselected package libcaca0:amd64.
Preparing to unpack .../055-libcaca0_0.99.beta20-4build2_amd64.deb ...
Unpacking libcaca0:amd64 (0.99.beta20-4build2) ...
Selecting previously unselected package libdv4t64:amd64.
Preparing to unpack .../056-libdv4t64_1.0.0-17.1build1_amd64.deb ...
Unpacking libdv4t64:amd64 (1.0.0-17.1build1) ...
Selecting previously unselected package libgstreamer-plugins-good1.0-0:amd64.
Preparing to unpack .../057-libgstreamer-plugins-good1.0-0_1.24.2-1ubuntu1.1_amd64.deb ...
Unpacking libgstreamer-plugins-good1.0-0:amd64 (1.24.2-1ubuntu1.1) ...
Selecting previously unselected package libgudev-1.0-0:amd64.
Preparing to unpack .../058-libgudev-1.0-0_1%3a238-5ubuntu1_amd64.deb ...
Unpacking libgudev-1.0-0:amd64 (1:238-5ubuntu1) ...
Selecting previously unselected package libiec61883-0:amd64.
Preparing to unpack .../059-libiec61883-0_1.2.0-6build1_amd64.deb ...
Unpacking libiec61883-0:amd64 (1.2.0-6build1) ...
Selecting previously unselected package libspeex1:amd64.
Preparing to unpack .../060-libspeex1_1.2.1-2ubuntu2.24.04.1_amd64.deb ...
Unpacking libspeex1:amd64 (1.2.1-2ubuntu2.24.04.1) ...
Selecting previously unselected package libshout3:amd64.
Preparing to unpack .../061-libshout3_2.4.6-1build2_amd64.deb ...
Unpacking libshout3:amd64 (2.4.6-1build2) ...
Selecting previously unselected package libtag1v5-vanilla:amd64.
Preparing to unpack .../062-libtag1v5-vanilla_1.13.1-1build1_amd64.deb ...
Unpacking libtag1v5-vanilla:amd64 (1.13.1-1build1) ...
Selecting previously unselected package libtag1v5:amd64.
Preparing to unpack .../063-libtag1v5_1.13.1-1build1_amd64.deb ...
Unpacking libtag1v5:amd64 (1.13.1-1build1) ...
Selecting previously unselected package libtwolame0:amd64.
Preparing to unpack .../064-libtwolame0_0.4.0-2build3_amd64.deb ...
Unpacking libtwolame0:amd64 (0.4.0-2build3) ...
Selecting previously unselected package libv4lconvert0t64:amd64.
Preparing to unpack .../065-libv4lconvert0t64_1.26.1-4build3_amd64.deb ...
Unpacking libv4lconvert0t64:amd64 (1.26.1-4build3) ...
Selecting previously unselected package libv4l-0t64:amd64.
Preparing to unpack .../066-libv4l-0t64_1.26.1-4build3_amd64.deb ...
Unpacking libv4l-0t64:amd64 (1.26.1-4build3) ...
Selecting previously unselected package libvpx9:amd64.
Preparing to unpack .../067-libvpx9_1.14.0-1ubuntu2.2_amd64.deb ...
Unpacking libvpx9:amd64 (1.14.0-1ubuntu2.2) ...
Selecting previously unselected package libwavpack1:amd64.
Preparing to unpack .../068-libwavpack1_5.6.0-1build1_amd64.deb ...
Unpacking libwavpack1:amd64 (5.6.0-1build1) ...
Selecting previously unselected package gstreamer1.0-plugins-good:amd64.
Preparing to unpack .../069-gstreamer1.0-plugins-good_1.24.2-1ubuntu1.1_amd64.deb ...
Unpacking gstreamer1.0-plugins-good:amd64 (1.24.2-1ubuntu1.1) ...
Selecting previously unselected package libxv1:amd64.
Preparing to unpack .../070-libxv1_2%3a1.0.11-1.1build1_amd64.deb ...
Unpacking libxv1:amd64 (2:1.0.11-1.1build1) ...
Selecting previously unselected package gstreamer1.0-x:amd64.
Preparing to unpack .../071-gstreamer1.0-x_1.24.2-1ubuntu0.2_amd64.deb ...
Unpacking gstreamer1.0-x:amd64 (1.24.2-1ubuntu0.2) ...
Selecting previously unselected package ipxe-qemu.
Preparing to unpack .../072-ipxe-qemu_1.21.1+git-20220113.fbbdc3926-0ubuntu2_all.deb ...
Unpacking ipxe-qemu (1.21.1+git-20220113.fbbdc3926-0ubuntu2) ...
Selecting previously unselected package ipxe-qemu-256k-compat-efi-roms.
Preparing to unpack .../073-ipxe-qemu-256k-compat-efi-roms_1.0.0+git-20150424.a25a16d-0ubuntu5_all.deb ...
Unpacking ipxe-qemu-256k-compat-efi-roms (1.0.0+git-20150424.a25a16d-0ubuntu5) ...
Selecting previously unselected package libbrlapi0.8:amd64.
Preparing to unpack .../074-libbrlapi0.8_6.6-4ubuntu5_amd64.deb ...
Unpacking libbrlapi0.8:amd64 (6.6-4ubuntu5) ...
Selecting previously unselected package libdecor-0-0:amd64.
Preparing to unpack .../075-libdecor-0-0_0.2.2-1build2_amd64.deb ...
Unpacking libdecor-0-0:amd64 (0.2.2-1build2) ...
Selecting previously unselected package libdecor-0-plugin-1-gtk:amd64.
Preparing to unpack .../076-libdecor-0-plugin-1-gtk_0.2.2-1build2_amd64.deb ...
Unpacking libdecor-0-plugin-1-gtk:amd64 (0.2.2-1build2) ...
Selecting previously unselected package libsamplerate0:amd64.
Preparing to unpack .../077-libsamplerate0_0.2.2-4build1_amd64.deb ...
Unpacking libsamplerate0:amd64 (0.2.2-4build1) ...
Selecting previously unselected package libjack-jackd2-0:amd64.
Preparing to unpack .../078-libjack-jackd2-0_1.9.21~dfsg-3ubuntu3_amd64.deb ...
Unpacking libjack-jackd2-0:amd64 (1.9.21~dfsg-3ubuntu3) ...
Selecting previously unselected package libwebrtc-audio-processing1:amd64.
Preparing to unpack .../079-libwebrtc-audio-processing1_0.3.1-0ubuntu6_amd64.deb ...
Unpacking libwebrtc-audio-processing1:amd64 (0.3.1-0ubuntu6) ...
Selecting previously unselected package libspa-0.2-modules:amd64.
Preparing to unpack .../080-libspa-0.2-modules_1.0.5-1ubuntu3_amd64.deb ...
Unpacking libspa-0.2-modules:amd64 (1.0.5-1ubuntu3) ...
Selecting previously unselected package libpipewire-0.3-0t64:amd64.
Preparing to unpack .../081-libpipewire-0.3-0t64_1.0.5-1ubuntu3_amd64.deb ...
Unpacking libpipewire-0.3-0t64:amd64 (1.0.5-1ubuntu3) ...
Selecting previously unselected package libpipewire-0.3-common.
Preparing to unpack .../082-libpipewire-0.3-common_1.0.5-1ubuntu3_all.deb ...
Unpacking libpipewire-0.3-common (1.0.5-1ubuntu3) ...
Selecting previously unselected package libpolkit-agent-1-0:amd64.
Preparing to unpack .../083-libpolkit-agent-1-0_124-2ubuntu1.24.04.2_amd64.deb ...
Unpacking libpolkit-agent-1-0:amd64 (124-2ubuntu1.24.04.2) ...
Selecting previously unselected package libxss1:amd64.
Preparing to unpack .../084-libxss1_1%3a1.2.3-1build3_amd64.deb ...
Unpacking libxss1:amd64 (1:1.2.3-1build3) ...
Selecting previously unselected package libsdl2-2.0-0:amd64.
Preparing to unpack .../085-libsdl2-2.0-0_2.30.0+dfsg-1ubuntu3.1_amd64.deb ...
Unpacking libsdl2-2.0-0:amd64 (2.30.0+dfsg-1ubuntu3.1) ...
Selecting previously unselected package libslirp0:amd64.
Preparing to unpack .../086-libslirp0_4.7.0-1ubuntu3_amd64.deb ...
Unpacking libslirp0:amd64 (4.7.0-1ubuntu3) ...
Selecting previously unselected package libspice-server1:amd64.
Preparing to unpack .../087-libspice-server1_0.15.1-1build2_amd64.deb ...
Unpacking libspice-server1:amd64 (0.15.1-1build2) ...
Selecting previously unselected package libtpms0:amd64.
Preparing to unpack .../088-libtpms0_0.9.3-0ubuntu4_amd64.deb ...
Unpacking libtpms0:amd64 (0.9.3-0ubuntu4) ...
Selecting previously unselected package libvirglrenderer1:amd64.
Preparing to unpack .../089-libvirglrenderer1_1.0.0-1ubuntu2_amd64.deb ...
Unpacking libvirglrenderer1:amd64 (1.0.0-1ubuntu2) ...
Selecting previously unselected package libvirt-daemon-driver-qemu.
Preparing to unpack .../090-libvirt-daemon-driver-qemu_10.0.0-2ubuntu8.7_amd64.deb ...
Unpacking libvirt-daemon-driver-qemu (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libvirt-daemon.
Preparing to unpack .../091-libvirt-daemon_10.0.0-2ubuntu8.7_amd64.deb ...
Unpacking libvirt-daemon (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libvirt-daemon-config-network.
Preparing to unpack .../092-libvirt-daemon-config-network_10.0.0-2ubuntu8.7_all.deb ...
Unpacking libvirt-daemon-config-network (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libvirt-daemon-config-nwfilter.
Preparing to unpack .../093-libvirt-daemon-config-nwfilter_10.0.0-2ubuntu8.7_all.deb ...
Unpacking libvirt-daemon-config-nwfilter (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package systemd-container.
Preparing to unpack .../094-systemd-container_255.4-1ubuntu8.8_amd64.deb ...
Unpacking systemd-container (255.4-1ubuntu8.8) ...
Selecting previously unselected package libvirt-daemon-system-systemd.
Preparing to unpack .../095-libvirt-daemon-system-systemd_10.0.0-2ubuntu8.7_all.deb ...
Unpacking libvirt-daemon-system-systemd (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package xml-core.
Preparing to unpack .../096-xml-core_0.19_all.deb ...
Unpacking xml-core (0.19) ...
Selecting previously unselected package polkitd.
Preparing to unpack .../097-polkitd_124-2ubuntu1.24.04.2_amd64.deb ...
Unpacking polkitd (124-2ubuntu1.24.04.2) ...
Selecting previously unselected package libvirt-daemon-system.
Preparing to unpack .../098-libvirt-daemon-system_10.0.0-2ubuntu8.7_amd64.deb ...
Unpacking libvirt-daemon-system (10.0.0-2ubuntu8.7) ...
Selecting previously unselected package libxml2-utils.
Preparing to unpack .../099-libxml2-utils_2.9.14+dfsg-1.3ubuntu3.3_amd64.deb ...
Unpacking libxml2-utils (2.9.14+dfsg-1.3ubuntu3.3) ...
Selecting previously unselected package lvm2.
Preparing to unpack .../100-lvm2_2.03.16-3ubuntu3.2_amd64.deb ...
Unpacking lvm2 (2.03.16-3ubuntu3.2) ...
Selecting previously unselected package mdevctl.
Preparing to unpack .../101-mdevctl_1.3.0-1ubuntu2_amd64.deb ...
Unpacking mdevctl (1.3.0-1ubuntu2) ...
Selecting previously unselected package python3-cairo.
Preparing to unpack .../102-python3-cairo_1.25.1-2build2_amd64.deb ...
Unpacking python3-cairo (1.25.1-2build2) ...
Selecting previously unselected package python3-gi-cairo.
Preparing to unpack .../103-python3-gi-cairo_3.48.2-1_amd64.deb ...
Unpacking python3-gi-cairo (3.48.2-1) ...
Selecting previously unselected package qemu-system-common.
Preparing to unpack .../104-qemu-system-common_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-system-common (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package qemu-system-data.
Preparing to unpack .../105-qemu-system-data_1%3a8.2.2+ds-0ubuntu1.7_all.deb ...
Unpacking qemu-system-data (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package libfdt1:amd64.
Preparing to unpack .../106-libfdt1_1.7.0-2build1_amd64.deb ...
Unpacking libfdt1:amd64 (1.7.0-2build1) ...
Selecting previously unselected package seabios.
Preparing to unpack .../107-seabios_1.16.3-2_all.deb ...
Unpacking seabios (1.16.3-2) ...
Selecting previously unselected package qemu-system-x86.
Preparing to unpack .../108-qemu-system-x86_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-system-x86 (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package qemu-system-modules-opengl.
Preparing to unpack .../109-qemu-system-modules-opengl_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-system-modules-opengl (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package qemu-system-gui.
Preparing to unpack .../110-qemu-system-gui_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-system-gui (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package qemu-system-modules-spice.
Preparing to unpack .../111-qemu-system-modules-spice_1%3a8.2.2+ds-0ubuntu1.7_amd64.deb ...
Unpacking qemu-system-modules-spice (1:8.2.2+ds-0ubuntu1.7) ...
Selecting previously unselected package swtpm.
Preparing to unpack .../112-swtpm_0.7.3-0ubuntu5.24.04.1_amd64.deb ...
Unpacking swtpm (0.7.3-0ubuntu5.24.04.1) ...
Selecting previously unselected package swtpm-tools.
Preparing to unpack .../113-swtpm-tools_0.7.3-0ubuntu5.24.04.1_amd64.deb ...
Unpacking swtpm-tools (0.7.3-0ubuntu5.24.04.1) ...
Selecting previously unselected package thin-provisioning-tools.
Preparing to unpack .../114-thin-provisioning-tools_0.9.0-2ubuntu5.1_amd64.deb ...
Unpacking thin-provisioning-tools (0.9.0-2ubuntu5.1) ...
Selecting previously unselected package gir1.2-libvirt-glib-1.0:amd64.
Preparing to unpack .../115-gir1.2-libvirt-glib-1.0_5.0.0-2build3_amd64.deb ...
Unpacking gir1.2-libvirt-glib-1.0:amd64 (5.0.0-2build3) ...
Selecting previously unselected package virt-manager.
Preparing to unpack .../116-virt-manager_1%3a4.1.0-3ubuntu0.1_all.deb ...
Unpacking virt-manager (1:4.1.0-3ubuntu0.1) ...
Selecting previously unselected package libnss-mymachines:amd64.
Preparing to unpack .../117-libnss-mymachines_255.4-1ubuntu8.8_amd64.deb ...
Unpacking libnss-mymachines:amd64 (255.4-1ubuntu8.8) ...
Selecting previously unselected package ovmf.
Preparing to unpack .../118-ovmf_2024.02-2ubuntu0.4_all.deb ...
Unpacking ovmf (2024.02-2ubuntu0.4) ...
Setting up libip4tc2:amd64 (1.8.10-3ubuntu2) ...
Setting up libpipewire-0.3-common (1.0.5-1ubuntu3) ...
Setting up logrotate (3.21.0-2build1) ...
Created symlink /etc/systemd/system/timers.target.wants/logrotate.timer → /usr/lib/systemd/system/logrotate.timer.
Setting up libvirt-daemon-config-network (10.0.0-2ubuntu8.7) ...
Setting up gir1.2-gstreamer-1.0:amd64 (1.24.2-1ubuntu0.1) ...
Setting up libraw1394-11:amd64 (2.1.2-2build3) ...
Setting up libxft2:amd64 (2.3.6-1build1) ...
Setting up libtag1v5-vanilla:amd64 (1.13.1-1build1) ...
Setting up python3-cairo (1.25.1-2build2) ...
Setting up libspeex1:amd64 (1.2.1-2ubuntu2.24.04.1) ...
Setting up libdevmapper-event1.02.1:amd64 (2:1.02.185-3ubuntu3.2) ...
Setting up libv4lconvert0t64:amd64 (1.26.1-4build3) ...
Setting up libpangoxft-1.0-0:amd64 (1.52.1+ds-1build1) ...
Setting up libtwolame0:amd64 (0.4.0-2build3) ...
Setting up gir1.2-gdkpixbuf-2.0:amd64 (2.42.10+dfsg-3ubuntu3.1) ...
Setting up gir1.2-spiceclientglib-2.0:amd64 (0.42-2ubuntu2) ...
Setting up gir1.2-atk-1.0:amd64 (2.52.0-1build1) ...
Setting up libip6tc2:amd64 (1.8.10-3ubuntu2) ...
Setting up libdbusmenu-glib4:amd64 (18.10.20180917~bzr492+repack1-3.1ubuntu5) ...
Setting up libspice-server1:amd64 (0.15.1-1build2) ...
Setting up netcat-openbsd (1.226-1ubuntu2) ...
update-alternatives: using /bin/nc.openbsd to provide /bin/nc (nc) in auto mode
Setting up gir1.2-libvirt-glib-1.0:amd64 (5.0.0-2build3) ...
Setting up msr-tools (1.3-5build1) ...
Setting up libwebrtc-audio-processing1:amd64 (0.3.1-0ubuntu6) ...
Setting up gettext-base (0.21-14ubuntu2) ...
Setting up libnftnl11:amd64 (1.2.6-2build1) ...
Setting up libharfbuzz-gobject0:amd64 (8.3.0-2build2) ...
Setting up libfdt1:amd64 (1.7.0-2build1) ...
Setting up libayatana-ido3-0.4-0:amd64 (0.10.1-1build2) ...
Setting up gir1.2-harfbuzz-0.0:amd64 (8.3.0-2build2) ...
Setting up acl (2.3.2-1build1.1) ...
Setting up ovmf (2024.02-2ubuntu0.4) ...
Setting up dns-root-data (2024071801~ubuntu0.24.04.1) ...
Setting up libasound2-data (1.2.11-1ubuntu0.1) ...
Setting up gir1.2-pango-1.0:amd64 (1.52.1+ds-1build1) ...
Setting up libgstreamer-plugins-good1.0-0:amd64 (1.24.2-1ubuntu1.1) ...
Setting up libasound2t64:amd64 (1.2.11-1ubuntu0.1) ...
Setting up libslang2:amd64 (2.3.3-3build2) ...
Setting up libvirglrenderer1:amd64 (1.0.0-1ubuntu2) ...
Setting up libspa-0.2-modules:amd64 (1.0.5-1ubuntu3) ...
Setting up libxv1:amd64 (2:1.0.11-1.1build1) ...
Setting up libshout3:amd64 (2.4.6-1build2) ...
Setting up thin-provisioning-tools (0.9.0-2ubuntu5.1) ...
Setting up udev (255.4-1ubuntu8.8) ...
Creating group 'input' with GID 995.
Creating group 'sgx' with GID 994.
Creating group 'kvm' with GID 993.
Creating group 'render' with GID 992.
fchownat() of /dev/snd/seq failed: Operation not permitted
fchownat() of /dev/snd/timer failed: Operation not permitted
fchownat() of /dev/loop-control failed: Operation not permitted
fchmod() of /dev/kvm failed: Operation not permitted
fchmod() of /dev/vhost-net failed: Operation not permitted
fchmod() of /dev/vhost-vsock failed: Operation not permitted
Setting up libvirt-daemon-driver-qemu (10.0.0-2ubuntu8.7) ...
Setting up libdv4t64:amd64 (1.0.0-17.1build1) ...
Setting up qemu-system-data (1:8.2.2+ds-0ubuntu1.7) ...
Setting up seabios (1.16.3-2) ...
Setting up systemd-hwe-hwdb (255.1.4) ...
Setting up libv4l-0t64:amd64 (1.26.1-4build3) ...
Setting up systemd-container (255.4-1ubuntu8.8) ...
Created symlink /etc/systemd/system/multi-user.target.wants/machines.target → /usr/lib/systemd/system/machines.targ
et.
Setting up libvpx9:amd64 (1.14.0-1ubuntu2.2) ...
Setting up libslirp0:amd64 (4.7.0-1ubuntu3) ...
Setting up alsa-topology-conf (1.2.5.1-2) ...
Setting up libtag1v5:amd64 (1.13.1-1build1) ...
Setting up cpu-checker (0.7-1.3build2) ...
Setting up libwavpack1:amd64 (5.6.0-1build1) ...
Setting up libnfnetlink0:amd64 (1.0.2-2build1) ...
Setting up ipxe-qemu (1.21.1+git-20220113.fbbdc3926-0ubuntu2) ...
Setting up libdecor-0-0:amd64 (0.2.2-1build2) ...
Setting up libpolkit-agent-1-0:amd64 (124-2ubuntu1.24.04.2) ...
Setting up ipxe-qemu-256k-compat-efi-roms (1.0.0+git-20150424.a25a16d-0ubuntu5) ...
Setting up sgml-base (1.31) ...
Setting up libbrlapi0.8:amd64 (6.6-4ubuntu5) ...
Setting up libxss1:amd64 (1:1.2.3-1build3) ...
Setting up libtpms0:amd64 (0.9.3-0ubuntu4) ...
Setting up dmidecode (3.5-3ubuntu0.1) ...
Setting up libxml2-utils (2.9.14+dfsg-1.3ubuntu3.3) ...
Setting up libvirt-daemon-config-nwfilter (10.0.0-2ubuntu8.7) ...
Setting up libsamplerate0:amd64 (0.2.2-4build1) ...
Setting up libayatana-indicator3-7:amd64 (0.9.4-1build1) ...
Setting up python3-gi-cairo (3.48.2-1) ...
Setting up libgtksourceview-4-common (4.8.4-5build4) ...
Setting up libpipewire-0.3-0t64:amd64 (1.0.5-1ubuntu3) ...
Setting up libdecor-0-plugin-1-gtk:amd64 (0.2.2-1build2) ...
Setting up libgudev-1.0-0:amd64 (1:238-5ubuntu1) ...
Setting up libaa1:amd64 (1.4p5-51.1) ...
Setting up libiec61883-0:amd64 (1.2.0-6build1) ...
Setting up mdevctl (1.3.0-1ubuntu2) ...
Setting up libavc1394-0:amd64 (0.5.4-5build3) ...
Setting up libdbusmenu-gtk3-4:amd64 (18.10.20180917~bzr492+repack1-3.1ubuntu5) ...
Setting up libnftables1:amd64 (1.0.9-1build1) ...
Setting up gstreamer1.0-x:amd64 (1.24.2-1ubuntu0.2) ...
Setting up nftables (1.0.9-1build1) ...
Setting up qemu-system-common (1:8.2.2+ds-0ubuntu1.7) ...
Created symlink /etc/systemd/system/multi-user.target.wants/qemu-kvm.service → /usr/lib/systemd/system/qemu-kvm.ser
vice.
Setting up libcaca0:amd64 (0.99.beta20-4build2) ...
Setting up alsa-ucm-conf (1.2.10-1ubuntu5.7) ...
Setting up gstreamer1.0-plugins-good:amd64 (1.24.2-1ubuntu1.1) ...
Setting up qemu-system-x86 (1:8.2.2+ds-0ubuntu1.7) ...
Setting up gir1.2-gtk-3.0:amd64 (3.24.41-4ubuntu1.3) ...
Setting up libparted2t64:amd64 (3.6-4build1) ...
Removing 'diversion of /lib/x86_64-linux-gnu/libparted.so.2 to /lib/x86_64-linux-gnu/libparted.so.2.usr-is-merged b
y libparted2t64'
Removing 'diversion of /lib/x86_64-linux-gnu/libparted.so.2.0.5 to /lib/x86_64-linux-gnu/libparted.so.2.0.5.usr-is-
merged by libparted2t64'
Setting up libgtksourceview-4-0:amd64 (4.8.4-5build4) ...
Setting up libnss-mymachines:amd64 (255.4-1ubuntu8.8) ...
Setting up libayatana-appindicator3-1 (0.5.93-1build3) ...
Setting up swtpm (0.7.3-0ubuntu5.24.04.1) ...
Setting up gir1.2-gtk-vnc-2.0:amd64 (1.3.1-1build2) ...
Setting up libvirt-daemon-system-systemd (10.0.0-2ubuntu8.7) ...
Setting up gir1.2-spiceclientgtk-3.0:amd64 (0.42-2ubuntu2) ...
Setting up libjack-jackd2-0:amd64 (1.9.21~dfsg-3ubuntu3) ...
Setting up libnetfilter-conntrack3:amd64 (1.0.9-6build1) ...
Setting up xml-core (0.19) ...
Setting up gir1.2-vte-2.91:amd64 (0.76.0-1ubuntu0.1) ...
Setting up libvirt-daemon (10.0.0-2ubuntu8.7) ...
Setting up libsdl2-2.0-0:amd64 (2.30.0+dfsg-1ubuntu3.1) ...
Setting up gir1.2-ayatanaappindicator3-0.1 (0.5.93-1build3) ...
Setting up gir1.2-gtksource-4:amd64 (4.8.4-5build4) ...
Setting up qemu-system-modules-opengl (1:8.2.2+ds-0ubuntu1.7) ...
Setting up iptables (1.8.10-3ubuntu2) ...
update-alternatives: using /usr/sbin/iptables-legacy to provide /usr/sbin/iptables (iptables) in auto mode
update-alternatives: using /usr/sbin/ip6tables-legacy to provide /usr/sbin/ip6tables (ip6tables) in auto mode
update-alternatives: using /usr/sbin/iptables-nft to provide /usr/sbin/iptables (iptables) in auto mode
update-alternatives: using /usr/sbin/ip6tables-nft to provide /usr/sbin/ip6tables (ip6tables) in auto mode
update-alternatives: using /usr/sbin/arptables-nft to provide /usr/sbin/arptables (arptables) in auto mode
update-alternatives: using /usr/sbin/ebtables-nft to provide /usr/sbin/ebtables (ebtables) in auto mode
Setting up qemu-system-gui (1:8.2.2+ds-0ubuntu1.7) ...
Setting up parted (3.6-4build1) ...
Setting up dnsmasq-base (2.90-2build2) ...
Setting up swtpm-tools (0.7.3-0ubuntu5.24.04.1) ...
info: Selecting GID from range 100 to 999 ...
info: Adding group `swtpm' (GID 107) ...
info: The home dir /var/lib/swtpm you specified can't be accessed: No such file or directory
info: Selecting UID from range 100 to 999 ...
info: Adding system user `swtpm' (UID 103) ...
info: Adding new user `swtpm' (UID 103) with group `swtpm' ...
info: Not creating home directory `/var/lib/swtpm'.
Setting up virt-manager (1:4.1.0-3ubuntu0.1) ...
Setting up qemu-system-modules-spice (1:8.2.2+ds-0ubuntu1.7) ...
Setting up liblvm2cmd2.03:amd64 (2.03.16-3ubuntu3.2) ...
Setting up dmeventd (2:1.02.185-3ubuntu3.2) ...
Created symlink /etc/systemd/system/sockets.target.wants/dm-event.socket → /usr/lib/systemd/system/dm-event.socket.
Setting up lvm2 (2.03.16-3ubuntu3.2) ...
Created symlink /etc/systemd/system/sysinit.target.wants/blk-availability.service → /usr/lib/systemd/system/blk-ava
ilability.service.
Created symlink /etc/systemd/system/sysinit.target.wants/lvm2-monitor.service → /usr/lib/systemd/system/lvm2-monito
r.service.
Created symlink /etc/systemd/system/sysinit.target.wants/lvm2-lvmpolld.socket → /usr/lib/systemd/system/lvm2-lvmpol
ld.socket.
Processing triggers for libglib2.0-0t64:amd64 (2.80.0-6ubuntu3.4) ...
Processing triggers for dbus (1.14.10-4ubuntu4.1) ...
Processing triggers for hicolor-icon-theme (0.17-2) ...
Processing triggers for libc-bin (2.39-0ubuntu8.4) ...
Processing triggers for man-db (2.12.0-4build2) ...
Processing triggers for sgml-base (1.31) ...
Setting up polkitd (124-2ubuntu1.24.04.2) ...
Creating group 'polkitd' with GID 991.
Creating user 'polkitd' (User for polkitd) with UID 991 and GID 991.
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of reload.
start-stop-daemon: unable to stat /usr/libexec/polkitd (No such file or directory)
Setting up libvirt-daemon-system (10.0.0-2ubuntu8.7) ...
useradd warning: libvirt-qemu's uid 64055 is greater than SYS_UID_MAX 999
chown: changing ownership of '/var/lib/libvirt/qemu/': Operation not permitted
dpkg: error processing package libvirt-daemon-system (--configure):
installed libvirt-daemon-system package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
libvirt-daemon-system
E: Sub-process /usr/bin/dpkg returned an error code (1)
📦[waylon@ubuntu ~]$ virt-manager
csv
perfect
just
Copier has a few quirks with vcs that I just discovered by trying to test out some changes. I may have some config that I have long forgotten about somewhere deep in my dotfiles, I don’t think so, but id love to be wrong and corrected, please reach out.
What Doesn’t Work #
I tried throwing everything at this template to make it work. I tried a bunch of flags that did not work. I tried making commits to the local repo to get rid of the dirty warning. I really wanted to test new changes locally without committing and pushing untested and potentially broken changes.
uvx copier copy ../markata-blog-starter .
uvx copier copy gh:waylonwalker/markata-blog-starter@develop .
uvx copier copy ../markata-blog-starter . -wlg --trust
What Works - –vcs-ref #
Finally after trying everything to get the local copy to work, and my guess of @branch not working I found this to work. It does require me to go to the repo on my develop branch.
uvx copier copy gh:waylonwalker/markata-blog-starter --vcs-ref develop .
git">What Works - delete .git #
Really this might be my best option to make quick changes and test them locally without going through a version control system. It is not ideal, but makes it easy to quickly iterate on. I might be renaming .git, or copying to /tmp for quick iteration.
rm -rf .git
uvx copier copy ../markata-blog-starter .
Copier I love #
Copier is a great templating tool. I really love it. I use it every single day to create posts on this blog using Tmux hotkey for copier templates. This is the first time this quirk has got me and it had me puzzled for 45 minutes as I did not expect this behavior whatsoever.
I’ve been using gitingest web ui [[ thoughts-516 ]] for quite awhile to serialize git repo into llm friendly text files. This gives tools context about repos that are not in the training data so that it knows about it and how to use the code in the repo. gitingest also has a python library [[ thoughts-517 ]]
I had a use case for a project not yet on git, and found yek.
Installing yek #
Their instructions tell you to curl to bash.
curl -fsSL https://bodo.run/yek.sh | bash
I don’t like curl to bash from random sites, so I have my own self hosted version of i.jpillora.com. I like using this because it pulls from github and I trust github as a source for artifacts as good as the repo I am pulling from.
curl https://i.jpillora.com/bodo-run/yek | bash
Using yek #
yek
/tmp/yek-output/yek-output-bb01e621.txt
This will give you a link to a text file that you can add to many llm tools. This happened so fast for me that I didn’t even believe that it worked properly.
more options #
As with most clis, you can run yek --help to see the options available.
yek --help
Today I discovered brightnessctl to adjust the screen brightness on my
AwesomeWM machine. Its a command line utility that you can use to adjust the
brightness of your screen. A command line interface like this gives you the
ability to bind keys with something like [[xbindkeys]] or your window manager
configuration.
sudo apt install brightnessctl
# or
paru -S brightnessctl
Now that you have it installed you can use it to adjust the brightness of your screen, this worked particularly well for my laptop screen, I don’t think this works for monitors, in my experience they are usually controlled by the built in osd.
# Increase brightness by 10%
brightnessctl set +10%
# Decrease brightness by 10%
brightnessctl set 10%-
# Set brightness to 50%
brightnessctl set 50%
# Set brightness to 100%
brightnessctl set 100%
Note
on my machine I had to use `sudo` to run the command, otherwise I got the following error:
Can't modify brightness: Permission denied
You should run this program with root privileges.
Alternatively, get write permissions for device files.
The rich console is themeable, I’ve been a long time user of rich and had no
Idea. You can define your own theme keywords and use them just like you use
normal rich keywords in square brackets like'[bold red]'.
from rich.console import Console
from rich.theme import Theme
custom_theme = Theme({
"info": "dim cyan",
"warning": "magenta",
"danger": "bold red"
})
console = Console(theme=custom_theme)
console.print("This is information", style="info")
console.print("[warning]The pod bay doors are locked[/warning]")
console.print("Something terrible happened!", style="danger")
You can unset multiple environment variables at once. I did not know this was a
thing, its something that ended up happening organically on a call and asking
someone to run unset. They had never done it before and did not know how it
works, but did exactly as I said instead of what I meant. I like this handy
shortcut doing it in one line rather than each one individually, I will be
using this in the future. You might need this for something like
running aws cli commands with localstack.
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DEFAULT_REGION
i3lock is a fantastic lockscreen for tiling window managers.
If you are using a tiling window manager within a public space you need to add a lockscreen. I have one machine that I take with me to a public space. Its secure enough that I can leave it, but not secure enough that I want to leave it unlocked. So when I need to leave it behind for the restroom I need to lock it up.
paru -S i3lock
# or
apt install i3lock
Now that you have i3lock installed lets lock that screen.
# lock it with a pure white flashbang
i3lock
# lock it with a black background
i3lock -c 000000
# lock it with a custom color
i3lock -c 2e1330
# lock it with a wallpaper
i3lock -c 000000 ~/Pictures/Wallpapers/mywallpaper.png
You can use your window manager or something more generic like xbindkeys to set
a hotkey. This way you don’t have to open a terminal and type out the command
every time you leave your desk. You can just press something like SUPER+L
like you would on other OS’s.
Fancy #
If you like it a bit fancier, you can use i3lock-fancy, it can blur,
pixelate, and greyscale your current screen. I did not really like this
because you can still tell what is going on the screen. It’s probably secure
enough and looks better, but I went with regular i3lock.
paru -S i3lock-fancy-git
# or
apt install i3lock-fancy
kraft
my nvim spellcheck setup
If you need to target a specific k8s node in the cluster, you can use labels. You want to treat your nodes as much like cattle as you can, but sometimes budgets get in the way. You might be like me and just run any free hardware you can get in your cluster, or you might have some large storage or gpu needs that you can’t afford to put on every node in the cluster.
kubectl get nodes --show-labels
# add the bigpool label
kubectl label node k8s-1 bigpool=true
kubectl get nodes --show-labels
# remove the bigpool label
kubectl label node k8s-1 bigpool-
To use the label in a pod set spec.nodeSelector to the label that you
applied.
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: busybox
nodeSelector:
bigpool: "true"
/wants
Linux Is About Choice
I’m currently [[replacing-google-search-apps-with-self-hosted-web-apps]] and decided to create a simple b64 encoder/decoder, just start typing to enter text, escape to deselect, then e/d to encode/decode.
I’m trying to make these apps super simple, self hosted out of minio, static html, and javascript. It’s been fun to get back to some simple interactive web development like this. No build just a website that does something. No broken builds, no containers to deploy, just push to minio.
encoded = btoa(content);
decoded = atob(encoded);
Here is the result.
tinyapps
I’m trying to replace my usage of google inline search apps with real apps, today I used a stopwatch to time some things out at work by opening stopwatch. This was something I just wanted running in a tab on another screen, it was not timing running code or anything, I was using it as a reminder to check browser caches every 5 minutes or so for some testing.
So tonight I whipped up a stopwatch, clock and timer, all of which are using the wakelock API to keep the screen on while the app is running.
// Wake Lock support
let wakeLock = null;
async function requestWakeLock() {
try {
if ('wakeLock' in navigator) {
wakeLock = await navigator.wakeLock.request('screen');
console.log("Wake lock acquired");
}
} catch (err) {
console.error("Wake lock error:", err);
}
}
document.addEventListener("visibilitychange", () => {
if (wakeLock !== null && document.visibilityState === "visible") {
requestWakeLock();
}
});
requestWakeLock();
I’ve been working on ninesui, inspired by k9s see thoughts-633. I want a good flow for making video for the readme and I am using charm.sh’s vhs for this. Its running in an archBTW distrobox and looks gawdaweful.
The over saturated colors give it a really retro look, seems fine, but not my
cup of tea. I tried to change the textual theme to tokyo-night and it might
have made it a bit better, but still over-saturated.
After #
What I found is that vhs has themes, setting it to dracula made everything much better.
# sort.tape
Output assets/sort.mp4
Output assets/sort.gif
Require echo
Set Shell "bash"
Set FontSize 32
Set Width 1920
Set Height 1080
+ Set Theme 'Dracula'
NinesUI #
I’m using these in my ninesui project, right now they are in the readme, but maybe some docs will grow eventually. Right now its hardcore explore phase.
m9a devlog 1
I’m trying to learn proper logs, monitoring, otel, and grafana. Today I imported a bunch of pre-made k8s dashboards and made a few of my own for specific apps, and it made me want to know how I can turn my own custom dashboards into infrastructure as code. Turns out grafana makes it pretty easy to do this, if you have the grafana dashboard sidecar running. It will pick up any ConfigMap with the grafana_dashboard label and import it.
Go to Dashboards -> Pick a Dashboard -> Export -> JSON.
apiVersion: v1
kind: ConfigMap
metadata:
name: my-dashboard
namespace: meta
labels:
grafana_dashboard: "1"
data:
my-dashboard.json: |
{
"annotations": {
"list": [
...
"uid": "fel2uhjhepg5ce",
"version": 3
}
hollow knight home row layout
fix feed descriptions
I’ve been using ruff to lint my python code for quite awhile now, I was pretty early to jump on it after release. Some of my projects have had a nice force-single-line setting and some have not. I dug into the docs and it was not clear what I needed to make it work.
[tool.ruff]
select = ['I'] # you probably want others as well
[tool.ruff.isort]
force-single-line = true
Turns out I was missing Isort in the select list.
I was looking back at my analytics page today and wondered what were my posts about back at the beginning. My blog is managed by markata so I looked at a few ways you could pull those posts up. Turns out it’s pretty simple to do, use the markata map with a filter.
from markata import Markata
m.map('title, slug, date', filter='date.year==2016', sort='date')
Note
the filter is python eval that should evaluate to a boolean, all of the
attributes of the post are available to filter on.
Result #
[
('⭐ jupyterlab jupyterlab', 'jupyterlab-jupyterlab', datetime.date(2016, 12, 13)),
('⭐ nickhould tidy-data-python', 'nickhould-tidy-data-python', datetime.date(2016, 12, 9)),
(
'⭐ mikeckennedy write-pythonic-code-demos',
'mikeckennedy-write-pythonic-code-demos',
datetime.date(2016, 11, 22)
),
(
'⭐ mikeckennedy write-pythonic-code-for-better-data-science-webcast',
'mikeckennedy-write-pythonic-code-for-better-data-science-webcast',
datetime.date(2016, 11, 22)
),
('⭐ rajshah4 dlgroup', 'rajshah4-dlgroup', datetime.date(2016, 11, 18)),
('⭐ pandas-dev pandas', 'pandas-dev-pandas', datetime.date(2016, 10, 5))
]
You could use the list command as well right within your shell and the same
map and filters work.
⬢ [devtainer-0.1.3] ❯ markata list --map title --filter='date.year==2016'
[22:35:06] 2088/2145 posts skipped skip.py:36
57/2145 posts not skipped skip.py:37
⭐ pandas-dev pandas
⭐ rajshah4 dlgroup
⭐ mikeckennedy write-pythonic-code-for-better-data-science-webcast
⭐ mikeckennedy write-pythonic-code-demos
⭐ nickhould tidy-data-python
⭐ jupyterlab jupyterlab
You could also do it with jin right inside of a markdown post using the jinja_md plugin.
{% raw %}
{% for title, slug, date in markata.map('title, slug, date', filter='date.year==2016', sort='date') %}
* [{{title}}]({{slug}}) - {{date}}
{% endfor %}
{% endraw %}
Note
You do have to `jinja: true` in the frontmatter of the post.
Result #
{% for title, slug, date in markata.map(’title, slug, date’, filter=‘date.year==2016’, sort=‘date’) %}
- {{title}} - {{date}} {% endfor %}
portal-platformer-devlog-1
backup distrobox image
Changing k8s Storage Class - Migration Job
homelab drive ids
Using pbpaste for command substitution keeps sensitive or long URLs out of
your shell history. Instead of typing git clone https://github.com/user/repo-with-long-name.git, copy the URL to clipboard and
run git clone "$(pbpaste)". This prevents the URL from appearing in
~/.bash_history or ~/.zsh_history.
To get pbpaste working on both Xorg and Wayland, add this to your shell config:
if [[ $(command -v wl-copy) ]]; then
alias pbcopy='wl-copy'
pbpaste() { wl-paste; }
elif [[ $(command -v xclip) ]]; then
alias pbcopy='xclip -selection clipboard'
pbpaste() { xclip -selection clipboard -o; }
fi
The function approach (instead of alias) enables command substitution, while
the quotes around $(pbpaste) handle spaces and special characters safely.
Now you can use it.
git clone "$(pbpaste)"
More importantly secrets can stay out of your history.
export GITHUB_TOKEN="$(pbpaste)"
export AWS_ACCESS_KEY_ID="$(pbpaste)"
export AWS_SECRET_ACCESS_KEY="$(pbpaste)"
export DATABASE_URL="$(pbpaste)"
THPS 1 Downtown Gaps
THPS 1 Skate Park Gaps
Trailmakers Pioneers
THPS 1 School Gaps
THPS 1 Mall Gaps
THPS 1 Warehouse Gaps
I run tailwind for my personal blog, whenever I update it, pre-commit goes in and fixes end of file. I’m sick of these things fighting each other, since it is a generated app it is going to et ignored from pre-commit from now on.
exclude: ^static/app.*\.css$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
fragmention
I’m building in a [[ fragmentions ]] implementation into my blog, I wanted to add some text before the fragment to indidate that it was the highlighted fragment that someone may have intended to share with you.
To get a newline in a :before I need to use \A and white-space: pre-line.
body :target::before,
body [fragmention]::before {
content: "Highlighted Fragment:\A";
white-space: pre-line;
@apply font-bold text-yellow-600;
}
Here is what it looks like on my not yet live implementation of fragmentions.
fixed long standing nvim startup error
Testing fresh nvim installs can be a pain, and hard to di without borking your
known good install. I’ve been using NVIM_APPNAME to run a test nvim in a
sandbox that wont bork my main install. This usually runs for me in under a
minute, can be down under 15s if I remove some of the TreeSitter installs at
the end. This beats a full docker build of my full devtainer to test out nvim
packaging woes.
rm ~/.cache/wwtest -rf
rm ~/.local/share/wwtest -rf
rm ~/.config/wwtest -rf
cp -r nvim/.config/nvim/ ~/.config/wwtest
NVIM_APPNAME=wwtest nvim --headless "+Lazy sync" +qa
NVIM_APPNAME=wwtest nvim --headless "+TSUpdateSync" "+sleep 5000m" +qa
NVIM_APPNAME=wwtest nvim --headless "+MasonUpdate" +qa
NVIM_APPNAME=wwtest nvim --headless "+TSInstallSync! c cpp go lua python rust tsx javascript typescript vimdoc vim bash yaml toml vue just" +qa
NVIM_APPNAME=wwtest nvim --headless "+MasonInstall lua-language-server rustywind ruff ruff-lsp html-lsp typescript-language-server beautysh fixjson isort markdownlint stylua yamlfmt python-lsp-server" +qa
NVIM_APPNAME=wwtest nvim
I’ve started to use this as a just recipe to run before deploying a new
version of my dotfiles. So far its pairing nicely with nvim-manager
Setting up 4G Backup with Google Fi and Netgear LM1200
ahrefs-cleanup-2024
When I want to put a date in a document like a blog post from vim I use !!date
from insert mode. Note that entering !! from normal mode puts you in command
mode with :.! filled out. This runs a shell command, i.e. date for this
example.
It outputs the following
Fri Jan 31 08:46:11 PM CST 2025
You can also pass in a date such as tommorrow by pasdding in the -d date -d tomorrow.
It outputs the following
Sat Feb 1 08:53:20 PM CST 2025
codeium just taught me this one with autocomplete
:put =strftime('%Y-%m-%d')
This outputs the following
2025-01-31
What I like about the :put =strftime( method is that you can add a format,
but that is a lot more for me to remember than !!date
A few weeks later #
I’m going through a bunch of blog posts and dont want my date formats to change
to the Wed Feb format so I broke down and made these keybindings. I think I’m
still going to be using .!date a lot, but these keybindings will be nice for
editing blog post frontmatter.
set("n", "<leader>dd", "<cmd>put =strftime('%Y-%m-%d')<cr>", { noremap = true, silent = true })
set("n", "<leader>dt", "<cmd>put =strftime('%Y-%m-%d %H:%M:%S')<cr>", { noremap = true, silent = true })
dd 2025-02-12 dt 2025-02-12 12:53:47 - :.!date Wed Feb 12 12:53:47 PM CST 2025
Today I ran into an interesting question, why am I being asked to configure
tzdata while installing npm. Turns out that the aptitude cli has a why
command that very handily nails down why you have something installed on a
debian based system.
Install aptitude #
apt install aptitude
Why tzdata #
Now we can query why we need tzdata and see the full chain with the root
package being npm.
root@47685221fb82:/# aptitude why tzdata
i npm Depends node-gyp
i A node-gyp Depends gyp (>= 0.1+20200513gitcaa6002)
i A gyp Depends python3:any
i A python3 Provides python3:any
i A python3 Depends python3.12 (>= 3.12.3-0~)
i A python3.12 Depends tzdata
Today I ran into this interactive prompt on ubuntu while installing node and npm, and I do not want to manually configure this interactively every time I run an install, moreso in docker I do not have the interactive terminal to do so.
Configuring tzdata
------------------
Please select the geographic area in which you live. Subsequent configuration questions will narrow this down by presenting a list of cities, representing the time zones in which they are located.
1. Africa 2. America 3. Antarctica 4. Arctic 5. Asia 6. Atlantic 7. Australia 8. Europe 9. Indian 10. Pacific 11. Etc 12. Legacy
Geographic area:
Why tzdata #
Checking aptitude why tzdata it shows that the chain goes back through npm.
root@47685221fb82:/# aptitude why tzdata
i npm Depends node-gyp
i A node-gyp Depends gyp (>= 0.1+20200513gitcaa6002)
i A gyp Depends python3:any
i A python3 Provides python3:any
i A python3 Depends python3.12 (>= 3.12.3-0~)
i A python3.12 Depends tzdata
The solution, configure tzdata #
export TZ="America/Chicago"
export DEBIAN_FRONTEND=noninteractive
apt update
apt install tzdata -y
ln -fs /usr/share/zoneinfo/$TZ /etc/localtime
dpkg-reconfigure -f noninteractive tzdata
DEBIAN_FRONTEND=noninteractive
This is required, because apt installing tzdata will trigger the
interactive prompt. You will manually configure it in the next two steps.
dst session one
https://www.youtube.com/watch?v=03KsS09YS4E&t=610s
Today I learned about the basic calculator, bc. At the very end of this video prime uses it to add numbers in vim.
REPL #
You can start a calculator repl at the command line, by running bc.
Vim #
Since bc supports standard unix pipes you can easily pipe data from vim into bc
and back out using !!bc. All you need is a string of math on the line you
want to calculate, go to normal mode and run !!bc to get the answer.
Traditionally I will open my system calculator or ipython to do something like this.
To keep the equation and the result in the same line you can send the equation to stderr and the result to stdout using tee.
:.!tee >(cat >&2) | bc
markdown split panel
Make MinIO Access Key
I’ve been back to putting some images on my blog lately and thinking about
making them a bit thinner through the use of aspect ratio for simplicity. I’m
leaning pretty heavy on tailwindcss these days due to some weird quirks of
markdown-it-attrs I cannot have slashes in classes from markdown so I made a
.cinematic class to achieve this.
.cinematic {
@apply aspect-[2.39/1];
}
Example
Attrs does not like ‘/’ characters in its classes, so to use some tailwind classes with custom values we must make new classes in our tailwind input css.
.cinematic {
@apply aspect-[2.39/1];
}
Given the following markdown with attrs added to the image and to the paragraph block.
{.aspect-[2.39/1]}
{.cinematic}
{.cinematic}

We get the following output with only the middle one working correctly.
Note
The inline version of `.cinematic` works, but `.aspect-[2.39/1]` does not,
it turns into text after the image. The block version with the class before the image applies to the paragraph, not the image.
markata 0.8.0
markata 0.8.0
markata search
Markata DidYouMean
cold builds
pesos
hover z-index and positioning
analytics
I fixed lists in my blog
I built out a tool for myself to manage my nvim configuration, and I wanted to
quickly see which one I am running in my starship prompt. Here’s the config I
ended up with. It warns if the NVIM_APPNAME environment variable is not set, and
it shows which nvim I am using if it is set.
[custom.nvim-manager-system]
when = '[[ ! -n "${NVIM_APPNAME}" ]]'
style = "bold yellow"
symbol = '[ ](fg:#15AABF)'
format = '$symbol[USING SYSTEM NVIM]($style)'
[env_var.NVIM_APPNAME]
style = "green"
symbol = '[ ](fg:#15AABF)'
format = '[$symbol${env_value}]($style)'
variable = "NVIM_APPNAME"
I recently noticed that my og images were missing emoji. They were taken using headless chrome in a container. I fixed it by adding an emoji font in the containerfile / dockerfile.
RUN apt-get update && apt-get install -y \
# Add fonts with emoji support
fonts-noto-color-emoji \
&& rm -rf /var/lib/apt/lists/*
Before #
Here’s what they were looking like with broken emoji fonts.
After #
And now with the fixed emoji font.
I put thought bubbles on my thoughts posts and stars on my github stars posts
a night of rounds
wondercraft session one
nvim-manager
/now
one-shot-s3-cleanup
Today I learned that the docs in postiz are a bit behind, (fantastic docs btw, they are to the point, and cover almost all of what you need). The docs state that you need to include an R2 bucket to handle uploads.
This issue shows that more work has been done, one of which is local storage. The compose file they use in the quick start has the required env variables to set this up.
STORAGE_PROVIDER: "local"
UPLOAD_DIRECTORY: "/uploads"
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
looking into my running instance I can see my images there.
⬢ [devtainer] ❯ podman exec postiz ls /uploads/2025/01/09
811747b3f703f5d9a7f10aff5103412ff0.jpeg
a221db10a76f0c414171ab417379b09ec.jpeg
we beat trailmakers
/start
slow nfs performance
/colophon
setting up ucore-zfs
thoughts 0.0.4
Today i got hit by this accessibility issue on my site. Low contrast links are not distiniquishable. I had not seen this error title before it was new to me, maybe I have bad memory or maybe it’s new to me.
I ended up dropping the background color of the site down a notch as I didn’t
really care for the semi-dark brown anyways. I’m liking the near black
bg-zinc-950 much better now.
Now I got that 100 A11y score in lighthouse.
Today I discovered the Urllink function in bash from the ujust tool from
ublue.it. Seems like a cool trick, but might not work
everywhere.
########
### Special text formating
########
## Function to generate a clickable link, you can call this using
# url=$(Urllink "https://ublue.it" "Visit the ublue website")
# echo "${url}"
function Urllink (){
URL=$1
TEXT=$2
# Generate a clickable hyperlink
printf "\e]8;;%s\e\\%s\e]8;;\e\\" "$URL" "$TEXT${n}"
}
```j
I’ve been debugging a cloudflared tunnel issue in my homelab all day today, and getting really frustrated. My issue ended up being that it was running twice, once without the correct config file and another with it. I believe that cacheing may have compounded the issue.
In yesterday’s post I setup a cloudflared tunnel on my ubuntu server to expose applications running on the server to the internet. I’m setting up a new server and running cloudflared in its own vm.
setup cloudflared tunnel on ubuntu
Check that dns is pointing to the correct tunnel #
dig subdomain.example.com
traceroute subdomain.example.com
Check that the tunnel is running #
export CLOUDFLARED_TUNNEL_ID = "my-tunnel-id"
cloudflared tunnel list
cloudflared tunnel info $CLOUDFLARED_TUNNEL_ID
I run a cloudflared tunnel on my ubuntu server to expose applications running on the server to the internet. I’m setting up a new server and running cloudflared in its own vm.
Get the cloudflared binary #
sudo wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -O /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared
#
Now setup the config directory. For the systemd service to work, the config
file needs to be in /etc/cloudflared. I like to give my user rights to edit
the config file without being sudo, we will do that here by creating a group
cloudflared, add ourselves to the group, give ownership of /etc/cloudflared
to the group, give group write access to the directory, and refresh groups.
sudo mkdir -p /etc/cloudflared
sudo groupadd cloudflared
sudo usermod -aG cloudflared $USER
sudo chown -R root:cloudflared /etc/cloudflared
sudo chmod g+w /etc/cloudflared
newgrp cloudflared
login #
Now we can log into the domain zone with cloudflared.
cloudflared tunnel login
This will give a url, follow it in a browser to log in.
cloudflared tunnel create <NAME>
mv ~/.cloudflared/cert.pem /etc/cloudflared/cert.pem
mv ~/.cloudflared/<tunnel-id>.json /etc/cloudflared/<tunnel-id>.json
config #
Now setup config. For the systemd service to work, the config file needs to be in /etc/cloudflared. The config that I have provided below will expose localhost:8000 to tester.example.com
export CLOUDFLARED_TUNNEL_ID=$(ls /etc/cloudflared/*.json | xargs -n 1 basename | sed 's/\.json$//')
mv ~/.cloudflared/${CLOUDFLARED_TUNNEL_ID}.json /etc/cloudflared/${CLOUDFLARED_TUNNEL_ID}.json
mv ~/.cloudflared/cert.pem /etc/cloudflared/cert.pem
echo "
tunnel: $(CLOUDFLARED_TUNNEL_ID)
credentials-file: /etc/cloudflared/$(CLOUDFLARED_TUNNEL_ID).json
ingress:
- hostname: tester.example.com
service: http://localhost:8000
- service: 'http_status:404'
" >> /etc/cloudflared/config.yaml
dns #
Now to get a dns record for tester.example.com to point to the cloudflared tunnel.
cloudflared tunnel route dns $(CLOUDFLARED_TUNNEL_ID) tester.example.com
systemd #
Now install the systemd service.
sudo cloudflared service install
sudo systemctl status cloudflared.service
# if its not running
sudo systemctl start cloudflared.service
I’ve been playing with 3d printing some items through the slant3d api. I’ve been pricing out different prints by running a slice request through their api.
make a project #
I’ve been using uv for project management. It’s been working well for quick projects like this while making it reproducible, I’m still all in on hatch for libraries.
mkdir slantproject
cd slantproject
uv init
uv venv
. ./.venv/bin/activate
uv add httpx rich python-dotenv
Get an api key #
You will need an api key from the slant api, which currently requires a google account and a credit card to create.
# .env
# replace with your api key from https://api-fe-two.vercel.app/
SLANT_API_KEY=sl-**
slicing an stl with teh slant api #
Then you can run the python script to price out your print. I’m not exactly sure how this compares to an order, especially when you add in different materials.
from dotenv import load_dotenv
import httpx
import os
load_dotenv()
stl_url = ''
api_key = os.environ["SLANT_API_KEY"]
api = httpx.Client(base_url="https://www.slant3dapi.com/api/slicer")
res = httpx.post(
"https://www.slant3dapi.com/api/slicer",
json={"fileURL": stl_url},
headers={"api-key": api_key, "Content-Type": "application/json"},
timeout=60,
)
print(res.json())
After first setting up a new k3s instance your kubeconfig file will be located in /etc/rancher/k3s/k3s.yaml.
You cans use it from here by setting $KUBECONFIG to that file.
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
Or you can copy it to ~/.kube/config
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
If you have installed k3s on a remote server and need the config on your local machine then you will need to modify the server address to reflect the remote server.
scp user@<server-ip>:/etc/rancher/k3s/k3s.yaml ~/.kube/config
Warning
only do this if you don’t already have a ~/.kube/config file, otherwise copy it to a new file and set your $KUBECONFIG env variable to use it.
Now you will need to open that file and change the server address, making sure to keep the port number.
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ****
server: https://<server-ip>:6443
name: default
Today I gave n8n a try using podman, their docs gave me docker commands, but it ran fine on my machine using podman.
podman volume create n8n_data
podman run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n
New-caps-for-porta-john
Vim has a handy feature to format text with gq. You can use it in visual
mode, give it a motion, or if you give it gqq it will format the current line.
I use this quite often while writing in markdown, I do not use softwraps in vim,
so gqq quickly formats my current line into a paragraph. Once I have done
this for a single line one time I typically switch to the motion for around
paragraph gqap to format the whole paragraph and not just the current line.
before formatting #
after formattting #
A slug is the part of the url that comes after the domain. Commonly matches the file name of a markdown file many blogging systems. These are typically human readable, unique identifiers for pages within the site.
Wikilinks are a core concept within obsidian to link to documents by Slug wrapped in double square brackets. These are commonly used within wiki site generators.
[[slug]]
Obsidian gives you a keybinding alt+enter to go to that file, but if it does not exist it will create the file for you in the root of the project. It’s a nice way to quickly make new documents.
blogging in 2024
It was not obvious to me, but if you have a wikilink such as Trying Obsidian, you can jump to the file in obsidian, just like you can with lsp go to definition, the keybinding is alt + enter.
I’ve long used copier to create all of my posts for my blog, and it works really well for my workflow. I think of a title, call a template, and give it a title. out of the box obsidian did not seem to work this way. It seems like it wants me to right click a file tree and make a new file using the tree, this is not my jam.
Here is what I came up with to replace my til template.
---
date: <% tp.file.creation_date() %>
templateKey: til
title: <%*
const originalFileName = await tp.system.prompt("Enter file name");
const toTitleCase = str => str.replace(
/\w\S*/g,
txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
);
const title = toTitleCase(originalFileName);
tR += title + '\n'; // Add the title to the template result
-%>
published: true
tags:
-
---
<%*
const fileName = originalFileName.toLowerCase().replace(/\s+/g, '-');
const newFilePath = `pages/til/${fileName}`;
await tp.file.move(newFilePath);
-%>
<% tp.file.cursor() %>
tRis a return value, and it gets placed directly into the place it is in the fileto.file.cursor()creates a tab-index point so I can tab into the content
Trying Obsidian
I’m giving obsidian a go as an editor for my blog and one of the main things I want to fix in my workflow is the ability to quickly drop in images. on first look through the community plugins I found Image Converter. I set it up to convert to webp and drop them in a git submodule. I may make it something other than a git repo in the future, but I’ve learned that adding images to my blog repo quickly makes it heavy and hard to clone on other machines.
Once the images are there they are pushed and deployed as their own site to
cloudflare pages. I made a quick edit to my sick wikilink hover plugin for
my blog. if it sees a wikilink ending in webp, convert the domain over to
obsidian-assets.waylonwalker.com, and clean up the remaining "! " that the
python md-it library leaves behind.
Note
after first try I needed to increase the width from 600 to 1400, the image in this post was unreadable.
This is part of me getting set up and Trying Obsidian
I’ve had a couple of uploads to twitter fail recently and has been a pain. I tried some online converters for convenience, but none of them worked. I reached out to chatgpt and found succeess with this ffmpeg command.
ffmpeg -i input.mp4 \
-vf "scale=trunc(oh*a/2)*2:min(720\,trunc(ih*a/2)*2)" \
-c:v libx264 -profile:v high -level:v 4.1 \
-b:v 3500k -maxrate 3500k -bufsize 7000k \
-pix_fmt yuv420p \
-c:a aac -b:a 128k -ar 44100 \
-movflags +faststart \
output.mp4
animal well codes
I’ve started leaning in on kubernetes kustomize to customize my manifests per deployment per environment. Today I learned that it comes with a diff command.
kubectl diff -k k8s/overlays/local
You can enable color diffs by using an external diff provider like colordiff.
export KUBECTL_EXTERNAL_DIFF="colordiff -N -u"
You might need to install colordiff if you don’t already have it.
sudo pacman -S colordiff
sudo apt install colordiff
Now I can try out kustomize changes and see the change with kustomize diff.
kubectl dash k
Kubernetes ships with a feature called kustomize that allows you to customize your manifests in a declarative way. It's a bit like helm, but easier to use. I...
Animal well does not let you remap keys, and really doesn’t even inform you that it is keyboard compatible. I had to play around and discover the keymap, which can be a bit tricky on a 40% board. This is what I found.
- wasd - move
- space - jump / a
- enter - interact / b
- x - throw
- c - inventory
- 1 - left item / rb
- 2 - open item menu / triangle
- 3 - right item / lb
I’ve been using fastapi more and more lately and one feature I just started using is background tasks [[ thoughts-333 ]].
Seealso
basic diskcache example <a href="/python-diskcache/" class="wikilink" data-title="How I setup a sqlite cache in python" data-description="When I need to cache some data between runs or share a cache accross multiple processes my go to library in python is . It's built on sqlite with just enough..." data-date="2022-03-29">How I setup a sqlite cache in python</a>
One Background Task per db entry #
I am using it for longer running tasks and I don’t want to give users the ability to spam these long running tasks with many duplicates running at the same time. And each fastapi worker will be running in a different process so I cannot keep track of work in memory, I have to do it in a distributed fashion. Since they are all running on the same machine with access to the same disk, diskcache is a good choice
What I need #
- check if a job is running
- automatically expire jobs
Less infrastructure complexity #
My brain first went to thinking I needed another service like redis running alongside fastapi for this, then it hit me that I can use diskcache.
How I used diskcache #
Here is how I used diskcache to debounce taking screenshots for a unique shot every 60 seconds.
from diskcache import Cache
jobs_cache = Cache("jobs-cache")
@shots_router.get("/shot/{shot_id}", responses={200: {"content": {"image/webp": {}}}})
@shots_router.get("/shot/{shot_id}/", responses={200: {"content": {"image/webp": {}}}})
async def get_shot_by_id(
background_tasks: BackgroundTasks,
request: Request,
shot_id: int,
):
shot = Shot.get(shot_id)
# check if the shot exists and return it or continue to create it.
is_running = jobs_cache.get(shot_id)
if is_running:
expire_time = datetime.fromtimestamp(jobs_cache.peekitem(expire_time=True)[1]) - datetime.now()
console.print("[red]Already running store_shot: ", shot_id)
console.print(f"[red]Can retry in {expire_time.seconds}s")
else:
jobs_cache.set(shot_id, True, 60)
background_tasks.add_task(
store_shot,
shot_id=shot_id,
)
kind cluster
Yesterday I realized that I have overlooked the default installation method of the sealed secrets controller for kubernetes kubeseal this whole time an jumped straight to the helm section. I spun up a quick kind cluster and had it up quickly. I can’t say this is any better or worse than helm as I have never needed to customize the install. According to the docs you can customize it with [[ kustomize ]] or helm.
# option if you don't have a cluster try with kind
kind create cluster
curl -L https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.0/controller.yaml > controller.yaml
kubectl apply -f controller.yaml
I’ve long had issues with my qmk keyboard media keys on my arch install, I always thought it was on the keyboard end. Today I learned that playerctl fixes this.
paru -S playerctl
Once it is installed all of my media keys started working right away.
I played around with it a bit more and came up with a way to display the current playing title in my notifictations.
notify-send "`playerctl metadata --format '{{lc(status)}}:{{artist}}-{{album}}-{{title}}'`"
What I'm learning in 2024
Today I am playing around with tailwind, flexing the css muscle and learning how to build new and different layouts with it.
I created a new post template that mimics a terminal look in css where I could inject the post title, description, and other frontmatter elements.
Tailscale allows you to ssh into all of your tailscale machines, it busts through firewalls and accross networks without complex setup. If you have used tailscale before this is an obvious no brainer. What is not obvious is that you can configure tailscale to allow ssh connections from devices within your tailnet without even a ssh daemon process running right through the tailscale daemon.
tailscale status
tailscale set --ssh
I picked this up from the tailscale youtube channel.
I just implemented a latest blog post link in Markata by asking for the first post slug from the blog feed. The implementation uses the jinja_md plugin to render jinja against the markdown and a tag to redirect.
My latest blog post is [[ {{ markata.feeds.blog.posts[0].slug }} ]]. Click the
link if you are not automatically redirected.
<meta http-equiv="Refresh" content="0; url='/{{ markata.feeds.blog.posts[0].slug }}'" />
Setting up the feed #
Feeds are setup in markata.toml configuration. They provide a handy way to
create an html feed, rss feed, and quickly reference a filtered set of posts
like this.
# you will need to enable the jinja_md plugin along with the defaults
[markata]
hooks = [
"markata.plugins.jinja_md",
"default",
]
# set up the blog feed
[[markata.feeds]]
slug = 'blog'
template = "feed.html"
filter = "date<=today and templateKey in ['blog-post'] and published"
sort = "date"
reverse = true
For more information on markata check out the full markata post.
Refactoring one line links into wikilinks
about this site
sick wikilink hover
markata
my linked in work history
Your LinkedIn is Garbage
just has been by go to tool for saving
commands in a way that I can replay them and have team members replay them
without relying on the shell history of any given machine. This is my go to
default step, it lets you pick a just command to run with a fuzzy picker.
default:
@just --list
TIL how to display the list of nfs mounts on your network.
showmount -e
You can even look for mounts of other machines on your network.
showmount -e <hostname>
To allow access only to the
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"admin:*"
]
},
{
"Effect": "Allow",
"Action": [
"kms:*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::<bucket>",
"arn:aws:s3:::<bucket>/*"
]
}
]
}
You can inspect sqlite tables with the sqlite shell.
note that you get into the shell with
sqlite3 database.db
.tables
I also learned that .tables, .index and .schema are helper functions that
query the sqlite_master table on the main database.
Here is an output from my redka database. The sqlite_master table contains all the sqlite objects type, name, tbl_name, rootpage, and sql to create them.
❯ sqlite3 database.db "SELECT * from sqlite_master;"
table|rkey|rkey|2|CREATE TABLE rkey (
id integer primary key,
key text not null,
type integer not null,
version integer not null,
etime integer,
mtime integer not null
)
index|rkey_key_idx|rkey|3|CREATE UNIQUE INDEX rkey_key_idx on rkey (key)
index|rkey_etime_idx|rkey|4|CREATE INDEX rkey_etime_idx on rkey (etime)
where etime is not null
trigger|rkey_on_type_update|rkey|0|CREATE TRIGGER rkey_on_type_update
before update of type on rkey
for each row
when old.type is not new.type
begin
select raise(abort, 'key type mismatch');
end
table|rstring|rstring|5|CREATE TABLE rstring (
key_id integer not null,
value blob not null,
foreign key (key_id) references rkey (id)
on delete cascade
)
index|rstring_pk_idx|rstring|6|CREATE UNIQUE INDEX rstring_pk_idx on rstring (key_id)
view|vstring|vstring|0|CREATE VIEW vstring as
select
rkey.id as key_id, rkey.key, rstring.value,
datetime(etime/1000, 'unixepoch') as etime,
datetime(mtime/1000, 'unixepoch') as mtime
from rkey join rstring on rkey.id = rstring.key_id
where rkey.type = 1
and (rkey.etime is null or rkey.etime > unixepoch('subsec'))
table|rhash|rhash|7|CREATE TABLE rhash (
key_id integer not null,
field text not null,
value blob not null,
foreign key (key_id) references rkey (id)
on delete cascade
)
index|rhash_pk_idx|rhash|8|CREATE UNIQUE INDEX rhash_pk_idx on rhash (key_id, field)
index|rhash_key_id_idx|rhash|9|CREATE INDEX rhash_key_id_idx on rhash (key_id)
view|vhash|vhash|0|CREATE VIEW vhash as
select
rkey.id as key_id, rkey.key, rhash.field, rhash.value,
datetime(etime/1000, 'unixepoch') as etime,
datetime(mtime/1000, 'unixepoch') as mtime
from rkey join rhash on rkey.id = rhash.key_id
where rkey.type = 4
and (rkey.etime is null or rkey.etime > unixepoch('subsec'))
With the liscense changes to redis there are several new forks out there. One that I am particularly interested in is redka.
curl https://i.jpillora.com/nalgeon/redka | bash
chmod +x redka
./redka database.db
We now have redis running on port 6379 that we can connect to with a redis client. And we have a sqlite database that we can inspect.
❯ sqlite3 database.db "SELECT name FROM sqlite_master;"
rkey
rkey_key_idx
rkey_etime_idx
rkey_on_type_update
rstring
rstring_pk_idx
vstring
rhash
rhash_pk_idx
rhash_key_id_idx
vhash
We can look at the values in the vstring table.
sqlite3 database.db "SELECT * from vstring;"
1|hi|hello there you||2024-04-17 01:46:26
The main system that I am concerned about is my arch BTW machine. I found a great article from the official archlinux site covering it.
For my machine I am concerned with this line.
The xz packages prior to version 5.6.1-2 (specifically 5.6.0-1 and 5.6.1-1) contain this backdoor.
I checked my xz package with AUR.">paru, and I am good.
paru -Qii zx
AUR.">paru has some nice features that I rarely use, and hav to look up when I need them. Here are two commands to help with dependency management.
❯ paru -Qii nodejs
Name : nodejs
Version : 21.7.2-1
Description : Evented I/O for V8 javascript
Architecture : x86_64
URL : https://nodejs.org/
Licenses : MIT
Groups : None
Provides : None
Depends On : icu libuv libnghttp2 libnghttp3 libngtcp2 openssl zlib brotli c-ares
Optional Deps : npm: nodejs package manager [installed]
Required By : node-gyp nodejs-nopt npm semver
Optional For : None
Conflicts With : None
Replaces : None
Installed Size : 46.86 MiB
Packager : Felix Yan <[email protected]>
Build Date : Thu 04 Apr 2024 05:11:09 AM CDT
Install Date : Mon 15 Apr 2024 07:27:02 AM CDT
Install Reason : Installed as a dependency for another package
Install Script : No
Validated By : Signature
Backup Files : None
Extended Data : pkgtype=pkg
You can check all the packages depended on by nodejs by running the following. This is everything from all of the repos you have configured, not what you have installed.
❯ pactree --reverse --sync --depth 1 nodejs
nodejs
├─acorn
├─ansible-language-server
├─asar
├─babel-cli
├─babel-core
├─bash-language-server
├─blinksocks
├─bower
├─browserify
├─coffeescript
├─dot-language-server
├─emscripten
├─eslint
├─eslint-language-server
├─eslint_d
├─gitlab
├─gnomon
├─grunt-cli
├─gtop
├─gulp
├─hedgedoc
├─jake
├─markdownlint-cli2
├─marked
├─marked-man
├─matrix-appservice-irc
├─modclean
├─node-gyp
├─nodejs-emojione
├─nodejs-material-design-icons
├─nodejs-nopt
├─nodejs-source-map
├─nodejs-yaml
├─npm
├─openui5
├─pm2
├─prettier
├─pyright
├─rapydscript-ng
├─s3rver
├─semver
├─serverless
├─stylelint
├─stylus
├─svelte-language-server
├─tailwindcss-language-server
├─ts-node
├─typescript
├─typescript-svelte-plugin
├─uglify-js
├─vscode-css-languageserver
├─vscode-html-languageserver
├─vscode-json-languageserver
├─vue-language-server
├─vue-typescript-plugin
├─wasm-bindgen
├─web-ext
├─wrangler
├─yaml-language-server
├─yarn
I recently had to update my copier-gallery command to trust my own templates because some of them have shell scripts that run afterwards. Be warned that this could be a dangerous feature to run on random templates you get off the internet, but these are all mine, so if I wreck it its my own fault.
copier copy --trust <template> <destination>
All the the copier copy api can be found with help.
❯ copier copy --help
copier copy 8.3.0
Copy from a template source to a destination.
Usage:
copier copy [SWITCHES] template_src destination_path
Hidden-switches:
-h, --help Prints this help message and quits
--help-all Prints help messages of all sub-commands and quits
-v, --version Prints the program's version and quits
Switches:
-C, --no-cleanup On error, do not delete destination if it was
created by Copier.
--UNSAFE, --trust Allow templates with unsafe features (Jinja
extensions, migrations, tasks)
-a, --answers-file VALUE:str Update using this path (relative to
`destination_path`) to find the answers file
-d, --data VARIABLE=VALUE:str Make VARIABLE available as VALUE when rendering the
template; may be given multiple times
-f, --force Same as `--defaults --overwrite`.
-g, --prereleases Use prereleases to compare template VCS tags.
-l, --defaults Use default answers to questions, which might be
null if not specified.
-n, --pretend Run but do not make any changes
-q, --quiet Suppress status output
-r, --vcs-ref VALUE:str Git reference to checkout in `template_src`. If you
do not specify it, it will try to checkout the
latest git tag, as sorted using the PEP 440
algorithm. If you want to checkout always the
latest version, use `--vcs-ref=HEAD`.
-s, --skip VALUE:str Skip specified files if they exist already; may be
given multiple times
-w, --overwrite Overwrite files that already exist, without asking.
-x, --exclude VALUE:str A name or shell-style pattern matching files or
folders that must not be copied; may be given
multiple times
Today I accidentally ran f2 in ipython to discover that it opens your $EDITOR!
I use this feature quite often in zsh, it is bound to <c-e> for me, and since
I have my environment variable EDITOR set to nvim it opens nvim when I hit
<c-e>. Today I discovered that Ipython has this bound to F2. If you know
how to set it to <c-e> let me know I’ve tried, a lot.
export EDITOR=nvim
ipython
<F2>
better yet add export EDITOR=nvim to your .zshrc
# ~/.zshrc
export EDITOR=nvim
I’ve really been enjoying using sqlmodel for my projects that need a database. One thing that I definitely lacked on for too long was indexing my database. I hit a point with one database where it was taking 7s for pretty simple paginated queries to return 10 records.
For every field that you will be querying on, you can create an index, by
setting it equal to Field(index=True)
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
example courtesy of the docs
Note
primary keys are indexed by default.
The docs cover this pretty well, and in quite depth - Optimizing Queries
inline-snapshot is a new tool that I am trying out for python testing. It
takes snapshots of your outputs and places them inline with the test.
Here is the most basic starter.
import inline_snapshot
def test_one():
assert 1 == snapshot()
Now when I run pytest my tests will fail because my assert has no value, but if I
run pytest --inline-snapshot=create it will fill out my snapshot values and the
file will then look like this.
import inline_snapshot
def test_one():
assert 1 == snapshot(1)
It also works with pydantic models.
class MyModel(BaseModel):
name: str
age: int
nickname: str | None = None
def test_my_model_instance():
assert MyModel(name="Waylon", age=1) == snapshot(MyModel(name="Waylon", age=1))
def test_my_model_fields():
me = MyModel(name="Waylon", age=1, nickname='Waylon')
assert me.name == snapshot("Waylon")
assert me.age == snapshot(1)
assert me.nickname == snapshot("Waylon")
Today I learned how to VACUUM a sqlite database and cut its size in about half. It’s a database that I have had running for quite awhile and has some decent traffic on it.
Why is it important to do a VACUUM? In short its becuase the file system gets fragmented with as data is updated. On delete the files are removed from the database and marked as available for reuse in the filesystem, but the space is not reclaimed.
To VACUUM a database, run the following sql command. You can do it right form
the sqlite shell by running sqlite3.
You will need about double the current size of the database as free space to do the VACUUM, you need space for a full copy, journaling or write ahead logs, and the existing database.
VACUUM;
The docs are fantastic for vacuum.
I learned that tailwind animations are pretty easy to add only needing a few classes. For some reason though my brain broke, thinking that I could dynamically change the number and you can’t cause there are only so many pre compiled classes without using an arbitrary value with brackets.
Here are the classes that I used to transition my colors very slowly.
<div id="square"
class="transition-colors ease-in-out duration-700">
</div>
And the entire square element.
<div id="square"
class="w-16 h-16 bg-rose-500 rounded border border-4 border-rose-800 hover:bg-indigo-600 hover:border-yellow-500 transition-colors ease-in-out duration-700">
</div>
I recently updated ollama, and it now installs a systemd service that I was not expecting. Seems like a great option, but I hadn’t expeted this and I was able to kill it previously. It was using up gpu, and I do other things on my machine with a gpu. I tried pkill, kill, and everything, it was still coming back.
No matter what it comes back
# stop it
systemctl stop ollama.service
# disable it if you want
systemctl disable ollama.service
# confirm its status
systemctl status ollama.service
You can confirm this with the following command.
# checking running processes
ps aux | grep ollama
pgrep ollama
# checking gpu processes
gpustat --show-cmd --show-pid
Next time you want to start you can do it as before with ollama serve.
Typer makes it easy to compose your cli applications, like you might with a web router if you are more familiar with that. This allows you to build smaller applications that compose into a larger application.
You will see similar patterns in the wild, namely the aws cli which always
has the aws <command> <subcommand> pattern.
Lets setup the cli app itself first. You can put it in project/cli/cli.py.
import typer
from project.cli.api import api_app
from project.cli.config import config_app
from project.cli.user import user_app
from project.cli.run import run_app
app = typer.Typer()
app.add_typer(api_app, name="api")
app.add_typer(config_app, name="config")
app.add_typer(user_app, name="user")
app.add_typer(run_app, name="run")
Creating an app that will become a command is the same as creating a regular app in Typer. We need to create a callback that will become our command, and a command that will become our subcommand in the parent app.
import typer
from rich.console import Console
from project.config import get_config
config_app = typer.Typer()
@config_app.callback()
def config():
"model cli"
@config_app.command()
def show(
):
project_config = get_config(env)
Console().print(fokais_config)
Setting up the entrypoint in pyproject.toml.
[project.scripts] # <- this project is part of the config DO NOT change it
project = "project.cli.cli:app" # <- This project is the project name, DO change it
Now you can see each cli application as a sub command.
❯ project --help
Usage: project [OPTIONS] COMMAND [ARGS]...
╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────╮
│ --install-completion [bash|zsh|fish|powershell|pwsh] Install completion for the specified shell.│
│ [default: None] │
│ --show-completion [bash|zsh|fish|powershell|pwsh] Show completion for the specified shell, │
│ to copy it or customize the installation. │
│ [default: None] │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ────────────────────────────────────────────────────────────────────────────────────────╮
│ api model cli │
│ config config cli │
│ user user cli │
│ run run cli │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯
In the example above we can run the command project config show to see the
current configuration of our project.
I learned not to fear the arbitrary size feature of tailwind. While building out reader.waylonwalker.com I kept getting content flowing off the screen, and struggling to keep it on the screen. I really felt that I should be able to do this with vanilla tailwind, but after some encouragement from Twitter I decided to lean on arbitrary values and it worked.
Don’t fear the arbitrary values.
<li class="max-w-[100vw]">
</li>
Learn more about using-arbitrary-values from their docs docs
Each time I go to set up npm I am frustrated by the errors saying that I don’t
have permission to npm i -g <package>, and it’s frustrating. And I forget
what I need to do to tell npm to install packages in a directory I own, and my
shell to look there so that I can use the executables.
mkdir ~/.npm-global
export NPM_CONFIG_PREFIX=~/.npm-global
export PATH=$PATH:~/.npm-global/bin
For the fix to remain persistent you need to put these two lines in your shell
profile like ~/.bashrc or ~/.zshrc.
export NPM_CONFIG_PREFIX=~/.npm-global
export PATH=$PATH:~/.npm-global/bin
One Day Build - Play Outside
If you are designing a website in dark mode the scrollbars can be finicky to match the theme. Here is a pretty sane default that looks nice without being obnoxiously contrast to the rest of the site.
<style>
::-webkit-scrollbar {
height: 1rem;
width: 1rem;
}
::-webkit-scrollbar-track {
background-color: rgb(24 24 27);
}
body::-webkit-scrollbar-track {
background-color: rgb(39 39 42);
}
::-webkit-scrollbar-thumb {
background-color: rgb(82 82 91);
}
::-webkit-scrollbar-thumb:hover {
background-color: rgb(113 113 122);
}
body::-webkit-scrollbar-thumb {
background-color: rgb(82 82 91);
}
body::-webkit-scrollbar-thumb:hover {
background-color: rgb(113 113 122);
}
::-webkit-scrollbar-corner {
background-color: rgb(39 39 42);
}
</style>
Want a rounded scrollbar thumb? add these styles.
::-webkit-scrollbar-thumb {
border-radius: 0.25rem;
border-radius: 9999px;
}
body::-webkit-scrollbar-thumb {
border-radius: 0.25rem;
border-radius: 9999px;
}
This makes a very nice looking default darkmode scrollbar.
Before deploying to cloudflare pages with wrangler you need a cloudflare api token. You can get one at dash.cloudflare.com/profile/api-tokens.
Install Wrangler #
Next install wrangler using npm.
npm i -g wrangler
Create a Project #
Before you deploy to cloudflare pages you need to create a project. You might already have one, or you might want to create one in the webui, but you have the option to create it at the command line with wrangler.
npx wrangler pages deploy markout --project-name reader-waylonwalker-com --branch markout
Deploy #
Now you can deploy your static application using wrangler to cloudflare pages.
In this example I have my application built into the markout directory, and since the production branch is named
markoutI need to pass that in here as well.
wrangler pages deploy markout --project-name reader-waylonwalker-com --branch markout
For my reader app I am using cronjobs to schedule my a new build and upload to
cloudflare pages every hour. In this example I have built a docker image
docker.io/waylonwalker/reader-waylonwalker-com and pushed it to dockerhub.
It uses a CLOUDFLARE_API_TOKEN secret to access cloudflare, and the
entrypoint itself does the build and upload.
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: reader
namespace: reader
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: reader-cronjob
namespace: reader
spec:
schedule: "0 * * * *"
successfulJobsHistoryLimit: 6
failedJobsHistoryLimit: 6
jobTemplate:
spec:
template:
spec:
containers:
- name: reader-container
image: docker.io/waylonwalker/reader-waylonwalker-com:latest
env:
- name: CLOUDFLARE_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-secret
key: cloudflare-secret
restartPolicy: OnFailure
I am working on a page for htmx-patterns and I ran into a situation with lots of duplication. Especially when i am using tailwind I run into situations where the duplication can get tedious to maintiain. The solution I found is macros.
Now I can use the same code for all of my links, and call the macro to use it.
{% macro link(id, text, boosted=false) -%}
<a
class="
{% if id is none %}
pointer-events-none bg-terminal-950 text-terminal-900 ring-terminal-900
{% else %}
bg-terminal-950 hover:bg-terminal-900 hover:text-terminal-400 text-terminal-500 shadow-lg shadow-terminal-300/20 hover:shadow-terminal-300/30 ring-terminal-300
{% endif %}
cursor-pointer block text-center font-bold py-2 px-4 rounded w-full ring-1
"
{% if id is not none %}
href="{{ url_for('boosted', id=id) }}"
{% endif %}
{% if boosted %}
hx-boost="true"
{% endif %}>
{{ text }}
</a>
{%- endmacro %}
<h2 class='text-3xl font-light mt-0 max-w-xl text-center prose-xl mt-8 text-terminal-500'>
Boosted Links
</h2>
<div class='flex flex-row gap-4'>
{{ link(prev_id, 'Previous', boosted=True) }}
{{ link(next_id, 'Next', boosted=True) }}
</div>
<h2 class='text-3xl font-light mt-0 max-w-xl text-center prose-xl mt-8 text-terminal-500'>
Normal Links
</h2>
<div class='flex flex-row gap-4'>
{{ link(prev_id, 'Previous', boosted=False) }}
{{ link(next_id, 'Next', boosted=False) }}
</div>
jinja has a loop variable that is very handy to use with htmx. Whether you want to implement a click to load more or an infinite scroll this loop variable is very handy.
{% for person in persons %}
<li
{% if loop.last %}
hx-get="{{ url_for('infinite', page=next_page) }}"
hx-trigger="intersect once"
hx-target="#persons"
hx-swap='beforeend'
hx-indicator="#persons-loading"
{% endif %}
{{ person.name.upper() }} -
{{ person.phone_number }}
</li>
{% endfor %}
Now for every chunk of contacts that we load we will trigger the infinite scroll by loading more once the last one has intersected the screen.
Out of the box FastAPI.">Starlette does not support url_for with query params. When trying to use url_for with query params it throws the following error.
starlette.routing.NoMatchFound: No route exists for name "infinite" and params "page"
In my searching for this I found starlette issue #560 quite helpful, but not complete, as it did not work for me.
import jinja2
if hasattr(jinja2, "pass_context"):
pass_context = jinja2.pass_context
else:
pass_context = jinja2.contextfunction
@pass_context
def url_for_query(context: dict, name: str, **params: dict) -> str:
request = context["request"]
url = str(request.url_for(name))
if params == {}:
return url
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
# Parse the URL
parsed_url = urlparse(url)
# Parse the query parameters
query_params = parse_qs(parsed_url.query)
# Update the query parameters with the new ones
query_params.update(params)
# Rebuild the query string
updated_query_string = urlencode(query_params, doseq=True)
# Rebuild the URL with the updated query string
updated_url = urlunparse(
(
parsed_url.scheme,
parsed_url.netloc,
parsed_url.path,
parsed_url.params,
updated_query_string,
parsed_url.fragment,
)
)
if os.environ.get("ENV") in ["dev", "qa", "prod"]:
updated_url = updated_url.replace("http", "https", 1)
return updated_url
def get_templates():
templates = Jinja2Templates(directory="templates")
templates.env.globals["url_for"] = url_for_query
return templates
https
If you want url_for to work in production you need some way to convert http
to https. Here is how I make it work, for local development I export ENV=local then for each environment that I am running on a server I include
it in the list and update ENV appropriately.
if os.environ.get("ENV") in ["dev", "qa", "prod"]:
updated_url = updated_url.replace("http", "https", 1)
The route might look something like this.
@infinite_router.get("/")
async def home(request: Request, page: int = 1, n: int = 10):
...
To access the home route using url_for in a jinja template you can use the
following, once you have applied the url_for_query function as your default
url_for
<a href="{{ url_for('home', page=1) }}">Home</a>
Kind (Kubernetes in Docker) is a tool that makes it easy to create and tear down local clusters quickly. I like to use it to test out new workflows.
Argocd is a continuous delivery tool that makes it easy to setup gitops workflows in kubernetes.
Here is how you can setup a new kind cluster and install argocd into it using helm, the kubernetes package manager.
kind create cluster --name argocd
# your first time through you need to add the argocd repo
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
# install argocd into the cluster
helm install argo argo/argo-cd --namespace argocd --create-namespace
# deploy the app of apps
kubectl apply -f apps/apps.yaml
If you want to add repos and apps to your cluster you can use the argo cli to do that, but first you will need forward the argocd port and login.
# Wait until Argo CD API server is available
echo "Waiting for Argo CD API server to be available..."
while ! kubectl wait --for=condition=available --timeout=60s deployment/argo-argocd-server -n argocd; do
echo "Waiting for Argo CD API server to be ready..."
sleep 10
done
kubectl port-forward svc/argo-argocd-server -n argocd 8080:443 &
argocd_admin_password=$(kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d)
argocd login localhost:8080 --insecure --username admin --password $argocd_admin_password
argocd repo add https://github.com/fokais-com/app.fokais.git --username waylonwalker --password ${GH_ARGO_PAT}
argocd app create app-fokais-local --repo https://github.com/fokais-com/app.fokais.git --path k8s/overlays/local --dest-server https://kubernetes.default.svc --sync-policy automated --sync-option Prune=true
feeds
Thoughts
In my homelab kubernetes cluster I am using kubeseal to encrypt secrets. I have been using it successfully for a few months now wtih great success. It allows me to commit all of my secrets manifests to git with out risk of leaking secrets.
You see kubeseal encrypts your secrets with a private key only stored in your cluster, so only the cluster itself can decrypt them using the kubeseal controller.
KubeSeal #
https://sealed-secrets.netlify.app/
installation #
Installation happens in two steps. You need the kubernetes controller and the client side cli to create a sealed secret.
For a more complete instruction see the [docs#installation](https://github.com/bitnami-labs/sealed-secrets?tab=readme-ov-file#installation]
installation - controller #
Warning
**context**
Make sure that you are in the right context before running any kubectl commands.
kubectl config current-context
sealed-secrets is installed using the helm package manager. To install sealed-secrets run the following command.
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets -n kube-system --set-string fullnameOverride=sealed-secrets-controller sealed-secrets/sealed-secrets
installation - client #
For the client you can check your OS package manager, brew, or the github-releases. For me I found it in the main arch repos.
paru -S kubeseal
# or
sudo pacman -S kubeseal
# or
brew install kubeseal
Note
You will need to install kubeseal on every device that you will want to
create sealed secrets on.
Example #
Most of these commands come straight from the docs. From my experience I have always specified the namespace, my projects go per namespace and I don’t have any reason that other namepsaces should see the secret, and if they do I deploy another secret in that namespace.
# Create a json/yaml-encoded Secret somehow:
# (note use of `--dry-run` - this is just a local file!)
echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o yaml -n thenamespace > mysecret.yaml
note that the key of the secret is
fooand the value isbar
results
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
creationTimestamp: null
name: mysecret
namespace: thenamespace
Note
The data is base64 encoded.
echo -n bar | base64
# YmFy
# This is the important bit:
kubeseal -f mysecret.yaml -w mysealedsecret.yaml
# At this point mysealedsecret.json is safe to upload to Github,
# post on Twitter, etc.
# Eventually:
kubectl create -f mysealedsecret.yaml -n thenamespace
# sealedsecret.bitnami.com/mysecret created
# Profit!
kubectl get secret mysecret
kubectl get secret mysecret -n thenamespace
# NAME TYPE DATA AGE
# mysecret Opaque 1 27s
cat mysealedsecret.yaml | kubeseal --validate
echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o yaml \
| kubeseal -o yaml -n thenamespace > mysealedsecret.yaml
echo -n baz | kubectl create secret generic mysecret --dry-run=client --from-file=bar=/dev/stdin -o yaml \
| kubeseal -o yaml -n thenamespace --merge-into mysealedsecret.yaml
Results
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: mysecret
namespace: thenamespace
spec:
encryptedData:
bar: AgBLkamltcLfH1dC1JxQ3qd8lJ8aBZF2ARoq3uo055hnzXOy8g2T5liTx5UPvyPV8yyWqABU8eOnwjNhDtzSATvYeBB3fGkucdOZziWEoiNsWTR9ZtFEkod7Ya6uGkzZOJwi3IkrHFVIT9oWZQUxxJZ6vFhPiFcx9Dorr8TNSzG4KOug25+PhWPPiHDgSup5N3CkWCZaYOF7dbZRVSA4nGP1fZxjFByHP4AsdjLCHptyVbkpLRKeiXTkLxfLX4K+JLZGM41S1On5bSP56mCfv1daTJx619kDXkRLw9l21Ot283/L0NMNAiw781AefYMVoO3aHmYgcT6wAtsQAKje9fyL7DQRHt8a5NZOWukp/P6XjdXRz/nfQasQlbSTrRkDpplKIM5/WdPcBoKi+yyoOL0rZ8x1X7YzUI3BggZmzWyEPD01BK1YAHGZnYIZbbCy1JSm8JCBvP+xWMg+i0Z/DCD8nclAhH1GX2Q7/NrNHF//589AJfuriymd2+mk7uaLA4RRsY0l5QeZD6HVAqSv5jWsVQQtSftWmI9vn9oL/Pno7sEUjSDpXPfF4nnsULhxsPEe2DFAMm1kZAjdF06ueF4/x2Fdy80ZQNyycaDx2CWm4z3b14A75WGyOXl2wJZQqxrFCz8el4hD2nH3zQFEzd6AIh49myqVAGuu2qGlYP4p94LJghVa+mQjztLD/2ZUZjY+anQ=
foo: AgAducXW6iUCY900cPDdmRfuj7tKnh2hY4C1+2hFoAtjyvjepsKNWsiPJ81t8anaMfFPat4ta060l5VtTrceFE8oS/rViz1tvNWGPBjL+GwL/QjkGl6H8ju87vKERKQn5qw7B/V5j24VM8AxOd+/vJNt/IeRIHLubvFft4hyMq4b0xmIxaemBSTxchQX/5364T3VJH2kHaqpqd+JJgQnTbiTQe/XnyZokDX8GSxw4rAbJUJSRUtY9DB9ZDu2zC5VngX+GJjbwHGbv9EKs8LShJIPrD8xHqrDmlSXGkkP01D4A6268Qoi3x5S0H5aqDgtrgBiWsNkzdKwjfrTNx7pKecOi41lyFdffHOGUew4aPPMqjzWR2TEms9WNNQXwnBdDHKMkFsisocF52BolEkcjF8g/u5h2Af92abMu6k16VybJrB7TV1set5A9W7rqG1iXI4+1W6XQfFnpja8xL/zJBvZcyHgeYMNaxa3C3s6PANhPzAUVaXV9eedAkptGJLN13IZj4LujpoAxRKo6bEdydv/5P23R3fx5PgTOpVI7riECAOIg2PThFsEoVCUwStmKCvIx1I2+YixIlv/OiaUWo4lrI/3ve5WGp4ZnuiJPk34JoYAlRbR6+sX14d8Ek6viq/pJUUIfVpNIkNMboUL4u+KpT47eyQ/mWih/KFduQyX9II/vQ+/IJGzEEHIipxAhdmV+K4=
template:
metadata:
creationTimestamp: null
name: mysecret
namespace: thenamespace
backing up your sealing key #
kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml >main.key
converting .env files to a secret #
Working with web applications .env is a common way to store credentials. Let’s look at how we can convert these to secrets.
kubectl create secret generic mysecret --from-env-file=.env -n thenamespace --dry-run=client -o yaml > mysecret.yaml
Now you have a secret that looks like this.
apiVersion: v1
data:
foo: YmFy
kind: Secret
metadata:
creationTimestamp: null
name: mysecret
namespace: thenamespace
Seal it up just like before.
kubeseal -f mysecret.yaml -w mysealedsecret.yaml
Using the secrets #
I typically use the secrets in the container spec.
containers:
- name: myapp
envFrom:
- secretRef:
name: mysecret
# You can still have other env vars
env:
- name: foo
value: bar
Sometimes I want to mount the secret as a volume.
containers:
- name: myapp
volumeMounts:
- name: mysecret
mountPath: /mysecret
volumes:
- name: mysecret
secret:
secretName: mysecret
Image Pull Secrets #
I also need to use imagePullSecrets. Let’s walk through the whole process. Starting with the secret.
kubectl create secret docker-registry regcred --docker-server=myprivateregistry.example.com --docker-username=foo --docker-password=bar --dry-run=client -o yaml
Generates the following secret.
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJteXByaXZhdGVyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6ImZvbyIsInBhc3N3b3JkIjoiYmFyIiwiYXV0aCI6IlptOXZPbUpoY2c9PSJ9fX0=
kind: Secret
metadata:
creationTimestamp: null
name: regcred
type: kubernetes.io/dockerconfigjson
---
the secret
Now we we can seal that secret.
kubeseal -f regcred.yaml -w regcred-sealed.yaml
And that gives us the following sealed secret that we can deploy into our cluster.
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: regcred
namespace: default
spec:
encryptedData:
.dockerconfigjson: AgATYVEywkyYEaoErQbJo6xEZfOnRn1ydNTLkO3Jt/NF/UH+0o9lHpDecRpN0XnVu8xJUcdjkgD9q2XkwP8e6qQDS2mMPTiTNIN+8gbJx97WrD1YQDT0lXBuoyi9I/iwlXxx6MgH/6GY6CeGTz5SRlvoU0Xhlt4d11s7/xapdE8QMLsAReqPEv8oZHEyAxDRrjXX0V+tO8dV+G+GXjUDMBBceLael9rvGzSKIwDVXACVqQhLkB6FoP98M+yyBE46RBNnSnS0ShQM5PprL24HKpRZ43x4RM53KBrQ7R/MxeshafY+B6vUvrolmVox4sud8xngMOcjTO28LLOrck5V8ZiDabhN7ajHEf03IESr1o/ADGf5k9988Vv1txJtsZW0K2mpRu0D7/BLVL9KzbZ5ywULqIoD/Ur2GIGnZqMAKOq4laGp/GJtMKLrhmEvekT397wC/Gf/xdDKVhHf2p4ocsPu7LKFuS5H/Auel/Q5grdn8L5wwrO4VWRv3eJroKh/Hux7Qd7f64O7qdi0XthDocf+gmtjys+Gy72M7tyf8f/O+3oKbS4CWQVTj4ZThMc9znrFnHqt2q/7pAyytTQCpk51wlzOsNvOhCueJM/jmeahaL0LuBrqngqISpnd65sgVzBcZpwK9i2Fckyt0DrZLH+NoIuvaqNhzlF+OMbAft/ylWWKCH4WUP+FKG+1LXM7ud7AA3MMbGSBxHL0/WK/INa7MB56xZKMqqyvvLLQHFTQUROJjkgkzsumdOgwZTRgIFnAZ4+vOX3/1Rtt3mAs3vdoJhL4GuKUYCnEHt908eKkWEVEs7eMk5SdSRtIsbaXO2s0dtADwg==
template:
metadata:
creationTimestamp: null
name: regcred
namespace: default
type: kubernetes.io/dockerconfigjson
Now that we have our sealed registry secret, we can deploy it into our cluster.
kubectl apply -f regcred-sealed.yaml
Now we can use it to pull images from our private registry.
containers:
imagePullSecrets:
- name: regcred
Full example #
Here is a full deployment example using all the secrets we have created.
- regcred
- mounting a secret
- envFrom secret
---
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
service: myservice
name: myservice
namespace: mynamespace
spec:
replicas: 1
selector:
matchLabels:
service: myservice
strategy:
type: Recreate
template:
metadata:
creationTimestamp: null
labels:
service: myservice
spec:
containers:
- envFrom:
- secretRef:
name: mysecret
env:
- name: foo
value: bar
image: private-registry.io/myimage:1.0.0
name: myimage
ports:
- containerPort: 5000
protocol: TCP
resources: {}
volumeMounts:
- mountPath: /mysecret
name: mysecret
restartPolicy: Always
volumes:
- name: mysecret
secret:
secretName: mysecret
imagePullSecrets:
- name: regcred
Downside #
Now the main downside I see with kubeseal is that it does not provide a way to store your secrets in a way that you can access outside of your cluster. So you need to make sure that you have another solution in place to store your secrets so that you still have them if you ever were to take the cluster down or move from k8s to something else.
Overall the likelyhood of you loosing a production cluster is pretty low, so maybe it’s ok to just trust it depending on what the secrets are. Especially for things you control and can rotate anyways its fine.
kubernetes 6 months in
poc is not product
So after months of fighting with gf not going to template files, I finally decided to put in some effort to make it work.
This was the dumbest keybind in my config, that I copied from someone else without understanding it.
What I am trying to do #
I have jinja templates in a directory called templates. I want to bind gf to
open a template file, but it is trying to open a new file ./base.html
{% extends "base.html" %}
{% if request.state.user %}
{% block title %}Fokais - {{ request.state.user.full_name }} {% endblock %}
{% else %}
{% block title %}Fokais {% endblock %}
{% endif %}
{% block content %}
{% if request.state.user %}
<h1 id="title"
class="inline-block mx-auto text-5xl font-black leading-loose
text-transparent bg-clip-text bg-gradient-to-r from-red-600
via-pink-500 to-yellow-400 ring-red-500 text-shadow-xl
text-shadow-zinc-950 ring-5">
{{ request.state.user.full_name }}
</h1>
{% endif %}
{% include "me_partial.html" %}
{% endblock %}
What did not work #
I tried all sorts of changes to my path, but it still didn’t work.
vim.api.nvim_command("set path+=templates/**")
What I found #
after digging into my keymap I found that I had remaped gf to edit years
ago. This works great if the file is in your current directory, and if it’s not
it makes the file. This bind completely breaks vim’s ability to :find files
and was a terrible keybind that I added probably from someone else years ago
and have literally never used this feature. If gf opens an empty file I
always close it and assume that vim failed to :find the file.
-- Allow gf to open non-existent files
set("", "gf", ":edit <cfile><CR>")
Yes, after that fix I still needed to adjust my path #
I ended up with the following in my options.lua.
-- look for jinja templates in the templates directory
vim.opt.path:append("templates/**")
thoughts on unit tests
Authentication from cli tools can be a bit of a bear, and I have to look it up every time. This is my reference guide for future me to remember how to easily do it.
I set up a fastapi server running on port 8000, it uses a basic auth with
waylonwalker as the username and asdf as the password. The server follows
along with what comes out of the docs. I have it setup to take basic auth,
form username and password, or a bearer token for authentication.
curl #
The og of command line url tools.
# basic auth
curl -u 'waylonwalker:asdf' -X POST localhost:8000/token
# basic auth with password prompt
curl -u 'waylonwalker' -X POST localhost:8000/token
# token
curl -H 'Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YXlsb253YWxrZXIiLCJleHAiOjE3MDI5NTI2MDJ9.GeYNt7DNal6LTiPoavJnqypaMt4vYeriXdq5lqu1ILg' -X POST localhost:8000/token
wget #
My go to if I want the result to go into a file.
# basic auth
wget -q -O - --auth-no-challenge --http-user=waylonwalker --http-password=asdf --post-data '' localhost:8000/token
# token
wget -q -O - --header="Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YXlsb253YWxrZXIiLCJleHAiOjE3MDI5NTI2MDJ9.GeYNt7DNal6LTiPoavJnqypaMt4vYeriXdq5lqu1ILg" -O - --post-data '' localhost:8000/token
httpx #
An http client written in python, primarilty used with the python api, but has a nice cli.
# install
python3 -m pip install httpx
# basic auth
httpx -m POST --auth waylonwalker asdf http://localhost:8000/token
# basic auth with password prompt
httpx -m POST --auth waylonwalker - http://localhost:8000/token
# token
httpx -m POST --headers="Authorization" "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YXlsb253YWxrZXIiLCJleHAiOjE3MDI5NTI2MDJ9.GeYNt7DNal6LTiPoavJnqypaMt4vYeriXdq5lqu1ILg" http://localhost:8000/token
httpie #
A modern http client written in python.
# install
python3 -m pip install httpie
# basic auth
http POST localhost:8000/token -a waylonwalker:asdf
# basic auth with password prompt
http POST localhost:8000/token -a waylonwalker
# token
http POST localhost:8000/token -A bearer -a eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ3YXlsb253YWxrZXIiLCJleHAiOjE3MDI5NTI2MDJ9.GeYNt7DNal6LTiPoavJnqypaMt4vYeriXdq5lqu1ILg
httpie with plugin #
# install
python3 -m pip install httpie-credential-store
# usage
http POST localhost:8000/token -A creds
httpie prompt #
http-prompt comes from the httpie org, and has an interactive cli interface
into apis. You can even specify a spec file to autocomplete on api methods.
http-prompt localhost:8000 --auth waylonwalker:asdf --spec openapi.json
I am working on fokais.com’s signup page, and I want to hide the form input during an htmx request. I was seeing some issues where I was able to prevent spamming the submit button, but was still able to get one extra hit on it.
It also felt like nothing was happening while sending the email to the user for verification. Now I get the form to disappear and a spinner to show during the request.
html">HTML #
Let’s start off with the form. It uses htmx to submit a post request to the
post_request route. Note that there is a spinner in the post_request with the
htmx-indicator class.
The intent is to hide the spinner until the request is running, and hide all of the form input during the request.
<form
id="signup-form"
hx-swap-oob="outerHTML"
class="m-4 mx-auto mb-6 flex w-80 flex-col rounded-lg b p-4 shadow-xlc shadow-cyan-500/10"
method="POST"
action="{{ url_for('post_signup') }}"
hx-post="{{ url_for('post_signup') }}"
>
<!--markata-attribution-->
<input
class="mx-8 mt-6 mb-4 border border-black bg-zinc-900 p-1 text-center focus:bg-zinc-800"
<!--markata-attribution-->
type="text"
<!--markata-attribution-->
value="{{ full_name }}"
<!--markata-attribution-->
name="full_name"
<!--markata-attribution-->
placeholder="Full Name"
/>
<!--markata-attribution-->
{% if full_name_error %}
<!--markata-attribution-->
<label class="-mt-6 mb-6 mx-8 text-red-500 p-1 text-center">
<!--markata-attribution-->
{{ full_name_error }}
<!--markata-attribution-->
</label>
<!--markata-attribution-->
{% endif %}
<!--markata-attribution-->
<input
class="mx-8 mb-4 border border-black bg-zinc-900 p-1 text-center focus:bg-zinc-800"
<!--markata-attribution-->
type="text"
<!--markata-attribution-->
value="{{ username }}"
<!--markata-attribution-->
name="username"
<!--markata-attribution-->
placeholder="username"
/>
<!--markata-attribution-->
{% if username_error %}
<!--markata-attribution-->
<label class="-mt-6 mb-6 mx-8 text-red-500 p-1 text-center">
<!--markata-attribution-->
{{ username_error }}
<!--markata-attribution-->
</label>
<!--markata-attribution-->
{% endif %}
<!--markata-attribution-->
<input
class="mx-8 mb-4 border border-black bg-zinc-900 p-1 text-center focus:bg-zinc-800"
<!--markata-attribution-->
type="email"
<!--markata-attribution-->
name="email"
<!--markata-attribution-->
value="{{ email }}"
<!--markata-attribution-->
placeholder="email"
/>
<!--markata-attribution-->
{% if email_error %}
<!--markata-attribution-->
<label class="-mt-6 mb-6 mx-8 text-red-500 p-1 text-center">
<!--markata-attribution-->
{{ email_error }}
<!--markata-attribution-->
</label>
<!--markata-attribution-->
{% endif %}
<!--markata-attribution-->
<input
class="mx-auto w-32 mb-4 border border-black bg-purple-900 p-1 text-center focus:bg-zinc-800"
<!--markata-attribution-->
type="submit"
<!--markata-attribution-->
value="sign up"
/>
<!--markata-attribution-->
<div role="status" class="mx-auto htmx-indicator">
<!--markata-attribution-->
<svg
<!--markata-attribution-->
class="mx-auto animate-spin h-5 w-5 text-white"
<!--markata-attribution-->
xmlns="https://www.w3.org/2000/svg"
<!--markata-attribution-->
fill="none"
<!--markata-attribution-->
viewBox="0 0 24 24"
>
<!--markata-attribution-->
<circle
<!--markata-attribution-->
class="opacity-25"
<!--markata-attribution-->
cx="12"
<!--markata-attribution-->
cy="12"
<!--markata-attribution-->
r="10"
<!--markata-attribution-->
stroke="currentColor"
<!--markata-attribution-->
stroke-width="4"
></circle>
<!--markata-attribution-->
<path
<!--markata-attribution-->
class="opacity-75"
<!--markata-attribution-->
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
<!--markata-attribution-->
</svg>
<!--markata-attribution-->
<p>Signing up...</p>
<!--markata-attribution-->
</div>
<!--markata-attribution-->
</form>
Yes this is styled using tailwindcss.
https://waylonwalker.com/still-loving-tailwind/
CSS #
Let’s take a look at how we achieve switching between only spinner an only form inputs using css.
.htmx-indicator {
@apply hidden;
opacity: 0;
transition: opacity 500ms ease-in;
}
.htmx-request button,
.htmx-request input[type="submit"],
.htmx-request input,
.htmx-request label {
@apply hidden;
}
.htmx-request .htmx-indicator {
opacity: 1;
@apply block;
}
.htmx-request.htmx-indicator {
opacity: 1;
@apply block;
}
Final Result #
Here is the final result of me signing up for a new account in fokais.
Today I am working on fokais.com, trying to get to a point where I can launch by workig through stripe integrations. This is my first time using stripe, so there has been quite a bit to learn, and I am probably building in more than I need to before launching, but I am learning, and not in a rush to launch.
I am building the fokais backent in python primarilyt with fastapi and sqlmodel on sqlite. My billing integration is going to be all Stripe.
Stripe Subscription Cancellations Docs #
Here is a link to the stripe docs for your refrence, especially if you want to see how to cancel subscriptions in other languages. They include code samples for many popular languages.
User Model #
This is the part of the user model that includes the cancel and reactivate methods. It pretty much follows the stripe guide.
class UserBase(SQLModel, table=False): # type: ignore[call-arg]
username: str = Field(unique=True)
full_name: str
email: str
email_verified: bool = False
disabled: bool = False
signup_date: Optional[datetime] = Field(default_factory=datetime.utcnow)
stripe_customer_id: Optional[str]
def cancel_subscription(self):
for subscription in self.active_subscriptions:
stripe.Subscription.modify(
subscription.id,
cancel_at_period_end=True,
)
self.refresh()
def reactivate_subscription(self):
for subscription in self.active_subscriptions:
stripe.Subscription.modify(
subscription.id,
cancel_at_period_end=False,
)
self.refresh()
Cancellations api #
Here is the cancellations api. I created an are you sure form that I can link
to from the accounts page with a normal anchor tag. Note that I am doing a
POST request to do the cancellation from a form. I want this to work for any
user whether there is js or not. This is an operation that will change the
users data, and I want to make sure that it avoids all browser and cdn caching.
As a scrappy startup we are running light on infrastructure and are caching
hard at the CDN to avoid excessive server hits.
Note
I am doing a `POST` request to do the cancellation from a form.
@pricing_router.get("/cancel")
@pricing_router.get("/cancel/")
def get_cancel(
request: Request,
current_user: Annotated[User, Depends(get_current_user_if_logged_in)],
):
return config.templates.TemplateResponse(
"cancel.html",
{
"request": request,
"prices": products.prices,
"products": products.products,
"current_user": current_user,
},
)
@pricing_router.post("/cancel")
@pricing_router.post("/cancel/")
def post_cancel(
request: Request,
current_user: Annotated[User, Depends(get_current_user_if_logged_in)],
):
current_user.cancel_subscription()
return HTMLResponse('<p id="cancel" hx-swap-oob="outerHTML">Your Subscription has been Cancelled</p>')
Reactivations #
Reactivating accounts looks just about the same as cancelling, only flippng True to False.
@pricing_router.get("/reactivate")
@pricing_router.get("/reactivate/")
def get_reactivate(
request: Request,
current_user: Annotated[User, Depends(get_current_user_if_logged_in)],
):
return config.templates.TemplateResponse(
"reactivate.html",
{
"request": request,
"prices": products.prices,
"products": products.products,
"current_user": current_user,
},
)
@pricing_router.post("/reactivate")
@pricing_router.post("/reactivate/")
def post_reactivate(
request: Request,
current_user: Annotated[User, Depends(get_current_user_if_logged_in)],
):
current_user.reactivate_subscription()
return HTMLResponse('<p id="reactivate" hx-swap-oob="outerHTML">Your Subscription has been reactivated</p>')
Full User Model #
This is the full user model, completely subject to change in the future, but it includes the cancel and reactivate methods.
class UserBase(SQLModel, table=False): # type: ignore[call-arg]
username: str = Field(unique=True)
full_name: str
email: str
email_verified: bool = False
disabled: bool = False
signup_date: Optional[datetime] = Field(default_factory=datetime.utcnow)
stripe_customer_id: Optional[str]
@property
def session(self):
return next(get_session())
@classmethod
def get_by_id(cls, id):
return next(get_session()).get(cls, id)
def refresh(self):
cache.set(f"active_subscriptions_{self.id}", None, 3600)
cache.set(f"active_products_{self.id}", None, 3600)
def get_checkout_sessions(self):
return [
stripe.checkout.Session.retrieve(s.stripe_checkout_session_id)
for s in self.session.exec(select(CheckoutSession).where(CheckoutSession.user_id == self.id)).all()
if s.stripe_checkout_session_id is not None
]
def get_active_subscriptions(self):
subscriptions = [
s.subscription
for s in [
stripe.checkout.Session.retrieve(s.stripe_checkout_session_id)
for s in self.session.exec(select(CheckoutSession).where(CheckoutSession.user_id == self.id)).all()
if s.stripe_checkout_session_id is not None
]
if s.status == "complete"
]
active_subscriptions = [stripe.Subscription.retrieve(subscription) for subscription in subscriptions]
return active_subscriptions
def has_active_subscription(self):
return len(self.active_subscriptions) > 0
@property
def active_subscriptions(self):
active_subscriptions = cache.get(f"active_subscriptions_{self.id}")
if active_subscriptions is not None:
return active_subscriptions
active_subscriptions = self.get_active_subscriptions()
cache.set(f"active_subscriptions_{self.id}", active_subscriptions, 3600)
return active_subscriptions
@property
def active_plans(self):
subscriptions = self.active_subscriptions
plans = [subscription.plan for subscription in subscriptions]
return plans
@property
def subscription_to_plan(self):
subscriptions = self.active_subscriptions
plans = {subscription.id: subscription.plan.id for subscription in subscriptions}
return plans
@property
def plan_to_subscription(self):
plans = {v: k for k, v in self.subscription_to_plan.items()}
return plans
def get_active_products(self):
plans = self.active_plans
products = [stripe.Product.retrieve(plan.product) for plan in plans]
return products
@property
def plan_to_product(self):
plans = self.active_plans
products = {plan.id: stripe.Product.retrieve(plan.product).id for plan in plans}
return products
@property
def prodct_to_plan(self):
plans = self.active_plans
products = {stripe.Product.retrieve(plan.product).id: plan.id for plan in plans}
return products
@property
def active_products(self):
products = cache.get(f"active_products_{self.id}")
if products is not None:
return products
products = self.get_active_products()
cache.set(f"active_products_{self.id}", products, 3600)
return products
@property
def best_active_subscription(self):
subscriptions = self.active_subscriptions
return subscriptions[0]
@property
def best_active_product(self):
products = self.active_products
products.sort(key=lambda p: p.metadata.get('level', 0))
return products[0]
@property
def best_active_subscription(self):
subscription_id = self.plan_to_subscription[self.prodct_to_plan[self.best_active_product.id]]
return stripe.Subscription.retrieve(subscription_id)
@property
def config(self):
product = self.best_active_product
return product.metadata
def subscription_status(self):
subscriptions = self.active_subscriptions()
def cancel_subscription(self):
for subscription in self.active_subscriptions:
stripe.Subscription.modify(
subscription.id,
cancel_at_period_end=True,
)
self.refresh()
def reactivate_subscription(self):
for subscription in self.active_subscriptions:
stripe.Subscription.modify(
subscription.id,
cancel_at_period_end=False,
)
self.refresh()
Looking for a Heroku replacement, What I found was shocking!
I’ve been using tailwind for a few months now and I can still say I’m loving it. I’ve been using it to create some rapid prototypes that may or may not ever become something, a document that is likely to go to print (a resume), and some quick dashboards.
I started using Tailwind a few month back #
A few months back in september of 2023 I made a case for tailwindcss. And have been using it on quite a few projects since.
- values are well thought out
- it’s really easy to use
- classes that make sense
- tree shakable
fokais.com #
I started working on fokais.com only a few weeks ago, It’s going to be a SAS to make blogging easier. I’ve started hosting some tools for this blog that I really like that I think I can turn into a service. It’s been fantastic to quickly pump out new pages with tailwind.
htmx">HTMX #
tailwind and htmx are a match made in heaven. They both really lean on Location of Behavior over Separation of concerns. They do really well at making small components that you can throw on and endpoint and stack into any page. With tailwind I just configure it to look at all my templates, and I can guarantee that the styles will be in app.css, and all I need to do is add classes to my component.
Heres a sample component for a user widget that will go on every page. It has everything it needs right in the template.
<div
hx-swap-oob="outerHTML"
id="user-header"
class="absolute top-0 right-0 mt-8 mr-4"
>
<!--markata-attribution-->
{% if current_user %}
<!--markata-attribution-->
<details
<!--markata-attribution-->
id="user-header-details"
<!--markata-attribution-->
open
class="group list-none px-4 py-2 self-center justify-self-center bg-neutral-600/10 shadow-lg shadow-zinc-950/20 ring-2 ring-zinc-950/5 rounded-xl flex justify-center align-center flex-col"
>
<summary style="list-style-type: none">{{ current_user.username }}</summary>
<!--markata-attribution-->
<div class="hidden group-hover:block my-4">
<!--markata-attribution-->
<a
class="mt-6 px-4 py-2 rounded bg-purple-950/5 ring-2 ring-cyan-500/30 text-cyan-500 font-bold"
<!--markata-attribution-->
href="{{ url_for('get_logout') }}"
>
<!--markata-attribution-->
Logout
</a>
<!--markata-attribution-->
</div>
<!--markata-attribution-->
</details>
{% else %}
<a
href="{{ url_for('post_login') }}"
class="mt-5 text-xl text-white font-bold text-shadow-xl text-shadow-zinc-950"
>
<!--markata-attribution-->
login
</a>
<!--markata-attribution-->
{% endif %}
<!--markata-attribution-->
</div>
internal apps #
I’ve built several interal apps, and tailwind has been really great for this. Its super quick to pop classes on components and get things to look decent quickly, or put some real polish into making them look nice.
My Website waylonwalker.com #
I’ve dropped my old decrepid css for some tailwind on my main site. My css was much smaller, but did not work quite as well on all devices, and most importantly was becoming a house of cards. Every time I fixed one thing several other things would fail. Colors were a bit muddy, and not as nicely configured as tailwind.
Most importantly was becoming a house of cards. Every time I fixed one thing several other things would fail.
One rough side of styling a blog in tailwind is that you don’t necessarily have control over granular details of how your pages get rendered without getting really deep into the markdown renderer, or writing your posts in html. It ends up looking a bit ugly, and is against the tailwind best practices, but it seems like the best way for a site like this.
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "./highlight.css";
.social {
@apply font-bold;
@apply flex flex-row;
@apply gap-4;
@apply justify-center;
@apply py-8;
}
#posts ul ul {
@apply backdrop-blur-sm;
@apply flex flex-col sm:grid grid-flow-row-dense;
@apply gap-4;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
@apply p-4;
}
grid #
I’ve struggled to use grid on my projects, and I’ve tried a few different times with no real success or adoption, but started using it on my resume, to have a main middle column, with two outer full bleed columns where I can make some elements full bleed to the edge. tailwind made this easy, once done, I had an admonition that was beautiful full bleed with a touch of color.
jpillora/installer is the install script generator I have been looking for. It downloads binaries for your machine from GitHub releases and unzips them for you. It grabs the latest release, so you can easily update them. I have tried scripting these installs in the past and struggled to consistently get the latest version for every package and unpack it correctly.
Also these pre-compiled binaries install rediculously fast compared to building them from source.
Check out some example links.
opening in a browser will show metadata
If you pass in script=true it will instead return the install script as it would by default through curl.
https://i.jpillora.com/serve?script=true
Use it to install neovim #
All you need to do to generate an install script is to pass in the GitHub repo slug with the org.
curl https://i.jpillora.com/neovim/neovim | bash
The shell script that it generates for neovim looks like this.
#!/bin/bash
if [ "$DEBUG" == "1" ]; then
set -x
fi
TMP_DIR=$(mktemp -d -t jpillora-installer-XXXXXXXXXX)
function cleanup {
rm -rf $TMP_DIR > /dev/null
}
function fail {
cleanup
msg=$1
echo "============"
echo "Error: $msg" 1>&2
exit 1
}
function install {
#settings
USER="neovim"
PROG="neovim"
ASPROG=""
MOVE="false"
RELEASE="stable"
INSECURE="false"
OUT_DIR="$(pwd)"
GH="https://github.com"
#bash check
[ ! "$BASH_VERSION" ] && fail "Please use bash instead"
[ ! -d $OUT_DIR ] && fail "output directory missing: $OUT_DIR"
#dependency check, assume we are a standard POISX machine
which find > /dev/null || fail "find not installed"
which xargs > /dev/null || fail "xargs not installed"
which sort > /dev/null || fail "sort not installed"
which tail > /dev/null || fail "tail not installed"
which cut > /dev/null || fail "cut not installed"
which du > /dev/null || fail "du not installed"
#choose an HTTP client
GET=""
if which curl > /dev/null; then
GET="curl"
if [[ $INSECURE = "true" ]]; then GET="$GET --insecure"; fi
GET="$GET --fail -# -L"
elif which wget > /dev/null; then
GET="wget"
if [[ $INSECURE = "true" ]]; then GET="$GET --no-check-certificate"; fi
GET="$GET -qO-"
else
fail "neither wget/curl are installed"
fi
#debug HTTP
if [ "$DEBUG" == "1" ]; then
GET="$GET -v"
fi
#optional auth to install from private repos
#NOTE: this also needs to be set on your instance of installer
AUTH="${GITHUB_TOKEN}"
if [ ! -z "$AUTH" ]; then
GET="$GET -H 'Authorization: $AUTH'"
fi
#find OS #TODO BSDs and other posixs
case `uname -s` in
Darwin) OS="darwin";;
Linux) OS="linux";;
*) fail "unknown os: $(uname -s)";;
esac
#find ARCH
if uname -m | grep -E '(arm|arch)64' > /dev/null; then
ARCH="arm64"
# no m1 assets. if on mac arm64, rosetta allows fallback to amd64
if [[ $OS = "darwin" ]]; then
ARCH="amd64"
fi
elif uname -m | grep 64 > /dev/null; then
ARCH="amd64"
elif uname -m | grep arm > /dev/null; then
ARCH="arm" #TODO armv6/v7
elif uname -m | grep 386 > /dev/null; then
ARCH="386"
else
fail "unknown arch: $(uname -m)"
fi
#choose from asset list
URL=""
FTYPE=""
case "${OS}_${ARCH}" in
"linux_amd64")
URL="https://github.com/neovim/neovim/releases/download/stable/nvim-linux64.tar.gz"
FTYPE=".tar.gz"
;;
"darwin_amd64")
URL="https://github.com/neovim/neovim/releases/download/stable/nvim-macos.tar.gz"
FTYPE=".tar.gz"
;;
*) fail "No asset for platform ${OS}-${ARCH}";;
esac
#got URL! download it...
echo -n "Downloading"
echo -n " $USER/$PROG"
if [ ! -z "$RELEASE" ]; then
echo -n " $RELEASE"
fi
if [ ! -z "$ASPROG" ]; then
echo -n " as $ASPROG"
fi
echo -n " (${OS}/${ARCH})"
echo "....."
#enter tempdir
mkdir -p $TMP_DIR
cd $TMP_DIR
if [[ $FTYPE = ".gz" ]]; then
which gzip > /dev/null || fail "gzip is not installed"
bash -c "$GET $URL" | gzip -d - > $PROG || fail "download failed"
elif [[ $FTYPE = ".tar.bz" ]] || [[ $FTYPE = ".tar.bz2" ]]; then
which tar > /dev/null || fail "tar is not installed"
which bzip2 > /dev/null || fail "bzip2 is not installed"
bash -c "$GET $URL" | tar jxf - || fail "download failed"
elif [[ $FTYPE = ".tar.gz" ]] || [[ $FTYPE = ".tgz" ]]; then
which tar > /dev/null || fail "tar is not installed"
which gzip > /dev/null || fail "gzip is not installed"
bash -c "$GET $URL" | tar zxf - || fail "download failed"
elif [[ $FTYPE = ".zip" ]]; then
which unzip > /dev/null || fail "unzip is not installed"
bash -c "$GET $URL" > tmp.zip || fail "download failed"
unzip -o -qq tmp.zip || fail "unzip failed"
rm tmp.zip || fail "cleanup failed"
elif [[ $FTYPE = ".bin" ]]; then
bash -c "$GET $URL" > "neovim_${OS}_${ARCH}" || fail "download failed"
else
fail "unknown file type: $FTYPE"
fi
#search subtree largest file (bin)
TMP_BIN=$(find . -type f | xargs du | sort -n | tail -n 1 | cut -f 2)
if [ ! -f "$TMP_BIN" ]; then
fail "could not find find binary (largest file)"
fi
#ensure its larger than 1MB
#TODO linux=elf/darwin=macho file detection?
if [[ $(du -m $TMP_BIN | cut -f1) -lt 1 ]]; then
fail "no binary found ($TMP_BIN is not larger than 1MB)"
fi
#move into PATH or cwd
chmod +x $TMP_BIN || fail "chmod +x failed"
DEST="$OUT_DIR/$PROG"
if [ ! -z "$ASPROG" ]; then
DEST="$OUT_DIR/$ASPROG"
fi
#move without sudo
OUT=$(mv $TMP_BIN $DEST 2>&1)
STATUS=$?
# failed and string contains "Permission denied"
if [ $STATUS -ne 0 ]; then
if [[ $OUT =~ "Permission denied" ]]; then
echo "mv with sudo..."
sudo mv $TMP_BIN $DEST || fail "sudo mv failed"
else
fail "mv failed ($OUT)"
fi
fi
echo "Downloaded to $DEST"
#done
cleanup
}
install
Self Host Your Own #
I’d reccomend self hosting your own. This way you know that it’s consistent and unlikely to change in a way that breaks your use.
curl -s https://i.jpillora.com/installer | bash
Repos I am using installer for #
Here are the repos I am using installer for.
atuinsh/atuin
benbjohnson/litestream
bootandy/dust
BurntSushi/ripgrep
chmln/sd
cjbassi/ytop
dalance/procs
dbrgn/tealdeer
ducaale/xh
go-task/task
imsnif/bandwhich
imsnif/diskonaut
kovidgoyal/kitty
mgdm/htmlq
neovim/neovim
ogham/dog
ogham/exa
pemistahl/grex
sharkdp/bat
sharkdp/fd
sharkdp/pastel
sirwart/ripsecrets
starship/starship
topgrade-rs/topgrade
zellij-org/zellij
I wanted to host some static files through fastapi. Typical use cases for this might be some static web content like html/css/js. It could also be images or some data that doesn’t need dynamically rendered.
From the Docs #
The docs cover how to host static files, and give this solution that is built into fastapi.
https://fastapi.tiangolo.com/tutorial/static-files/
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
Authenticated Static Files #
Thanks to #858.
OscartGiles posted this solution to add authentication to static files. I tried this out on my thoughts and it worked flawlessly.
import typing
from pathlib import Path
import secrets
from fastapi import FastAPI, Request, HTTPException, status
from fastapi.staticfiles import StaticFiles
from fastapi.security import HTTPBasic, HTTPBasicCredentials
PathLike = typing.Union[str, "os.PathLike[str]"]
app = FastAPI()
security = HTTPBasic()
async def verify_username(request: Request) -> HTTPBasicCredentials:
credentials = await security(request)
correct_username = secrets.compare_digest(credentials.username, "user")
correct_password = secrets.compare_digest(credentials.password, "password")
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
class AuthStaticFiles(StaticFiles):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
async def __call__(self, scope, receive, send) -> None:
assert scope["type"] == "http"
request = Request(scope, receive)
await verify_username(request)
await super().__call__(scope, receive, send)
app.mount(
"/static",
AuthStaticFiles(directory=Path(__file__).parent / "static"),
name="static",
)
If you want both then, all you have to do is mount AuthStaticFiles to a
different route. Now you can have private, or paid content behind
/restricted.
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount(
"/restricted",
AuthStaticFiles(directory=Path(__file__).parent / "restricted"),
name="restricted"
)
I recently se tup minio object storage in my homelab for litestream sqlite backups. The litestream quickstart made it easy to get everything up and running on localhost, but I hit a wall when dns was involved to pull it from a different machine.
Here is what I got to work #
First I had to configure the Key ID and Secret Access Key generated in the minio ui.
❯ aws configure
AWS Access Key ID [****************VZnD]:
AWS Secret Access Key [****************xAm8]:
Default region name [us-east-1]:
Default output format [None]:
Then set the the s3 signature_version to s3v4.
aws configure set default.s3.signature_version s3v4
Now when I have minio running on https://my-minio-endpoint.com I can use the aws cli to access the bucket.
Note that
https://my-minio-endpoint.comresolves to the bucket endpoint (default 9000) not the ui (default 9001).
aws --endpoint-url https://my-minio-endpoint.com s3 ls my_bucket
Now Configuring Litestream #
Litestream also accepts the endpoint argument via config. I could not get it
to work just with the ui.
Note the
aws configurestep above is not required for litestream, only the aws cli.
dbs:
- path: /path/to/database.db
replicas:
- url: s3://my_bucket/
endpoint: https://my-minio-endpoint.com
region: us-east-1
access-key-id: ****************VZnD
secret-access-key: ************************************xAm8
Now run a litestream replication.
litestream replicate -config litestream.yml
# or put the config in /etc/litestream.yml and just run replicate
litestream replicate
why-is-postgres-default
I’ve recently given tailwindcss a second chance and am really liking it. Here is how I set it up for my python based projects.
https://waylonwalker.com/a-case-for-tailwindcss
Installation #
npm is used to install the cli that you will need to configure and compile tailwindcss.
npm install -g tailwindcss-cli
Setup #
You will need to create a tailwind.config.js file, to get this you can use the cli.
npx tailwindcss init
Using tailwind with jinja templates #
To set up tailwind to work with jinja templates you will need to point the tailwind config content to your jinja templates directory.
module.exports = {
content: ["templates/**/*.html"],
};
Setting up the base styles #
I like to use the @tailwind base;, to do this I set up an input.css file.
@tailwind base;
@tailwind components;
@tailwind utilities;
Compiling #
Now that it’s all setup you can run the tailwindcss command. You will get an output.css with base tailwind plus any of the classes that you used.
tailwindcss -i ./input.css -o ./output.css --watch
A Case For Tailwindcss
Make the easy things easy
Underground Bases with Wyatt
The next version of markata will be around a full second faster at building it’s docs, that’s a 30% bump in performance at the current state. This performance will come when virtual environments are stored in the same directory as the source code.
What happened?? #
I was looking through my profiler for some unexpected performance hits, and
noticed that the docs plugin was taking nearly a full second (sometimes
more), just to run glob.
| |- 1.068 glob markata/plugins/docs.py:40
| | |- 0.838 <listcomp> markata/plugins/docs.py:82
| | | `- 0.817 PathSpec.match_file pathspec/pathspec.py:165
| | | [14 frames hidden] pathspec, <built-in>, <string>
Python scandir ignores hidden directories #
I started looking for different solutions and what I found was that I was hitting pathspec with way more files than I needed to.
len(list(Path().glob("**/*.py")))
# 6444
len([Path(f) for f in glob.glob("**/*.py", recursive=True)])
# 110
After digging into the docs I found that glob.glob uses os.scandir which
ignores ‘.’ and ‘..’ directories while Path.glob does not.
https://docs.python.org/3/library/os.html#os.scandir
results? #
Now glob.py from the docs plugin does not even show up in the profiler.
I opened up ipython and saw the following results. For some reason as I hit docs.glob it was only hitting 488 ms from ipython, but it was still a massive improvement over the original.
%timeit docs.glob(m)
# 488 ms ± 3.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit docs.glob(m)
# 9.37 ms ± 90.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
AUR.">paru is an aur helper that allows you to use a package manager to install packages from the aur.
What’s the Aur #
The Aur is a set of community managed packages that can be installed on arch based distros.
Why a helper? #
paru just makes it easy, no clone and run makepkg. You can do everything paru can do using the built in pacman installer.
Manual Install from the Aur #
You will need to manually instal pacman from the aur in order to get started.
sudo pacman -S --needed base-devel
git clone https://aur.archlinux.org/paru.git
cd paru
makepkg -si
Installing packages with paru #
Once setup you are ready to install packages from the AUR just like the core repos.
# you can update your system using paru
paru -Syu
# you can install packages from the AUR
paru -S tailscale
paru -S prismlauncher
# even core repo packages can be installed
paru -S docker
Paru in Docker #
Here is a snippet from my devtainer dockerfile. Where I use paru to install packages from the AUR inside of a dockerfile.
FROM archlinux
RUN echo '[multilib]' >> /etc/pacman.conf && \
echo 'Include = /etc/pacman.d/mirrorlist' >> /etc/pacman.conf && \
pacman --noconfirm -Syyu && \
pacman --noconfirm -S base-devel git && \
groupadd --gid 1000 devtainer && \
useradd --uid 1000 --gid 1000 -m -r -s /bin/bash devtainer && \
passwd -d devtainer && \
echo 'devtainer ALL=(ALL) ALL' > /etc/sudoers.d/devtainer && \
mkdir -p /home/devtainer/.gnupg && \
echo 'standard-resolver' > /home/devtainer/.gnupg/dirmngr.conf && \
chown -R devtainer:devtainer /home/devtainer && \
mkdir /build && \
chown -R devtainer:devtainer /build && \
cd /build && \
sudo -u devtainer git clone --depth 1 https://aur.archlinux.org/paru.git && \
cd paru && \
sudo -u devtainer makepkg --noconfirm -si && \
sed -i 's/#RemoveMake/RemoveMake/g' /etc/paru.conf && \
pacman -Qtdq | xargs -r pacman --noconfirm -Rcns && \
rm -rf /home/devtainer/.cache && \
rm -rf /build
USER devtainer
RUN sudo -u devtainer paru --noconfirm --skipreview --useask -S \
bat \
cargo \
direnv \
dua-cli \
dust \
fd
Final Thoughts #
There are other options out there, paru seemed to be the most supported at the time I started using arch and there has been no other reason for me to change it. It’s treated me well for nearly a year now.
I took a break
Playing Star Wars Text Adventure with a 10 yr old
Pydantic and singledispatch
Quickly and easily create new versions of your Python package with the gh release
command. Get the version number, changelog, and
Releasing a new version of your Python package can be a daunting task. You need to make
sure that all the necessary files are included, and that the version number is correct.
But now, with the help of the gh release command, you can make the process much
smoother.
The gh release command allows you to quickly and easily create a new version of your
Python package. All you need to do is provide the version number, the changelog, and the
distribution files. For example, if you wanted to create a new version of your package
with the version number v1.2.3, you could use the following command:
gh release create v1.2.3 -F CHANGELOG.md dist/*.whl dist/*.tar.gz
This command will create a new version of your package with the specified version number, and include the changelog and the distribution files. It’s a great way to make sure that all the necessary files are included in the release, and that the version number is correct.
The gh release command is a great tool for quickly and easily creating new versions of
your Python package. With just a few simple commands, you can make sure that all the
necessary files are included, and that the version number is correct. So if you’re looking
for an easy way to release a new version of your Python package, give the gh release
command a try.
Fix Arch Linux randomly rejecting passwords with one command. Try ‘faillock –user $USER’ to reset login counter and regain access. Quick solution for a smooth computing"
If you’re an Arch Linux user, you may have experienced a frustrating issue where your password is randomly not being accepted by the system. This can be a major inconvenience and can cause a lot of frustration, especially if it happens frequently.
The good news is that there is a simple fix for this issue. The following bash code can be used to fix the problem:
bash faillock --user $USER
This command is used to reset the failed login count for the current user. By running this command, you will be able to reset the system’s login counter and regain access to your account.
It’s important to note that this command should only be used as a temporary solution. If you find yourself frequently having to run this command, it’s likely that there is a deeper issue with your system that needs to be addressed.
In any case, if you’re experiencing problems with your Arch Linux system not accepting your password, give the above command a try and see if it resolves the issue for you.
Give github actions the -e flag in the shebang #! so they fail on any one
command failure. Otherwise each line will set the exit status, but only the
last one will be passed to ci.
#!/bin/bash -e
What is -e #
The -e flag to the bash command allows your script to exit immediately if any command within the script returns a non-zero exit status. This can be useful for ensuring that your script exits with an error if any of the commands it runs fail, which can help you identify and debug issues in your script. For example, if you have a script that runs several commands and one of those commands fails, the script will continue running without the -e flag, but will exit immediately if the -e flag is present. This can make it easier to troubleshoot your script and ensure that it runs correctly.
Solution for Windows #
In windows the solution is not quite as simple. You can define a function in a Windows batch script that wraps an if statement to check the exit status of a command and handle any errors that may have occurred. Here is an example of how you might define a function called “check_error” that does this:
:check_error
if errorlevel 1 (
echo An error occurred!
exit /b 1
)
To use this function in your script, you would simply call it after running a command, like this:
some_command
call :check_error
This would run the “some_command” and then call the “check_error” function to check the exit status and handle any errors that may have occurred. This approach allows you to reuse the error-checking logic in your script, which can make it easier to write and maintain.
minecraft documentary
I recently setup some vm’s on my main machine and got sick of signing in with passwords.
ssh-keygen
ssh-copy-id -i ~/.ssh/id_rsa.pub virt
Moving panes between tmux sessions is something that makes tmux a very flexible and powerful tool. I don’t need this feature very often, but it comes in clutch when you need it.
Pull a pane from any other session #
Using choose-window I was able to come up with a way to select any pane
withing any other session and join it into my current session.
# Choose a pane to join in horizontally
bind f choose-window -Z 'join-pane -h -s "%%"'
Push/Pull from scratch #
I’ve long had this one in my tmux config, I always have a “scratch” session
that I’m running, I often use for looking at things like k9s accross repos
within a popup.
This use case puts a pane into the scratch session, then pulls it back out. I will use this to move a pane between sessions in the rare cases I need to do this.
# push the active pane into the scratch session horizonally
bind -n M-f join-pane -ht scratch
# pull the last active pane from the scratch session horizonally into this session
bind -n M-F join-pane -hs scratch
I just shared some ssh keys with myself and ran into this error telling me that I did not set the correct permissions on my key.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0750 for '/home/waylon/.ssh/id_*******' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/home/waylon/.ssh/id_*******": bad Permissions
repo: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
I changed them with the following commands.
chmod 644 ~/.ssh/id_*******.pub
chmod 600 ~/.ssh/id_*******
With the latest release of version of nvim 0.8.0 we get access to a new winbar feature. One thing I have long wanted somewhere in my nvim is navigation for pairing partners or anyone watching can keep track of where I am. As the driver it’s easy to keep track of the file/function you are in. But when you make big jumps in a few keystrokes it can be quite disorienting to anyone watching, and having this feedback to look at is very helpful.
winbar #
nvim exposes the winbar api in lua, and you can send any text to the winbar as follows.
vim.o.winbar = "here"
You can try it for yourself right from the nvim command line.
:lua vim.o.winbar = "here"
Now you will notice one line above your file with the word here at the very
beginning.
Clearing the winbar #
If you want to clear it out, you can just set it to an empty string or nil.
:lua vim.o.winbar = ""
:lua vim.o.winbar = nil
Setting up nvim-navic #
You will need to install nvim-navic if you want to use it. I added it to my
plugins using Plug as follows.
call plug#begin('~/.local/share/nvim/plugged')
Plug 'SmiteshP/nvim-navic'
call plug#end()
Note!
nvim-navicdoes require the use of the nvim lsp, so if you are not using it then maybe this won’t work for you.
I created an on_attach function long ago, cause that’s what Teej told me to
do. Now I am glad I did, because it made this change super easy.
local function on_attach(client, bufnr)
if client.server_capabilities.documentSymbolProvider then
navic.attach(client, bufnr)
end
end
Then you need to use that on_attach function on all of the lsp’s that you
want navic to work on.
Then in a lua file you need to setup the winbar, for now I put this in my lsp-config settings file, but eventually I want to move my settings to lua and put it there.
vim.o.winbar = " %{%v:lua.vim.fn.expand('%F')%} %{%v:lua.require'nvim-navic'.get_location()%}"
What my winbar looks like #
What I have right now is everything someone who is watching would need to know to navigate to the same place that I am in the project.
waylonwalker/app.py Link > on_click
Diff #
Here are the changes that I made to to my plugins list and my lsp-config to get it.
/home/u_walkews/.config/nvim/plugins.vim
call plug#begin('~/.local/share/nvim/plugged')
+Plug 'SmiteshP/nvim-navic'
# /home/u_walkews/.config/nvim/lua/waylonwalker/lsp-config.lua
-local function on_attach() end
+local navic = require("nvim-navic")
+local function on_attach(client, bufnr)
+ if client.server_capabilities.documentSymbolProvider then
+ navic.attach(client, bufnr)
+ end
+end
+
+vim.o.winbar = " %{%v:lua.vim.fn.expand('%F')%} %{%v:lua.require'nvim-navic'.get_location()%}"
GH commit #
If you want to see the change on GitHub, here is the diff
I really like having global cli command installed with pipx. Since textual
0.2.x (the css release) is out I want to be able to pop into textual devtools
easily from anywhere.
Pipx Install #
You can pipx install textual.
pipx install textual
But if you try to run any textual cli commands you will run into a
ModuleNotFoundError, because you need to install the optional dev
dependencies.
Traceback (most recent call last):
File "/home/u_walkews/.local/bin/textual", line 5, in <module>
from textual.cli.cli import run
File "/home/u_walkews/.local/pipx/venvs/textual/lib/python3.10/site-packages/textual/cli/cli.py", line 4, in <module>
import click
ModuleNotFoundError: No module named 'click'
Pipx Inject #
In order to install optional dependencies with pipx you need to first install
the library, then inject in the optional dependencies using the square bracket
syntax.
pipx install textual
pipx inject textual 'textual[dev]'
I am working through the textual tutorial, and I want to put it in a proper cli
that I can pip install and run the command without textual run --dev app.py.
This is a fine pattern, but I also want this to work when I don’t have a file
to run.
pyproject.toml entrypoints #
I set up a new project running hatch new, and added the following entrypoint,
giving me a tutorial cli command to run.
...
[project.scripts]
tutorial = 'textual_tutorial.tui:tui'
https://waylonwalker.com/hatch-new-cli/
setup.py entrypoints #
If you are using setup.py, you can set up entrypoints in the setup command.
from setuptools import setup
setup(
...
entry_points={
"console_scripts": ["tutorial = textual_tutorial.tui:tui"],
},
...
)
https://waylonwalker.com/minimal-python-package/
tui.py #
adding features
Now to get devtools through a cli without running through textual run --dev.
I pulled open the textual cli source code, and this is what it does at the time
of writing.
Note: I used sys.argv as a way to implement a
--devquickly tutorial. For a real project, I’d setup argparse, click, or typer.typeris my go to these days, unless I am really trying to limit dependencies, then the standard libraryargparsemight be what I go with.
def tui():
from textual.features import parse_features
import os
import sys
dev = "--dev" in sys.argv # this works, but putting it behind argparse, click, or typer would be much better
features = set(parse_features(os.environ.get("TEXTUAL", "")))
if dev:
features.add("debug")
features.add("devtools")
os.environ["TEXTUAL"] = ",".join(sorted(features))
app = StopwatchApp()
app.run()
if __name__ == "__main__":
tui()
Other Flags??? #
If you look at the source, there is one other flag for headless mode.
FEATURES: Final = {"devtools", "debug", "headless"}
Run it #
Here it is running with tutorial --dev on the left, and textual console on
the right.
For far too long I have had to fidget with v4l2oloopback after reboot. I’ve had this happen on ubuntu 18.04, 22.04, and arch.
After a reboot the start virtual camera button won’t work, It appears and is clickable, but never turns on. Until I run this command.
sudo modprobe v4l2loopback video_nr=10 card_label="OBS Video Source" exclusive_caps=1
Today I learned that you can turn on kernel modules through some files in /etc/modules...
This is what I did to my arch system to get it to work right after boot.
echo "v4l2loopback" | sudo tee /etc/modules-load.d/v4l2loopback.conf
echo "options v4l2loopback video_nr=10 card_label=\"OBS Video Source\" exclusive_caps=1" | sudo tee /etc/modprobe.d/v4l2loopback.conf
I ran into an issue where I was unable to ask localstack for its status. I would run the command and it would tell me that it didn’t have permission to read files from my own home directory. Let’s fix it
The issue #
I would run this to ask for the status.
localstack status
And get this error
PermissionError: [Errno 13] Permission denied: '/home/waylon/.cache/localstack/image_metadata'
What happened #
It dawned on me that the first time I ran localstack was straight docker, not the python cli. When docker runs it typically runs as root unless the Dockerfile sets up a user and group for it.
How to fix it #
If you have sudo access to the machine you are on you can recursively change
ownership to your user and group. I chose to just give myself ownership of my
whole ~/.cache directory you could choose a deeper directory if you want. I
feel pretty safe giving myself ownership to my own cache directory on my own
machine.
whoami
# waylon
chown -R waylon:waylon ~/.cache
Now it’s working #
Running localstack status now gives me a nice status message rather than an error.
❯ localstack status
┌─────────────────┬───────────────────────────────────────────────────────┐
│ Runtime version │ 1.2.1.dev │
│ Docker image │ tag: latest, id: dbbfe0ce0008, 📆 2022-10-15T00:51:03 │
│ Runtime status │ ✖ stopped │
└─────────────────┴───────────────────────────────────────────────────────┘
Markata now allows you to create jinja extensions that will be loaded right in
with nothing more than a pip install.
From the Changelog #
The entry for 0.5.0.dev2 from markata’s changelog
- Created entrypoint hook allowing for users to extend marka with jinja exensions #60 0.5.0.dev2
markata-gh #
The first example that you can use right now is markata-gh. It will render
repos by GitHub topic and user using the gh cli, which is available in github
actions!
Get it with a pip install
pip install markata-gh
Use it with some jinja in your markdown.
## Markata plugins
It uses the logged in uer by default.
{% gh_repo_list_topic "markata" %}
You can more explicitly grab your username, and a topic.
{% gh_repo_list_topic "waylonwalker", "personal-website" %}
How is this achieved #
The jinja extension details are for another post, but this is how markata-gh
exposes itslef as a jinja extension.
class GhRepoListTopic(Extension):
tags = {"gh_repo_list_topic"}
def __init__(self, environment):
super().__init__(environment)
def parse(self, parser):
line_number = next(parser.stream).lineno
try:
args = parser.parse_tuple().items
except AttributeError:
raise AttributeError(
"Invalid Syntax gh_repo_list_topic expects <username>, or <username>,<topic> both must have the comma"
)
return nodes.CallBlock(self.call_method("run", args), [], [], "").set_lineno(
line_number
)
def run(self, username=None, topic=None, caller=None):
"get's markdown to inject into post"
return repo_md(username=username, topic=topic)
Entrypoints #
Then markata-gh exposes itself as an extension through entrypoints.
Creating entrypoints in pyproject.toml #
If your project is using pyproject.toml for packaging you can setup an
entrypoint as follows.
[project.entry-points."markata.jinja_md"]
markta_gh = "markata_gh.repo_list:GhRepoListTopic"
Creating entrypoints in setup.py #
If your project is using setup.py for packaging you can setup an
entrypoint as follows.
setup(
...
entry_points={
"markata.jinja_md": ["markta_gh" = "markata_gh.repo_list:GhRepoListTopic"]
},
...
)
In my adventure to learn django, I want to be able to setup REST api’s to feed into dynamic front end sites. Potentially sites running react under the hood.
Install #
To get started lets open up a todo app that I created with django-admin startproject todo.
pip install djangorestframework
Install APP #
Now we need to declare rest_framwork as an INSTALLED_APP.
INSTALLED_APPS = [
...
"rest_framework",
...
]
create the api app #
Next I will create all the files that I need to get the api running.
mkdir api
touch api/__init__.py api/serializers.py api/urls.py api/views.py
base/models.py #
I already have the following model from last time I was playing with django. It will suffice as it is not the focus of what I am learning for now.
Note the name of the model class is singular, this is becuase django will automatically pluralize it in places like the admin panel, and you would end up with Itemss.
from django.db import models
# Create your models here.
class Item(models.Model):
name = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.priority} {self.name}"
Next I will make some dummy data to be able to return. I popped open ipython
and made a few records.
from base.models import Item
Item.objects.create(name='first')
Item.objects.create(name='second')
Item.objects.create(name='third')
api/serializers.py #
Next we need to set up a serializer to seriaze and de-serialize data between
our model and json. You can specify each field individually or all of them by
passing in __all__.
from rest_framework import serializers
from base.models import Item
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = '__all__'
api/views.py #
Now we need a view leveraging the djangorestframework. The serializer we
just created will be used to serialize all of the rows into a list of objects
that Response can handle.
Note: to return a collection of model objects we need to set many to
True
from rest_framework.decorators import api_view
from rest_framework.response import Response
from base.models import Item
from .serializers import ItemSerializer
@api_view(["GET"])
def get_data(request):
items = Item.objects.all()
serializer = ItemSerializer(items, many=True)
return Response(serializer.data)
@api_view(['POST'])
def add_item(request):
serializer = ItemSerializer(data = request.data)
if serializer.is_valid():
serializer.save()
return Response()
api/urls.py #
Now we need to setup routing to access the views through an url.
from django.urls import path
from . import views
urlpatterns = [
path('', views.get_data),
path('add/', views.add_item),
]
todo/urls.py #
Then we need to include these urls from our api in the urls specified by settings.ROOT_URLCONf
from django.urls import path
urlpatterns = [
...
path("api/", include("api.urls")),
]
Run it #
python manage.py runserver
Running the developement server and going to localhost:8000/api we can see
the full list of items in th api.
Markata now uses hatch as its build backend, and version bumping tool.
setup.py, and setup.cfg are completely gone.
0.5.0 is big #
Markata 0.5.0 is now out, and it’s huge. Even though it’s the backend of this blog I don’t actually have that many posts directly about it. I’ve used it a bit for blog fuel in generic ways, like talking about pluggy and diskcache, but very little have I even mentioned it.
Over the last month I made a big push to get 0.5.0 out, which adds a whole
bunch of new configurability to markata.
Here’s the changelog entry.
- Moved to PEP 517 build #59 0.5.0.dev1
My Personal Simple CI/CD #
Before cutting all of my personal projects over to hatch. The first thing I did was to setup a solid github action, hatch-actionthat I can resue.
It automatically bumps versions, using pre-releases on all branches other than main, with special branches for bumping major, minor, patch, dev, alha, beta, and dev.
hatch new –init #
To convert the project over to hatch, and get rid of setup.py/setup.cfg, I ran
hatch new --init. This automatically grabs all the metadata for the project
and makes a pyproject.toml that has most of what I need.
hatch new --init
I then manually moved over my isort config, put flake8 config into .flake8,
and dropped setup.cfg.
lint-test #
Part of my hatch-action is to run a before-command, for markata, this runs
all of my linting and testing in one hatch script called lint-test. If this
fails CI will fail and I can read the report in the logs, make a fix and
re-publish.
[tool.hatch.envs.default.scripts]
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=markata --cov=tests"
no-cov = "cov --no-cov"
lint = "flake8 markata"
format = "black --check markata"
sort-imports = "isort markata"
build-docs = "markata build"
lint-test = [
"lint",
"format",
"seed-isort-config",
"sort-imports",
"cov",
]
test-lint = "lint-test"
Typical branching workflow #
with automatic versioning
My typical workflow is to work on features in their own branch where they do
not automatically version or publish, they keep the same version they were
branched off of. Then I do a pr to develop, which will do a minor,dev bump
and publish a pre-relese to pypi.
# starting with version 0.0.0
Feature1 -- │
Feature2 -- ├── dev 0.1.0.dev1,2,3 ── main 0.1.0
Feature3 -- │
I will let several features collect in develop before cutting a full relese over to main. This gives me time to make sure the solution is what makes the most sense, I try to use it in a few projects, and generally its edges show, and another pr is warranted to make the feature useful for more use cases. After running and using these new releases in a few projects, I am confident that its ready and release to main.
managing prs #
Doing PR’s with gh, probably deserves its own post but here are some helpful commands.
gh pr create --base develop --fill
gh pr edit
gh pr diff | dunk
gh pr merge -ds
Building and publishing #
hatch makes building and publishing pretty straightforward. It’s one command inside my hatch-action to build and one to publish. On each project that uses my hatch-action I only need to give it a token that I get from PyPi.
env:
HATCH_INDEX_USER: __token__
HATCH_INDEX_AUTH: ${{ secrets.pypi_password }}
Full set of changes #
If you want to see all of the details on how markata moved over to hatch, you can check out this diff.
https://github.com/WaylonWalker/markata/compare/v0.4.0..v0.5.0.dev0
My next step into django made me realize that I do not have access to the admin panel, turns out that I need to create a cuper user first.
Run Migrations #
Right away when trying to setup the superuser I ran into this issue
django.db.utils.OperationalError: no such table: auth_user
Back to the tutorial
tells me that I need to run migrations to setup some tables for the
INSTALLED_APPS, django.contrib.admin being one of them.
python manage.py migrate
yes I am still running remote on from my chromebook.
python manage.py createsuperuser
The super user has been created.
CSRF FAILURE #
My next issue trying to run off of a separate domain was a cross site request forgery error.
Since this is a valid domain that we are hosting the app from we need to tell
Django that this is safe. We can do this again in the settings.py, but this
time the variable we need is not there out of the box and we need to add it.
CSRF_TRUSTED_ORIGINS = ['https://localhost.waylonwalker.com']
I made it!! #
And we are in, and welcomed for the first time with this django admin panel.
Remote Hosting #
You might find these settings helpful as well if you are trying to run your site on a remote host like aws, digital ocean, linode, or any sort of cloud providor. I had it running in my home lab while I was out of the house and ssh’d in over with a chromebook.
I am continuing my journey into django, but today I am not at my workstation. I
am ssh’d in remotely from a chromebook. I am fully outside of my network, so I
can’t access it by localhost, or it’s ip. I do have cloudflared tunnel
installed and dns setup to a localhost.waylonwalker.com.
Settings #
I found this in settings.py and yolo, it worked first try. I am in from my
remote location, and even have auth taken care of thanks to cloudflare. I am
really hoping to learn how to setup my own auth with django as this is one of
the things that I could really use in my toolbelt.
ALLOWED_HOSTS = ['localhost.waylonwalker.com']
I have no experience in django, and in my exploration to become a better python developer I am dipping my toe into one of the most polished and widely used web frameworks Django to so that I can better understand it and become a better python developer.
If you found this at all helpful make sure you check out the django tutorial
install django #
The first thing I need to do is render out a template to start the project.
For this I need the django-admin cli. To get this I am going the route of
pipx it will be installed globally on my system in it’s own virtual
environment that I don’t have to manage. This will be useful only for using
startproject as far as I know.
pipx install django
django-admin startproject try_django
cd try_django
Make a venv #
Once I have the project I need a venv for all of django and all of my
dependencies I might need for the project. I have really been diggin hatch
lately, and it has a one line “make a virtual environment and manage it for
me” command.
hatch shell
If hatch is a bit bleeding edge for you, or it has died out by the time you read this. The ol trusty venv will likely stand the test of time, this is what I would use for that.
python -m .venv --prmpt `basename $PWD`
. ./.venv/bin/activate
Start the webserver #
Next up we need to start the webserver to start seeing that development content. The first thing I did was run it as stated in the tutorial and find it clashed with a currently running web server port.
python manage.py runserver
I jumped over to that tmux session, killed the process and I was up and running.
What’s running #
The default django hello world looks well designed. You are first presented with this page.
Next #
I opened up the urls.py to discover that the only configured url was at
/admin. I tried to log in as admin, but was unable to as I have not yet
created a superuser. Next time I play with django that is what I will explore.
While updating my site to use Markata’s new configurable head I ran into some escaping issues. Things like single quotes would cause jinja to fail as it was closing quotes that it shouldnt have.
Jinja Escaping Strings #
Jinja comes with a handy utility for escaping strings. I definitly tried to
over-complicate this before realizing. You can just pipe your variables into
e to escape them. This has worked pretty flawless at solving some jinja
issues for me.
<p>
{{ title|e }}
</p>
Creating meta tags in Markata #
The issue I ran into was when trying to setup meta tags with the new
configurable head, some of my titles have single quotes in them. This is what
I put in my markata.toml to create some meta tags.
[[markata.head.meta]]
name = "og:title"
content = "{{ title }}"
Using my article titles like this ended up causing this syntax error when not escaped.
SyntaxError: invalid syntax. Perhaps you forgot a comma?
Exception ignored in: <function Forward.__del__ at 0x7fa9807192d0>
Traceback (most recent call last):
...
TypeError: 'NoneType' object is not callable
jinja2 escape #
After making a complicated system of using html.escape I realized that jinja
included escaping out of the box so I updated my markata.toml to include the
escaping, and it all just worked!.
[[markata.head.meta]]
name = "og:title"
content = "{{ title|e }}"
When I am developing python code I often have a repl open alongside of it running snippets ofcode as I go. Ipython is my repl of choice, and I hace tricked it out the best I can and I really like it. The problem I recently discovered is that I have way overcomplicated it.
What Have I done?? #
So in the past the way I have setup a few extensions for myself is to add
something like this to my ~/.ipython/profile_default/startup directory. It
sets up some things like rich highlighting or in this example automatic
imports. I even went as far as installing some of these in the case I didn’t have them installed.
import subprocess
from IPython import get_ipython
from IPython.core.error import UsageError
ipython = get_ipython()
try:
ipython.run_line_magic("load_ext pyflyby", "inline")
except UsageError:
print("installing pyflyby")
subprocess.Popen(
["pip", "install", "pyflyby"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
).wait()
ipython.run_line_magic("load_ext pyflyby", "inline")
print("installing isort")
subprocess.Popen(
["pip", "install", "isort"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
What I missed? #
I missed the fact that some of these tools like pyflyby and rich already
have an ipython extension maintained by the library that just works. It’s less
complicated and more robust to future changes in the library. If anything ever
changes with these I will not have to worry about which version is installed,
the extension will just take care of itself.
How to activate these. #
The reccomended way is to add them to your
~/.ipython/profile_default/ipython_config.py
c.InteractiveShellApp.extensions.append('rich')
c.InteractiveShellApp.extensions.append('markata')
c.InteractiveShellApp.extensions.append('pyflyby')
The issue that I found with this is that you can end up with a sea of errors flooding your terminal. Personally I will know immediately if ipython is working right or not and typically have scriped venv installs so I have everything I need, so If I don’t have everything it’s probably for a reason and I don’t need an error message lighting up.
My way around this was to test if the module was importable and if it had a
load_ipython_extension attribute before appending it as an extension.
def activate_extension(extension):
try:
mod = importlib.import_module(extension)
getattr(mod, "load_ipython_extension")
c.InteractiveShellApp.extensions.append(extension)
except ModuleNotFoundError:
"extension is not installed"
except AttributeError:
"extension does not have a 'load_ipython_extension' function"
extensions = ["rich", "markata", "pyflyby"]
for extension in extensions:
activate_extension(extension)
My Change #
If you want to see what I did to my config see this commit.
A long needed feature of markata has been the ability to really configure out templates with configuration rather. It’s been long that you needed that if you really want to change the style, meta tags, or anything in the head you needed to write a plugin or eject out of the template and use your own.
Adding some Head #
Now you can add some extra style to your site with the existing built-in template.
[[markata.head]]
text = """
<style>
img {
width: 100%;
height: auto;
}
ul {
display: flex;
flex-wrap: wrap;
}
</style>
"""
You can have more than one Head #
Each text entry in markata.head just gets appended raw into the head.
[[markata.head]]
text = """
<style>
img {
width: 100%;
height: auto;
}
ul {
display: flex;
flex-wrap: wrap;
}
</style>
"""
[[markata.head]]
text = """
<script>
console.log('hey there')
</script
"""
Still need more? #
If this does not take you far enough yet, you can still eject out and use your own template pretty easy. If you are going for a full custom site it’s likely that this will be the workflow for awhile. Markata should only get better and make this required less often as it matures.
[markata]
post_template = "pages/templates/post_template.html"
Once you have this in your markata.toml you can put whatever you want in your
own template.
I’m really getting into using hatch as my go to build system, and I am really
liking it so far. I am slowly finding new things that just work really well.
hatch new is one of those things that I didn’t realize I needed until I had
it.
creating new versions created by myself with stable diffusion
❯ pipx run hatch new --help
Usage: hatch new [OPTIONS] [NAME] [LOCATION]
Create or initialize a project.
Options:
-i, --interactive Interactively choose details about the project
--cli Give the project a command line interface
--init Initialize an existing project
-h, --help Show this message and exit.
Note! I am running all of these commands with pipx. I like to use pipx for all of my system level cli applications. To emphasis this point in the article I am going to use
pipx run hatch, but you canpipx install hatchthen just runhatchfrom there.
Interacively create a new project #
Running hatch new -i will ask let you interactivly choose details about the
project, such as the project’s name.
pipx run hatch new -i
After running and naming the project Hatch New we end up with the following filetree.
❯ tree .
.
├── hatch_new
│ ├── __about__.py
│ └── __init__.py
├── LICENSE.txt
├── pyproject.toml
├── README.md
└── tests
└── __init__.py
Non-Interative #
You can also fill in the project name ahead of time, and it will run without any questions.
❯ pipx run hatch new "Another Project"
another-project
├── another_project
│ ├── __about__.py
│ └── __init__.py
├── tests
│ └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml
Note! all of these examples will create a project directory within your current working directory.
–init #
existing project
hatch new has an --init flag in order to initialize a new hatch
pyproject.toml in an existing project. This feels like it would be useful if
you are converting a project to hatch, or if like me you sometimes start making
something before you realize it’s something that you want to package. Honestly
this doesn’t happen too much anymore I package most things, and I hope hatch new completely breaks this habbit of mine.
Let’s say I have the following existing project.
❯ tree
.
└── hatch_init
└── __init__.py
1 directory, 1 file
I can setup packaging with hatch by running.
pipx run hatch new --init
The pyproject.toml that comes out is pretty similar to the one that comes out
of the normal hatch new, but without any other files.
Note that you will need to setup a
__about__.pyyourself for the dynamic versioning that it has setup for you.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "hatch-init"
description = 'initialize an existing project using hatch'
readme = "README.md"
requires-python = ">=3.7"
license = "MIT"
keywords = []
authors = [
{ name = "Waylon S. Walker", email = "[email protected]" },
]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = []
dynamic = ["version"]
[project.urls]
Documentation = "https://github.com/unknown/hatch-init#readme"
Issues = "https://github.com/unknown/hatch-init/issues"
Source = "https://github.com/unknown/hatch-init"
[tool.hatch.version]
path = "hatch_init/__about__.py"
[tool.hatch.envs.default]
dependencies = [
"pytest",
"pytest-cov",
]
[tool.hatch.envs.default.scripts]
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=hatch_init --cov=tests"
no-cov = "cov --no-cov"
[[tool.hatch.envs.test.matrix]]
python = ["37", "38", "39", "310", "311"]
[tool.coverage.run]
branch = true
parallel = true
omit = [
"hatch_init/__about__.py",
]
[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
cli #
hatch new does not stop there, it also has a --cli flag to give you a cli
out of the box as well.
❯ pipx run hatch new "new cli" --cli
new-cli
├── new_cli
│ ├── cli
│ │ └── __init__.py
│ ├── __about__.py
│ ├── __init__.py
│ └── __main__.py
├── tests
│ └── __init__.py
├── LICENSE.txt
├── README.md
└── pyproject.toml
When you use the --cli flag you also get click as a dependency and
project.scripts setup automatically.
[project]
name = "new-cli"
# ...
dependencies = [
"click",
]
# ...
[project.scripts]
new-cli = "new_cli.cli:new_cli"
what’s in the cli #
It’s a hello-world click application.
# SPDX-FileCopyrightText: 2022-present Waylon S. Walker <[email protected]>
#
# SPDX-License-Identifier: MIT
import click
from ..__about__ import __version__
@click.group(context_settings={'help_option_names': ['-h', '--help']}, invoke_without_command=True)
@click.version_option(version=__version__, prog_name='new cli')
@click.pass_context
def new_cli(ctx: click.Context):
click.echo('Hello world!')
sneak peek #
I’ll dive more into environments and the run command later, but we can run the cli pretty damn quick with two commands. In under 5s I was able to run this cli that it created. This is a pretty incredible startup time.
Hatch has an amazing versioning cli for python packages that just works. It takes very little config to get going and you can start bumping versions without worry.
creating new versions created by myself with stable diffusion
project layout #
For trying out the hatch version cli let’s make a simple project with the
terrible name pkg.
❯ tree .
.
├── pkg
│ ├── __about__.py
│ └── __init__.py
├── pyproject.toml
└── README.md
1 directory, 4 files
pyproject.toml #
The main hero of this post is the pyproject.toml. This is what defines all
of our PEP 517 style project setup.
[project]
name = "pkg"
description = "Show how to version packages with hatch"
readme = "README.md"
dynamic = [
"version",
]
[build-system]
requires = [
"hatchling>=1.4.1",
]
build-backend = "hatchling.build"
[tool.hatch.version]
path = "pkg/__about__.py"
statically versioning #
project.version
It is possible to set the version number inside the pyproject.toml
statically. This is fine if you just want to version your package manually,
and not through the hatch cli.
[project]
name = "pkg"
version = "0.0.0"
# ...
Statically versioning in pyproject.toml will not work with
hatch version
Cannot set version when it is statically defined by the `project.version` field
dynamically Versioning #
project.dynamic
Setting the project verion dynamically can be done by changing up the following
to your pyproject.toml. Hatch only accepts a path to store your version. If
you need to reference it elsewhere in your project you can grab it from the
package metadata for that file. I would not put anything else that could
possibly clash with the version, as you might accidently change both things.
If you really need to set it in more places use a package like bump2version.
[project]
name = "pkg"
dynamic = [
"version"
]
# ...
[tool.hatch.version]
path = "pkg/__about__.py"
Note: you can configure hatch to use a different pattern https://hatch.pypa.io/1.2/version/#configuration, but I have not found it to be something that I need.
about.py #
The hatch project itself uses a
about.py
to store it’s version. It’s sole content is a single __version__ variable. I
don’t have any personal issues with this so I am going to be following this in
my projects that use hatch.
__version__ = "0.0.0"
versioning #
Hatch has a pretty intuitive versioning api. hatch version gives you the
version. If you pass in a version like hatch version "0.0.1" it will set it
to that version as long as it is in the future, otherwise it will error.
# print the current version
hatch version
# set the version to 0.0.1
hatch version "0.0.1"
bumping #
You can bump parts of the semver version.
# minor bump
hatch version minor
# beta pre-release bump
# If published to pypi this can be installed with the --pre flag to pip
hatch version b
# bump minor and beta
hatch version minor,b
# release all of the --pre-release flags such as alpha beta rc
hatch release
Example #
Here is a screenshot of bumping a projet along.
GitOps #
In my github actions flow I will be utilizing this to automate my versions. In
my side projects I use the develop branch to release –pre releases. I have
all of my own dependent projets running on these –pre releases, this allows me
to cut myself in my own projects before anyone else. Then on main I
automatically release this beta version.
GitHub Actions #
Here is what the ci/cd for markata looks like. There might be a better
workflow strategy, but I use a single github actions workflow and cut branches
to release –pre releases and full release. These steps will bump, tag,
commit, and deploy for me.
- name: automatically pre-release develop branch
if: github.ref == 'refs/heads/develop'
run: |
git config --global user.name 'autobump'
git config --global user.email '[email protected]'
VERSION=`hatch version`
# if current version is not already beta then bump minor and beta
[ -z "${b##*`hatch version`*}" ] && hatch version b || hatch version minor,b
NEW_VERSION=`hatch version`
git add markta/__about__.py
git commit -m "Bump version: $VERSION → $NEW_VERSION"
git tag $VERSION
git push
git push --tags
- name: automatically release main branch
if: github.ref == 'refs/heads/main'
run: |
git config --global user.name 'autobump'
git config --global user.email '[email protected]'
VERSION=`hatch version`
hatch version release
NEW_VERSION=`hatch version`
git add markta/__about__.py
git commit -m "Bump version: $VERSION → $NEW_VERSION"
git tag $VERSION
git push
git push --tags
- name: build
run: |
python -m build
- name: pypi-publish
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
uses: pypa/[email protected]
with:
password: ${{ secrets.pypi_password }}
Hatch Version Action #
I am setting up a github custom action waylonwalker/hatch-version-action that will lint, test, bump, and publish for me in one step. More on that in the future.
Markata is a great python framework that allows you to go from markdown to a full website very quickly. You can get up and running with nothing more than Markdown. It is also built on a full plugin architecture, so if there is extra functionality that you want to add, you can create a plugin to make it behave like you want.
Full transparancy… I built markata.
The talk #
The talk is live on YouTube. Make sure you check out the other videos from the conference. There were quite a few quality talks that deserve a watch as well.
Packages I Maintain
I spoke at python webconf in March 2022 about how I deploy this blog on a continuous basis.
Building this blog has brought me a lot of benefits. I have a set of custom curated notes to help describe a problem and how to solve it to me. At theis point it’s not uncommon to google an Issue I am having and finding my own blog with exactly the solution I need at the top.
I also bump into people from time to time that recognize me from the blog, its a nice conversation starter, and street cred.
The Talk #
The talk recently released on Youtube, you can watch it without having a ticket to the conference for free. There were a bunch of other talks that you should check out too!
I got all the pypi packages that I own behind 2 factor authentication. 💪
Recently this really made it’s rounds in the python news since pypi was requiring critical package maintainers to have 2FA on and even offering them hardware tokens to help them turn this on.
I feel like this caused a bit of confusion as turning on 2FA does not mean that you need to do anything different to deploy a package, and it DOES NOT require a hardware token. You can continue using your favorite 2FA app.
You might wonder what this means for my projects. It means that to edit any sensitive content such as pull a new api token, add/remove maintainers, or deleting a release I need to use a TOPT (time based one time password) application such as Google Authenticator, Microsoft Authenticator, Authy, or FreeOTP.
This has very little change to my overall workflow as my CI system still automatically deploys for me with the same api token as before.
This is one small thing that maintainers can do to prevent supply chain attacks on their projects that they put so much work into.
Login #
When I log in I now get this extra screen asking for an auth token.
My packages #
Once I turned on 2FA for my account I could then turn on 2FA requirement for each project. I am not sure how much safety there is in pypi, it might require all maintainers to have it turned on before it allows packages to have it turned on.
Once turned on it requires anyone who maintains the project to have 2FA on to be able to edit any sensitive content.
I was on Talk Python
I just love how some features of vim are so discoverable and memorable once you really start to grasp it. Sorting and uniqing your files or ranges is one of those examples for me.
" sort the file
:sort
" sort the file only keeping unique lines
:sort u
" sort a range
:'<,'> sort
" sort a range only keeping unique lines
:'<,'> sort u
I recently used this to dedupe my autogenerated links section for rich-syntax-range-style. More often I am using it to sort and uniqify objects like arrays and lists.
Here is what the markdown looks like.
* [py-tree-sitter](https://github.com/tree-sitter/py-tree-sitter)
* [rich](https://github.com/Textualize/rich)
* [@textualizeio](https://twitter.com/textualizeio)
* [rich](https://github.com/Textualize/rich)
* [another post](https://waylonwalker.com/designing-kedro-router)
* [print-register-pipelines](https://screenshots.waylonwalker.com/print-register-pipelines.webp)
* [rich](https://github.com/Textualize/rich)
* [console-print-register-pipelines](https://screenshots.waylonwalker.com/console-print-register-pipelines.webp)
* [rich](https://github.com/Textualize/rich)
* [syntax-print-register-pipelines](https://screenshots.waylonwalker.com/syntax-print-register-pipelines.webp)
* [rich](https://github.com/Textualize/rich)
* [syntax-print-register-pipelines-highlight-line](https://screenshots.waylonwalker.com/syntax-print-register-pipelines-highlight-line.webp)
* [py-tree-sitter](https://github.com/tree-sitter/py-tree-sitter)
Then typing vap:sort u yields a uniqly sorted list of links.
* [@textualizeio](https://twitter.com/textualizeio)
* [another post](https://waylonwalker.com/designing-kedro-router)
* [console-print-register-pipelines](https://screenshots.waylonwalker.com/console-print-register-pipelines.webp)
* [print-register-pipelines](https://screenshots.waylonwalker.com/print-register-pipelines.webp)
* [py-tree-sitter](https://github.com/tree-sitter/py-tree-sitter)
* [rich](https://github.com/Textualize/rich)
* [syntax-print-register-pipelines-highlight-line](https://screenshots.waylonwalker.com/syntax-print-register-pipelines-highlight-line.webp)
* [syntax-print-register-pipelines](https://screenshots.waylonwalker.com/syntax-print-register-pipelines.webp)
Today I’ve been playing with py-tree-sitter a bit and I wanted to highlight match ranges, but was unable to figure out how to do it with rich, so I reached out to @textualizeio for help.
https://twitter.com/_WaylonWalker/status/1562469770766589952
While waiting for that reply let’s show how we got this far.
imports #
Lets import all the classes that we need from rich and setup a console to print to.
from rich.console import Console
from rich.syntax import Syntax
from rich.style import Style
console = Console()
some code #
Now we need some code to highlight. I am going to rip my register_pipeline
from another post.
code = '''
from find_kedro import find_kedro
def register_pipelines(self) -> Dict[str, Pipeline]:
"""Register the project's pipeline.
Returns:
A mapping from a pipeline name to a ``Pipeline`` object.
"""
return find_kedro()
'''
print #
We could simply print out the code we have as a variable, but thats a bit hard to read.
console.print #
printing with rich’s console makes it a little better, but not much by default.
Syntax #
We can pull from rich’s syntax module to really pretty this up.
syntax = Syntax(code, 'python', line_numbers=True)
console.print(syntax)
Now we are getting some really impressive print outs right in the terminal!
note that I have ipython set to use rich, you will need to console.print() in scripts
highlight lines #
Now we can start highlighting lines right when we initialize our Syntax
instance. It looks ok. It’s not super visible, but more importantly its not
granular enough. I want to highlight specific ranges like the word
register_pipelines.
syntax = Syntax(code, 'python', line_numbers=True, highlight_lines=[4])
console.print(syntax)
This hows the line, but still is not very accurate.
highlight text #
[@textualizeio] got back to me, let’s see if What we can do with stylize_range!
https://twitter.com/textualizeio/status/1562487302274043904
syntax = Syntax(code, 'python', line_numbers=True)
style = Style(bgcolor='deep_pink4')
syntax.stylize_range(style, (4, 4), (4, 22))
console.print(syntax)
This gives us the final result we are looking for, we can easily see what is
being targeted here. In this case the function name register_pipelines.
This turns out to be exacly what I am looking for. Now I have an easy way to print out highlighted code wtih my py-tree-sitter query results.
Links #
How to vimgrep over hidden files.
I needed to delete all build pipeline steps that were named upload docs. I
currently have about 60 projects running from the same template all running
very similar builds. In the past I’ve scripted out migrations for large
changes like this, they involved writing a python script that would load the
yaml file into a dictionary, find the corresponding steps make the change and
write it back out.
Today’s job was much simplar, just delete the step, were all steps are
surrounded by newlines. My first thought was to just open all files in vim and
run dap. I just needed to get these files:positions into my quickfix. My
issue is that all the builds reside within hidden directories by convention.
The issue #
variability
After searching through all the projects it was clear that all the steps were
in their own paragraph, though I was not 100% confident enough to completely
automate it, and the word upload docs was in the paragraph.
some were a two liner
- name: upload docs
script: aws s3 ...
Some had a variation in the name
- name: upload docs to s3
script: aws s3 ...
some were more than 2 lines.
- name: upload docs
script: |
aws s3 ...
some used a different command.
- name: upload docs
script: |
python ...
Templates are great #
but they change
Templates are amazing, and tools like cookiecutter and copier are essential in my workflow, but those templates change over time. Some things are a constant, and others like this one are an ever evolving beast until they are tamed into something the team is happy with.
vimgrep over hidden files #
I know all the files that I care to search for are called build.yml, and they are in a hidden directory.
:args `fd -H build.yml`
:vimgrep /upload docs/ ##
Once opened as a buffer by using args, and a handy fd command I can vimgrep
over all the open buffers using ##
Open buffers are represented by ##
Now I can just dap and :cnext my way through the list of changes that I
have, and know that I hit every one of them when I am at the end of my list.
And can double check this in about 10s by scrolling back through the quickfix
list.
Vim points achieved #
You’re not a true vim enthusiast until you have spent 10 minutes writing a blog post about how vim saved you 5 minutes. Check out all the other times this has happened to me in the vim tag.
a sprinter edging out his opponent by Dall-e
It’s about time to release Markata 0.3.0. I’ve had 8 pre-releases since the
last release, but more importantly it has about 3 months of updates. Many of
which are just cleaning up bad practices that were showing up as hot spots on
my pyinstrument reports
Markata started off partly as a python developer frustrated with using nodejs for everything, and a desire to learn how to make frameworks in pluggy. Little did I know how flexible pluggy would make it. It started out just as my blog generator, but has turned into quite a bit more.
Over time this side project has grown some warts and some of them were now becoming a big enough issue it was time to cut them out.
Let’s compare #
I like to use my tils articles for examples and tests like this as there are enough articles for a good test, but they are pretty short and quick to render.
mkdir ~/git/tils/tils
cp ~/git/waylonwalker.com/pages/til/ ~/tils/tils -r
cd ~/git/tils/tils
running tils on 0.2.0 #
At the time of writing this is the current version of markata, so just make a new venv and run it.
python3 -m venv .venv --prompt $(basename $PWD)
pip install markata
markata clean
markata build
cold tils: 14.523 warm tils: 1.028
running tils on 0.3.0b8 #
python3 -m venv .venv --prompt $(basename $PWD)
# --pre installs pre-releases that include a b in their version name
pip install markata --pre
markata clean
markata build
cold tils: 11.551 (+20%) warm tils: 0.860 (+16%)
pyinstrument #
These measurements were taken with pyinstrument mostly out of convenience since there is already a pyinstrument hook built in, but also because I like pyinstrument.
Here is the pyinstrument report from the last run.
My Machine #
This comparison was not very exhaustive. It was ran on my pretty new to me Ryzen 5 3600 machine.
The changes #
Most of these changes revolve in how the lifecycle is ran. It was trying to be extra cautious and run previous steps for you if it thought it might be needes, in reality it was rerunning a few steps multiple times no matter what.
The other thing I turned off by default, but can be opted into, is beautifulasoup’s prettify. That was one of the slower steps ran on my site.
0.3.0 #
It should be out by the time you see this, I wanted to compare the changes I had made and make sure that it was still making forward progress and thought I would share the results.
Deliberative #
People exceptionally talented in the Deliberative theme are best described by the serious care they take in making decisions or choices. They anticipate obstacles.
I am risk-adverse. I want everything well thought out and calculated before I make any sort of change. I have never gambled in my life and just the thought of it makes me anxious.
Aim it #
I can use this as a strength to plan out potential issues and prevent them. I do this quite often with my role in infrastructure.
I need to make sure that I use deadlines to keep this as a strength and not hinderence.
Automation #
One of the biggest ways that I utilize this skill is automation. I am all about automating things, not just because I don’t want to do the manual work, but I am not sure when I am going to need to do something again.
A common meta thing that I need in python is to find the version of a package.
Most of the time I reach for package_name.__version__, but that does not
always work.
but not all projects have a __version__ #
In searching the internet for an answer nearly every one of them pointed me to
__version__. This works for most projects, but is simply a convention, its
not required. Not all projects implement a __version__, but most do. I’ve
never seen it lie to me, but there is nothing stopping someone from shipping
mismatched versions.
If you maintain a project ship a __version__ #
I appreciate it
While its not required its super handy and easy for anyone to remember off the
top of their head. It makes it easy to start debugging differences between
what you have vs what you see somewhere else. You can do this by dropping a
__version__ variable inside your __init__.py file.
## __init__.py
__version__ = 1.0.0
SO #
stack overflow saves the day
Special thanks to this Stack Overflow post for answering this question for me.
So what do you do… #
importlib
Your next option is to reach into the package metadata of the package that you are interested in, and this has changed over time as highlighted in the stack overflow post.
for Python >= 3.8:
from importlib.metadata import version
version('markata')
# `0.3.0.b4`
I only really use python >= 3.8 these days, but if you need to implement it for an older version check out the stack overflow post.
Another option.. #
use the command line
Another common option uses pip at the command line.
❯ pip show markata
Name: markata
Version: 0.3.0b4
Summary: Static site generator plugins all the way down.
Home-page: https://markata.dev
Author: Waylon Walker
Author-email: [email protected]
License: MIT
Location: /home/waylon/git/waylonwalker.com/.venv/lib/python3.11/site-packages
Requires: anyconfig, beautifulsoup4, checksumdir, diskcache, feedgen, jinja2, more-itertools, pathspec, pillow, pluggy, pymdown-extensions, python-frontmatter, pytz, rich, textual, toml, typer
Required-by:
And if the package implements a command line its common to ship a version
command such as --version or -V.
❯ markata --version
Markata CLI Version: 0.3.0.b4
Why did I need to do this? #
Well we have a cli tool that wraps around piptools and we wanted to include the
version of piptools in the comments that it produces dynamically. This is why
I wanted to dynamically grab the version inside python without shelling out to
pip show. Now along with the version of our internal tool you will get the
version of piptools even though piptools does not ship a __version__
variable.
Fin #
In the end, I am glad I learned that its so easy to use the more accurate
package metadata, but still appreciate packages shipping __version__ for all
of us n00b’s out here.
The one reason I switched to arch
Recently I added two new bash/zsh aliases to make my git experience just a tad better.
trackme #
Most of our work repos were recently migrated to new remote urls, we scriped
out the update to all of the repos, but I was left with a tracking error for
all of my open branches. To easily resolve this I just made an alias so that I
can just run trackme anytime I see this error.
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream develop origin/<branch>
getting the branch #
The following command will always return the currently checked out branch name.
git symbolic-ref --short HEAD
Injecting this into the suggested git command as a subshell gives us this
alias that when ran with trackme will automatically fix tracking for my
branch.
alias trackme='git branch --set-upstream-to=origin/$(git symbolic-ref --short HEAD)'
rebasemain #
I sometimes get a bit lazy at checking main for changes before submitting any prs, so again I made a quick shell alias that will rebase main into my branch before I open a pr.
alias rebasemain='git pull origin main --rebase'
The Aliases #
Here are both of the alias’s, feel free to steal and modify them into your
dotfiles. If you are uniniatiated a common starting place to put these is
either in your ~/.bashrch or ~/.zshrc depending on your shell of choice.
alias trackme='git branch --set-upstream-to=origin/$(git symbolic-ref --short HEAD)'
alias rebasemain='git pull origin main --rebase'
So many terminal applications bind q to exit, even the python debugger, its
muscle memory for me. But to exit ipython I have to type out exit<ENTER>.
This is fine, but since q is muscle memory for me I get this error a few times
per day.
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ <ipython-input-1-2b66fd261ee5>:1 in <module> │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
NameError: name 'q' is not defined
After digging way too deep into how IPython implements its ExitAutoCall I
realized there was a very simple solution here. IPython automatically
imports all the scripts you put in your profile directory, all I needed was to
create ~/.ipython/profile_default/startup/q.py with the following.
q = exit
It was that simple. This is not a game changer by any means, but I will now
see one less error in my workflow. I just press q<Enter> and I am out,
without error.
It’s no secret that I love automation, and lately my templating framework of
choice has been copier. One hiccup I recently ran into was having spaces in my
templated directory names. This makes it harder to run commands against as you
need to escape them, and if they end up in a url you end up with ugly %20 all
over.
Cookiecutter has the solution #
Yes the solution comes from a competing templating framework.
I install copier with pipx, so I need to inject cookiecutter in to my copier environment to use the slugify filter.
pipx inject copier cookiecutter
If you are using a normal virtual environment you can just pip install it.
pip install copier cookiecutter
add the extension to your template #
copier.yml
Now to enable the extension you need to declare it in your copier.yml file in
your template.
_jinja_extensions:
- cookiecutter.extensions.SlugifyExtension
Use it | slugify #
use-it
Now to use it, anywhere that you want to slugify a variable, you just pipe it into slugify.
❯ tree .
.
├── copier.yml
├── README.md
└── {{ site_name|slugify }}
└── markata.toml.jinja
1 directory, 3 files
Here is a slimmed down version of what the copier.yml looks like.
site_name:
type: str
help: What is the name of your site, this shows in seo description and the site title.
default: Din Djarin
_jinja_extensions:
- cookiecutter.extensions.SlugifyExtension
Results #
Running the template looks a bit like this.
straight from their docs #
The next section is straight from the cookiecutter docs
Slugify extension #
The cookiecutter.extensions.SlugifyExtension extension provides a slugify
filter in templates that converts string into its dashed (“slugified”) version:
{% "It's a random version" | slugify %}
Would output:
it-s-a-random-version
It is different from a mere replace of spaces since it also treats some special
characters differently such as ' in the example above. The function accepts
all arguments that can be passed to the slugify function of
python-slugify_. For example to change the output from
it-s-a-random-version to it_s_a_random_version, the separator parameter
would be passed: slugify(separator='_').
Textual has devtools in the upcoming css branch, and its pretty awesome!
It’s still early #
Textual is still very early and not really ready for prime time, but it’s quite amazing how easy some things such as creating keybindings is. The docs are coming, but missing right now so if you want to use textual be ready for reading source code and examples.
On to the devtools #
As @willmcgugan shows in this tweet it’s pretty easy to setup, it requires having two terminals open, or using tmux, and currently you have to use the css branch.
https://twitter.com/willmcgugan/status/1531294412696956930
Why does textual need its own devtools #
Textual is a tui application framework. Unlike when you are building cli applications, when the tui takes over the terminal in full screen there is no where to print statement debug, and breakpoints don’t work.
getting the css branch #
In the future it will likely be in main and not need this, but for now you need to get the css branch to get devtools.
git clone https://github.com/Textualize/textual
git fetch --alll
git checkout css
install in a virtual environment #
Now you can create a virtual environment, feel free to use whatever virtual environment tool you want, venv is built in to most python distributions though, and should just be there.
python3 -m venv .venv --prompt textual
source .venv/bin/activate
pip install .
Now that we have textual installed #
Once textual is installed you can open up the devtools by running textual console.
textual console
Using Different versions of python with pipx | pyenv
LIVE-REPLAY - Python dev | Markata todoui | 4/6/2022
How I Quickly Capture Screenshots directly into My Blog
Copier < 6.0.0b0 considered dangerous
PyOhio CFP's
functools.total_ordering makes adding all of six of the rich comparison operators to your custom classes much easier, and more likely that you remember all of them.
From the Docs: The class must define one of __lt__(), __le__(), __gt__(), or __ge__ In addition, the class should supply an __eq__() method.
one of these
- lt()
- le()
- gt()
- ge()
and required to have this one
- eq()
Here is an example using the Enum I was working on the other day.
from enum import Enum, auto
from functools import total_ordering
@total_ordering
class LifeCycle(Enum):
configure = auto()
glob = auto()
load = auto()
pre_render = auto()
render = auto()
post_render = auto()
save = auto()
def __lt__(self, other):
try:
return self.value < other.value
except AttributeError:
return self.value < other
def __eq__(self, other):
try:
return self.value == other.value
except AttributeError:
return self.value == other
Smoother Python with automatic imports | pyflyby
You must use augroup with autocmd in vim | Here's how
Code Review from the comfort of vim | Diffurcate
Setup a yaml schema | yamlls for a silky smooth setup
Open files FAST from zsh | or bash if thats your thing
How linux users install a text editor
30 days dotfile ricing
JUT | Read Notebooks in the Terminal
Waylon Walker
kedro catalog create
nvim conf 2021 | IDE's are slow | Waylon Walker
Just Ask Ipython for help
Hacktoberfest 2021?
I made a neovim plugin
Setting Parameters in kedro
Writing your first kedro Nodes
Running your Kedro Pipeline from the command line
kedro Virtual Environment
Kedro Install
Kedro Git Init
Kedro New
What is Kedro
How I Kedro
tmux show-messages
tmux ta
tmux display-message
killing tmux
tmux has-session
tmux list-keys
tmux status-bar
tmux source-file
tmux fzf session jumper
tmux next/prev session
tmux start application
tmux rename session
tmux targeted session
tmux new-session
tmux detach
tmux attach
Git in Depth Notes
tmux ls
tmux command line
tmux copy-mode
tmux join-pane
tmux break-pane
tmux zoom
tmux new-window
tmux slect-pane
tmux rotate-window
tmux select-layout
tmux resize-panes
tmux choose-tree
tmux prefix
tmux splitting panes
tmux last session
tmux floating popups
tmux popups
Incremental Versioned Datasets in Kedro
I Started Streaming on Twitch
Kedro Spaceflights - part 2 | Stream replay June 7, 2021
🌱 Digital Gardening | gif to Mp4 | Stream replay June 4, 2021
Kedro Spaceflights - part 1 | Stream replay June 4, 2021
Using Kedro In Scripts
Silence Kedro Logs
Python Diskcahe is locked
How I navigate tmux in 2021
Trim unused git branches
What is if __name__ == "__main___", and how do I use it.
Create a Virtual File Gallery with Symlinks
How to Install micromamba on linux (from the comamnd line only)
Vim Wsl Clipboard
kedro replit
Pytest capsys
Building Rich a Dev Server
Site Down During Build
Kedro pipeline_registry.py
Stand With Your Team
Blogging For Me
Minimal Kedro Pipeline
Markdown Cli
My Content Strategy For 2021
Quickly Edit Posts
Gitui is a blazing fast terminal git interface
Gitui is a terminal-based git user interface (TUI) that will change the way that you work with git.
Gitui is a blazing fast terminal git interface
Kedro - My Data Is Not A Table
Quickly Change Conda Env With Fzf
Vim Replace Visual Star
Minimal Python Package
If Tmux
Save Vim Macro
Live Substitution In Neovim
Newsboat
Large Refactor At The Command Line
Ipython-Config
Custom Ipython Prompt
Automating my Post Starter
Windowing Python Lists
Adding Audio to my blog posts
gatsby-remark-embedder
Expand One Line Links
reasons-to-kedro
What's New in Kedro 0.16.6
A brain dump of stories
Fix git commit author
Designing a "Router" for kedro
Reclaim memory usage in Jupyter
Strip Trailing Whitespace from Git projects
Chrome Extensions I use
Creating Reusable Bash Scripts
Three things to Automate with Python using Pandas
How to Install miniconda on linux (from the command line only)
How to crush amazing posts on DEV
Black Tech Pipeline
Review of the git-auto-commit-action
What's New in Kedro 0.16.4
Integration testing with Python, TestProject.io, and GitHub Actions
🐍 Practice Python Online
How python tools configure
Edit On GitHub
Why use a cms
🐍 Parsing RSS feeds with Python
🤓 What's on your GitHub Profile
🙋♂️ Can Anyone Explain Twitter Cards to me?
How I Built My GitHub Profile
SLIDES - understanding python \*args and \*\*kwargs
Gracefully adopt kedro, the catalog
How to find things in your kedro catalog
My first eight years as a working professional.
How Kedro handles your inputs
Master No More
Refactoring your blog urls
understanding python \*args and \*\*kwargs
pre-commit is awesome
Building kedro.dev
Kedro Static Viz 0.3.0 is out with Hooks Support
Create Configurable Kedro Hooks
Brainstorming Kedro Hooks
How to get Dev Comments from an article Url
Four github actions for your website
Create Custom Kedro Dataset
Interrogate is a pretty awesome, brand new, cli for Python packages
drawing ascii boxes
creating the kedro-preflight hook
📢 Announcing find-kedro
Explicit vs Implicit Returns in Javascript
TIL: Bind arguments to dynamically generated lambdas in python
Four Github Actions for Python
Variables names don't need their type
Send Emails with GitHub Actions
What Are GitHub Actions
Getting Started with GitHub Actions
Today I learned `git diff feature..main`
Create New Kedro Project
What is YOUR Advice for New Data Scientists
Do You Hoist
What is Kedro
Custom Scrollbar Design
Don’t waste your time learning everything
2020 waylonwalker.com rebrand
I just added react-headroom to my site
I finally fixed my Styled-Components in gatsby.js
Should I switch to Zeit Now
git push without setting upstream
Out of Space
simple click
Personal URL shortener with Netlify Redirects
cmd.exe tips
What is something you should have learned or understood earlier?
Supercharge Zsh Startup
Keep Location List Closed
Quick Progress Bars in python using TQDM
Clean up Your Data Science with Named Tuples
Background Tasks in Python for Data Science
Autoreload in Ipython
Keyboard Driven VSCode
Realistic Git Workflow
Forestry.io
Rewrite History with Git
It's not all about winning
Update Git User
2019 goals
2018 Retrospective
Do More of What Brings You Joy
D3 Day 5
D3 Day 4
D3 Day 3
FlexBox
Vim Notes
Llms
Robots
User-agent: *







