Update to Debian trixie

This commit is contained in:
Jan Schär 2026-03-21 09:45:05 +01:00
parent 7a83c50208
commit 6118557715
12 changed files with 193 additions and 140 deletions

View file

@ -13,7 +13,7 @@ import datetime
import urllib.request
DISTRIBUTION = "bookworm"
DISTRIBUTION = "trixie"
VARIANT_LABEL = {
"contestant": "contest",

View file

@ -19,12 +19,6 @@ systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
DISABLE_DESKTOP="dpkg-statoverride --force-statoverride-add --update --add root root 640"
$DISABLE_DESKTOP /usr/share/applications/gnome-bluetooth-panel.desktop
$DISABLE_DESKTOP /usr/share/applications/gnome-online-accounts-panel.desktop
$DISABLE_DESKTOP /usr/share/applications/gnome-sharing-panel.desktop
# Disable kexec-tools services.
# We want to load kexec manually, and execution of kexec is already done by systemd.
systemctl disable kexec-load.service
systemctl disable kexec.service
# Restrict access to the config which contains the WiFi password.
chmod og= /etc/NetworkManager/system-connections/contest.nmconnection

View file

@ -2,21 +2,27 @@
// Because of that, this gnome extension is distributed under
// the terms of the GNU General Public License, version 2 or later.
const {
AccountsService, Atk, Clutter, Gio,
GLib, Graphene, Meta, Shell, St,
} = imports.gi;
import AccountsService from 'gi://AccountsService';
import Atk from 'gi://Atk';
import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Graphene from 'gi://Graphene';
import Meta from 'gi://Meta';
import Shell from 'gi://Shell';
import St from 'gi://St';
const Background = imports.ui.background;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const ScreenShield = imports.ui.screenShield;
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
import * as Background from 'resource:///org/gnome/shell/ui/background.js';
import * as Layout from 'resource:///org/gnome/shell/ui/layout.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as ScreenShield from 'resource:///org/gnome/shell/ui/screenShield.js';
// half of the time for which a frame is displayed
const HALF_FRAME_TIME_MS = 8;
const BLUR_BRIGHTNESS = 0.55;
const BLUR_SIGMA = 60;
const BLUR_BRIGHTNESS = 0.65;
const BLUR_RADIUS = 90;
const POINTER_HIDE_TIMEOUT = 10 * GLib.USEC_PER_SEC;
@ -43,6 +49,7 @@ let countdownTimeoutId = 0;
let configFile;
let configMonitor;
let configLoadCancellable;
let config;
let startTime;
@ -59,49 +66,47 @@ function extLogError (msg) {
printerr(`[contest-lock] Error: ${msg}`);
}
function loadConfig () {
configFile.load_contents_async(null, (obj, res) => {
// If there is a poblem with the config file, log an error and keep
// using the old config.
let newConfig;
try {
const [ok, bytes] = configFile.load_contents_finish(res);
// TextDecoder is used in upstream gnome-shell, but not yet
// supported in current Debian.
const contentStr = imports.byteArray.toString(bytes);
//const contentStr = new TextDecoder().decode(bytes);
newConfig = JSON.parse(contentStr);
} catch (err) {
logError(err, '[contest-lock] config file');
return;
}
if (!(typeof newConfig === 'object' && newConfig != null)) {
extLogError('config file: invalid format');
return;
}
if (typeof newConfig.title !== 'string') {
extLogError('config file: "title" must be a string');
return;
}
if (typeof newConfig.message !== 'string') {
extLogError('config file: "message" must be a string');
return;
}
if (
typeof newConfig.startTime !== 'string' ||
!/^\d{4,}-\d\d-\d\dT\d\d:\d\d:\d\d\+\d\d:\d\d$/.test(newConfig.startTime)
) {
extLogError('config file: "startTime" must be a string with format 0000-00-00T00:00:00+00:00');
return;
}
async function loadConfig () {
configLoadCancellable?.cancel();
configLoadCancellable = new Gio.Cancellable();
extLog('Loaded new config.')
config = newConfig;
startTime = (new Date(newConfig.startTime)).getTime();
// If there is a poblem with the config file, log an error and keep
// using the old config.
let newConfig;
try {
const [ok, bytes] = await configFile.load_contents(configLoadCancellable);
const contentStr = new TextDecoder().decode(bytes);
newConfig = JSON.parse(contentStr);
} catch (err) {
logError(err, '[contest-lock] config file');
return;
}
if (typeof newConfig !== 'object' || newConfig == null) {
extLogError('config file: invalid format');
return;
}
if (typeof newConfig.title !== 'string') {
extLogError('config file: "title" must be a string');
return;
}
if (typeof newConfig.message !== 'string') {
extLogError('config file: "message" must be a string');
return;
}
if (
typeof newConfig.startTime !== 'string' ||
!/^\d{4,}-\d\d-\d\dT\d\d:\d\d:\d\d\+\d\d:\d\d$/.test(newConfig.startTime)
) {
extLogError('config file: "startTime" must be a string with format 0000-00-00T00:00:00+00:00');
return;
}
updateConfig();
syncActive();
});
extLog('Loaded new config.')
config = newConfig;
startTime = (new Date(newConfig.startTime)).getTime();
updateConfig();
syncActive();
}
function syncActive () {
@ -190,7 +195,7 @@ function updateBackgrounds () {
effect: new Shell.BlurEffect({
name: 'blur',
brightness: BLUR_BRIGHTNESS,
sigma: BLUR_SIGMA,
radius: BLUR_RADIUS,
}),
});
@ -382,7 +387,9 @@ function deactivate () {
isActiveChanging = false;
}
function init (extension) {
var isInitialized = false;
function init () {
actor = Main.layoutManager.screenShieldGroup;
const userName = GLib.get_user_name();
@ -395,7 +402,7 @@ function init (extension) {
Main.layoutManager.connect('monitors-changed', updateBackgrounds);
cursorTracker = Meta.CursorTracker.get_for_display(global.display);
cursorTracker = global.backend.get_cursor_tracker();
if (!Main.layoutManager._startingUp) {
isShellReady = true;
@ -406,19 +413,6 @@ function init (extension) {
});
}
// TODO: When we drop compatibility with gnome <42, remove this code,
// rename the stylesheet back to stylesheet.css (so that it is loaded
// by the extension system) and add a session-modes property which
// includes unlock-dialog to metadata.json.
const theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
const stylesheetFile = extension.dir.get_child('stylesheet-always.css');
theme.load_stylesheet(stylesheetFile);
// TODO: When we drop compatibility with gnome <42, remove this code.
// gnome 38 has a bug that causes extensions to break when running
// `dconf update` while the screen is locked.
Main.extensionManager.reloadExtension = function () {};
configFile = Gio.File.new_for_path('/etc/contest-lock.json');
configMonitor = configFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
configMonitor.set_rate_limit(1000);
@ -426,12 +420,18 @@ function init (extension) {
loadConfig();
}
function enable () {
isExtensionEnabled = true;
syncActive();
}
export default class ContestLockExtension extends Extension {
enable () {
if (!isInitialized) {
init();
isInitialized = true;
}
isExtensionEnabled = true;
syncActive();
}
function disable () {
isExtensionEnabled = false;
syncActive();
disable () {
isExtensionEnabled = false;
syncActive();
}
}

View file

@ -3,6 +3,7 @@
"uuid": "contest-lock@soi.ch",
"name": "Contest lock screen",
"description": "A custom lock screen for contests.",
"shell-version": [ "3.38", "42", "43" ],
"session-modes": [ "user", "unlock-dialog" ],
"shell-version": [ "48" ],
"url": ""
}

View file

@ -1,24 +1,24 @@
.contest-lock-stack {
color: white;
text-align: center;
spacing: 24px;
spacing: 32px;
}
.contest-lock-countdown {
font-size: 64pt;
font-size: 96pt;
font-weight: 300;
font-feature-settings: "tnum"; /* tabular figures */
}
.contest-lock-title {
font-size: 16pt;
font-size: 24pt;
}
.contest-lock-user {
font-size: 20pt;
font-size: 32pt;
}
.contest-lock-message {
padding-top: 24px;
font-size: 16pt;
padding-top: 32px;
font-size: 24pt;
}

View file

@ -1,5 +1,11 @@
const { St, Clutter, GLib, Gio, AccountsService } = imports.gi;
const Main = imports.ui.main;
import AccountsService from 'gi://AccountsService';
import Clutter from 'gi://Clutter';
import GLib from 'gi://GLib';
import St from 'gi://St';
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
let panelBin;
let userLabel;
@ -11,29 +17,33 @@ function updateUser () {
if (realName != null) userLabel.text = realName;
}
function init () {
panelBin = new St.Bin({
style_class: 'panel-bin',
});
userLabel = new St.Label({
text: 'No user',
y_align: Clutter.ActorAlign.CENTER,
});
panelBin.set_child(userLabel);
export default class UserIndicatorExtension extends Extension {
constructor(metadata) {
super(metadata);
const userName = GLib.get_user_name();
user = AccountsService.UserManager.get_default().get_user(userName);
if (!user) return;
panelBin = new St.Bin({
style_class: 'panel-bin',
});
userLabel = new St.Label({
text: 'No user',
y_align: Clutter.ActorAlign.CENTER,
});
panelBin.set_child(userLabel);
user.connect('notify::is-loaded', updateUser);
user.connect('changed', updateUser);
updateUser();
}
function enable () {
Main.panel._rightBox.insert_child_at_index(panelBin, 0);
}
function disable () {
Main.panel._rightBox.remove_child(panelBin);
const userName = GLib.get_user_name();
user = AccountsService.UserManager.get_default().get_user(userName);
if (!user) return;
user.connect('notify::is-loaded', updateUser);
user.connect('changed', updateUser);
updateUser();
}
enable () {
Main.panel._rightBox.insert_child_at_index(panelBin, 0);
}
disable () {
Main.panel._rightBox.remove_child(panelBin);
}
}

View file

@ -3,6 +3,6 @@
"uuid": "user-indicator@soi.ch",
"name": "User indicator",
"description": "Shows the user's real name in the top bar.",
"shell-version": [ "3.38", "42", "43" ],
"shell-version": [ "48" ],
"url": ""
}

View file

@ -60,3 +60,60 @@ Ret0lkql+RTCtyWh95sr1kgGyyQCyF/Jv7NSntcQlJL3whphCpOkvOvK+HlBoY5U
McvDuGKIXk111Z3nrF4DeIIc/U6ICQ==
=CCk2
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFkQsPABEACnBDDC1d1e6h3CH0VNjJ/gtExITglzThVvPADCFBOFpwUKgqnf
mEj6Rx/citX3QO1MAwyACCP7UsFad7JDNpBWHRBGGGZFQE1EYOvXccWN4dqMyH11
06+GtzPuMRxo+qEpWzGbIGHDZH9GVjCM0QykHZQawJShPdnsgpYuDtGMlvNQKLk7
eSNGsHZ/NxHeYbC8Ung4ts48a2N0js+dayv1DVSZI8L9TnzbgxkOTcUqytBhub7u
ZygYzalzvqN9BiHVsTXi2rd7T/XfK7Zl0rawOHQUQavY/5IsUuRmqKwimmM6dkcu
9500nwQTDkFB6Q50IsL9pZVf2tXT70OpKDTMiTlK9VkJNnKFIHdFfZkk/O3opFhR
jI7iD41sgSVH5X176lzS2nd15GGn3qW2jSVXZ7XnlgUML4GqJVKU+T//WQjudVkF
bpfeTCOzkxj9wF0ixW64MxhkOoaHDreFhKdyn9yjSWId8m4igS+pe1xHJKREZwTR
1kgQY8Od0eOxyyvZQlQons6Ac80gF0220Z2+qnpEawhGGOKBDmh8tgQkw4s+nJc5
cHIB5o9oyhRo0R9yA7kj7D/bymN1uu7fYFnGOhwJ0jTW3LEKohRLJVgC14C/i44+
1XZB6lOF98TcmjY7plBB1eSSBFk9mfHsb7PH2DE17xTczHC4mpLxd5w+WwARAQAB
tCxTdWJsaW1lIEhRIFB0eSBMdGQgPHN1cHBvcnRAc3VibGltZXRleHQuY29tPokC
NAQTAQIAHgUCWRCw8AIbLwMLCQcEFQoJCAUWAgMBAAIeAQIXgAAKCRDKRkqaIi0j
0LQsD/93IlZevTYbg9NmTPsVZx9zlBLO9qAIb4co+dOk9Fohc7FMYSevenpA7PTl
O+3yARJhqqB75+7u5LatjEyYo/VEpvZhDyhorPUHshoIcHnsDpC+Ua5zq42efNdl
PE9Vr3klVR7gKbX2FxjqUL5SE6/YLIIr2PhpdbbMQbOCH6s4kXklsVysjRu7k0Ip
U4XF90s4inXTILMruA8LdW59q6dg5Ej4tUv54d9//Jl2Rz0z8TbguYPwFEHaXRNv
hNv0oFnjt16sZENaar81E+icw7laHxGKPnZ1XRUlvsa40/hTYj62yBGzUhLbWPHv
Os7UrIkvsyiE+53cDfEfoFh/e6yLzkSEv12xwhk4p5GjeLnkgC7Wu4VHBZDj9atO
Muwtcxk/eNpdXKg0VA1+Zo1qCuLad7ciaSKGHTasGSEVUjvtnDMPaAzvc8o0kmi4
lzZl6jHeDq2IKrgkT/FKEF9HrER7mFp+mjlVp65AM/xwZ7Ja23ocnuAVz4j9S5s4
iz6dkdJNYkzZiZ67TMth+1Qc0D6LoTwHufMJQHUmUE1+ffRKOpXim4CE21eO+WbX
vOcPh20Khv4E7O4h669+i5Oz9w/qDK2pVEYwiS/7LxaSoYaLHg1xJWk1sQx4gryF
vkhZMlN+2B3cJD4XISa65TWhDSMyWrwElnXchfEc90svjkG/nw==
=bBxZ
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGkJdvYBEADWhdySHM8j/ADp4YuVsnB6RkLVJoQI9A50uMiuDuNk8f05NpZJ
29bStn4m4/sB0jM2L7L6DK6IneHvU+6oUfsMylevZ6SrsjTibt3Ado7vmtEfXz1q
zZ1QbExJpYBXzUNGIIdsdUGgtkTO6dBPzZWA9zhsN8BkMJEJfnJgyJXV7eXqiHUa
LIfCFKy3f97BrLss/bXLY2d3d1J/14uoGhnpTV1udurw/i4F7WrpigauUAyKqvWc
pSCY6iXFGAhX7wTAKnkC7o/nRI4HxpZA01bUt43BMMM0S71xxW54kN/JVRRsVXEt
gE+ZgYB5UG9NjSIkF695PEqjtgv35tpkKnDU94m7Ws+yc51DkyW/6KgGO2FScxkD
bymF8VJl7JO9rZUcA60x45bmbPU6ex1BT4BIiE14mZMrOH80TmmgJDuU1KljMDL0
L+ts4tsv5haPRDByOhLhQeuRRMnbhhQAQeP9oi3SvLl1by76ztwy25wvfkwC4Hrm
O4KoeuDeoP5wrlIVkJv4BO9y0jJypQ+YjSxS3hvChtZjIaNygjzV+fRf9OqVeZV/
S0Lg7jgcDL6B1IMSbXp4uPTAkfciDjaNhgIXiK4jhSQZMLmJvKs0UQdIO09cTCUV
7qTx2IPP8A/VpXAJFLpw3NQGY50uLSYwR5DlOqn726rNF2xGgfLPcOqunQARAQAB
tCxTdWJsaW1lIEhRIFB0eSBMdGQgPHN1cHBvcnRAc3VibGltZXRleHQuY29tPokC
UQQTAQoAOxYhBOvHM7eKqzUtx3O/hX/i+hLPbjjyBQJpCXb2AhsDBQsJCAcCAiIC
BhUKCQgLAgQWAgMBAh4HAheAAAoJEH/i+hLPbjjy3HkQALMuihzYrqTzeM4j8FAN
V2y+cz/myqDsB+Z8Xnfj8/ZP7mA0hBOe87yV6lna2naaWm2p0uMFgpRc8qZ1Hgha
lBIfkprWBlukynFnUlGRzs449ayhieFQK7nHPz5R+d/1ZK1wVeHd7VWqR8vBw7G1
Lc/ZHZCYLVh2DA23UgFBIOosIUAaaa2c2Xv1y5ljEDiRWxq64vgInSzcHjAydaIo
joT5tEYLYLPbXPQ/jSywsDSszkVAdGVJShaQLMY3+w5z7cszhQlcK3IuPCTjIdG+
p7FGLimzmmjctkpGW1mHuoY58fPfcB7dTAo2sCkhh0+Vuml9ullI/8HVxQ/fGDV1
XrBXE56psff+F2LYKBxAQTjTGGJlIQ77LDOKzLs7cbwF040LNnTnpDsXFUnIK8p5
IPm+U+I8lAtudio5VZNj9NHiYyPfYXHRi2P85yDunoI5NxoxTcUFtjrozp2H23OF
jbY1kZVH/i94kaNkbikzFT42xw4XyMxg2QbtHn6376pJ3bRMNsE397UrZ4c9Cve5
wW7nALCu2N74dFiPJSeJK9XBieEvTS43sIWDaLpQgZlc6Msd/3sYnygc71LR5Zdm
/g98Eq55oyqp8tOTSyH2Hnyerk3Qg2juFNe1qUXbHdDRZbr3DKOj1FRAZDmiHCc/
AwZ5B2NwXZBg532zpvLNBlhp
=hSCO
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -1,5 +1,5 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.7 (GNU/Linux)
Version: BSN Pgp v1.1.0.0
mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT
LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV
@ -7,13 +7,13 @@ LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV
OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j
H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr
M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs
ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC
AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH
/32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe
MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy
7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV
KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ
XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+
NdCFTW7wY0Fb1fWJ+/KTsC4=
=J6gs
ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATQEEwEI
AB4FAlYxWIwCGwMGCwkIBwMCAxUIAwMWAgECHgECF4AACgkQ6z6Urb4SKc+P9gf/
diY2900wvWEgV7iMgrtGzx79W/PbwWiOkKoD9sdzhARXWiP8Q5teL/t5TUH6TZ3B
ENboDjwr705jLLPwuEDtPI9jz4kvdT86JwwG6N8gnWM8Ldi56SdJEtXrzwtlB/Fe
6tyfMT1E/PrJfgALUG9MWTIJkc0GhRJoyPpGZ6YWSLGXnk4c0HltYKDFR7q4wtI8
4cBu4mjZHZbxIO6r8Cci+xxuJkpOTIpr4pdpQKpECM6x5SaT2gVnscbN0PE19KK9
nPsBxyK4wW0AvAhed2qldBPTipgzPhqB2gu0jSryil95bKrSmlYJd1Y1XfNHno5D
xfn5JwgySBIdWWvtOI05gw==
=zPfd
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -27,7 +27,7 @@ evince gnome-terminal konsole xterm byobu make cmake
nautilus-extension-gnome-terminal
file-roller
# video codecs for grader gifs
libavcodec59
libavcodec61
# for drawing on screenshots
drawing
# documentation

View file

@ -41,17 +41,8 @@ if efibootmgr --quiet; then
efibootmgr --bootnum 0150 --delete-bootnum || true
efibootmgr --bootnum 0151 --delete-bootnum || true
# efibootmgr --create-only --bootnum 0150 --label "Removable media" --file-dev-path --loader '\EFI\BOOT\BOOTx64.EFI'
# efibootmgr --create-only --bootnum 0151 --label "Debian" --file-dev-path --loader '\EFI\debian\shimx64.efi'
# The version of efibootmgr in bookworm does not support the --file-dev-path
# argument, so here are commands that directly write to efivarfs.
# When upgrading from bookworm to trixie, remove the commands below and
# uncomment the commands above.
echo "BwAAAAEAAAA0AFIAZQBtAG8AdgBhAGIAbABlACAAbQBlAGQAaQBhAAAABAQwAFwARQBGAEkAXABCAE8ATwBUAFwAQgBPAE8AVAB4ADYANAAuAEUARgBJAAAAf/8EAA==" | \
base64 --decode - > /sys/firmware/efi/efivars/Boot0150-8be4df61-93ca-11d2-aa0d-00e098032b8c
echo "BwAAAAEAAAA4AEQAZQBiAGkAYQBuAAAABAQ0AFwARQBGAEkAXABkAGUAYgBpAGEAbgBcAHMAaABpAG0AeAA2ADQALgBlAGYAaQAAAH//BAA=" | \
base64 --decode - > /sys/firmware/efi/efivars/Boot0151-8be4df61-93ca-11d2-aa0d-00e098032b8c
efibootmgr --create-only --bootnum 0150 --label "Removable media" --file-dev-path --loader '\EFI\BOOT\BOOTx64.EFI'
efibootmgr --create-only --bootnum 0151 --label "Debian" --file-dev-path --loader '\EFI\debian\shimx64.efi'
efibootmgr --bootorder 0150,0151
fi

View file

@ -36,12 +36,12 @@ You can try replacing it with `--cap-add=sys_admin,mknod --security-opt apparmor
```bash
mkdir -p osbuild/build
sudo podman pull debian:bookworm
sudo podman run --rm -it --privileged --mount type=bind,source="$(pwd)",target=/work --workdir /work/osbuild/build debian:bookworm
sudo podman pull debian:trixie
sudo podman run --rm -it --privileged --mount type=bind,source="$(pwd)",target=/work --workdir /work/osbuild/build debian:trixie
# Alternative with Docker:
sudo docker pull debian:bookworm
sudo docker run --rm -it --privileged --mount type=bind,source="$(pwd)",target=/work --workdir /work/osbuild/build debian:bookworm
sudo docker pull debian:trixie
sudo docker run --rm -it --privileged --mount type=bind,source="$(pwd)",target=/work --workdir /work/osbuild/build debian:trixie
```
Inside the container, run the following commands.