Compare commits
23 Commits
571fb2ff99
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
6772afb845
|
|||
|
e9755d41c6
|
|||
|
68411d9459
|
|||
|
7fd5b790ff
|
|||
|
37bca1fdd1
|
|||
|
75ca09949c
|
|||
|
2fcdee5d81
|
|||
|
c01f797e79
|
|||
|
59a2bfa126
|
|||
|
e486bb28b0
|
|||
|
d33fd60ce4
|
|||
|
37428d922b
|
|||
|
b1cfe1e31b
|
|||
|
df2bc27f54
|
|||
|
77236af589
|
|||
|
f71d156ea8
|
|||
|
0c517e0957
|
|||
|
37620c76fe
|
|||
|
ac76b8c842
|
|||
|
df7c4cec83
|
|||
|
5b52e41496
|
|||
|
a60b40eeac
|
|||
|
b341f7f4fc
|
@@ -2,6 +2,7 @@
|
||||
f011c8d71ba09bd94ab04b8d771858b90a03fbf9
|
||||
3aff25b4486a143cd6282f8845c16216598e1c7e
|
||||
2204b12fadf27886058e6945806ce93a547f5278
|
||||
77236af5896524218605badcd3cdfc2267b213da
|
||||
|
||||
# host rename
|
||||
cfe4c43887a41e52be4e6472474c0fc3788f86e8
|
||||
|
||||
@@ -22,12 +22,16 @@
|
||||
];
|
||||
default = "systemd-boot";
|
||||
};
|
||||
|
||||
plymouth.enable = lib.mkEnableOption "plymouth boot splash";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
# request the largest framebuffer uefi offers; plymouth inherits it
|
||||
boot.loader.systemd-boot.consoleMode = "max";
|
||||
}
|
||||
|
||||
(lib.mkIf (cfg.mode == "systemd-boot") {
|
||||
@@ -41,6 +45,28 @@
|
||||
pkiBundle = "/var/lib/sbctl";
|
||||
};
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.plymouth.enable {
|
||||
# plymouth needs systemd-initrd to render the luks prompt cleanly
|
||||
boot.initrd.systemd.enable = true;
|
||||
|
||||
# host is responsible for early-KMS so plymouth lands on the gpu driver,
|
||||
# not simpledrm (e.g. hardware.amdgpu.initrd.enable on amd hosts)
|
||||
boot.plymouth.enable = true;
|
||||
stylix.targets.plymouth.logoAnimated = false;
|
||||
|
||||
boot.kernelParams = [
|
||||
"quiet"
|
||||
"splash"
|
||||
"loglevel=3"
|
||||
"rd.systemd.show_status=false"
|
||||
"rd.udev.log_level=3"
|
||||
"udev.log_priority=3"
|
||||
"plymouth.force-scale=1"
|
||||
];
|
||||
boot.consoleLogLevel = 0;
|
||||
boot.initrd.verbose = false;
|
||||
})
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,16 +9,18 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
inputs,
|
||||
osConfig,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = osConfig.features.claude;
|
||||
packages = inputs.self.outputs.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.packages = [
|
||||
pkgs.claude-code
|
||||
packages.claude-code
|
||||
pkgs.mcp-nixos
|
||||
];
|
||||
};
|
||||
|
||||
@@ -65,13 +65,18 @@
|
||||
|
||||
xdg.portal = {
|
||||
enable = true;
|
||||
xdgOpenUsePortal = true;
|
||||
extraPortals = with pkgs; [
|
||||
xdg-desktop-portal-wlr
|
||||
xdg-desktop-portal-gtk
|
||||
];
|
||||
};
|
||||
|
||||
# honor persist_mode so electron apps don't re-prompt for screencast every login
|
||||
systemd.user.services.xdg-desktop-portal-wlr.environment.XDPW_PERSIST_MODE = "permanent";
|
||||
|
||||
# enable ozone/wayland for electron apps so idle detection works
|
||||
environment.sessionVariables.NIXOS_OZONE_WL = "1";
|
||||
|
||||
fonts.packages = with pkgs; [
|
||||
font-awesome
|
||||
nerd-fonts.jetbrains-mono
|
||||
@@ -115,7 +120,7 @@
|
||||
bolt-launcher
|
||||
libnotify
|
||||
bibata-cursors
|
||||
vesktop
|
||||
discord
|
||||
rocketchat-desktop
|
||||
telegram-desktop
|
||||
slack
|
||||
@@ -127,12 +132,13 @@
|
||||
wl-mirror
|
||||
protonmail-bridge
|
||||
ledger-live-desktop
|
||||
imv
|
||||
yazi
|
||||
nemo
|
||||
file-roller
|
||||
libreoffice-still
|
||||
];
|
||||
|
||||
xdg.mime.defaultApplications = {
|
||||
"application/pdf" = "org.pwmt.zathura.desktop";
|
||||
};
|
||||
|
||||
# kindle udev rules for calibre
|
||||
features.udev.kindle.enable = lib.mkDefault true;
|
||||
})
|
||||
@@ -158,8 +164,110 @@
|
||||
cfg = osConfig.features.desktop;
|
||||
in
|
||||
{
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.file.".assets".source = inputs.assets;
|
||||
};
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
home.file.".assets".source = inputs.assets;
|
||||
}
|
||||
|
||||
(lib.mkIf cfg.apps.enable {
|
||||
# TODO:(@janezicmatej) consider moving nvim desktop entry to neovim feature
|
||||
xdg.desktopEntries.nvim = {
|
||||
name = "Neovim";
|
||||
exec = "ghostty -e nvim %F";
|
||||
terminal = false;
|
||||
mimeType = [
|
||||
"text/plain"
|
||||
"application/json"
|
||||
"text/markdown"
|
||||
];
|
||||
};
|
||||
|
||||
xdg.mimeApps = {
|
||||
enable = true;
|
||||
defaultApplications = {
|
||||
# text
|
||||
"text/plain" = "nvim.desktop";
|
||||
"application/json" = "nvim.desktop";
|
||||
"text/markdown" = "nvim.desktop";
|
||||
|
||||
# web
|
||||
"text/html" = "google-chrome.desktop";
|
||||
"application/xhtml+xml" = "google-chrome.desktop";
|
||||
"x-scheme-handler/http" = "google-chrome.desktop";
|
||||
"x-scheme-handler/https" = "google-chrome.desktop";
|
||||
"x-scheme-handler/ftp" = "google-chrome.desktop";
|
||||
"x-scheme-handler/about" = "google-chrome.desktop";
|
||||
"x-scheme-handler/unknown" = "google-chrome.desktop";
|
||||
|
||||
# mail and calendar
|
||||
"x-scheme-handler/mailto" = "thunderbird.desktop";
|
||||
"message/rfc822" = "thunderbird.desktop";
|
||||
"text/calendar" = "thunderbird.desktop";
|
||||
|
||||
# documents
|
||||
"application/pdf" = "org.pwmt.zathura.desktop";
|
||||
"application/postscript" = "org.pwmt.zathura.desktop";
|
||||
"image/vnd.djvu" = "org.pwmt.zathura.desktop";
|
||||
"application/epub+zip" = "org.pwmt.zathura.desktop";
|
||||
|
||||
# office
|
||||
"application/msword" = "libreoffice-writer.desktop";
|
||||
"application/vnd.ms-excel" = "libreoffice-calc.desktop";
|
||||
"application/vnd.ms-powerpoint" = "libreoffice-impress.desktop";
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" =
|
||||
"libreoffice-writer.desktop";
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" = "libreoffice-calc.desktop";
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation" =
|
||||
"libreoffice-impress.desktop";
|
||||
"application/vnd.oasis.opendocument.text" = "libreoffice-writer.desktop";
|
||||
"application/vnd.oasis.opendocument.spreadsheet" = "libreoffice-calc.desktop";
|
||||
"application/vnd.oasis.opendocument.presentation" = "libreoffice-impress.desktop";
|
||||
"text/csv" = "libreoffice-calc.desktop";
|
||||
|
||||
# images
|
||||
"image/png" = "imv-dir.desktop";
|
||||
"image/jpeg" = "imv-dir.desktop";
|
||||
"image/gif" = "imv-dir.desktop";
|
||||
"image/webp" = "imv-dir.desktop";
|
||||
"image/tiff" = "imv-dir.desktop";
|
||||
"image/bmp" = "imv-dir.desktop";
|
||||
"image/svg+xml" = "google-chrome.desktop";
|
||||
|
||||
# video
|
||||
"video/mp4" = "mpv.desktop";
|
||||
"video/x-matroska" = "mpv.desktop";
|
||||
"video/webm" = "mpv.desktop";
|
||||
"video/quicktime" = "mpv.desktop";
|
||||
"video/x-msvideo" = "mpv.desktop";
|
||||
|
||||
# audio
|
||||
"audio/mpeg" = "mpv.desktop";
|
||||
"audio/flac" = "mpv.desktop";
|
||||
"audio/ogg" = "mpv.desktop";
|
||||
"audio/wav" = "mpv.desktop";
|
||||
"audio/aac" = "mpv.desktop";
|
||||
|
||||
# archives
|
||||
"application/zip" = "org.gnome.FileRoller.desktop";
|
||||
"application/x-tar" = "org.gnome.FileRoller.desktop";
|
||||
"application/gzip" = "org.gnome.FileRoller.desktop";
|
||||
"application/x-rar-compressed" = "org.gnome.FileRoller.desktop";
|
||||
"application/x-7z-compressed" = "org.gnome.FileRoller.desktop";
|
||||
"application/x-bzip2" = "org.gnome.FileRoller.desktop";
|
||||
"application/x-xz" = "org.gnome.FileRoller.desktop";
|
||||
|
||||
# file manager
|
||||
"inode/directory" = "nemo.desktop";
|
||||
|
||||
# app deep links
|
||||
"x-scheme-handler/tg" = "org.telegram.desktop.desktop";
|
||||
"x-scheme-handler/discord" = "discord.desktop";
|
||||
"x-scheme-handler/slack" = "slack.desktop";
|
||||
};
|
||||
};
|
||||
})
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,9 +57,13 @@
|
||||
"ip=${mkIpString cfg.ip}"
|
||||
];
|
||||
|
||||
boot.initrd.systemd.enable = true;
|
||||
|
||||
# remote unlock may take a while; don't let device units give up
|
||||
boot.initrd.systemd.settings.Manager.DefaultDeviceTimeoutSec = "infinity";
|
||||
|
||||
boot.initrd.network = {
|
||||
enable = true;
|
||||
udhcpc.enable = !cfg.ip.enable;
|
||||
ssh = {
|
||||
enable = true;
|
||||
port = 22;
|
||||
@@ -69,10 +73,18 @@
|
||||
];
|
||||
inherit (cfg) authorizedKeys;
|
||||
};
|
||||
postCommands = ''
|
||||
echo 'cryptsetup-askpass' >> /root/.profile
|
||||
'';
|
||||
};
|
||||
|
||||
# systemd-networkd retries DHCP indefinitely, unlike udhcpc
|
||||
boot.initrd.systemd.network.networks = lib.mkIf (!cfg.ip.enable) {
|
||||
"10-initrd" = {
|
||||
matchConfig.Driver = cfg.networkModule;
|
||||
networkConfig.DHCP = "yes";
|
||||
};
|
||||
};
|
||||
|
||||
# forward LUKS password prompt to the SSH session
|
||||
boot.initrd.systemd.users.root.shell = "/bin/systemd-tty-ask-password-agent";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
programs.neovim = {
|
||||
enable = true;
|
||||
sideloadInitLua = true;
|
||||
vimAlias = true;
|
||||
defaultEditor = true;
|
||||
package = inputs.neovim-nightly-overlay.packages.${pkgs.stdenv.hostPlatform.system}.default;
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
"flakes"
|
||||
];
|
||||
download-buffer-size = 2 * 1024 * 1024 * 1024;
|
||||
download-attempts = 3;
|
||||
fallback = true;
|
||||
warn-dirty = false;
|
||||
substituters = [
|
||||
"https://cache.nixos.org"
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
extraSessionCommands = ''
|
||||
# fix for java awt apps not rendering
|
||||
export _JAVA_AWT_WM_NONREPARENTING=1
|
||||
# propagate XDG_DATA_DIRS to dbus/systemd for d-bus activated apps
|
||||
dbus-update-activation-environment --systemd XDG_DATA_DIRS
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -43,19 +43,15 @@
|
||||
config = lib.mkIf cfg.enable (
|
||||
lib.mkMerge [
|
||||
{
|
||||
services.qemuGuest.enable = true;
|
||||
services.spice-vdagentd.enable = lib.mkIf (!cfg.headless) true;
|
||||
|
||||
boot.kernelParams = lib.mkIf cfg.headless [ "console=ttyS0,115200" ];
|
||||
|
||||
# 9p autoloads on first mount
|
||||
boot.initrd.availableKernelModules = [
|
||||
"9p"
|
||||
"9pnet_virtio"
|
||||
];
|
||||
boot.kernelModules = [
|
||||
"9p"
|
||||
"9pnet_virtio"
|
||||
];
|
||||
|
||||
networking = {
|
||||
useDHCP = true;
|
||||
@@ -68,7 +64,6 @@
|
||||
curl
|
||||
wget
|
||||
htop
|
||||
sshfs
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
60
flake.lock
generated
60
flake.lock
generated
@@ -106,11 +106,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1773889306,
|
||||
"narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=",
|
||||
"lastModified": 1776613567,
|
||||
"narHash": "sha256-gC9Cp5ibBmGD5awCA9z7xy6MW6iJufhazTYJOiGlCUI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "5ad85c82cc52264f4beddc934ba57f3789f28347",
|
||||
"rev": "32f4236bfc141ae930b5ba2fb604f561fed5219d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -273,11 +273,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1776030105,
|
||||
"narHash": "sha256-b4cNpWPDSH+/CTTiw8++yGh1UYG2kQNrbIehV2iGoeo=",
|
||||
"lastModified": 1776777932,
|
||||
"narHash": "sha256-0R3Yow/NzSeVGUke5tL7CCkqmss4Vmi6BbV6idHzq/8=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "49088dc2e7a876e338e510c5f5f60f659819c650",
|
||||
"rev": "5d5640599a0050b994330328b9fd45709c909720",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -317,11 +317,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775952282,
|
||||
"narHash": "sha256-iJcGy0pW0wX7q6HAQuKx8sskTyu8an0l0gI3TBgzk3E=",
|
||||
"lastModified": 1776729909,
|
||||
"narHash": "sha256-wGu/N42PJqrj8ju9GoXdppg4rwaKzZqdAjsgxJbCvfY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "neovim-nightly-overlay",
|
||||
"rev": "f719e136a8e0cd91e70515e590385356abce1341",
|
||||
"rev": "ff21a18bde28b4c8ca0bc1f9a5b7186a1b89a3d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -333,11 +333,11 @@
|
||||
"neovim-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1775949028,
|
||||
"narHash": "sha256-JXrr9lxKfTIm/VW4jvaB1RU9r+7pAoaXeDsy24TGPiw=",
|
||||
"lastModified": 1776727374,
|
||||
"narHash": "sha256-iP5SviNXW5W+ay4ZmwjDFsfQjfM+fYlUxRlLPHjpwWI=",
|
||||
"owner": "neovim",
|
||||
"repo": "neovim",
|
||||
"rev": "4a289bfce3e71bf00d1eced168a6a7bbb270b95b",
|
||||
"rev": "901b3f0c394a53961781ebeee682e64ad690a242",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -364,11 +364,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1775888245,
|
||||
"narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=",
|
||||
"lastModified": 1776329215,
|
||||
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "13043924aaa7375ce482ebe2494338e058282925",
|
||||
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -395,11 +395,11 @@
|
||||
},
|
||||
"nixpkgs-master": {
|
||||
"locked": {
|
||||
"lastModified": 1776031281,
|
||||
"narHash": "sha256-MCXhNHfTvsvbdkn9WV3Rv5Z0tUig1CtINZV+jaWh04k=",
|
||||
"lastModified": 1776807375,
|
||||
"narHash": "sha256-LDnHG0T54OEHyRydmGUlAND8ham0KrRNWjgoS+6GUd4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4ee46f65286df51761a238bb0f024f8d696ac683",
|
||||
"rev": "553ecb1686a2edb75dee44c9f72e1674e6adc26a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -411,11 +411,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1775811116,
|
||||
"narHash": "sha256-t+HZK42pB6N+i5RGbuy7Xluez/VvWbembBdvzsc23Ss=",
|
||||
"lastModified": 1776560675,
|
||||
"narHash": "sha256-p68udKWWh7+V4ZPpcMDq0gTHWNZJnr4JPI+kHPPE40o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "54170c54449ea4d6725efd30d719c5e505f1c10e",
|
||||
"rev": "e07580dae39738e46609eaab8b154de2488133ce",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -427,11 +427,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1775710090,
|
||||
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
|
||||
"lastModified": 1776548001,
|
||||
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
|
||||
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -550,11 +550,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775971308,
|
||||
"narHash": "sha256-VKp9bhVSm0bT6JWctFy06ocqxGGnWHi1NfoE90IgIcY=",
|
||||
"lastModified": 1776771786,
|
||||
"narHash": "sha256-DRFGPfFV6hbrfO9a1PH1FkCi7qR5FgjSqsQGGvk1rdI=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "31ac5fe5d015f76b54058c69fcaebb66a55871a4",
|
||||
"rev": "bef289e2248991f7afeb95965c82fbcd8ff72598",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -583,11 +583,11 @@
|
||||
"tinted-zed": "tinted-zed"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775936757,
|
||||
"narHash": "sha256-KJO/7qoxJ+hlsb3WlFSl6IGrExBIf1GvKdrhOlnGdKY=",
|
||||
"lastModified": 1776170745,
|
||||
"narHash": "sha256-Tl1aZVP5EIlT+k0+iAKH018GLHJpLz3hhJ0LNQOWxCc=",
|
||||
"owner": "danth",
|
||||
"repo": "stylix",
|
||||
"rev": "d3e447786b74d62c75f665e17cb3e681c66e90c7",
|
||||
"rev": "e3861617645a43c9bbefde1aa6ac54dd0a44bfa9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
{ inputs, ... }:
|
||||
_:
|
||||
|
||||
{
|
||||
flake.overlays.default =
|
||||
_: prev:
|
||||
let
|
||||
pkgs-stable = import inputs.nixpkgs-stable {
|
||||
inherit (prev.stdenv.hostPlatform) system;
|
||||
inherit (prev) config;
|
||||
};
|
||||
pkgs-master = import inputs.nixpkgs-master {
|
||||
inherit (prev.stdenv.hostPlatform) system;
|
||||
inherit (prev) config;
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit (pkgs-master) claude-code;
|
||||
};
|
||||
flake.overlays.default = _: _: { };
|
||||
}
|
||||
|
||||
@@ -13,19 +13,38 @@
|
||||
documentation.enable = false;
|
||||
environment.defaultPackages = [ ];
|
||||
|
||||
# compressed qcow2, no channel copy
|
||||
# qcow2, no channel copy; post-processed with parallel zstd on qcow2 v3
|
||||
# (~half the size of zlib v2, faster decompress)
|
||||
image.modules.qemu =
|
||||
{ config, modulesPath, ... }:
|
||||
{
|
||||
system.build.image = lib.mkForce (
|
||||
import (modulesPath + "/../lib/make-disk-image.nix") {
|
||||
inherit lib config pkgs;
|
||||
inherit (config.virtualisation) diskSize;
|
||||
let
|
||||
rawImage = import (modulesPath + "/../lib/make-disk-image.nix") {
|
||||
inherit lib config pkgs;
|
||||
inherit (config.virtualisation) diskSize;
|
||||
inherit (config.image) baseName;
|
||||
format = "qcow2";
|
||||
copyChannel = false;
|
||||
partitionTableType = "legacy";
|
||||
};
|
||||
inherit (config.image) baseName;
|
||||
format = "qcow2-compressed";
|
||||
copyChannel = false;
|
||||
partitionTableType = "legacy";
|
||||
}
|
||||
in
|
||||
pkgs.runCommand baseName { nativeBuildInputs = [ pkgs.qemu-utils ]; } ''
|
||||
mkdir -p $out
|
||||
# qemu-img caps -m at 16
|
||||
cores="''${NIX_BUILD_CORES:-4}"
|
||||
[ "$cores" -gt 0 ] || cores=4
|
||||
[ "$cores" -gt 16 ] && cores=16
|
||||
qemu-img convert \
|
||||
-f qcow2 \
|
||||
-O qcow2 \
|
||||
-c \
|
||||
-o compression_type=zstd \
|
||||
-m "$cores" \
|
||||
${rawImage}/${baseName}.qcow2 \
|
||||
$out/${baseName}.qcow2
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
@@ -70,7 +89,7 @@
|
||||
features.neovim.dotfiles = inputs.nvim;
|
||||
|
||||
# ensure .config exists with correct ownership before automount
|
||||
systemd.tmpfiles.rules = [ "d /home/matej/.config 0755 matej users -" ];
|
||||
systemd.tmpfiles.rules = [ "d /home/matej/.config 0700 matej users -" ];
|
||||
|
||||
# TODO:(@janezicmatej) replace ssh with virtio-console (hvc0) when qemu 11.0 lands
|
||||
# https://www.mail-archive.com/qemu-devel@nongnu.org/msg1162844.html
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
inputs.nixos-hardware.nixosModules.framework-16-amd-ai-300-series
|
||||
];
|
||||
|
||||
features.bootloader.plymouth.enable = true;
|
||||
features.desktop.bluetooth.enable = true;
|
||||
features.gnupg.yubikey.enable = true;
|
||||
features.udev = {
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
{
|
||||
features.nix-settings.towerCache.enable = false;
|
||||
features.bootloader.mode = "lanzaboote";
|
||||
features.bootloader = {
|
||||
mode = "lanzaboote";
|
||||
plymouth.enable = true;
|
||||
};
|
||||
features.desktop.bluetooth.enable = true;
|
||||
features.gnupg.yubikey.enable = true;
|
||||
features.udev = {
|
||||
@@ -23,6 +26,8 @@
|
||||
nix.settings.secret-key-files = [ config.sops.secrets.nix-signing-key.path ];
|
||||
|
||||
boot.kernelParams = [ "btusb.reset=1" ];
|
||||
# early kms so plymouth lands on amdgpu, not simpledrm
|
||||
hardware.amdgpu.initrd.enable = true;
|
||||
|
||||
services.udisks2.enable = true;
|
||||
|
||||
|
||||
91
packages/claude-code/package.nix
Normal file
91
packages/claude-code/package.nix
Normal file
@@ -0,0 +1,91 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (pkgs) stdenv lib;
|
||||
version = "2.1.116";
|
||||
|
||||
# upstream ships platform-native binaries as separate npm packages under
|
||||
# @anthropic-ai/claude-code-<platform>; the wrapper package is just a
|
||||
# postinstall shim that copies the matching one into place
|
||||
sources = {
|
||||
"x86_64-linux" = {
|
||||
slug = "linux-x64";
|
||||
hash = "sha256-QEjJ4CRk35TubDNW02Dzcu+EMRLLndJUXJeP3BFT3b8=";
|
||||
};
|
||||
"aarch64-linux" = {
|
||||
slug = "linux-arm64";
|
||||
hash = "sha256-/Hqp8GQx8Hub8K4w0Fnx/AksksY61vRC44XxrJVwF5w=";
|
||||
};
|
||||
"x86_64-darwin" = {
|
||||
slug = "darwin-x64";
|
||||
hash = "sha256-O3J/ew2fWbUQePs6tHEhK0Q9E3Mx/BDSL7b7NL3FRc8=";
|
||||
};
|
||||
"aarch64-darwin" = {
|
||||
slug = "darwin-arm64";
|
||||
hash = "sha256-O41sf7b05SJfXVjszMeTp838mja+PgZ+aEKykLsHeNo=";
|
||||
};
|
||||
};
|
||||
|
||||
source =
|
||||
sources.${stdenv.hostPlatform.system}
|
||||
or (throw "claude-code: unsupported system ${stdenv.hostPlatform.system}");
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "claude-code";
|
||||
inherit version;
|
||||
|
||||
src = pkgs.fetchzip {
|
||||
url = "https://registry.npmjs.org/@anthropic-ai/claude-code-${source.slug}/-/claude-code-${source.slug}-${version}.tgz";
|
||||
inherit (source) hash;
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.makeWrapper
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [ pkgs.patchelf ];
|
||||
|
||||
dontBuild = true;
|
||||
dontConfigure = true;
|
||||
dontStrip = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
install -Dm755 claude $out/bin/claude
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
# NOTE:(@janezicmatej) upstream is a bun single-file-executable; the
|
||||
# embedded script payload sits at the tail of the ELF, so autoPatchelfHook's
|
||||
# section-layout changes corrupt it — only the interpreter can be rewritten
|
||||
postFixup =
|
||||
lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
patchelf --set-interpreter ${stdenv.cc.bintools.dynamicLinker} $out/bin/claude
|
||||
''
|
||||
+ ''
|
||||
wrapProgram $out/bin/claude \
|
||||
--set DISABLE_AUTOUPDATER 1 \
|
||||
--set-default FORCE_AUTOUPDATE_PLUGINS 1 \
|
||||
--set DISABLE_INSTALLATION_CHECKS 1 \
|
||||
--unset DEV \
|
||||
--prefix PATH : ${
|
||||
lib.makeBinPath (
|
||||
[
|
||||
pkgs.procps
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
pkgs.bubblewrap
|
||||
pkgs.socat
|
||||
]
|
||||
)
|
||||
}
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "Agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster";
|
||||
homepage = "https://github.com/anthropics/claude-code";
|
||||
downloadPage = "https://www.npmjs.com/package/@anthropic-ai/claude-code";
|
||||
license = lib.licenses.unfree;
|
||||
mainProgram = "claude";
|
||||
platforms = lib.attrNames sources;
|
||||
};
|
||||
}
|
||||
53
packages/claude-code/update.sh
Executable file
53
packages/claude-code/update.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p curl jq nix
|
||||
# shellcheck shell=bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PKG_FILE="$SCRIPT_DIR/package.nix"
|
||||
|
||||
# keep in sync with the `sources` attrset in package.nix
|
||||
PLATFORMS=(linux-x64 linux-arm64 darwin-x64 darwin-arm64)
|
||||
|
||||
prefetch() {
|
||||
local url="$1"
|
||||
nix --extra-experimental-features 'nix-command flakes' \
|
||||
store prefetch-file --unpack --json "$url" 2>/dev/null | jq -r '.hash'
|
||||
}
|
||||
|
||||
main() {
|
||||
echo "fetching latest version from npm..."
|
||||
local latest current
|
||||
latest=$(curl -sf "https://registry.npmjs.org/@anthropic-ai/claude-code/latest" | jq -r '.version')
|
||||
current=$(grep 'version = ' "$PKG_FILE" | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
|
||||
if [[ "$current" == "$latest" ]]; then
|
||||
echo "claude-code already at $latest"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "updating claude-code: $current -> $latest"
|
||||
|
||||
sed -i "s|version = \"$current\"|version = \"$latest\"|" "$PKG_FILE"
|
||||
|
||||
local slug url new_hash old_hash
|
||||
for slug in "${PLATFORMS[@]}"; do
|
||||
url="https://registry.npmjs.org/@anthropic-ai/claude-code-${slug}/-/claude-code-${slug}-${latest}.tgz"
|
||||
echo " prefetching $slug..."
|
||||
new_hash=$(prefetch "$url")
|
||||
old_hash=$(awk -v slug="$slug" '
|
||||
$0 ~ "slug = \"" slug "\";" { found=1; next }
|
||||
found && /hash = "sha256-/ {
|
||||
match($0, /sha256-[A-Za-z0-9+\/]+=*/)
|
||||
print substr($0, RSTART, RLENGTH)
|
||||
exit
|
||||
}
|
||||
' "$PKG_FILE")
|
||||
sed -i "s|$old_hash|$new_hash|" "$PKG_FILE"
|
||||
echo " $new_hash"
|
||||
done
|
||||
|
||||
echo "claude-code updated to $latest"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -27,14 +27,32 @@ info() {
|
||||
|
||||
# globals for cleanup trap
|
||||
CLEANUP_OVERLAY=""
|
||||
CLEANUP_TMPDIR=""
|
||||
QEMU_PID=""
|
||||
VM_READY=false
|
||||
cleanup() {
|
||||
[ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null && wait "$QEMU_PID" 2>/dev/null
|
||||
[ -n "$CLEANUP_OVERLAY" ] && rm -rf "$CLEANUP_OVERLAY"
|
||||
# preserve tmpdir on abnormal exit so the qemu log survives for inspection
|
||||
if [ -n "$CLEANUP_TMPDIR" ]; then
|
||||
if [ "$VM_READY" = true ]; then
|
||||
rm -rf "$CLEANUP_TMPDIR"
|
||||
else
|
||||
echo "qemu log preserved: $CLEANUP_TMPDIR/qemu.log" >&2
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# returns 0 once the guest's sshd is speaking (first bytes are "SSH-")
|
||||
awaiting_ssh_banner() {
|
||||
local port="$1"
|
||||
local banner
|
||||
banner=$(timeout 2 bash -c "exec 3<>/dev/tcp/localhost/$port; head -c 4 <&3" 2>/dev/null) || return 1
|
||||
[ "$banner" = "SSH-" ]
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: ephvm-run.sh [options]
|
||||
@@ -55,6 +73,8 @@ EOF
|
||||
main() {
|
||||
setup_colors
|
||||
|
||||
[ "$EUID" -eq 0 ] && die "ephvm-run.sh must not run as root"
|
||||
|
||||
local ssh_port="" memory=4G cpus=2 claude=true disk_size="" serial=false
|
||||
local -a mounts=()
|
||||
|
||||
@@ -110,15 +130,13 @@ main() {
|
||||
CLEANUP_OVERLAY=$(mktemp -d)
|
||||
local overlay="$CLEANUP_OVERLAY/overlay.qcow2"
|
||||
qemu-img create -f qcow2 -b "$(realpath "$image")" -F qcow2 "$overlay" "$disk_size"
|
||||
drive_arg="file=$overlay,format=qcow2"
|
||||
drive_arg="if=none,id=hd0,file=$overlay,format=qcow2,cache=writeback,aio=threads,discard=unmap,detect-zeroes=unmap"
|
||||
else
|
||||
drive_arg="file=$image,format=qcow2,snapshot=on"
|
||||
drive_arg="if=none,id=hd0,file=$image,format=qcow2,snapshot=on,cache=writeback,aio=threads,discard=unmap,detect-zeroes=unmap"
|
||||
fi
|
||||
|
||||
command -v qemu-system-x86_64 &>/dev/null || die "qemu-system-x86_64 not found"
|
||||
|
||||
local accel="tcg"
|
||||
[ -r /dev/kvm ] && accel="kvm"
|
||||
[ -r /dev/kvm ] || die "/dev/kvm not readable; kvm is required"
|
||||
|
||||
# auto-allocate ssh port unless serial mode
|
||||
if [ "$serial" = false ] && [ -z "$ssh_port" ]; then
|
||||
@@ -128,28 +146,33 @@ main() {
|
||||
done
|
||||
fi
|
||||
|
||||
local nic_arg="user"
|
||||
local nic_arg="user,model=virtio-net-pci"
|
||||
if [ -n "$ssh_port" ]; then
|
||||
nic_arg="user,hostfwd=tcp::${ssh_port}-:22"
|
||||
nic_arg="user,model=virtio-net-pci,hostfwd=tcp:127.0.0.1:${ssh_port}-:22"
|
||||
fi
|
||||
|
||||
local -a qemu_args=(
|
||||
qemu-system-x86_64
|
||||
-accel "$accel"
|
||||
-accel kvm
|
||||
-cpu host
|
||||
-m "$memory"
|
||||
-smp "$cpus"
|
||||
-drive "$drive_arg"
|
||||
-device "virtio-blk-pci,drive=hd0"
|
||||
-device virtio-rng-pci
|
||||
-nic "$nic_arg"
|
||||
-nographic
|
||||
-sandbox "on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny"
|
||||
)
|
||||
|
||||
if [ "$accel" != "tcg" ]; then
|
||||
qemu_args+=(-cpu host)
|
||||
fi
|
||||
|
||||
local fs_id=0 mount_path name tag
|
||||
for mount_path in "${mounts[@]}"; do
|
||||
[ -e "$mount_path" ] || die "--mount path does not exist: $mount_path"
|
||||
mount_path=$(realpath "$mount_path")
|
||||
# qemu parses -virtfs as csv, a comma in the path would inject options
|
||||
case "$mount_path" in
|
||||
*,*) die "--mount path may not contain commas: $mount_path" ;;
|
||||
esac
|
||||
name=$(basename "$mount_path")
|
||||
tag="m_${name:0:29}"
|
||||
qemu_args+=(
|
||||
@@ -163,6 +186,9 @@ main() {
|
||||
mkdir -p "$CLAUDE_CONFIG_DIR"
|
||||
local claude_dir
|
||||
claude_dir=$(realpath "$CLAUDE_CONFIG_DIR")
|
||||
case "$claude_dir" in
|
||||
*,*) die "claude config dir may not contain commas: $claude_dir" ;;
|
||||
esac
|
||||
|
||||
qemu_args+=(
|
||||
-virtfs "local,path=$claude_dir,mount_tag=claude,security_model=none,id=fs${fs_id}"
|
||||
@@ -171,27 +197,38 @@ main() {
|
||||
fi
|
||||
|
||||
info "---"
|
||||
info "Accel: $accel"
|
||||
[ -n "$ssh_port" ] && info "SSH: ssh -p $ssh_port matej@localhost"
|
||||
info "---"
|
||||
|
||||
if [ "$serial" = true ]; then
|
||||
exec "${qemu_args[@]}"
|
||||
fi
|
||||
|
||||
CLEANUP_TMPDIR=$(mktemp -d)
|
||||
local qemu_log="$CLEANUP_TMPDIR/qemu.log"
|
||||
|
||||
# start qemu in background and auto-ssh
|
||||
"${qemu_args[@]}" &>/dev/null &
|
||||
"${qemu_args[@]}" &>"$qemu_log" &
|
||||
QEMU_PID=$!
|
||||
|
||||
# throwaway ssh key (vm accepts any key via AuthorizedKeysCommand)
|
||||
local ssh_key="$CLEANUP_TMPDIR/id_ed25519"
|
||||
ssh-keygen -t ed25519 -f "$ssh_key" -N "" -q
|
||||
|
||||
info "waiting for vm (port $ssh_port)..."
|
||||
local attempts=0
|
||||
while ! (echo > /dev/tcp/localhost/"$ssh_port") 2>/dev/null; do
|
||||
# poll for the real SSH banner, not TCP accept: qemu's user-mode nic
|
||||
# accepts host-side the moment qemu starts, well before guest sshd is up
|
||||
while ! awaiting_ssh_banner "$ssh_port"; do
|
||||
attempts=$((attempts + 1))
|
||||
[ $attempts -gt 60 ] && die "vm did not become ready in 60s"
|
||||
[ $attempts -gt 120 ] && die "vm did not become ready in 60s"
|
||||
kill -0 "$QEMU_PID" 2>/dev/null || die "qemu exited unexpectedly"
|
||||
sleep 1
|
||||
sleep 0.5
|
||||
done
|
||||
VM_READY=true
|
||||
|
||||
ssh -p "$ssh_port" -t \
|
||||
-i "$ssh_key" \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o UserKnownHostsFile=/dev/null \
|
||||
-o LogLevel=ERROR \
|
||||
|
||||
Reference in New Issue
Block a user