Compare commits

...

11 Commits

16 changed files with 190 additions and 365 deletions

View File

@@ -13,7 +13,7 @@ let
deployment = { deployment = {
targetHost = self.nixosConfigurations.${hostname}.config.ssh.publicHostname; targetHost = self.nixosConfigurations.${hostname}.config.ssh.publicHostname;
targetUser = self.nixosConfigurations.${hostname}.config.ssh.username; targetUser = self.nixosConfigurations.${hostname}.config.ssh.username;
buildOnTarget = builtins.any (t: t != "local") tags; buildOnTarget = builtins.any (t: t != "local" && t != "arm") tags;
inherit tags; inherit tags;
}; };
}; };
@@ -22,15 +22,16 @@ let
hostname: mkNode hostname (utils.hostMeta ../hosts/${hostname}).deployment.tags hostname: mkNode hostname (utils.hostMeta ../hosts/${hostname}).deployment.tags
); );
in in
inputs.colmena.lib.makeHive { inputs.colmena.lib.makeHive (
meta = { {
nixpkgs = import inputs.nixpkgs { meta = {
localSystem = "x86_64-linux"; nixpkgs = import inputs.nixpkgs {
localSystem = "x86_64-linux";
};
nodeNixpkgs = builtins.mapAttrs (_: v: v.pkgs) self.nixosConfigurations;
nodeSpecialArgs = builtins.mapAttrs (_: v: v._module.specialArgs or { }) self.nixosConfigurations;
}; };
}
nodeNixpkgs = builtins.mapAttrs (_: v: v.pkgs) self.nixosConfigurations; // nodes
nodeSpecialArgs = builtins.mapAttrs (_: v: v._module.specialArgs or { }) self.nixosConfigurations; )
};
inherit nodes;
}

View File

