Merge branch 'main' of ssh://gurgul.pro/artur/environment

This commit is contained in:
Artur Gurgul 2025-09-07 08:29:13 +02:00
commit 297d02ad00
81 changed files with 3110 additions and 54 deletions

4
bin/admin/certbot-cert Normal file
View file

@ -0,0 +1,4 @@
#/usr/bin/env bash
certbot certonly --manual --preferred-challenges=dns -d "*.gurgul.org" -d "gurgul.org"

7
bin/admin/install-gui Executable file
View file

@ -0,0 +1,7 @@
## missing in the install script
apt install zsh curl
apt install sway libgtk-3-0 libasound2
wget https://github.com/zen-browser/desktop/releases/download/1.14.11b/zen.linux-x86_64.tar.xz

21
bin/admin/install-idf Normal file
View file

@ -0,0 +1,21 @@
if [ -z "$SUDO_USER" ]; then
echo "This script must be run within sudo."
exit 1
fi
# from says ~/esp
ESP=/pkg/esp
apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
sudo -u "$SUDO_USER" bash <<EOF
mkdir -p $ESP
cd $ESP
git clone -b v5.4.1 --recursive https://github.com/espressif/esp-idf.git
cd "$ESP/esp-idf"
export IDF_TOOLS_PATH="$ESP/esp-idf/.espressif"
./install.sh esp32,esp32c2,esp32c3,esp32c5,esp32c6,esp32c61,esp32h2,esp32p4,esp32s2,esp32s3
EOF

View file

@ -0,0 +1,2 @@
export IDF_TOOLS_PATH="/pkg/esp/esp-idf/.espressif"
. /pkg/esp/esp-idf/export.sh

80
bin/admin/single-user Executable file
View file

@ -0,0 +1,80 @@
#!/bin/bash
## This script make autologin. If user has phisical access
## to the device we can trust him, otherwiese we require password
## for remote access
#!/bin/bash
set -euo pipefail
# Settings
USERNAME="user"
if [ "$(id -u)" -ne 0 ]; then
echo "Please run this script as root." >&2
exit 1
fi
id "$USERNAME" &>/dev/null || { echo "User '$USERNAME' does not exist." >&2; exit 1; }
echo "Configuring passwordless sudo ONLY on local TTYs for $USERNAME"
echo
SUDOERS_FILE="/etc/sudoers.d/90-$USERNAME-sudo"
if [ ! -f "$SUDOERS_FILE" ]; then
echo "$USERNAME ALL=(ALL:ALL) ALL" > "$SUDOERS_FILE"
chmod 0440 "$SUDOERS_FILE"
visudo -cf "$SUDOERS_FILE" >/dev/null || { echo "sudoers validation failed"; exit 1; }
fi
# PAM: allow passwordless sudo on physical ttys, require password otherwise.
PAM_FILE="/etc/pam.d/sudo"
BACKUP_FILE="/etc/pam.d/sudo.bak"
if ! grep -q "BEGIN local TTY passwordless" "$PAM_FILE"; then
echo "Backing up $PAM_FILE to $BACKUP_FILE"
cp "$PAM_FILE" "$BACKUP_FILE"
# Insert at the top of the file:
# - If TTY matches /dev/ttyN (real VT), immediately permit auth (no password).
# - Otherwise fall through to the normal rules (which will ask for a password).
#
# Notes:
# - pam_succeed_if.so is in the libpam-modules package on Debian/Ubuntu (ensure installed).
# - The 'success=done' short-circuits the auth stack on real TTYs.
tmp="$(mktemp)"
cat > "$tmp" <<'PAMHEAD'
# --- BEGIN local TTY passwordless ---
# Passwordless sudo on real virtual consoles only:
# /dev/tty1, /dev/tty2, ... (NOT /dev/pts/* used by SSH)
auth [success=done default=ignore] pam_succeed_if.so tty =~ ^/dev/tty[0-9]+$
# --- END local TTY passwordless ---
PAMHEAD
cat "$PAM_FILE" >> "$tmp"
mv "$tmp" "$PAM_FILE"
echo "PAM updated. On SSH (/dev/pts/*) a password will be required."
else
echo "PAM sudo already has local TTY rule."
fi
# 3) Autologin on tty1 (physical console)
echo "Configuring systemd autologin on tty1 for $USERNAME..."
mkdir -p /etc/systemd/system/getty@tty1.service.d
AUTOLOGIN_CONF="/etc/systemd/system/getty@tty1.service.d/override.conf"
AGETTY_BIN="$(command -v agetty || true)"
: "${AGETTY_BIN:=/sbin/agetty}"
cat > "$AUTOLOGIN_CONF" <<EOF
[Service]
ExecStart=
ExecStart=-$AGETTY_BIN --autologin $USERNAME --noclear %I \$TERM
EOF
systemctl daemon-reload
systemctl restart getty@tty1
echo
echo "• $USERNAME will auto-login on tty1."
echo "• On real TTYs (/dev/ttyN), sudo won't prompt for a password."
echo "• Over SSH (/dev/pts/*), sudo will require the user's password."

28
bin/apas Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
function lhash {
echo -n "${1}" | openssl dgst -sha512 | cut -d' ' -f2 | openssl dgst -md5 | cut -d' ' -f2
}
# Prompt user for input
read -p "Website: " url
read -p "Login: " login
read -s -p "Password: " password
echo
domain=$(echo "$url" | sed -E 's~https?://([^/]+).*~\1~')
hash=$(lhash "$login")
pass_path="web/${domain}/${hash}"
# Entry content
entry="${password}
url: ${url}
login: ${login}
"
echo "$entry" | pass insert -m "$pass_path"
# url
# login
# tags
# note

7
bin/certbot Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env zsh
export PYTHONPATH="/pkg/certbot/4.2.0/local/lib/python3.11/dist-packages"
export PATH="/pkg/certbot/4.2.0/local/bin:$PATH"
/pkg/certbot/4.2.0/local/bin/certbot

98
bin/cht Executable file
View file

@ -0,0 +1,98 @@
#!/usr/bin/env node
import path from "path"
import fs from "fs"
// DAT_ROOT should exists in ~/.zshrc-local
const indexPath = path.join(process.env["NOTES_DIR"], "")
const processFile = path.join(process.cwd(), process.argv[2])
const data = fs.readFileSync(processFile, 'utf8')
const jsonData = JSON.parse(data)
function wantSave(str) {
return typeof str == "string" && str.split("\n").length > 10
}
function saveToFileSync(content, file) {
const dir = path.dirname(file);
try {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true })
}
fs.writeFileSync(file, content, 'utf8')
} catch (err) {
console.error('Error saving file:', err)
}
}
const toSave = jsonData.flatMap(o => {
const date = new Date(o.create_time * 1000)
// const formattedDate = date.toLocaleDateString('en-GB', {
// day: '2-digit',
// month: '2-digit',
// year: '2-digit',
// })
// return formattedDate
const day = String(date.getDate()).padStart(2, '0')
const month = String(date.getMonth() + 1).padStart(2, '0')
const year = String(date.getFullYear()).slice(-2)
let baseNotePath = path.join(indexPath, "gpt", `${day}-${month}-${year}`, `${o.title}`)
let conversations = []
for (const [key, value] of Object.entries(o.mapping)) {
//console.log(key, value)
conversations.push({
path: path.join(baseNotePath, `${key}.json`),
content: JSON.stringify(value, null, 2)
})
let parts = value?.message?.content.parts
if (Array.isArray(parts)) {
if (parts.length == 0) {
// skip
} else if (parts.length == 1) {
let fileName = path.join(baseNotePath, `${key}.md`)
let md = parts[0]
if (wantSave(md)) {
conversations.push({
path: fileName,
content: md
})
}
} else {
for (const [key, value] of parts.entries()) {
let fileName = path.join(baseNotePath, `${key}-${key+1}.md`)
let md = parts[key]
if (wantSave(md)) {
conversations.push({
path: fileName,
content: md
})
}
}
}
}
//process.exit(-1)
}
return conversations
})
for(const f of toSave) {
//for(const f of toSave.slice(0,5)) {
saveToFileSync(f.content,f.path)
}

17
bin/dat
View file

@ -12,6 +12,7 @@ options = OpenStruct.new
# take the first so OptionParser will not see it # take the first so OptionParser will not see it
subcommand = ARGV.shift&.to_sym subcommand = ARGV.shift&.to_sym
options.name = ARGV[0] && ARGV[0] !~ /^-/ ? ARGV.shift : nil
OptionParser.new do |opt| OptionParser.new do |opt|
opt.on('-i', '--install', 'Install dat for the user') { |o| options.type = :install } opt.on('-i', '--install', 'Install dat for the user') { |o| options.type = :install }
@ -26,6 +27,9 @@ OptionParser.new do |opt|
opt.on('--cache', 'Use cache') do |o| opt.on('--cache', 'Use cache') do |o|
options.use_cache = true options.use_cache = true
end end
opt.on('--forced', "if case of existing data, delete and recreate") do |o|
options.forced = true
end
opt.on('-t', '--target TARGET', ['user', 'usr', 'package', 'pkg', 'system', 'sys'], 'Target type (user, package, system)') do |target| opt.on('-t', '--target TARGET', ['user', 'usr', 'package', 'pkg', 'system', 'sys'], 'Target type (user, package, system)') do |target|
@ -55,6 +59,19 @@ when :make
puts "making..." puts "making..."
require 'make' require 'make'
Make.command(options) Make.command(options)
when :pkgmake
puts "Making package #{options.name}"
require 'make'
Make.create_package(options)
when :pkginstall
require 'make'
# TODO: wrong naming it is not name, but path
Make.install_package(options.name)
when :execute
puts "Ececuting: #{options.name}"
require 'execute'
puts "Arguments: #{options}"
Execute.file(options)
when :goto when :goto
Dir.chdir(ENV["DAT_ROOT"]) Dir.chdir(ENV["DAT_ROOT"])
when :vm when :vm

57
bin/get Executable file
View file

@ -0,0 +1,57 @@
ARCHIVE=false
DESTINATION_LOCAL_DIR="./"
if [[ "$1" == "--archive" ]]; then
ARCHIVE=true
shift
fi
if [[ $# -lt 2 ]]; then
echo "Usage: $0 [--archive] user@host remote_source_dir [local_destination_dir]"
exit 1
fi
USER_HOST="$1"
REMOTE_USER="${USER_HOST%@*}"
HOST="${USER_HOST#*@}"
REMOTE_SOURCE_DIR="$2"
if [[ -n "$3" ]]; then
DESTINATION_LOCAL_DIR="$3"
fi
if [[ "$REMOTE_SOURCE_DIR" = /* ]]; then
IS_ABSOLUTE=true
else
IS_ABSOLUTE=false
fi
echo "ARCHIVE=$ARCHIVE"
echo "REMOTE_USER=$REMOTE_USER"
echo "HOST=$HOST"
echo "REMOTE_SOURCE_DIR=$REMOTE_SOURCE_DIR"
echo "DESTINATION_LOCAL_DIR=$DESTINATION_LOCAL_DIR"
REMOTE_BASE_DIR=""
if $IS_ABSOLUTE; then
REMOTE_BASE_DIR="/"
else
REMOTE_BASE_DIR=/home/$REMOTE_USER
fi
# Example that works
# ssh debian@gurgul.org "tar --zstd -cf - -C /home/debian .dat" | tar --zstd -xf - -C .
# get debian@gurgul.org .dat
# get --archive debian@gurgul.org /etc
TAR_COMMAND="tar --zstd -cf - -C $REMOTE_BASE_DIR $REMOTE_SOURCE_DIR"
if $ARCHIVE; then
ssh $REMOTE_USER@$HOST $TAR_COMMAND > "$DESTINATION_LOCAL_DIR/$(basename "$REMOTE_SOURCE_DIR")-$(date +"%d-%m-%Y").tar.zst"
else
ssh $REMOTE_USER@$HOST $TAR_COMMAND | tar --zstd -xf - -C $DESTINATION_LOCAL_DIR
fi

16
bin/index Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
# find $NOTES_DIR/gpt -type d -path "$NOTES_DIR/gpt/[0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*" | while read -r path; do
# echo $(basename "$path")
# done | sort -u | fzf
SEARCH_DIR="$NOTES_DIR/gpt"
DELIMITER="|"
selected=$(find "$SEARCH_DIR" -type d -path "$SEARCH_DIR/[0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*" | while read -r path; do
topic=$(basename "$path")
echo -e "$topic\t$path"
done | fzf --delimiter='\t' --with-nth=1)
selected_path=$(echo "$selected" | cut -f2)
echo "Path: $selected_path"

152
bin/make-winux Executable file
View file

@ -0,0 +1,152 @@
#!/usr/bin/env zsh
#export LINUX_DISK_IMG=/home/artur/winux/VM/linux/linux.img
export LINUX_DISK_IMG=/home/artur/winux/VM/deb-orig/debian.img
export WINUX_DISK_IMG=./winux.img
# Mount the image of oryginal
#
# Mounting the image
function wxumount {
echo "unmounting :$1"
while lsof +D $1 >/dev/null 2>&1 || fuser $1 >/dev/null 2>&1
do
fuser -a $1
echo "Waiting for $1 to be free..."
sleep 1
done
mkdir -p $1
if mountpoint -q $1; then
echo "Unmounting $1..."
umount $1
if [ $? -eq 0 ]; then
echo "Successfully unmounted $1."
else
echo "Failed to unmount $1."
fi
else
echo "$1 is not mounted."
fi
}
wxumount /mnt/winux
wxumount /mnt/linux
# (re)create an empty sparsed file
rm -rf $WINUX_DISK_IMG
dd if=/dev/zero of=$WINUX_DISK_IMG bs=1 count=0 seek=20G
# Check size
# real size: du -h winux.img
# appeared as: ls -lh winux.img
# Copy boot
IMG="/home/artur/winux/VM/linux/linux.img"
SECTOR_SIZE=$(LC_ALL=C sfdisk -d "$LINUX_DISK_IMG" | awk 'match($0,/sector-size:\s*([0-9]+)/,m){print m[1]; exit}')
START=$(LC_ALL=C sfdisk -d "$LINUX_DISK_IMG" | awk 'match($0,/start=\s*([0-9]+)/,m){print m[1]; exit}')
echo "Sector size: $SECTOR_SIZE"
echo "Start sector: $START"
dd if="$LINUX_DISK_IMG" of="$WINUX_DISK_IMG" bs="$SECTOR_SIZE" count="$START" conv=notrunc
# Formatting the disk
# No altering the first 1MB
echo '2048,,83,*' | sudo sfdisk --no-reread $WINUX_DISK_IMG
# List all loops: sudo losetup -a
WINUX_LOOP_DEV=$(sudo losetup --find --show $WINUX_DISK_IMG)
# Scan also for partitions: sudo losetup --find --show --partscan winux.img
echo "Loop device: $WINUX_LOOP_DEV"
export WINUX_LOOP_DEV="$(losetup --find --show -P $WINUX_LOOP_DEV)"
mkfs.ext2 ${WINUX_LOOP_DEV}p1
tune2fs ${WINUX_LOOP_DEV}p1 -U 1be261e2-a12d-4468-aa45-cbd7e68636eb
mount ${WINUX_LOOP_DEV}p1 /mnt/winux
## The image is formatted and ready to copy files over
export LINUX_LOOP_DEV="$(losetup --find --show --partscan $LINUX_DISK_IMG)"
mount ${LINUX_LOOP_DEV}p1 /mnt/linux
# copying files
SAVED_PWD=`pwd`
cd /mnt/linux
tar --numeric-owner --xattrs --acls -cpf - \
--exclude='lost+found' \
--exclude='var/log/*' \
--exclude='var/tmp/*' \
--exclude='tmp/*' \
--exclude='var/cache/*' \
--exclude='swapfile' \
--exclude='dev/*' \
--exclude='proc/*' \
--exclude='sys/*' \
--exclude='run/*' \
. | tar --numeric-owner --xattrs --acls -xpf - -C /mnt/winux
cd "$SAVED_PWD"
#### TESTTING CODE
# losetup -j $WINUX_DISK_IMG
# Clean up after building
sync
sleep 1
wxumount /mnt/winux
wxumount /mnt/linux
for dev in $(losetup -j $WINUX_DISK_IMG | cut -d: -f1); do
losetup -d "$dev"
done
for dev in $(losetup -j $LINUX_DISK_IMG | cut -d: -f1); do
losetup -d "$dev"
done
echo "making the archive"
# Best compressing rato, but slow and memory hungry (I LIKE IT)
#tar --sparse -I 'zstd -T0 --ultra -22 --long=31' \
# -cvf winux.img.tar.zst winux.img
## SO I PICKED NOW FOR SPEED
tar --sparse -I 'zstd -T0 -9' -cvf winux.img.tar.zst winux.img
# extract
# brew install gnu-tar
# tar -I zstd -xvf winux.img.tar.zst
# on macOS gtar or /opt/homebrew/opt/gnu-tar/libexec/gnubin/tar -I 'zstd --long=31' -xvf winux.img.tar.zst winux.img
# FINISHED
#=======================================================
# compressing with XZ. Supprisely it seems to have worst compresion rate than zstd
# tar --sparse -I 'xz -T0 -9e' -cvf winux.img.tar.xz winux.img
## Extracting
# tar -I xz -xvf winux.img.tar.xz
#
# zstd -T0 --ultra -22 winux.img -o winux.img.zst
# # decompression with
# # unzstd --sparse winux.img.zst
# # or
# # zstd -d --sparse winux.img.zst -o winux.img

20
bin/password Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env ruby
# password for account, must be one or the default
# password amazon
# copy login
# password amazon -l
# search for password for given account
# password -l artur@gurgul.org
# http://example.com/login
# password example -l artur@gurgul.org
# set -euo pipefail
# login="${DEFAULT_INTERNET_LOGIN:-${1-}}"
# file="$(find web -type f -path "web/*/$DEFAULT_INTERNET_LOGIN.gpg" -print | fzf --delimiter='/' --with-nth=2)"
# password_entry="${file%.gpg}"
# pass -c "$password_entry"

108
bin/recipes/edk2/make Executable file
View file

@ -0,0 +1,108 @@
#!/usr/bin/env bash
rm -rf edk2
rm -rf output
git clone https://github.com/tianocore/edk2.git
cd edk2
git checkout fc0fffa7e9089e7b79b9ae7babb950f1f153e0ae
# 0) Make sure LLVM is installed
brew install llvm acpica nasm
brew install lld
# 1) Point PATH and tool vars to Homebrews LLVM (Apple Silicon path shown)
export LLVM_PREFIX="/opt/homebrew/opt/llvm" # Intel Macs: /usr/local/opt/llvm
export LLD_PREFIX="/opt/homebrew/opt/lld"
export PATH="$LLVM_PREFIX/bin:$LLD_PREFIX/bin:$PATH"
hash -r
# 2) Explicitly select LLVM tools so EDK2 doesnt fall back to Apples
export CC="$LLVM_PREFIX/bin/clang"
export CXX="$LLVM_PREFIX/bin/clang++"
export LD="$LLD_PREFIX/bin/ld.lld"
export AR="$LLVM_PREFIX/bin/llvm-ar"
export RANLIB="$LLVM_PREFIX/bin/llvm-ranlib"
export NM="$LLVM_PREFIX/bin/llvm-nm"
export STRIP="$LLVM_PREFIX/bin/llvm-strip"
export OBJCOPY="$LLVM_PREFIX/bin/llvm-objcopy"
export OBJDUMP="$LLVM_PREFIX/bin/llvm-objdump"
# 3) Sanity check — these MUST point into .../opt/llvm/bin
which clang; clang --version
which ld.lld
which llvm-ar
# 4) Rebuild tools & firmware
make -C BaseTools -j
source ./edksetup.sh
build -a AARCH64 -t CLANGDWARF \
-p ShellPkg/ShellPkg.dsc \
-m ShellPkg/Application/KeyTools/KeyTool/KeyTool.inf \
-b RELEASE
cd ..
mkdir -p output/keys/EFI/Boot
openssl x509 -in "microsoft corporation kek 2k ca 2023.crt" -outform DER -out output/keys/kek2023.cer
openssl x509 -in "windows uefi ca 2023.crt" -outform DER -out output/keys/db2023.cer
open /Users/artur/projs/edk2
exit 0
# (Optional) clean the previous failed build to avoid stale flags/objects
rm -rf Build/ArmVirtQemu-AARCH64
build -a AARCH64 \
-t CLANGDWARF \
-p ArmVirtPkg/ArmVirtQemu.dsc \
-D SECURE_BOOT_ENABLE=TRUE \
-b DEBUG
# CODE="/Volumes/Cache/vms/image/win11-arm64/win11/QEMU_EFI.fd"
# VARS="/Volumes/Cache/vms/image/win11-arm64/win11/QEMU_VARS.fd"
# # Make blank 64 MiB raws
# qemu-img create -f raw QEMU_EFI-pflash.raw 64M
# qemu-img create -f raw QEMU_VARS-pflash.raw 64M
# # Copy firmware into the front of each file without truncating the 64 MiB size
# dd if="$CODE" of=QEMU_EFI-pflash.raw conv=notrunc
# dd if="$VARS" of=QEMU_VARS-pflash.raw conv=notrunc
# # Confirm size is exactly 67108864 bytes
# stat -f "%z %N" QEMU_EFI-pflash.raw QEMU_VARS-pflash.raw
# https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-secure-boot-key-creation-and-management-guidance?view=windows-11
# https://go.microsoft.com/fwlink/?linkid=2239775
# https://go.microsoft.com/fwlink/?linkid=2239776
# -drive if=none,id=mskeys,format=raw,file=fat:rw:keys
# -device usb-storage,drive=mskeys
# 5. Enroll the certs in firmware
# Boot into UEFI shell, run FS0:\EFI\Boot\KeyTool.efi.
# In KeyTool:
# Go to Edit Keys → KEK → Add New Key, pick kek2023.cer.
# Go to Edit Keys → db → Add New Key, pick db2023.cer.
# If no PK is present yet, you must enroll a Platform Key (PK) first (can be self-signed with OpenSSL; KeyTool can also generate). Without a PK, Secure Boot remains “greyed out.”
# ✅ After PK + KEK + DB are enrolled, reboot → go into firmware setup → Secure Boot option will be toggleable → set it to Enabled.
# Now Windows 11 installer/bootloader should run under Secure Boot.
# Ref: 68adf715-f29c-832e-89d7-a37025f63cf3

61
bin/recipes/edk2/notes.md Normal file
View file

@ -0,0 +1,61 @@
That error means your **WORKSPACE** points to a *different* edk2 tree:
```
/Users/artur/.dat/bin/recipes/edk2/edk2
```
so `build` is looking for `ShellPkg/.../KeyTool.inf` **there**, not in `~/projs/edk2`.
Fix by resetting your env and sourcing `edksetup.sh` from the repo you actually want to build.
## Do this exactly
```bash
# 0) Go to the edk2 you want to use
cd ~/projs/edk2
git rev-parse --show-toplevel # should print /Users/artur/projs/edk2
# 1) Clear any stale EDK2 env from previous shells
unset WORKSPACE EDK_TOOLS_PATH PACKAGES_PATH
# 2) Rebuild BaseTools (with your LLVM/lld PATH still set like earlier)
make -C BaseTools -j
# 3) Reset Conf inside THIS tree and re-source
rm -f Conf/target.txt Conf/tools_def.txt
source ./edksetup.sh
# Sanity checks — these must point into ~/projs/edk2
echo "WORKSPACE=$WORKSPACE"
echo "EDK_TOOLS_PATH=$EDK_TOOLS_PATH"
test -f "$WORKSPACE/ShellPkg/Application/KeyTools/KeyTool/KeyTool.inf" && echo "KeyTool found"
# 4) Build KeyTool for AArch64 with clang/lld
build -a AARCH64 -t CLANGDWARF \
-p ShellPkg/ShellPkg.dsc \
-m ShellPkg/Application/KeyTools/KeyTool/KeyTool.inf \
-b RELEASE
```
### If you still see it picking the wrong path
You probably have these variables exported in your shell config. Force them for this shell:
```bash
export WORKSPACE="$PWD"
export EDK_TOOLS_PATH="$WORKSPACE/BaseTools"
export PACKAGES_PATH="$WORKSPACE"
source ./edksetup.sh
```
Then re-run the `build` command above.
### Where the binary will land
Typical output path (adjust for your build target):
```
Build/Shell/RELEASE_CLANGDWARF/AARCH64/ShellPkg/Application/KeyTools/KeyTool/KeyTool/OUTPUT/KeyTool.efi
```
Once youve got `KeyTool.efi`, mount it along with your `kek2023.cer` and `db2023.cer`, enroll **PK → KEK → db**, reboot into the firmware UI, and enable **Secure Boot**.

Binary file not shown.

View file

@ -0,0 +1 @@
sudo apt-get install task-gnome-desktop

20
bin/recipes/net/net-up Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
sudo mkdir -p /etc/systemd/network
cat <<'EOF' | sudo tee /etc/systemd/network/20-enp0s1.network
[Match]
Name=enp0s1
[Network]
DHCP=yes
EOF
sudo systemctl enable --now systemd-networkd
sudo systemctl enable --now systemd-resolved
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo ip link set enp0s1 up
sudo networkctl reload
sudo networkctl reconfigure enp0s1

16
bin/recipes/net/notes.md Normal file
View file

@ -0,0 +1,16 @@
# bring the link up
sudo ip link set enp0s1 up
# give yourself an address in QEMU's usernet
sudo ip addr add 10.0.2.15/24 dev enp0s1
# default gateway for slirp
sudo ip route add default via 10.0.2.2
# DNS via slirp (or use 1.1.1.1 if you prefer)
echo "nameserver 10.0.2.3" | sudo tee /etc/resolv.conf > /dev/null
# sanity checks
ip -4 a show enp0s1
ping -c2 10.0.2.2
ping -c2 deb.debian.org

93
bin/recipes/vnc/notes.md Normal file
View file

@ -0,0 +1,93 @@
Heres what those two pieces typically look like in a QEMU VNC + TLS + SASL setup.
# `/etc/pki/qemu` (TLS x509 creds)
QEMUs `-object tls-creds-x509,...,dir=/etc/pki/qemu,endpoint=server` expects this directory to hold the servers cert/key and the CA it should trust for client certs.
Example layout:
```
/etc/pki/qemu/
├── ca-cert.pem # CA cert used to verify client certificates (if verify-peer=yes)
├── server-cert.pem # Server certificate (CN should match the host, or use subjectAltName)
├── server-key.pem # Private key for server-cert.pem (chmod 600, root-only)
└── crl.pem # (optional) Certificate Revocation List
```
Typical QEMU arg:
```
-object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes
-vnc :0,tls-creds=tls0,sasl
```
Notes:
* `verify-peer=yes` means the VNC client must present a client certificate signed by `ca-cert.pem`. Omit or set `no` if you only want encryption without client certs (and rely on SASL for auth).
* File names above are the conventional ones QEMU looks for in that dir in server mode.
* Permissions matter: keep `server-key.pem` readable only by the QEMU user (e.g., `chmod 600`).
(If you wanted the client side for certificate auth, youd create a *client* bundle with `client-cert.pem` / `client-key.pem` and give the servers CA to the client.)
# `/etc/sasl2/qemu.conf` (SASL settings)
This file tells Cyrus SASL how to authenticate for the “qemu” service. A simple, local-password (sasldb) setup might be:
```
# /etc/sasl2/qemu.conf
mech_list: scram-sha-256 digest-md5
pwcheck_method: auxprop
auxprop_plugin: sasldb
sasldb_path: /etc/sasldb2
```
What each line does:
* `mech_list`: Which SASL mechanisms to allow. (If your build doesnt have SCRAM, use `digest-md5` and/or `plain`/`login` — but prefer SCRAM or DIGEST over PLAIN.)
* `pwcheck_method: auxprop` + `auxprop_plugin: sasldb`: use the local SASL database.
* `sasldb_path`: where the password DB lives (default is `/etc/sasldb2` on many distros).
Create users in the sasldb:
```bash
sudo saslpasswd2 -a qemu -c alice
# (it will prompt for a password)
sudo sasldblistusers2 -f /etc/sasldb2 # verify entry exists
```
Then run QEMU with both TLS and SASL:
```bash
qemu-system-x86_64 \
-object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=no \
-vnc :0,tls-creds=tls0,sasl
```
* Client connects over **TLS** (encrypted), then is prompted for **SASL username/password** (“alice” and the password you set).
* If you set `verify-peer=yes`, the client must also present a valid client cert signed by your CA.
## Quick OpenSSL one-liners (for testing)
> For production, use a proper CA workflow and strong key handling.
```bash
# CA
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca-cert.pem -subj "/CN=QEMU Test CA"
# Server cert
openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server.csr -subj "/CN=your.host.name"
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca.key -CAcreateserial -out server-cert.pem -days 825 -sha256
install -m 600 server-key.pem /etc/pki/qemu/
install -m 644 server-cert.pem ca-cert.pem /etc/pki/qemu/
```
## Common pitfalls
* **Wrong filenames/paths** in `/etc/pki/qemu` → QEMU wont find the certs.
* **Permissions too open** on `server-key.pem` → QEMU may refuse or its a security risk.
* **SASL mechanism mismatch** → ensure your client supports one from `mech_list`.
* **No TLS but SASL with PLAIN/LOGIN** → credentials go over the wire unencrypted; always pair PLAIN/LOGIN with TLS.
If you tell me your distro, I can tailor the exact package names (Cyrus SASL modules) and service paths.

6
bin/services/ca/Gemfile Normal file
View file

@ -0,0 +1,6 @@
source "https://rubygems.org"
ruby ">= 3.0"
gem "roda"
gem "puma"
gem "rackup"

View file

@ -0,0 +1,26 @@
GEM
remote: https://rubygems.org/
specs:
nio4r (2.7.4)
puma (6.6.1)
nio4r (~> 2.0)
rack (3.2.0)
rackup (2.2.1)
rack (>= 3)
roda (3.95.0)
rack
PLATFORMS
arm64-darwin-24
ruby
DEPENDENCIES
puma
rackup
roda
RUBY VERSION
ruby 3.4.5p51
BUNDLED WITH
2.6.9

12
bin/services/ca/app.rb Normal file
View file

@ -0,0 +1,12 @@
require "roda"
class App < Roda
# automatically sets application/json and encodes Ruby objects
plugin :json
route do |r|
r.get "hello" do
{ message: "world" }
end
end
end

View file

@ -0,0 +1,2 @@
require_relative "app"
run App.freeze.app

0
bin/services/ca/make Executable file
View file

View file

@ -0,0 +1,9 @@
bundle exec rackup -p 9292 -o 0.0.0.0
RACK_ENV=production bundle exec rackup -p 8080 -o 0.0.0.0
curl http://0.0.0.0:9292/hello --verbose

1
bin/set-zsh Normal file
View file

@ -0,0 +1 @@
chsh -s $(which zsh)

100
bin/sq Executable file
View file

@ -0,0 +1,100 @@
#!/usr/bin/env ruby
require 'socket'
# Path to QEMU monitor socket
sock_path = "/tmp/qemu-monitor.sock"
# Connect to UNIX socket
sock = UNIXSocket.new(sock_path)
# Read greeting from QEMU (optional)
puts sock.readline rescue nil
# Function to send a single keystroke
def send_key(sock, key)
cmd = "sendkey #{key}\n"
puts ">>> #{cmd.strip}"
sock.write(cmd)
end
def char_to_key(ch)
case ch
# Control characters
when "\n" then "ret"
when "\t" then "tab"
when " " then "spc"
when "\e" then "esc"
when "\b" then "backspace"
# Lowercase letters
when "a".."z" then ch
# Uppercase letters (shift + letter)
when "A".."Z" then "shift-#{ch.downcase}"
# Digits
when "0".."9" then ch
# Shifted number row
when "!" then "shift-1"
when "@" then "shift-2"
when "#" then "shift-3"
when "$" then "shift-4"
when "%" then "shift-5"
when "^" then "shift-6"
when "&" then "shift-7"
when "*" then "shift-8"
when "(" then "shift-9"
when ")" then "shift-0"
when "-" then "minus"
when "_" then "shift-minus"
when "=" then "equal"
when "+" then "shift-equal"
when "[" then "bracket_left"
when "{" then "shift-bracket_left"
when "]" then "bracket_right"
when "}" then "shift-bracket_right"
when "\\" then "backslash"
when "|" then "shift-backslash"
when ";" then "semicolon"
when ":" then "shift-semicolon"
when "'" then "apostrophe"
when "\"" then "shift-apostrophe"
when "," then "comma"
when "<" then "shift-comma"
when "." then "dot"
when ">" then "shift-dot"
when "/" then "slash"
when "?" then "shift-slash"
when "`" then "grave_accent"
when "~" then "shift-grave_accent"
else
raise "Unsupported character: #{ch.inspect}"
end
end
if ARGV[0]
puts ARGV[0]
input = ARGV[0]
puts "Typing: #{input.inspect}"
input.chars.each do |ch|
send_key(sock, char_to_key(ch))
sleep 0.1
end
else
# Example: type "hello<Enter>"
%w[p a s s ret].each do |k|
send_key(sock, k)
sleep 0.1
end
end
sock.close
# (qemu) screendump /Volumes/Cache/homes/debian/guaset.ppm
# ffmpeg -y -i guaset.ppm guest.png

95
bin/ssl Executable file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env ruby
require "fileutils"
require "open3"
def cmd(*cmd)
stdout, stderr, status = Open3.capture3(*cmd)
unless status.success?
warn "Command failed: #{cmd.join(' ')}"
warn stderr
exit status.exitstatus || 1
end
stdout
end
subcommand = ARGV.shift&.to_sym
arg1 = ARGV[0] && ARGV[0] !~ /^-/ ? ARGV.shift : nil
arg2 = ARGV[0] && ARGV[0] !~ /^-/ ? ARGV.shift : nil
# $domains = ["mediacenter.lan"]
# $ips = ["192.168.0.94"]
$domains = ["testing.self"]
$ips = ["127.0.0.2"]
$ips = []
### Setting up for client
$cn = $domains.first
name = $cn.gsub("*", "wildcard")
dir = "certs"
FileUtils.mkdir_p(dir)
$key = File.join(dir, "#{name}.key.pem")
$csr = File.join(dir, "#{name}.csr.pem")
$crt = File.join(dir, "#{name}.crt.pem")
$ext = File.join(dir, "#{name}.ext")
############################
### Setting up for CA
$ca_name = "My Lab"
#############################
def create_CA
cmd "openssl", "genrsa", "-out", "rootCA.key.pem", "4096"
cmd "chmod", "600", "rootCA.key.pem"
cmd "openssl", "req", "-x509", "-new", "-nodes", "-key",
"rootCA.key.pem", "-sha256", "-days", "3650",
"-out", "rootCA.crt.pem",
"-subj", "/C=XX/ST=Lab/L=Local/O=#{$ca_name}/CN=#{$ca_name} Root CA"
end
def create_CSR
cmd "openssl", "genrsa", "-out", $key, "2048"
cmd "openssl", "req", "-new", "-key", $key, "-out", $csr, "-subj", "/CN=#{$cn}/O=#{$ca_name}"
end
def create_extfile
ext_lines = []
ext_lines << "basicConstraints=CA:FALSE"
ext_lines << "keyUsage=digitalSignature,keyEncipherment"
ext_lines << "extendedKeyUsage=serverAuth"
ext_lines << "subjectAltName=@alt_names"
ext_lines << "[alt_names]"
$domains.each_with_index do |d, i|
ext_lines << "DNS.#{i + 1}=#{d}"
end
$ips.each_with_index do |ip, j|
ext_lines << "IP.#{j + 1}=#{ip}"
end
File.write($ext, ext_lines.join("\n") + "\n")
end
def sign_with_CA
cmd "openssl", "x509", "-req", "-in", $csr,
"-CA", "rootCA.crt.pem", "-CAkey", "rootCA.key.pem",
"-CAcreateserial", "-out", $crt, "-days", "397", "-sha256",
"-extfile", $ext
end
case subcommand
when :ca
create_CA
when :csr
create_CSR
when :casign
create_extfile
sign_with_CA
else
puts "no command handler"
end

4
bin/use Normal file
View file

@ -0,0 +1,4 @@
use python 3.11
.zshrc-using < setting the environment for the user/serice

107
bin/vm
View file

@ -1,27 +1,90 @@
#!/usr/bin/env ruby
# mount -t 9p -o trans=virtio,version=9p2000.L share /home/user require 'optparse'
require 'ostruct'
require 'virtual-machine'
# this add to brake and inspect /dev if /dev/sda1 not found # Paramteters
# -append "root=/dev/sda1 console=ttyS0 rd.break" # -env: DAT_VM_DATA=~/.cache/vm
function run {
qemu-system-x86_64 -append "root=/dev/sda1 console=ttyS0 rd.break" \
-kernel "vmlinuz-linux" \
-initrd "initramfs-linux.img" \
-m 2048 \
-smp $(sysctl -n hw.logicalcpu) \
-cpu qemu64 \
-virtfs local,path=.,security_model=none,mount_tag=share \
-drive id=root-disk,if=none,format=raw,file=linux.img \
-device ide-hd,bus=ide.0,drive=root-disk \
-drive id=data-disk,if=none,format=qcow2,file=dat.qcow2 \
-device ide-hd,bus=ide.1,drive=data-disk \
-nographic
}
function reset { # vm setup debian --arch "<host:arch64>" --name "<image_name: debian>"
qemu-img create -f qcow2 dat.qcow2 32G
}
$1 options = OpenStruct.new
subcommand = ARGV.shift&.to_sym
parameter = ARGV[0] && ARGV[0] !~ /^-/ ? ARGV.shift : nil
OptionParser.new do |opt|
opt.on('--arch ARCH', 'Architecture arm64 or x86_64') do |arch|
options.arch = arch
end
opt.on('--name NAME', 'Virtaul Machine name') do |name|
options.name = name
end
opt.on('--cdrom CDROM', 'Use local cdrom image') do |cdrom|
options.cdrom = cdrom
end
opt.on('--tpm', 'Use tpm') do
options.tpm = true
end
opt.on('--shell', 'QEMU process must be detached to exec ssh') do
options.shell = true
options.detached = true
end
opt.on('--detached') do
options.detached = true
end
# --port (ssh port)
# --attached (do not leave the shell process)
# new from copy
# fetch from the server
end.parse!
case subcommand
when :create
puts "vm create debian-copy --name debian"
puts "Creating image base on existing one...."
when :run
# same as setup but fetches image not
puts "Run the image or fetch and run...."
options[:distro] = parameter
VirtualMachine.run(options)
when :setup
options[:distro] = parameter
VirtualMachine.setup(options)
when :archive
# whole image or files that are not present in the archived
# --type files (zst all files by files)
# by default
#before start if should create index of files that are present on
# the vm, so later we can copy new files or changed
options[:distro] = parameter
VirtualMachine.archive(options)
when :apply
puts "apply chnages from VM to the host os"
when :restore
options[:distro] = parameter
VirtualMachine.restore(options)
#puts "download image from remote or if local copy exists then the local copy"
when :daemon
puts "setup a pprocess so i can attach to it"
# paramaters exatly as run/setup
when :execute
puts "vm execute debian --file example.sh --attach (block terminal till done otherwiese notifi when done)"
puts "if name not provided then use the last accessed"
when :status
puts "Print all images and status stopped/daemon(port)/running(port/)"
when :attach
puts "if not paramteres attaches to last used"
else
puts "Error not found #{options.type}"
end

222
bin/vm-cmd Executable file
View file

@ -0,0 +1,222 @@
#!/usr/bin/env bash
rm -rf /Volumes/Cache/vms/image/win11-arm64/win11/code.fd
rm -rf /Volumes/Cache/vms/image/win11-arm64/win11/vars.fd
cp /opt/homebrew/share/qemu/edk2-aarch64-code.fd /Volumes/Cache/vms/image/win11-arm64/win11/code.fd
cp /opt/homebrew/share/qemu/edk2-arm-vars.fd /Volumes/Cache/vms/image/win11-arm64/win11/vars.fd
# brew install qemu swtpm
# qemu-system-aarch64 -drive if=pflash,format=raw,unit=0,readonly=on,file=/Volumes/Cache/vms/image/win11-arm64/win11/code.fd -drive if=pflash,format=raw,unit=1,file=/Volumes/Cache/vms/image/win11-arm64/win11/vars.fd -display cocoa -device qemu-xhci,id=xhci -device usb-kbd -device usb-tablet -device virtio-keyboard-device -device virtio-mouse-device -device virtio-gpu -device virtio-net,netdev=n0 -netdev user,id=n0 -accel hvf -machine virt -cpu max -m 16384 -smp 6 -name FirstVM -boot order=d -drive file=/Volumes/Cache/vms/image/win11-arm64/win11/root.img,if=virtio,cache=writeback,format=raw,id=nvme0 -drive id=cd,format=raw,file=/Volumes/Cache/downloads/win11arm64.iso,media=cdrom -device usb-storage,drive=cd,bootindex=1 -device ramfb -chardev socket,id=chrtpm,path=/Users/agurgul/Downloads/tpm/tpm.sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis-device,tpmdev=tpm0
# vm setup win11 --tpm --cdrom /Volumes/Cache/downloads/win11arm64.iso
# qemu-system-aarch64 -drive if=pflash,format=raw,unit=0,readonly=on,file=/Volumes/Cache/vms/image/win11-arm64/win11/code.fd -drive if=pflash,format=raw,unit=1,file=/Volumes/Cache/vms/image/win11-arm64/win11/vars.fd -display cocoa -device qemu-xhci,id=xhci -device usb-kbd -device usb-tablet -device virtio-keyboard-device -device virtio-mouse-device -device virtio-gpu -device virtio-net,netdev=n0 -netdev user,id=n0 -accel hvf -machine virt -cpu max -m 16384 -smp 6 -name FirstVM -boot order=d -drive file=/Volumes/Cache/vms/image/win11-arm64/win11/root.img,if=virtio,cache=writeback,format=raw,id=nvme0 -drive id=cd,format=raw,file=/Volumes/Cache/downloads/win11arm64.iso,media=cdrom -device usb-storage,drive=cd,bootindex=1 -device ramfb -chardev socket,id=chrtpm,path=/Users/agurgul/Downloads/tpm/tpm.sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis-device,tpmdev=tpm0
# -netdev user,id=net0,hostfwd=tcp::33890-:3389,hostfwd=tcp::59850-:5985 \
# -machine virt,accel=hvf,highmem=off \
# -device virtio-keyboard-pci \
# -device virtio-tablet-pci \
# -device ich9-ahci,id=ahci0 \
# -device ide-hd,drive=drv0,bus=ahci0.0 \
# -device nvme,drive=drv0,serial=nvme0
# -boot order=d \
# -device usb-storage,drive=udisk,port=3\
# wget http://http.us.debian.org/debian/pool/main/e/edk2/qemu-efi-aarch64_2025.02-8_all.deb
# qemu-system-aarch64 \
# -machine virt,accel=hvf \
# -cpu host -smp 6 -m 8G \
# --boot order=d,menu=on \
# -bios /Users/artur/Downloads/tpm/QEMU_EFI.fd \
# -device qemu-xhci,id=xhci \
# \
# -drive file=/Volumes/Cache/vms/image/win11-arm64/win11/root.img,format=raw \
# \
# -device virtio-scsi-pci,id=scsi0 \
# -drive file=/Volumes/Cache/downloads/win11arm64.iso,if=none,media=cdrom,id=cd0 \
# -device scsi-cd,drive=cd0,bus=scsi0.0,bootindex=1 \
# -device ramfb \
# -device usb-kbd,bus=xhci.0,port=1 \
# -device usb-tablet,bus=xhci.0,port=2 \
# -netdev user,id=net0,hostfwd=tcp::35890-:3589 \
# -device virtio-net-pci,netdev=net0 \
# -chardev socket,id=chrtpm,path=/Users/artur/Downloads/tpm/tpm.sock \
# -tpmdev emulator,id=tpm0,chardev=chrtpm \
# -device tpm-tis-device,tpmdev=tpm0 \
# -monitor stdio
# in Shell
# FS0:
# cd EFI\BOOT
# BOOTAA64.EFI
# Disk driver: https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/
# Used: https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.271-1/
# Attempt 1
# qemu-system-aarch64 \
# -machine virt,accel=hvf \
# -cpu host -smp 6 -m 8G \
# --boot order=d,menu=on \
# -drive if=pflash,format=raw,file=/Volumes/Cache/vms/image/win11-arm64/win11/code.fd,readonly=on \
# -drive if=pflash,format=raw,file=/Volumes/Cache/vms/image/win11-arm64/win11/vars.fd \
# -drive file=/Volumes/Cache/vms/image/win11-arm64/win11/root.img,if=none,format=raw,id=drv0 \
# -device nvme,drive=drv0,serial=nvme0 \
# -device virtio-scsi-pci,id=scsi0 \
# -drive file=/Volumes/Cache/downloads/win11arm64.iso,if=none,media=cdrom,id=cd0 \
# -device scsi-cd,drive=cd0,bus=scsi0.0,bootindex=1 \
# -device ramfb \
# -device qemu-xhci,id=xhci \
# -device usb-kbd,bus=xhci.0,port=1 \
# -device usb-tablet,bus=xhci.0,port=2 \
# -netdev user,id=net0,hostfwd=tcp::33890-:3389 \
# -device virtio-net-pci,netdev=net0 \
# -chardev socket,id=chrtpm,path=/Users/artur/Downloads/tpm/tpm.sock \
# -tpmdev emulator,id=tpm0,chardev=chrtpm \
# -device tpm-tis-device,tpmdev=tpm0 \
# -monitor stdio
# Attempt 2
# qemu-system-aarch64 \
# -machine virt,accel=hvf \
# -cpu host -smp 6 -m 8G \
# --boot order=d,menu=on \
# -drive if=pflash,format=raw,file=/Volumes/Cache/vms/image/win11-arm64/win11/code.fd,readonly=on \
# -drive if=pflash,format=raw,file=/Volumes/Cache/vms/image/win11-arm64/win11/vars.fd \
# -drive file=/Volumes/Cache/vms/image/win11-arm64/win11/root.img,if=none,format=raw,id=drv0 \
# -device ich9-ahci,id=ahci0 \
# -device ide-hd,drive=drv0,bus=ahci0.0 \
# -device virtio-scsi-pci,id=scsi0 \
# -drive file=/Volumes/Cache/downloads/win11arm64.iso,if=none,media=cdrom,id=cd0 \
# -device scsi-cd,drive=cd0,bus=scsi0.0,bootindex=1 \
# -device ramfb \
# -device qemu-xhci,id=xhci \
# -device usb-kbd,bus=xhci.0,port=1 \
# -device usb-tablet,bus=xhci.0,port=2 \
# -netdev user,id=net0,hostfwd=tcp::33890-:3389 \
# -device virtio-net-pci,netdev=net0 \
# -chardev socket,id=chrtpm,path=/Users/artur/Downloads/tpm/tpm.sock \
# -tpmdev emulator,id=tpm0,chardev=chrtpm \
# -device tpm-tis-device,tpmdev=tpm0 \
# -monitor stdio
# Attempt 3
# qemu-system-aarch64 \
# -machine virt,accel=hvf \
# -cpu host -smp 6 -m 8G \
# --boot menu=on \
# \
# -drive if=pflash,format=raw,readonly=on,file=/Volumes/Cache/vms/image/win11-arm64/win11/code.fd \
# -drive if=pflash,format=raw,file=/Volumes/Cache/vms/image/win11-arm64/win11/vars.fd \
# \
# -drive if=none,file=/Volumes/Cache/vms/image/win11-arm64/win11/root.qcow2,format=qcow2,id=drv0 \
# -device nvme,drive=drv0,serial=nvme-1 \
# \
# -device qemu-xhci,id=xhci \
# -drive if=none,id=winiso,media=cdrom,readonly=on,file=/Volumes/Cache/downloads/win11arm64.iso \
# -device usb-storage,drive=winiso,bootindex=1 \
# \
# -device ramfb \
# -device usb-kbd \
# -device usb-tablet \
# -netdev user,id=net0,hostfwd=tcp::33890-:3389 \
# -device virtio-net-pci,netdev=net0 \
# \
# -chardev socket,id=chrtpm,path=/Users/artur/Downloads/tpm/tpm.sock \
# -tpmdev emulator,id=tpm0,chardev=chrtpm \
# -device tpm-tis-device,tpmdev=tpm0
# -serial mon:stdio
# File: bypass.reg
# Windows Registry Editor Version 5.00
# [HKEY_LOCAL_MACHINE\SYSTEM\Setup\LabConfig]
# "BypassTPMCheck"=dword:00000001
# "BypassSecureBootCheck"=dword:00000001
# "BypassCPUCheck"=dword:00000001
# "BypassRAMCheck"=dword:00000001
# hdiutil makehybrid -iso -joliet -o bypass.iso bypass.reg
# Shift+F10
# reg import D:\bypass.reg
## Build EDK2
# brew install nasm iasl python@3 openssl pkg-config
# brew install llvm acpica nasm
# git clone https://github.com/tianocore/edk2.git
# cd edk2
# git submodule update --init
# make -C BaseTools
# source ./edksetup.sh
# export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
# build -a X64 \
# -t XCODE5 \
# -p OvmfPkg/OvmfPkgX64.dsc \
# -D SECURE_BOOT_ENABLE=TRUE
# build -a AARCH64 \
# -t XCODE5 \
# -p ArmVirtPkg/ArmVirtQemu.dsc \
# -D SECURE_BOOT_ENABLE=TRUE
# build -a AARCH64 \
# -t CLANGDWARF \
# -p ArmVirtPkg/ArmVirtQemu.dsc \
# -D SECURE_BOOT_ENABLE=TRUE \
# -b DEBUG
# Steps moved to /bin/recipes/make-edk2
# Attempt 4
qemu-system-aarch64 \
-machine virt,accel=hvf \
-cpu host -smp 6 -m 8G \
--boot menu=on \
\
-drive if=pflash,format=raw,readonly=on,file=/Volumes/Cache/vms/image/win11-arm64/win11/QEMU_EFI-pflash.raw \
-drive if=pflash,format=raw,file=/Volumes/Cache/vms/image/win11-arm64/win11/QEMU_VARS-pflash.raw \
\
-drive if=none,file=/Volumes/Cache/vms/image/win11-arm64/win11/root.qcow2,format=qcow2,id=drv0 \
-device nvme,drive=drv0,serial=nvme-1 \
\
-device qemu-xhci,id=xhci \
-drive if=none,id=winiso,media=cdrom,readonly=on,file=/Volumes/Cache/downloads/win11arm64.iso \
-device usb-storage,drive=winiso,bootindex=1 \
\
-device ramfb \
-device usb-kbd \
-device usb-tablet \
-netdev user,id=net0,hostfwd=tcp::33890-:3389 \
-device virtio-net-pci,netdev=net0 \
\
-chardev socket,id=chrtpm,path=/Users/artur/Downloads/tpm/tpm.sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis-device,tpmdev=tpm0

1
bin/vm-runner Executable file
View file

@ -0,0 +1 @@
[Install Zen on Linux | Flathub](https://flathub.org/apps/app.zen_browser.zen)

View file

@ -6,4 +6,6 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
elif [[ "$ARCH" == "x86_64" ]]; then elif [[ "$ARCH" == "x86_64" ]]; then
eval "$(/usr/local/bin/brew shellenv)" eval "$(/usr/local/bin/brew shellenv)"
fi fi
export PATH="$(brew --prefix ruby)/bin:$PATH"
fi fi

View file

@ -3,12 +3,15 @@
export PATH="$DAT_ROOT/bin:$HOME/.local/bin:$PATH" export PATH="$DAT_ROOT/bin:$HOME/.local/bin:$PATH"
export GEM_HOME="$HOME/.gems"
export GEM_PATH=$HOME/.gems
export RUBYLIB="$DAT_ROOT/lib" export RUBYLIB="$DAT_ROOT/lib"
export PASSWORD_STORE_DIR=$HOME/.local/lib/secure-vault/passwords export PASSWORD_STORE_DIR=$HOME/.local/lib/secure-vault/passwords
export NOTES_DIR=$HOME/.local/lib/notes export NOTES_DIR=$HOME/.local/lib/notes
path=("$GEM_HOME/bin" $path) path=("$GEM_HOME/bin" "$HOME/.cargo/bin" $path)
alias gf='git log --all --oneline | fzf' alias gf='git log --all --oneline | fzf'
@ -16,6 +19,10 @@ function cdd {
cd $DAT_ROOT cd $DAT_ROOT
} }
function cdp {
cd $PASSWORD_STORE_DIR
}
. $DAT_ROOT/bin/zshrc/prompt . $DAT_ROOT/bin/zshrc/prompt
. $DAT_ROOT/bin/zshrc/utils . $DAT_ROOT/bin/zshrc/utils

37
doc/execute.md Normal file
View file

@ -0,0 +1,37 @@
It is how you can describe config of your server
```ruby
user :artur
user :lucyna
service :forgejo do |context|
context.home_page = true
# per default all users are allowed
context.users = [:artur]
end
service :zulip
# this should be already installed, becouse it is required by others
service :postgresql
install :nvim, :python
```
And then save it as `Configfile` as default or with specyfic name
```bash
# if default file name
dat execute
# if non standard file
dat execute file-name
```

View file

@ -1,4 +1,5 @@
require("command_runner")
require("plugins") require("plugins")
require("options") require("options")
require("keymaps") require("keymaps")
require("lsp") require("lsp")

View file

@ -0,0 +1,36 @@
local M = {}
function M.run_shell_command_under_cursor()
local cmd = vim.api.nvim_get_current_line()
if cmd == "" then
print("No command on this line.")
return
end
-- Run command and capture output
local output = vim.fn.systemlist(cmd)
-- Save current window to return later
local current_win = vim.api.nvim_get_current_win()
-- Move to the right window (assumes it's already split)
vim.cmd("wincmd l")
-- Optional: clear buffer before writing
vim.api.nvim_buf_set_lines(0, 0, -1, false, {})
-- Set output in current buffer (right side)
vim.api.nvim_buf_set_lines(0, 0, -1, false, output)
-- Optional: make it editable plain text
vim.bo.filetype = "text"
vim.bo.modifiable = true
vim.bo.readonly = false
-- Return to the left window
vim.api.nvim_set_current_win(current_win)
end
return M

View file

@ -5,6 +5,11 @@ map('n', '<leader>fg', require('telescope.builtin').live_grep, { desc = "Live gr
map('n', '<leader>fb', require('telescope.builtin').buffers, { desc = "Buffers" }) map('n', '<leader>fb', require('telescope.builtin').buffers, { desc = "Buffers" })
map('n', '<leader>fh', require('telescope.builtin').help_tags, { desc = "Help tags" }) map('n', '<leader>fh', require('telescope.builtin').help_tags, { desc = "Help tags" })
vim.keymap.set("n", "zr", function()
require("command_runner").run_shell_command_under_cursor()
end, { desc = "Run shell command under cursor and show output in right split" })
-- Project-specific ignored dirs for Telescope -- Project-specific ignored dirs for Telescope
local function load_local_ignore() local function load_local_ignore()
local cwd = vim.fn.getcwd() local cwd = vim.fn.getcwd()

View file

@ -16,6 +16,13 @@ lspconfig.gopls.setup {
}, },
} }
-- ruby LSP
lspconfig.ruby_lsp.setup({
cmd = { "ruby-lsp" }, -- or provide full path if needed
filetypes = { "ruby" },
root_dir = lspconfig.util.root_pattern("Gemfile", ".git"),
})
-- Completion -- Completion
local cmp = require("cmp") local cmp = require("cmp")
cmp.setup({ cmp.setup({

View file

@ -33,7 +33,20 @@ require("lazy").setup({
{ "hrsh7th/vim-vsnip" }, { "hrsh7th/vim-vsnip" },
-- Treesitter -- Treesitter
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate" }, {
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = { "ruby", "lua", "vim", "bash", "json" }, -- include ruby
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
},
})
end
},
-- Appearance -- Appearance
{ "tomasiser/vim-code-dark" }, { "tomasiser/vim-code-dark" },

15
install
View file

@ -1,5 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
## Software for bootstraping
# curl, gcc
# TODO: Packages that need to be added
# gem install ruby-lsp
#
# ./install --system --forced # ./install --system --forced
# --forced (not forced, does not delete old version, just ends in failure) # --forced (not forced, does not delete old version, just ends in failure)
# --system (refault user) # --system (refault user)
@ -28,7 +35,7 @@ if [ "$system" = true ] && [ "$EUID" -ne 0 ]; then
exec sudo INSTALL_HOME="$INSTALL_HOME" "$0" "$@" exec sudo INSTALL_HOME="$INSTALL_HOME" "$0" "$@"
fi fi
REPO_URL="https://gitlab.com/artur.gurgul/home.git" REPO_URL="https://gurgul.pro/artur/environment.git"
is_debian_like() { is_debian_like() {
if [ -r /etc/os-release ]; then if [ -r /etc/os-release ]; then
@ -75,7 +82,7 @@ export DAT_ROOT=$(echo "$DAT_ROOT" | envsubst)
debian_install_packages() { debian_install_packages() {
# List of required packages # List of required packages
local packages=("git" "ruby") local packages=("git" "ruby" "zsh")
local to_install=() local to_install=()
# Determine if we need to use sudo # Determine if we need to use sudo
@ -101,11 +108,13 @@ debian_install_packages() {
else else
echo "All required packages are already installed." echo "All required packages are already installed."
fi fi
chsh -s $(which zsh)
} }
macos_install_packages() { macos_install_packages() {
# List of required packages # List of required packages
local packages=("git" "ruby") local packages=("git" "ruby" "zsh")
local to_install=() local to_install=()
# Check for Homebrew # Check for Homebrew

155
lib/archive.rb Normal file
View file

@ -0,0 +1,155 @@
require 'net/http'
require 'uri'
require 'open3'
require 'fileutils'
require 'open-uri'
require 'shellwords'
module Archive
MAGIC_NUMBERS = {
"\x1F\x8B" => 'gzip',
"\x50\x4B\x03\x04" => 'zip',
"\x28\xB5\x2F\xFD" => 'zstd',
"\xFD\x37\x7A\x58" => 'xz'
}
def self.extract(archive_path, destination_dir)
format = detect_format_from_url(archive_path)
unless format
format = detect_format_from_magic(archive_path)
end
extract_archive(archive_path, destination_dir, format)
end
def self.fetch_and_extract(url, cache_path, destination_dir)
uri = URI.parse(url)
file_name = File.basename(uri.path)
archive_path = File.join(destination_dir, file_name)
format = detect_format_from_url(url)
puts "format #{format}"
download_file(uri, archive_path)
# Probably do not work
unless format
puts "case 1"
format = detect_format_from_headers(uri)
end
unless format
puts "case 2"
format = detect_format_from_magic(archive_path)
end
raise "Could not determine archive format" unless format
extract_archive(archive_path, format)
end
def self.download_file(uri, output_path, forced=false)
return if File.exist?(output_path) && !forced
# Ensure the directory exists
dir = File.dirname(output_path)
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
URI.open(uri) do |input|
File.open(output_path, 'wb') do |output|
IO.copy_stream(input, output)
end
end
end
private
def self.detect_format_from_url(url)
case File.extname(url)
when '.gz', '.tgz' then 'gzip'
when '.zip' then 'zip'
when '.zst' then 'zstd'
when '.xz' then 'xz'
else nil
end
end
def self.detect_format_from_headers(uri)
response = Net::HTTP.get_response(uri)
content_type = response['content-type']
case content_type
when /gzip/ then 'gzip'
when /zip/ then 'zip'
when /zstd/ then 'zstd'
when /xz/ then 'xz'
else nil
end
end
def self.detect_format_from_magic(file_path)
File.open(file_path, 'rb') do |f|
bytes = f.read(4)
MAGIC_NUMBERS.each do |magic, format|
return format if bytes.start_with?(magic)
end
end
nil
end
require 'shellwords'
def self.extract_archive(file_path, destination_dir, format)
# TODO: need to be checked
escaped_file = Shellwords.escape(file_path)
escaped_dir = Shellwords.escape(destination_dir)
system("mkdir -p #{escaped_dir}")
case format
when 'gzip'
system("tar -xzf #{escaped_file} -C #{escaped_dir}")
when 'zip'
system("unzip -d #{escaped_dir} #{escaped_file}")
when 'zstd'
if `file #{escaped_file}`.include?('tar archive')
system("unzstd -c #{escaped_file} | tar -xf - -C #{escaped_dir}")
else
system("unzstd -o \"#{escaped_dir}\" #{escaped_file}")
end
when 'xz'
if `file #{escaped_file}`.include?('tar archive')
system("tar -xJf #{escaped_file} -C #{escaped_dir}")
else
system("xz -dk #{escaped_file}")
decompressed = file_path.sub(/\.xz$/, '')
system("mv #{Shellwords.escape(decompressed)} #{escaped_dir}/")
end
else
raise "Unsupported archive format: #{format}"
end
end
# def self.extract_archive(file_path, destination_dir, format)
# case format
# when 'gzip'
# system("tar -xzf #{Shellwords.escape(file_path)}")
# when 'zip'
# system("unzip #{Shellwords.escape(file_path)}")
# when 'zstd'
# system("unzstd #{Shellwords.escape(file_path)}")
# when 'xz'
# # if file_path.end_with?('.tar.xz')
# if `file #{Shellwords.escape(file_path)}`.include?('tar archive')
# system("tar -xJf #{Shellwords.escape(file_path)}")
# else
# system("xz -dk #{Shellwords.escape(file_path)}")
# end
# else
# raise "Unsupported archive format: #{format}"
# end
# end
end
# Example usage:
# fetch_and_extract("https://codeberg.org/forgejo/forgejo/releases/download/v12.0.1/forgejo-12.0.1-linux-amd64.xz")

11
lib/certbot.rb Normal file
View file

@ -0,0 +1,11 @@
module Certbot
def self.create_certificate(domain, wildcard = false)
command = "/home/artur/.dat/bin/certbot"
if wildcard
system "sudo #{command} certonly --manual --preferred-challenges=dns -d \"*.#{domain}\" -d \"#{domain}\""
else
system "sudo #{command} --nginx -d #{domain}"
end
end
end

View file

@ -0,0 +1,21 @@
module VirtualMachine
ISO_URLS = {
debian: {
arm64: {
install: "https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-13.0.0-arm64-netinst.iso"
},
x86_64: {
install: "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.0.0-amd64-netinst.iso",
live: "https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/debian-live-13.0.0-amd64-standard.iso"
}
},
archlinux: {
x86_64: {
live: "https://geo.mirror.pkgbuild.com/iso/2025.08.01/archlinux-x86_64.iso"
}
}
}
end

View file

@ -26,4 +26,4 @@ server {
ssl_certificate_key /etc/letsencrypt/live/<%= domain %>/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/<%= domain %>/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
} }

View file

@ -0,0 +1,3 @@
local all all peer
#hostssl all all 0.0.0.0/0 scram-sha-256
host all all 127.0.0.1/32 trust

View file

@ -0,0 +1,36 @@
[Unit]
Description=PostgreSQL <%= version %>
After=network.target
[Service]
Type=notify
#Type=simple
User=postgres
Group=services
ExecStart=<%= postgres_bin %> -D <%= database_dir %>
StandardOutput=journal
StandardError=journal
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
TimeoutSec=300
Restart=on-failure
NotifyAccess=all
# Security
#ProtectSystem=full
#ProtectHome=true
ReadWritePaths=<%= database_dir %>
# PrivateTmp=true
NoNewPrivileges=true
# Resource Limits
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,57 @@
#data_directory = '/var/lib/postgresql/15/main'
#hba_file = '/etc/postgresql/15/main/pg_hba.conf'
#ident_file = '/etc/postgresql/15/main/pg_ident.conf'
#listen_addresses = 'localhost'
listen_addresses = '*'
port = 5432
max_connections = 100
unix_socket_directories = '/tmp, <%= unix_socket %>'
# unix_socket_directories = '/tmp'
password_encryption = scram-sha-256
### TODO Add support for ssl
#ssl = on
#
##ssl_ca_file = ''
#ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
##ssl_crl_file = ''
##ssl_crl_dir = ''
#ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_min_protocol_version = 'TLSv1.2'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off
shared_buffers = 128MB
dynamic_shared_memory_type = posix # the default is usually the first option
max_wal_size = 1GB
min_wal_size = 80MB
log_line_prefix = '%m [%p] %q%u@%d '
log_timezone = 'Etc/UTC'
#cluster_name = '15/main'
datestyle = 'iso, mdy'
timezone = 'Etc/UTC'
lc_messages = 'C.UTF-8'
lc_monetary = 'C.UTF-8'
lc_numeric = 'C.UTF-8'
lc_time = 'C.UTF-8'
default_text_search_config = 'pg_catalog.english'
# include files ending in '.conf' from
# include_dir = 'conf.d'

34
lib/downloader.rb Normal file
View file

@ -0,0 +1,34 @@
require 'uri'
require 'addressable/uri'
require 'user'
require 'fileutils'
require 'open-uri'
module Downloader
# brew install gnu-tar
# use_cache => save in the home and return path to it
# forced => download even if the file exists in the cache, saves to chace if use_cache == true
def self.get(url, use_cache = true, forced = false)
uri = Addressable::URI.parse(url)
path = File.join(User.cache_path, "downloads", uri.domain, uri.path)
#uri = URI.parse(url)
#file_name = File.basename(uri.path)
# Ensure the directory exists
dir = File.dirname(path)
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
unless File.exist?(path)
puts "File does not exist, downloading..."
URI.open(uri) do |input|
File.open(path, 'wb') do |output|
IO.copy_stream(input, output)
end
end
end
yield path
end
end

153
lib/execute.rb Normal file
View file

@ -0,0 +1,153 @@
module Execute
end
require 'rubygems'
require 'ostruct'
# dat execute progress
# dat execute < default Configfile
# dat execute --dry-run
# Execute single command
# dat command service zulip
module Execute
attr_accessor :options
def context(new_value = nil)
@options = new_value
end
class ServiceInstallContext
attr_accessor :bin_dir, :data_dir, :version, :user_name, :forced
def initialize(bin_dir, data_dir, user_name, version, forced = false)
@bin_dir = bin_dir
@data_dir = data_dir
@user_name = user_name
@version = version
@forced = forced
end
end
class UserInstallContext
attr_accessor :user_name, :type, :can_login
def initialize(user_name, type = :user, can_login = nil)
@user_name = user_name
@type = type
if can_login == nil
case type
when :service
@can_login = false
when :user
@can_login = true
else
raise "Can not create user for type: #{type}"
end
else
@can_login = can_login
end
end
end
def dependency(name)
puts "Checking for #{name}..."
unless gem_installed?(name)
puts "Installing #{name}..."
system("gem install #{name}")
else
puts "#{name} is already installed."
end
require name
rescue LoadError => e
puts "Failed to load #{name}: #{e.message}"
end
def gem_installed?(name)
Gem::Specification::find_all_by_name(name).any?
end
def get_install_executor(name)
require 'make'
pdata = Make.context(name)
bin_dir = pdata.get_prefix
data_dir = "/data/#{pdata.name}/#{pdata.version.split(".").first}"
service_install_context = ServiceInstallContext.new(
bin_dir, data_dir, name, pdata.version, @options.forced
)
case name
when :postgres
require 'setup/postgres'
-> { Setup::PostgreSQL.install(service_install_context) }
# ->(context) {
# Setup::PostgreSQL.install(context)
# }
else
raise "Can't find the executor"
end
end
def execute_with_context(context)
end
# def postgres
# require 'setup/postgresql'
# Setup::PostgreSQL.install
# end
def service(name)
executor = get_install_executor(name)
#executor.call(service_install_context)
executor.call
if block_given?
context = OpenStruct.new
# service context
output = yield context
puts output
puts "context: #{context}"
else
puts "No block provided"
end
end
def domain(name)
require 'certbot'
wildcard = name.strip.start_with?("*.")
domain = name.strip.delete_prefix("*.")
Certbot.create_certificate(domain, wildcard)
end
def user(*users)
require 'user'
users.each do |name|
context = UserInstallContext.new(name, :user)
User.install(context)
end
end
def install(*packages)
packages.each do |pkg|
puts "Installing #{pkg}"
end
end
def self.file(options)
dsl = Object.new
dsl.extend(Execute)
dsl.options = options
# Configfile
path = File.join(Dir.pwd, options.name || "./Configfile")
dsl.instance_eval(File.read(path), path)
end
end

View file

@ -0,0 +1,10 @@
module User
# Make user to be trusted when he use
# the computer locally
def make_local_trusted(user)
File.mkdir_p("/etc/systemd/system/getty@tty1.service.d")
end
end

View file

@ -7,6 +7,7 @@ require 'fileutils'
require 'open3' require 'open3'
# make for: the user, system, package # make for: the user, system, package
# user/usr, system/sys, package/pkg
# as regular user, if dependencies provided # as regular user, if dependencies provided
# user: $HOME/.local # user: $HOME/.local
@ -28,13 +29,14 @@ module Make
class Context class Context
attr_accessor :name, :use_cache, :environment, :steps, :packages, :repository attr_accessor :name, :use_cache, :environment, :steps, :packages, :repository
attr_accessor :target, :archive, :source_type attr_accessor :target, :archive, :source_type, :version, :forced
def initialize(options: OpenStruct.new) def initialize(options: OpenStruct.new)
@target = options.target || :user @target = options.target || :user
@name = options.name @name = options.name
@use_cache = options.use_cache || false @use_cache = options.use_cache || false
@forced = options.forced || false
#System.detect_os #System.detect_os
# rbenv: brew install rbenv && rbenv install 3.0.0 # rbenv: brew install rbenv && rbenv install 3.0.0
@ -54,15 +56,21 @@ module Make
@packages = makefile["packages"] || [] @packages = makefile["packages"] || []
project_sources = { project_sources = {
repository: makefile["repository"] != nil, repository: makefile["repository"],
archive: makefile["archive"] != nil archive: makefile["archive"]
}.select { |_, v| v } }.select { |_, v| v }
if project_sources.size == 1 if project_sources.size == 1
source_data = project_sources.values.first
@source_type = project_sources.keys.first @source_type = project_sources.keys.first
@version = source_data["version"] || source_data["branch"]
raise "No version provided" if @version == nil
@version = @version.to_s
else else
raise "Exactly one type of source: 'repository' or 'archive'" raise "Exactly one type of source: 'repository' or 'archive'"
end end
puts @source_type puts @source_type
@repository = Make.rostruct(makefile["repository"] || OpenStruct.new) @repository = Make.rostruct(makefile["repository"] || OpenStruct.new)
@ -96,7 +104,7 @@ module Make
when :user when :user
"#{ENV["HOME"]}/.local" "#{ENV["HOME"]}/.local"
when :package when :package
"#{sys_prefix}/pkg/#{@name}/#{@repository.branch}" "#{sys_prefix}/pkg/#{@name}/#{@version}"
when :system when :system
"#{sys_prefix}/" "#{sys_prefix}/"
else else
@ -122,6 +130,12 @@ module Make
"#{ENV["HOME"]}/.cache/dat/repo/#{@name}.git" "#{ENV["HOME"]}/.cache/dat/repo/#{@name}.git"
end end
def download_path
require 'addressable/uri'
uri = Addressable::URI.parse(archive.url)
File.join("#{ENV["HOME"]}/.cache/dat/downloads/", uri.domain, uri.path)
end
def to_s def to_s
vars = instance_variables.map do |var| vars = instance_variables.map do |var|
"#{var.to_s.delete('@')}: #{instance_variable_get(var).inspect}" "#{var.to_s.delete('@')}: #{instance_variable_get(var).inspect}"
@ -183,10 +197,16 @@ module Make
end end
def download_and_extract def download_and_extract
archive = @context.archive # gem install addressable
archive = @context.archive
puts @context.download_path
puts archive puts archive
exit -1
require 'archive'
Archive.download_file(@context.archive.url, @context.download_path)
Archive.extract(@context.download_path, @cwd)
end end
def checkout def checkout
@ -214,13 +234,45 @@ module Make
def install def install
System.install(context.packages) System.install(context.packages)
end end
def create_package(options)
puts "Creating packahge for options: #{options}"
puts @context
puts "Prefix #{@context.get_prefix}"
system("tar --zstd -Pcf #{@context.name}-#{@context.version}.pkg #{@context.get_prefix}")
end
end end
# dat make -t pkg --cache --name dry-run # dat make -t pkg --cache --name dry-run
# dat make --name dry-run # dat make --name dry-run
def self.command(options) def self.command(options)
context = Context.new(options: options) context = Context.new(options: options)
builder = Builder.new(context) builder = Builder.new(context)
builder.build builder.build
end end
# dat pkginstall {file-path}
# an example `dat pkginstall postgresql-17.5.pkg`
def self.install_package(path)
puts "Installing package: #{path}"
system("sudo tar --zstd -xf #{path} -C /")
end
# dat pkgmake {name}
def self.create_package(options)
options["target"] = :package if options["target"] == nil
context = Context.new(options: options)
builder = Builder.new(context)
builder.create_package(options)
end
# TODO: better name, it is just to obtain information
# about the package
def self.context(package_name, target = :package)
options = OpenStruct.new
options["target"] = target
options["name"] = package_name
Context.new(options: options)
end
end end

View file

@ -3,7 +3,12 @@ require 'erb'
class NGINXProxy class NGINXProxy
class << self class << self
attr_accessor :domain, :port, :service, :user attr_accessor :domain, :port, :service, :user, :willcard
def willcard(value = nil)
@willcard = value unless value.nil?
@willcard
end
def domain(value = nil) def domain(value = nil)
@domain = value unless value.nil? @domain = value unless value.nil?
@ -27,8 +32,11 @@ class NGINXProxy
@service @service
end end
## bundle exec rackup -s puma -b unix:///run/user/1000/http.sock
def generate def generate
template = File.read("proxy.erb") template_path = File.join(__dir__, 'data', 'templates', 'nginx', 'proxy.erb')
template = File.read(template_path)
template = ERB.new(template) template = ERB.new(template)
template.result(binding) template.result(binding)
end end
@ -48,12 +56,3 @@ class NGINXProxy
end end
end end
class ExampleProxy < NGINXProxy
domain "gurgul.org"
service "forgejo"
user "git"
port 3000
end
puts ExampleProxy.generate
puts ExampleProxy.path

83
lib/setup/postgres.rb Normal file
View file

@ -0,0 +1,83 @@
module Setup
require_relative '../templates'
require_relative "../user"
require_relative "../execute"
# dat execute postgres
# example.rcp
module PostgreSQL
extend Templates
def self.write_as(user, path, content)
puts "wriiting by #{user} to #{path}"
# If executed as root we can just use File.write
# File.write(postgresql_conf_path, postgresql_conf_content)
IO.popen(["sudo", "-u", user.to_s, "tee", path], "w") do |io|
io.write(content)
end
end
# attr_accessor :te
def self.init_db(context)
puts "Mode, isForced #{context.forced}"
if Dir.exist?(context.data_dir)
if context.forced
puts("sudo rm -rf #{context.data_dir}")
else
raise "PostgreSQL data already exists"
end
end
system("sudo mkdir -p #{context.data_dir}")
system("sudo chown #{context.user_name}:services #{context.data_dir}")
system("sudo -u postgres #{File.join(context.bin_dir, "/bin/initdb")} -D #{context.data_dir} --username=postgres")
end
def self.setup_systemd(context)
puts "DataDir: #{context.data_dir}"
pg_hba = render("pg_hba.conf")
pg_hba_path = "#{File.join(context.data_dir, "pg_hba.conf")}"
write_as(context.user_name, pg_hba_path, pg_hba)
## TODO: move this to user module
#uid = Etc.getpwnam(context.user_name.to_s).uid
#socket_path = "/run/user/#{uid}"
socket_path = "/run/user/#{context.user_name}"
#socket_path = "/tmp"
system("sudo mkdir -p #{socket_path}")
system("sudo chown #{context.user_name}:services #{socket_path}")
system("sudo chmod 711 #{socket_path}")
postgresql_conf = render("postgresql.conf", unix_socket: socket_path)
postgresql_conf_path = "#{File.join(context.data_dir, "postgresql.conf")}"
write_as(context.user_name, postgresql_conf_path, postgresql_conf)
postgres_service = render(
"postgres.service",
postgres_bin: File.join(context.bin_dir, "/bin/postgres"),
version: context.version,
database_dir: context.data_dir
)
postgres_service_path = "/etc/systemd/system/postgres.service"
write_as("root", postgres_service_path, postgres_service)
system("sudo systemctl daemon-reexec")
system("sudo systemctl daemon-reload")
system("sudo systemctl enable postgres")
system("sudo systemctl start postgres")
# debug service
# sudo systemctl daemon-reexec && sudo systemctl daemon-reload && sudo systemctl restart postgresql.service
end
def self.install(context)
user_context = Execute::UserInstallContext.new(context.user_name, :service)
User.install(user_context)
init_db(context)
setup_systemd(context)
end
end
end

View file

@ -1,5 +1,7 @@
require_relative('system/architecture')
module System module System
def self.detect_os def self.detect_os
case RUBY_PLATFORM case RUBY_PLATFORM
when /darwin/ when /darwin/
@ -29,7 +31,42 @@ module System
raise "Operating system not supported" raise "Operating system not supported"
end end
ARCH = normalize_architecture_string(arch)
def self.os_info def self.os_info
puts os_name puts os_name
end end
def self.qemu_paths
{
code_fd: qemu_code_fd_path,
vars_fd: qemu_vars_fd_path
}
end
def self.arch_to_symbol(arch)
normalize_architecture_string(arch)
end
def self.exec_ssh(port)
require "socket"
host = "localhost"
cmd = "ssh -p #{port} user@#{host}"
# Wait until the port is open
puts "Waiting for #{host}:#{port}..."
until begin
TCPSocket.new(host, port).close
true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
false
end
sleep 0.1
end
puts "Port open! Executing: #{cmd}"
exec cmd
end
end end

View file

@ -0,0 +1,55 @@
module System
def self.normalize_architecture_string(s)
str = s.to_s.strip.downcase
exact = {
"x86_64" => :x86_64, "amd64" => :x86_64,
"i386" => :x86_32, "i486" => :x86_32, "i586" => :x86_32, "i686" => :x86_32,
"arm64" => :arm64, "aarch64" => :arm64,
"armhf" => :armv7, "armel" => :armv5, "armv7l" => :armv7, "armv6l" => :armv6, "armv5tel" => :armv5,
"riscv64" => :riscv64,
"ppc64el" => :ppc64le, "ppc64le" => :ppc64le,
"ppc64" => :ppc64, "powerpc64" => :ppc64,
"ppc" => :ppc32, "powerpc" => :ppc32,
"s390x" => :s390x,
"mips64el" => :mips64el, "mips64" => :mips64,
"mipsel" => :mipsel, "mips" => :mips,
"loong64" => :loongarch64, "loongarch64" => :loongarch64,
"sparc64" => :sparc64,
"alpha" => :alpha,
"hppa" => :hppa,
"ia64" => :ia64,
}
return exact[str] if exact.key?(str)
return :x86_64 if str.include?("x64") || str.include?("amd64")
return :x86_32 if str =~ /\Ai[3-6]86\z/ || str.include?("x86")
return :arm64 if str.include?("aarch64") || str.include?("arm64")
return :armv7 if str.include?("armv7")
return :armv6 if str.include?("armv6")
return :armv5 if str.include?("armv5")
return :riscv64 if str.include?("riscv64")
return :ppc64le if str.include?("ppc64el") || str.include?("ppc64le")
return :ppc64 if str.include?("ppc64")
return :ppc32 if str.include?("ppc") || str.include?("powerpc")
return :s390x if str.include?("s390x")
return :mips64el if str.include?("mips64el")
return :mips64 if str.include?("mips64")
return :mipsel if str.include?("mipsel")
return :mips if str.start_with?("mips")
return :loongarch64 if str.include?("loong") || str.include?("loongarch")
return :sparc64 if str.include?("sparc64")
return :alpha if str.include?("alpha")
return :hppa if str.include?("hppa")
return :ia64 if str.include?("ia64")
:unknown
end
end

View file

@ -1,4 +1,5 @@
require 'open3' require 'open3'
require_relative "utils"
module DebianSystem module DebianSystem
def os_name def os_name
@ -10,6 +11,10 @@ module DebianSystem
stdout.strip.to_i stdout.strip.to_i
end end
def arch
sh("dpkg --print-architecture")
end
def install(packages) def install(packages)
missing_packages = packages.reject { |pkg| package_installed?(pkg) } missing_packages = packages.reject { |pkg| package_installed?(pkg) }
@ -49,4 +54,12 @@ module DebianSystem
puts "Failed to uninstall some packages." puts "Failed to uninstall some packages."
end end
end end
def qemu_code_fd_path()
raise "not supported yet"
end
def qemu_vars_fd_path()
raise "not supported yet"
end
end end

View file

@ -1,10 +1,15 @@
require 'open3' require 'open3'
require_relative "utils"
module MacOSSystem module MacOSSystem
def os_name def os_name
"macOS" "macOS"
end end
def arch
sh("sysctl -n hw.machine")
end
def cpus def cpus
stdout, stderr, status = Open3.capture3("sysctl -n hw.ncpu") stdout, stderr, status = Open3.capture3("sysctl -n hw.ncpu")
stdout.strip.to_i stdout.strip.to_i
@ -49,4 +54,23 @@ module MacOSSystem
puts "Failed to uninstall some packages." puts "Failed to uninstall some packages."
end end
end end
def qemu_code_fd_path()
case arch_to_symbol(arch)
when :arm64
"/opt/homebrew/share/qemu/edk2-aarch64-code.fd"
else
raise "not supported yet"
end
end
def qemu_vars_fd_path()
case arch_to_symbol(arch)
when :arm64
"/opt/homebrew/share/qemu/edk2-arm-vars.fd"
else
raise "not supported yet"
end
end
end end

6
lib/system/utils.rb Normal file
View file

@ -0,0 +1,6 @@
def sh(cmd)
out = `#{cmd} 2>/dev/null`.to_s.strip
out.empty? ? "" : out
rescue
""
end

20
lib/templates.rb Normal file
View file

@ -0,0 +1,20 @@
require 'erb'
module Templates
def render(name, locals = {})
# caller_module = Module.nesting.first.to_s.split('::').last&.downcase || 'common'
caller_file = caller_locations(1, 1)[0].absolute_path
inferred_dir = File.basename(caller_file).sub(/^install-/, '').sub(/\.rb$/, '')
puts "caller name: #{inferred_dir}"
template_path = File.join(__dir__, 'data', 'templates', inferred_dir, "#{name}.erb")
template = File.read(template_path)
erb = ERB.new(template)
# erb.result(binding)
context = Struct.new(*locals.keys).new(*locals.values)
erb.result(context.instance_eval { binding })
end
end

41
lib/user.rb Normal file
View file

@ -0,0 +1,41 @@
module User
require 'system'
def self.install(context)
puts "Creating #{context.type}: #{context.user_name}"
System.install(["zsh"])
user_exists = system("getent passwd #{context.user_name} > /dev/null")
group = case context.type
when :user
"users"
when :service
"services"
else
rise "Can not create user for unknow type: #{context.type}"
end
group_exists = system("getent group #{group} > /dev/null")
unless group_exists
puts "Group '#{group}' does not exist. Creating it..."
system("sudo groupadd #{group}")
end
if user_exists
puts "User #{context.user_name} already exists. Updating shell and adding to group '#{group}'."
system("sudo usermod -s /usr/bin/zsh #{context.user_name}")
system("sudo usermod -g #{group} #{context.user_name}")
else
puts "User #{context.user_name} does not exist. Creating user..."
system("sudo adduser --disabled-login --gecos \"\" --ingroup #{group} --shell /usr/bin/zsh #{context.user_name}")
end
end
def self.cache_path
ENV["DAT_CACHE_PATH"] || "#{ENV["HOME"]}/.cache/dat/"
end
end

169
lib/virtual-machine.rb Normal file
View file

@ -0,0 +1,169 @@
require 'downloader'
require 'system'
require_relative 'data/resources/iso-images'
require 'vm/qemu'
require 'vm/archive'
module VirtualMachine
def self.distro(name, arch, type = :install)
ISO_URLS[name][arch][type]
end
def self.image_id_dir(options)
File.join("#{options[:distro].to_s}-#{options[:arch].to_s}", options[:name])
end
def self.vm_root_path
ENV["DAT_VM_DATA"] || File.join(User.cache_path, "vm")
end
def self.vm_dir(options)
File.join(vm_root_path, "image", image_id_dir(options))
end
def self.archive_dir(options)
File.join(vm_root_path, "archive", image_id_dir(options))
end
def self.create_archive_path(options)
File.join(archive_dir(options), "archive.tar.zst")
end
def self.get_recent_archive_path(options)
File.join(archive_dir(options), "archive.tar.zst")
end
# https://artur.gurgul.pro/vm/
# https://artur.gurgul.pro/vm/debian-arm64/debian/archive.tar.zst
def self.vm_archive_url(options)
return nil unless ENV["DAT_VM_REPO_URL"]
image_id = image_id_dir(options)
uri = Addressable::URI.parse(ENV["DAT_VM_REPO_URL"])
uri.path = File.join(uri.path, image_id, "archive.tar.zst")
return uri.to_s
end
def self.root_img_path(options)
File.join(vm_dir(options), "root.img")
end
def self.fill_defaults(options)
if options[:name] == nil
options[:name] = options[:distro]
end
if options[:arch] == nil
options[:arch] = System::ARCH
else
options[:arch] = System.arch_to_symbol(options[:arch])
end
if options[:detached] == nil
options[:detached] = false
end
if options[:tpm] == nil
options[:tpm] = false
end
unless options[:vars_fd]
options[:vars_fd] = File.join(vm_dir(options), "vars.fd")
end
unless options[:code_fd]
options[:code_fd] = File.join(vm_dir(options), "code.fd")
end
end
def self.archive(options)
fill_defaults(options)
Archive.create(vm_dir(options), out: create_archive_path(options))
end
def self.restore(options)
fill_defaults(options)
Archive.restore(get_recent_archive_path(options), vm_dir(options))
end
def self.run(options)
fill_defaults(options)
disk_img_path = root_img_path(options)
unless File.exist?(disk_img_path)
url = vm_archive_url(options)
Downloader.get(url, use_cache: false) do |path|
Archive.restore(path, vm_dir(options))
end
#raise "file do not exists"
end
# TODO:
# - if image path not exists, check the cache
# - if cache do not exists try to download
puts "Starting image: #{disk_img_path}"
Qemu.launch(
options[:arch],
disk_img_path,
code_fd: options[:code_fd],
vars_fd: options[:vars_fd],
cpus: [1, System.cpus - 2].max,
detach: options[:detached],
tpm: options[:tpm],
shell: options[:shell]
)
end
# vm setup windows --arch arm64 --iso /Users/agurgul/Downloads/win.iso --tpm
# vm setup windows --tpm --cdrom /Users/agurgul/Downloads/win.iso
def self.setup(options)
fill_defaults(options)
# puts options
# exit -1
get_cdrom_image(options) do |path|
disk_img_path = root_img_path(options)
create_disk_image(disk_img_path, 64000 * 4)
Qemu.launch(
options[:arch],
disk_img_path,
code_fd: options[:code_fd],
vars_fd: options[:vars_fd],
cpus: [1, System.cpus - 2].max,
cdrom: path,
detach: options[:detached],
display: DisplayMode.window,
tpm: options[:tpm]
)
end
end
def self.get_cdrom_image(options)
if options[:cdrom] == nil
url = distro(options[:name], options[:arch], :install)
Downloader.get(url) do |path|
yield path
end
else
yield options[:cdrom]
end
end
# size in MB
# lsof /Users/artur/.cache/dat/vm/debian/arm64/debian/root.img
def self.create_disk_image(path, size)
size_in_bytes = 1024 * 1024 * size
# Ensure the directory exists
dir = File.dirname(path)
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
File.open(path, "wb") do |f|
f.truncate(size_in_bytes)
# f.seek(size_in_bytes - 1)
# f.write("\0")
end
end
end

37
lib/vm/archive.rb Normal file
View file

@ -0,0 +1,37 @@
require "fileutils"
module Archive
def self.cmd(*cmd)
puts "cmd: #{cmd.join(" ")}"
# return
stdout, stderr, status = Open3.capture3(*cmd)
unless status.success?
warn "Command failed: #{cmd.join(' ')}"
warn stderr
exit status.exitstatus || 1
end
stdout
end
# it preserved sparsiveness
# gtar -S --sparse-version=2 -I 'zstd -T0 -19' -cpf test.tar.zst /Volumes/Projs/VM/VM-data/image/debian-arm64/debian/
# tar --zstd -xpf test.tar.zst -C ./r
def self.create(path, **options)
out = options[:out] || File.join(Dir.pwd, "archive.tar.zst")
FileUtils.mkdir_p(File.dirname(out))
cmd "gtar", "-S", "--sparse-version=2", "-I",
"zstd -T0 -19", "-cpf", out, "-C" , path, "."
end
def self.restore(file, path, **options)
puts file
puts path
puts options
FileUtils.mkdir_p(path)
cmd "gtar", "-S", "--sparse-version=2", "-I", "zstd", "-xpf",
file, "-C", path
end
end

344
lib/vm/qemu.rb Normal file
View file

@ -0,0 +1,344 @@
require "fileutils"
require "ostruct"
module DisplayMode
def self.none
0
end
def self.fullscreen
1
end
def self.window
2
end
def self.vnc
3
end
end
module Qemu
def self.qemu_bin_for(arch)
{
x86_64: "qemu-system-x86_64",
x86_32: "qemu-system-i386",
arm64: "qemu-system-aarch64",
armv7: "qemu-system-arm",
armv6: "qemu-system-arm",
armv5: "qemu-system-arm",
riscv64: "qemu-system-riscv64",
ppc64le: "qemu-system-ppc64",
ppc64: "qemu-system-ppc64",
ppc32: "qemu-system-ppc",
s390x: "qemu-system-s390x",
mips64el: "qemu-system-mips64el",
mips64: "qemu-system-mips64",
mipsel: "qemu-system-mipsel",
mips: "qemu-system-mips",
loongarch64: "qemu-system-loongarch64",
sparc64: "qemu-system-sparc64",
alpha: "qemu-system-alpha",
hppa: "qemu-system-hppa",
ia64: "qemu-system-ia64",
}.fetch(arch) { raise "Unsupported arch: #{arch.inspect}" }
end
def self.accel_args
host = RbConfig::CONFIG["host_os"]
if host =~ /linux/i && File.exist?("/dev/kvm")
["-accel", "kvm"]
elsif host =~ /darwin/i
# hvf exists on Apple Silicon + Intel macOS; QEMU falls back if not available
["-accel", "hvf"]
elsif host =~ /freebsd/i
# if QEMU was built with it; otherwise it will ignore
["-accel", "bhyve"]
else
["-accel", "tcg"]
end
end
def self.machine_args_for(arch)
case arch
when :x86_64, :x86_32
[]
when :arm64
# -machine type=virt
# -cpu cortex-a72
["-machine", "virt", "-cpu", "max"]
when :armv7, :armv6, :armv5
["-machine", "virt", "-cpu", "cortex-a15"]
when :riscv64
["-machine", "virt"]
when :loongarch64
["-machine", "virt"]
else
[]
end
end
#def self.launch(arch, disk_path, cdrom = nil, detach = true)
def self.launch(arch, disk_path, **options)
defaults = {
arch: System::ARCH,
cdrom: nil,
detach: true,
shell: false,
ram: 2048 * 8,
cpus: 1,
display: DisplayMode::none,
mount: {
wd: nil,
home: nil
}
}
# for testing only
defaults[:detach] = false
defaults[:display] = DisplayMode.fullscreen
defaults[:display] = DisplayMode.window
# defaults[:display] = DisplayMode.none
#defaults[:display] = DisplayMode.vnc
opts = defaults.merge(options)
puts options
puts opts
qemu = qemu_bin_for(arch)
args = []
if System::OS == :macos && arch == :arm64
# args += ["-bios", "/opt/homebrew/share/qemu/edk2-aarch64-code.fd"]
# cp /opt/homebrew/share/qemu/edk2-arm-vars.fd ~/edk2-arm-vars.fd
unless File.exist?(opts[:vars_fd])
#System.qemu_paths
FileUtils.cp(System.qemu_vars_fd_path, opts[:vars_fd])
end
unless File.exist?(opts[:code_fd])
FileUtils.cp(System.qemu_code_fd_path, opts[:code_fd])
end
args += ["-drive", "if=pflash,format=raw,unit=0,readonly=on,file=#{opts[:code_fd]}"]
args += ["-drive", "if=pflash,format=raw,unit=1,file=#{opts[:vars_fd]}"]
ssh_port = nil
if opts[:shell]
ssh_port = rand(4000..9999)
# args += ['-netdev', "user,id=net0,hostfwd=tcp:127.0.0.1:#{ssh_port}-:22"]
# args += ['-device', 'virtio-net-device,netdev=net0']
# args += ['-netdev', "user,id=n0,hostfwd=tcp:127.0.0.1:#{ssh_port}-10.0.2.15:22"]
# args += ['-device', 'virtio-net,netdev=n0']
# args += ['-netdev', "user,id=net0,hostfwd=tcp:127.0.0.1:#{ssh_port}-10.0.2.15:22"]
# args += ['-device', 'virtio-net-device,netdev=net0"']
args += ['-nic', "user,model=virtio-net-pci,hostfwd=tcp:127.0.0.1:#{ssh_port}-:22"]
puts "ssh -p #{ssh_port} user@localhost"
# conf that works
#args += ["-device", "virtio-net,netdev=n0", "-netdev", "user,id=n0"]
end
# -virtfs local,path=.,mount_tag=hostfs,security_model=passthrough,id=hostfs
# mount -t 9p -o trans=virtio,version=9p2000.L hostfs /mnt
# sudo mount -t 9p hostfs /home/user/Share -o trans=virtio,version=9p2000.L,uid=1000,gid=1000,msize=262144,cache=mmap
# hostfs /home 9p trans=virtio,version=9p2000.L,uid=1000,gid=1000,msize=262144,cache=mmap,nofail 0 0
# hostfs /share 9p trans=virtio,version=9p2000.L,uid=1000,gid=1000,msize=262144,cache=mmap,nofail 0 0
if opts[:mount][:wd]
args += ['-virtfs', 'local,path=.,mount_tag=hostfs,security_model=passthrough,id=hostfs']
end
if opts[:mount][:home]
#args += ['-homefs', "local,path=#{VMDATA},mount_tag=hostfs,security_model=passthrough,id=hostfs"]
end
if opts[:display] == DisplayMode::none
port = 2222
args += ['-nographic']
args += ['-netdev', "user,id=net0,hostfwd=tcp:127.0.0.1:#{port}-:22,udp:127.0.0.1:6544-:6544=on"]
#args += ['-device', 'e1000,netdev=net0']
args += ['-device', 'virtio-net-pci,netdev=net0']
puts "ssh -p #{port} user@localhost"
elsif opts[:display] == DisplayMode::vnc
# Note: this outputs serial on the console
#args += ['-nographic']
args += ["-display", "none"]
args += ["-device", "virtio-gpu-pci"]
args += ["-device", "virtio-keyboard-pci"]
args += ["-device", "virtio-mouse-pci"]
args += ['-vnc', '127.0.0.1:0,password=on']
# tunnel ssh -L 5900:127.0.0.1:5900 user@your-host
# -monitor unix:/tmp/qemu-mon,server,nowait
# -vnc 127.0.0.1:0,password=on
# printf 'change vnc password\nMySecret\n' | socat - UNIX-CONNECT:/tmp/qemu-mon
# SASL auth (username + password)
# -vnc :0,sasl
# TLS (certificates, optional password)
# -object tls-creds-x509,id=tls0,...
# -vnc :0,tls-creds=tls0
# qemu-system-x86_64 \
# -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server \
# -vnc :0,tls-creds=tls0,sasl
else
#args += ["-device", "virtio-gpu-device"]
if opts[:display] == DisplayMode::fullscreen
# #args += ["-display", "cocoa,full-screen=on"]
# # attempts:
# #args += ["-display", "cocoa,full-screen=on,retina=on"]
# # brew install gtk+3 sdl2
# # args += ["-display", "sdl,gl=on,full-screen=on"]
# #args += ["-display", "gtk,gl=on,full-screen=on"]
# #args += ["-display", "cocoa,full-screen=on"]
# #args += ["-display", "cocoa,gl=es,full-screen=on"]
#### TODO: try make it work with custom build
# args += ["-device", "virtio-gpu-gl-pci"]
# args += ["-display", "sdl,gl=on,full-screen=on"]
args += ["-display", "cocoa,full-screen=on"]
else
args += ["-display", "cocoa"]
end
args += ["-device", "qemu-xhci,id=xhci"]
args += ["-device", "usb-kbd"]
args += ["-device", "usb-tablet"]
args += ["-device", "virtio-keyboard-device"]
args += ["-device", "virtio-mouse-device"]
args += ["-device", "virtio-gpu"]
#args += ['-nic', 'user,model=virtio-net-pci']
unless opts[:shell]
args += ["-device", "virtio-net,netdev=n0", "-netdev", "user,id=n0"]
end
### TODO: remove
port = 2222
args += ['-device', 'virtio-net-pci,netdev=net0']
args += ['-netdev', "user,id=net0,hostfwd=tcp:127.0.0.1:#{port}-:22,hostfwd=udp:127.0.0.1:6544-:6544"]
args += ['-virtfs', 'local,path=.,mount_tag=hostfs,security_model=passthrough,id=hostfs']
### TODO END
# macOS vmnet (shares Macs LAN)
# -netdev vmnet-shared,id=n1 \
# -device virtio-net-pci,netdev=n1
end
# copy to /Users/artur/.cache/dat/vm/debian/arm64/debian/vars.fd
# /opt/homebrew/share/qemu/edk2-aarch64-vars.fd
# args += ["-drive", "if=pflash,format=raw,unit=1,file=/Users/artur/.cache/dat/vm/debian/arm64/debian/vars.fd"]
end
args += accel_args
args += machine_args_for(arch)
args += ["-m", opts[:ram].to_s, "-smp", opts[:cpus].to_s]
args += ["-name", "FirstVM", "-boot", "order=d"] # boot from CD first
args += ["-drive", "file=#{disk_path},if=virtio,cache=writeback,format=raw,id=nvme0"]
#args += ["-device", "nvme,serial=nvme0,drive=nvme0,bootindex=2"]
if opts[:cdrom] != nil
#args += ["-cdrom", opts[:cdrom]]
# args += ["-device", "virtio-scsi-pci,id=scsi"]
# args += ["-drive", "if=none,id=cd,format=raw,file=#{opts[:cdrom]},media=cdrom"]
# args += ["-device", "scsi-cd,drive=cd,bootindex=1"]
args += ["-drive", "id=cd,format=raw,file=#{opts[:cdrom]},media=cdrom"]
args += ["-device", "usb-storage,drive=cd,bootindex=1"]
args += ["-device", "ramfb"]
# args += ["-device", "virtio-gpu-pci"]
# args += ["-display", "default,show-cursor=on"]
end
if opts[:tpm]
# brew install swtpm
# swtpm socket --tpm2 --ctrl type=unixio,path=./tpm/tpm.sock --tpmstate dir=./tpm --daemon
["swtpm", "socket", "--tpm2", "--ctrl", "type=unixio,path=./tpm/tpm.sock", "--tpmstate", "dir=./tpm"]
# args += ["-chardev", "socket,id=chrtpm,path=/Users/agurgul/Downloads/tpm/tpm.sock"]
# args += ["-tpmdev", "emulator,id=tpm0,chardev=chrtpm"]
# args += ["-device", "tpm-crb-device,tpmdev=tpm0"]
args += ["-chardev", "socket,id=chrtpm,path=/Users/agurgul/Downloads/tpm/tpm.sock"]
args += ["-tpmdev", "emulator,id=tpm0,chardev=chrtpm"]
#args += ["-device", "tpm-tis,tpmdev=tpm0"]
args += ["-device", "tpm-tis-device,tpmdev=tpm0"]
# nic user,ipv6=off,model=rtl8139,mac=84:1b:77:c9:03:a6
# TODO: Shared network on macOS
# -netdev vmnet-shared,id=net0
end
args += ['-monitor', 'stdio']
# args += ["-device", "virtio-net,netdev=n0", "-netdev", "user,id=n0"] # user-mode NAT
# optional: uncomment to run headless with VNC on :5901
# args += ["-display", "none", "-vnc", "127.0.0.1:1"]
cmd = [qemu, *args]
puts "Launching: #{cmd.join(' ')}"
if opts[:detach]
log = File.open("log.txt", "w")
pid = Process.spawn(*cmd, pgroup: false, out: log, err: log)
Process.detach(pid)
puts "QEMU pid=#{pid}"
if opts[:shell]
System.exec_ssh(ssh_port)
end
else
pid = Process.spawn(*cmd, pgroup: true, out: $stdout, err: $stderr)
Process.wait(pid)
status = $?
puts "Exit status: #{status.exitstatus}"
end
end
end
## Works on MacOS=
# -monitor unix:/tmp/qemu-monitor.sock,server,nowait
# nc -U /tmp/qemu-monitor.sock
# instead of args += ['-monitor', 'stdio']
# 9P
# sudo mount -t 9p hostfs /home/user/Share \
# -o trans=virtio,version=9p2000.L,msize=262144,cache=mmap,access=any,dfltuid=1000,dfltgid=1000
# sudo chown -hR 1000:1000 /home/user/Share

View file

@ -4,3 +4,12 @@
```bash ```bash
curl -sSL https://gurgul.pro/artur/environment/raw/branch/main/install | bash curl -sSL https://gurgul.pro/artur/environment/raw/branch/main/install | bash
``` ```
Config:
| Variable Name | Default Value | Description |
|---------------------------|--------------------------------|-------------|
| `DAT_CACHE_PATH` | `${HOME}/.cache/dat/` | Path where cached data is stored. |
| `DEFAULT_INTERNET_LOGIN` | not set | |
| `DAT_VM_DATA` | `${DAT_CACHE_PATH}/vm` | Directory for storing VM-related data. |

View file

@ -0,0 +1,21 @@
environment:
PYTHONPATH: /home/artur/test
dependencies:
- python
packages:
- libffi-dev
- libssl-dev
repository:
url: https://github.com/certbot/certbot
branch: v4.2.0
version: 4.2.0
steps:
- $SUDO pip3 install --prefix=$PREFIX certbot
- $SUDO pip3 install --prefix=$PREFIX certbot-nginx
# can be executed like: pip install --prefix=$PREFIX certbot certbot-nginx
# to see installed plugins: `certbot plugins`

11
recipes/example-roda.rb Normal file
View file

@ -0,0 +1,11 @@
require 'nginx'
class ExampleProxy < NGINXProxy
domain "gurgul.org"
service "forgejo"
user "git"
port 3000
end
puts ExampleProxy.generate
puts ExampleProxy.available_path

View file

@ -1,6 +1,8 @@
service:
name: forgejo
archive: archive:
url: https://codeberg.org/forgejo/forgejo/releases/download/v12.0.1/forgejo-12.0.1-linux-amd64.xz url: https://codeberg.org/forgejo/forgejo/releases/download/v12.0.1/forgejo-12.0.1-linux-amd64.xz
steps: steps:
- echo "ls" - mv forgejo-12.0.1-linux-amd64 ~/.local/bin/forgejo

View file

@ -14,6 +14,6 @@ repository:
steps: steps:
- ./contrib/download_prerequisites - ./contrib/download_prerequisites
- ./configure --prefix=$HOME/.local --enable-languages=c,c++ --disable-multilib - ./configure --prefix=$PREFIX --enable-languages=c,c++ --disable-multilib
- make -j$(nproc) - make -j$(nproc)
- make install - $SUDO make install

40
recipes/nginx/debian.yml Normal file
View file

@ -0,0 +1,40 @@
packages:
- build-essential
- libpcre2-dev
- zlib1g
- zlib1g-dev
- libssl-dev
repository:
url: https://github.com/nginx/nginx.git
branch: release-1.29.0
version: 1.29.0
# TODO: finish the configuration: last message of 6898551e-70a0-8331-8617-bcfc9bcf6af8
steps:
- echo "starting"
- |
./auto/configure \
--prefix=$PREFIX \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-stream \
--with-http_v2_module \
--with-pcre-jit \
--with-threads \
--with-file-aio \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_gzip_static_module \
--with-stream=dynamic \
--with-stream_ssl_module \
--sbin-path=$PREFIX/nginx/sbin/nginx \
--conf-path=$PREFIX/nginx/conf/nginx.conf \
--pid-path=$PREFIX/nginx/run/nginx.pid \
--lock-path=$PREFIX/nginx/run/nginx.lock \
--error-log-path=$PREFIX/nginx/logs/error.log \
--http-log-path=$PREFIX/nginx/logs/access.log
- make
- $SUDO make install

View file

@ -0,0 +1,23 @@
sudo tee /etc/systemd/system/nginx-custom.service >/dev/null <<'EOF'
[Unit]
Description=nginx (custom prefix)
After=network.target
[Service]
Type=forking
PIDFile=/pkg/nginx/run/nginx.pid
ExecStart=/pkg/nginx/sbin/nginx
ExecReload=/pkg/nginx/sbin/nginx -s reload
ExecStop=/pkg/nginx/sbin/nginx -s quit
TimeoutStopSec=5
#User=nginx
#Group=nginx
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
# sudo systemctl daemon-reload
# sudo systemctl enable --now nginx-custom

View file

@ -5,8 +5,9 @@ packages:
repository: repository:
url: https://github.com/neovim/neovim.git url: https://github.com/neovim/neovim.git
branch: release-0.11 branch: release-0.11
version: 0.11
steps: steps:
- make distclean - make distclean
- make CMAKE_BUILD_TYPE=Release CMAKE_INSTALL_PREFIX=$HOME/.local - make CMAKE_BUILD_TYPE=Release CMAKE_INSTALL_PREFIX=$PREFIX
- make install - $SUDO make install

View file

@ -0,0 +1,24 @@
environment:
PG_DOMAIN: gurgul.org
packages:
- postgresql
- postgresql-contrib
steps:
- $SUDO systemctl enable postgresql
- $SUDO systemctl start postgresql
# Installing certificates
- $SUDO mkdir /etc/postgresql/ssl
- $SUDO cp /etc/letsencrypt/live/$PG_DOMAIN/fullchain.pem /etc/postgresql/ssl/server.crt
- $SUDO cp /etc/letsencrypt/live/$PG_DOMAIN/privkey.pem /etc/postgresql/ssl/server.key
- $SUDO chown postgres:postgres /etc/postgresql/ssl/server.*
- $SUDO chmod 600 /etc/postgresql/ssl/server.key
actions:
# dat action postgresql:add-user -u user
add-user:
- sudo -u postgres createuser --no-superuser --no-createdb --no-createrole $DB_USER
- sudo -u postgres createdb -O $DB_USER $DB_USER

View file

@ -0,0 +1,25 @@
packages:
- build-essential
- libreadline-dev
- zlib1g-dev
- flex
- bison
- libssl-dev
- libxml2-dev
- libxslt1-dev
- libpam0g-dev
- libedit-dev
repository:
# Original repository
# https://git.postgresql.org/gitweb/?p=postgresql.git
url: https://github.com/postgres/postgres.git
branch: REL_17_5
version: 17.5
steps:
- ./configure --prefix=$PREFIX --with-openssl --with-systemd
- make -j$CPUS
- $SUDO mkdir -p $PREFIX
- $SUDO make install

28
recipes/qemu/macos.yml Normal file
View file

@ -0,0 +1,28 @@
packages:
- meson
- ninja
- pkg-config
- glib
- pixman
- sdl2
- libepoxy
- libslirp
- gettext
# pkg-config --modversion sdl2 # should print a version
# pkg-config --modversion epoxy # should print a version
repository:
url: https://gitlab.com/qemu-project/qemu.git
branch: v10.0.3
steps:
- mkdir build
- cd build
- |
../configure \
--enable-sdl \
--enable-opengl \
--target-list=aarch64-softmmu,x86_64-softmmu \
--prefix="$HOME/.local/qemu-sdl"

View file

@ -16,6 +16,7 @@ packages:
repository: repository:
url: https://github.com/ruby/ruby.git url: https://github.com/ruby/ruby.git
branch: v3_4_5 branch: v3_4_5
version: 3.4.5
steps: steps:
- ./autogen.sh - ./autogen.sh

45
recipes/vnc-viewer.yml Normal file
View file

@ -0,0 +1,45 @@
# # 1) Build & install FLTK 1.4 to /opt/fltk-1.4
# git clone https://github.com/fltk/fltk.git
# cd fltk
# git checkout branch-1.4
# cmake -S . -B build \
# -DOPTION_BUILD_SHARED_LIBS=ON \
# -DOPTION_USE_SYSTEM_LIBPNG=ON \
# -DOPTION_USE_SYSTEM_LIBJPEG=ON \
# -DOPTION_USE_SYSTEM_ZLIB=ON \
# -DCMAKE_INSTALL_PREFIX=/opt/fltk-1.4
# cmake --build build -j
# sudo cmake --install build
# # 2) Point pkg-config and CMake at the new install
# export PKG_CONFIG_PATH=/opt/fltk-1.4/lib/pkgconfig:$PKG_CONFIG_PATH
# export CMAKE_PREFIX_PATH=/opt/fltk-1.4:$CMAKE_PREFIX_PATH
# # (optional but sometimes necessary)
# export FLTK_DIR=/opt/fltk-1.4/lib/cmake/FLTK
packages:
- cmake
- ninja
- pkg-config
- fltk
- jpeg-turbo
- libpng
- zlib
- gnutls
- ffmpeg
# --recursive
repository:
url: https://github.com/TigerVNC/tigervnc.git
url: v1.15.0
steps:
- mkdir build && cd build
- |
cmake -G Ninja .. \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_VIEWER=ON \
-DBUILD_SERVER=OFF \
-DWITH_GNUTLS=ON
- ninja