@@ -1,221 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
readonly ALLOWED_MAIN_BRANCHES=("main" "master" "develop")
readonly BRANCH_TYPES=(
"feat For new features"
"hotfix For urgent fixes"
"fix For fixes"
"release For preparing releases"
"chore For non-code tasks"
)
error() {
echo "Error: $1" >&2
exit 1
}
warn() {
echo "Warning: $1" >&2
}
check_dependencies() {
local missing=()
for cmd in git fzf; do
if ! command -v "$cmd" &> /dev/null; then
missing+=("$cmd")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
error "Missing required commands: ${missing[*]}"
fi
}
check_git_repo() {
if ! git rev-parse --git-dir &> /dev/null; then
error "Not in a git repository"
fi
}
check_current_branch() {
local current_branch
current_branch=$(git branch --show-current)
local is_main_branch=false
for branch in "${ALLOWED_MAIN_BRANCHES[@]}"; do
if [[ "$current_branch" == "$branch" ]]; then
is_main_branch=true
break
fi
done
if [[ "$is_main_branch" == false ]]; then
warn "Not branching from a main branch (current: $current_branch)"
read -rp "Continue anyway? [y/N] " response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
exit 0
fi
fi
}
get_user_email() {
local email
email=$(git config --get user.email 2>/dev/null)
if [[ -z "$email" ]]; then
error "Git user email not configured. Run: git config user.email 'your@email.com'"
fi
echo "$email"
}
select_branch_type() {
local selected
selected=$(printf '%s\n' "${BRANCH_TYPES[@]}" | \
fzf --prompt="Select branch type: " \
--height=40% \
--border \
--info=inline) || error "Branch type selection cancelled"
echo "${selected%% *}"
}
select_jira_ticket() {
local email=$1
if ! command -v jira &> /dev/null; then
warn "Jira CLI not found. Proceeding without ticket ID."
return 0
fi
echo "Fetching Jira tickets for $email..." >&2
local jira_data
jira_data=$(jira issue list --assignee="$email" --order-by=priority --plain --no-headers 2>/dev/null) || {
warn "Could not fetch Jira tickets. Proceeding without ticket ID."
return 0
}
if [[ -z "$jira_data" ]]; then
warn "No Jira tickets found. Proceeding without ticket ID."
return 0
fi
echo "$jira_data" >&2
echo "" >&2
local formatted_tickets
formatted_tickets=$(echo "$jira_data" | awk '{
ticket_id = $2
$1 = $2 = ""
description = $0
gsub(/^[ \t]+/, "", description)
if (length(description) > 60) {
description = substr(description, 1, 57) "..."
}
print ticket_id " - " description
}')
if [[ -z "$formatted_tickets" ]]; then
warn "No tickets to display. Proceeding without ticket ID."
return 0
fi
local selected_ticket
selected_ticket=$(echo -e "SKIP - Create branch without ticket ID\n$formatted_tickets" | \
fzf --prompt="Select Jira ticket (or skip): " \
--height=40% \
--border \
--info=inline) || error "Ticket selection cancelled"
if [[ "$selected_ticket" != "SKIP"* ]]; then
echo "${selected_ticket%% -*}"
fi
}
get_branch_description() {
local ticket_id=$1
local editor="${EDITOR:-vi}"
local tmpfile
tmpfile=$(mktemp)
trap "rm -f '$tmpfile'" EXIT
if [[ -n "$ticket_id" ]]; then
cat > "$tmpfile" << EOF
# Selected ticket: $ticket_id
# Enter your branch description below in kebab-case (e.g., my-description):
# The ticket ID will be automatically included in the branch name.
# Lines starting with # will be ignored.
EOF
else
cat > "$tmpfile" << 'EOF'
# Enter your branch description below in kebab-case (e.g., my-description):
# Lines starting with # will be ignored.
EOF
fi
"$editor" "$tmpfile" < /dev/tty > /dev/tty
local desc
desc=$(grep -v '^#' "$tmpfile" | tr -d '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo "$desc"
}
validate_description() {
local desc=$1
if [[ -z "$desc" ]]; then
error "No description provided"
fi
if [[ ! "$desc" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
error "Invalid branch description format.\nUse lowercase letters, numbers, and hyphens only.\nNo trailing or consecutive hyphens allowed.\nExample: my-feature-description"
fi
}
create_branch() {
local type=$1
local ticket_id=$2
local desc=$3
local branch
if [[ -n "$ticket_id" ]]; then
branch="$type/$ticket_id-$desc"
else
branch="$type/$desc"
fi
if git show-ref --verify --quiet "refs/heads/$branch"; then
error "Branch '$branch' already exists"
fi
echo ""
echo "Creating branch: $branch"
git checkout -b "$branch"
}
main() {
check_dependencies
check_git_repo
check_current_branch
local email
email=$(get_user_email)
local type
type=$(select_branch_type)
echo "About to call select_jira_ticket" >&2
local ticket_id=""
ticket_id=$(select_jira_ticket "$email")
local desc
desc=$(get_branch_description "$ticket_id")
validate_description "$desc"
create_branch "$type" "$ticket_id" "$desc"
}
main "$@"

View File

@@ -42,11 +42,11 @@
}, },
"nixCats": { "nixCats": {
"locked": { "locked": {
"lastModified": 1770584904, "lastModified": 1774835836,
"narHash": "sha256-9Zaz8lbKF2W9pwXZEnbiGsicHdBoU+dHt3Wv3mCJoZ8=", "narHash": "sha256-6ok7iv/9R82vl6MYe3Lwyyb6S5bmW2PxEZtmjzlqyPs=",
"owner": "BirdeeHub", "owner": "BirdeeHub",
"repo": "nixCats-nvim", "repo": "nixCats-nvim",
"rev": "538fdde784d2909700d97a8ef307783b33a86fb1", "rev": "ebb9f279a55ca60ff4e37e4accf6518dc627aa8d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -73,11 +73,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1770843696, "lastModified": 1775608838,
"narHash": "sha256-LovWTGDwXhkfCOmbgLVA10bvsi/P8eDDpRudgk68HA8=", "narHash": "sha256-2ySoGH+SAi34U0PeuQgABC0WiH9LQ3tkyHTiE93KUeg=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "2343bbb58f99267223bc2aac4fc9ea301a155a16", "rev": "9a01fad67a57e44e1b3e1d905c6881bcfb209e8a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -106,11 +106,11 @@
"plugins-helm-ls-nvim": { "plugins-helm-ls-nvim": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1768584652, "lastModified": 1773934114,
"narHash": "sha256-jnMc87OjURNcqsva0npYgVyUrWc5C6L7yHpNvt9eSmg=", "narHash": "sha256-8trqFsA7nTKSdtkiAL0Sa9bXjh5ONtAqN7XNE/B8ukM=",
"owner": "qvalentin", "owner": "qvalentin",
"repo": "helm-ls.nvim", "repo": "helm-ls.nvim",
"rev": "f0b9a1723890971a6d84890b50dbf5f40974ea1b", "rev": "20df43509b02a3ce3c6b3eee254d6e2bffa9a370",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -78,8 +78,6 @@
mcp-hub mcp-hub
nixd nixd
nixfmt nixfmt
nodePackages.prettier
nodePackages.typescript-language-server
ormolu ormolu
prettierd prettierd
rust-analyzer rust-analyzer
@@ -88,6 +86,7 @@
stylelint stylelint
stylua stylua
tree-sitter tree-sitter
typescript-language-server
vscode-langservers-extracted vscode-langservers-extracted
vtsls vtsls
yaml-language-server yaml-language-server

View File

@@ -70,9 +70,6 @@
}; };
in in
{ {
nix.nixPath = [
"nixpkgs=${inputs.nixpkgs}"
]; # <https://github.com/nix-community/nixd/blob/main/nixd/docs/configuration.md>
nixosConfigurations = nixosConfigurations =
(lib.genAttrs hostDirNames ( (lib.genAttrs hostDirNames (
host: host:

View File

@@ -0,0 +1,71 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.ai-tools.claude-code;
rtk-version = "0.18.1";
in
{
options.ai-tools.claude-code = {
enable = lib.mkEnableOption "claude code with rtk and ccline";
};
config = lib.mkIf cfg.enable {
home.packages = with pkgs; [
claude-code
(pkgs.stdenv.mkDerivation {
name = "ccline";
src = pkgs.fetchurl {
url = "https://github.com/Haleclipse/CCometixLine/releases/download/v1.0.8/ccline-linux-x64.tar.gz";
hash = "sha256-Joe3Dd6uSMGi66QT6xr2oY/Tz8rA5RuKa6ckBVJIzI0=";
};
unpackPhase = ''
tar xzf $src
'';
installPhase = ''
mkdir -p $out/bin
cp ccline $out/bin/
chmod +x $out/bin/ccline
'';
meta = with pkgs.lib; {
description = "CCometixLine Linux x64 CLI (Claude Code statusline)";
homepage = "https://github.com/Haleclipse/CCometixLine";
license = licenses.mit;
platforms = [ "x86_64-linux" ];
};
})
(pkgs.stdenv.mkDerivation {
name = "rtk-${rtk-version}";
version = rtk-version;
src = pkgs.fetchurl {
url = "https://github.com/rtk-ai/rtk/releases/download/v${rtk-version}/rtk-x86_64-unknown-linux-gnu.tar.gz";
hash = "sha256-XoTia5K8b00OzcKYCufwx8ApkAS31DxUCpGSU0jFs2Q=";
};
unpackPhase = ''
tar xzf $src
'';
installPhase = ''
mkdir -p $out/bin
cp rtk $out/bin/
chmod +x $out/bin/rtk
'';
meta = with pkgs.lib; {
description = "RTK - AI coding tool enhancer";
homepage = "https://www.rtk-ai.app";
license = licenses.mit;
platforms = [ "x86_64-linux" ];
};
})
mcp-nixos
];
};
}

View File

@@ -1,116 +1,7 @@
{ {
lib, imports = [
config, ./claude-code.nix
pkgs, ./opencode.nix
... ./tirith.nix
}:
let
cfg = config.ai-tools;
rtk-version = "0.18.1";
in
{
options.ai-tools = {
claude-code.enable = lib.mkEnableOption "claude code with rtk and ccline";
tirith.enable = lib.mkEnableOption "tirith shell security guard";
opencode.enable = lib.mkEnableOption "opencode";
};
config = lib.mkMerge [
(lib.mkIf cfg.claude-code.enable {
home.packages = with pkgs; [
claude-code
(pkgs.stdenv.mkDerivation {
name = "ccline";
src = pkgs.fetchurl {
url = "https://github.com/Haleclipse/CCometixLine/releases/download/v1.0.8/ccline-linux-x64.tar.gz";
hash = "sha256-Joe3Dd6uSMGi66QT6xr2oY/Tz8rA5RuKa6ckBVJIzI0=";
};
unpackPhase = ''
tar xzf $src
'';
installPhase = ''
mkdir -p $out/bin
cp ccline $out/bin/
chmod +x $out/bin/ccline
'';
meta = with pkgs.lib; {
description = "CCometixLine Linux x64 CLI (Claude Code statusline)";
homepage = "https://github.com/Haleclipse/CCometixLine";
license = licenses.mit;
platforms = [ "x86_64-linux" ];
};
})
(pkgs.stdenv.mkDerivation {
name = "rtk-${rtk-version}";
version = rtk-version;
src = pkgs.fetchurl {
url = "https://github.com/rtk-ai/rtk/releases/download/v${rtk-version}/rtk-x86_64-unknown-linux-gnu.tar.gz";
hash = "sha256-XoTia5K8b00OzcKYCufwx8ApkAS31DxUCpGSU0jFs2Q=";
};
unpackPhase = ''
tar xzf $src
'';
installPhase = ''
mkdir -p $out/bin
cp rtk $out/bin/
chmod +x $out/bin/rtk
'';
meta = with pkgs.lib; {
description = "RTK - AI coding tool enhancer";
homepage = "https://www.rtk-ai.app";
license = licenses.mit;
platforms = [ "x86_64-linux" ];
};
})
mcp-nixos
];
})
(lib.mkIf cfg.tirith.enable {
home.packages = with pkgs; [
tirith
];
})
(lib.mkIf (cfg.tirith.enable && cfg.claude-code.enable) {
home.file.".claude/hooks/tirith-check.py" = {
source = ./tirith-check.py;
executable = true;
};
home.activation.tirith-claude-code = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
${pkgs.tirith}/bin/tirith setup claude-code --with-mcp --scope user --force 2>/dev/null || true
'';
})
(lib.mkIf cfg.opencode.enable {
home.packages = with pkgs; [
opencode
];
home.file.".config/opencode/opencode.json".text = builtins.toJSON {
"$schema" = "https://opencode.ai/config.json";
permission = {
external_directory = {
"/run/secrets/" = "deny";
"~/.config/sops/age/keys.txt" = "deny";
"~/.ssh/id_rsa" = "deny";
"~/.ssh/id_ed25519" = "deny";
"~/.ssh/id_ecdsa" = "deny";
"~/.ssh/id_dsa" = "deny";
"/etc/ssh/ssh_host_rsa_key" = "deny";
"/etc/ssh/ssh_host_ed25519_key" = "deny";
"/etc/ssh/ssh_host_ecdsa_key" = "deny";
"/etc/ssh/ssh_host_dsa_key" = "deny";
};
command = {
sops = "deny";
};
};
plugin = [ "@mohak34/opencode-notifier@latest" ];
};
})
]; ];
} }

View File

@@ -0,0 +1,23 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.ai-tools.opencode;
in
{
options.ai-tools.opencode = {
enable = lib.mkEnableOption "opencode";
};
config = lib.mkIf cfg.enable {
home.packages = with pkgs; [
opencode
];
home.file.".config/opencode/opencode.json".text = builtins.toJSON {
plugin = [ "@mohak34/opencode-notifier@latest" ];
};
};
}

View File

@@ -0,0 +1,32 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.ai-tools;
in
{
options.ai-tools.tirith = {
enable = lib.mkEnableOption "tirith shell security guard";
};
config = lib.mkMerge [
(lib.mkIf cfg.tirith.enable {
home.packages = with pkgs; [
tirith
];
})
(lib.mkIf (cfg.tirith.enable && cfg.claude-code.enable) {
home.file.".claude/hooks/tirith-check.py" = {
source = ./tirith-check.py;
executable = true;
};
home.activation.tirith-claude-code = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
${pkgs.tirith}/bin/tirith setup claude-code --with-mcp --scope user --force 2>/dev/null || true
'';
})
];
}

View File

@@ -39,6 +39,10 @@ in
// { // {
"*" = { "*" = {
addKeysToAgent = "yes"; addKeysToAgent = "yes";
identityFile = [
"~/.ssh/id_ed25519_sk"
"~/.ssh/id_ed25519"
];
}; };
}; };
}; };

View File

@@ -31,19 +31,20 @@ in
../../modules/firewall ../../modules/firewall
../../modules/fonts ../../modules/fonts
../../modules/gaming ../../modules/gaming
../../modules/networking ../../modules/hcloud
../../modules/keyboard ../../modules/keyboard
../../modules/localization ../../modules/localization
../../modules/networking
../../modules/nvidia ../../modules/nvidia
(import ../../modules/secrets { inherit lib inputs config; }) (import ../../modules/secrets { inherit lib inputs config; })
../../modules/ssh ../../modules/ssh
../../modules/storage ../../modules/storage
../../modules/stylix ../../modules/stylix
../../modules/syncthing ../../modules/syncthing
../../modules/tailscale
../../modules/users ../../modules/users
../../modules/wol ../../modules/wol
../../modules/yubikey ../../modules/yubikey
../../modules/hcloud
]; ];
home-manager.users.${config.host.username} = import ../../home/hosts/andromache { home-manager.users.${config.host.username} = import ../../home/hosts/andromache {
@@ -62,7 +63,11 @@ in
inherit (config.host) username; inherit (config.host) username;
nixSigningKey.enable = true; nixSigningKey.enable = true;
}; };
tailscale.enable = true;
docker.user = config.host.username; docker.user = config.host.username;
hcloud = { hcloud = {
enable = true; enable = true;
inherit (config.host) username; inherit (config.host) username;

View File

@@ -1 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOOXPEhdKOVnb6mkeLLUcFGt+mnUR5pMie17JtjrxwgO h@andromache sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAILoWbtyewa2LUWpWCYK46OCWs+2E+BeIsk+0We20qsf9AAAABHNzaDo= h@andromache

View File

@@ -1 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIzP1PjIDb1tN9nhPOK88HYDtTNk9SN9ZpEem2id49Fa h@astyanax sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIPz79WrdgQKWFxMYEqtDaMW/PCZ8xP1U1KXEAxuPTiQyAAAABHNzaDo= h@astyanax

View File

@@ -25,6 +25,7 @@ in
system.stateVersion = lib.mkDefault "25.05"; system.stateVersion = lib.mkDefault "25.05";
nix = { nix = {
nixPath = [ "nixpkgs=${inputs.nixpkgs}" ]; # https://github.com/nix-community/nixd/blob/main/nixd/docs/configuration.md
optimise = { optimise = {
automatic = true; automatic = true;
dates = [ "05:00" ]; dates = [ "05:00" ];

View File

@@ -14,7 +14,10 @@ in
chmod 644 "$HOST_DIR/ssh_host.pub" chmod 644 "$HOST_DIR/ssh_host.pub"
fi fi
USER_KEY="/home/${username}/.ssh/id_ed25519.pub" USER_KEY="/home/${username}/.ssh/id_ed25519_sk.pub"
if [ ! -f "$USER_KEY" ]; then
USER_KEY="/home/${username}/.ssh/id_ed25519.pub"
fi
if [ -f "$USER_KEY" ] && [ -d "$HOST_DIR" ]; then if [ -f "$USER_KEY" ] && [ -d "$HOST_DIR" ]; then
cp "$USER_KEY" "$HOST_DIR/ssh_user.pub" cp "$USER_KEY" "$HOST_DIR/ssh_user.pub"
chown ${username}:users "$HOST_DIR/ssh_user.pub" chown ${username}:users "$HOST_DIR/ssh_user.pub"

View File

@@ -0,0 +1,19 @@
{
lib,
config,
...
}:
{
# TODO: handle auth declaratively to skip `tailscale up`
options.tailscale = {
enable = lib.mkEnableOption "tailscale";
};
config = lib.mkIf config.tailscale.enable {
services.tailscale = {
enable = true;
openFirewall = true;
};
};
}