diff options
| -rw-r--r-- | .DS_Store | bin | 0 -> 8196 bytes | |||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 75 | ||||
| -rw-r--r-- | flake.lock | 141 | ||||
| -rw-r--r-- | flake.nix | 90 | ||||
| -rw-r--r-- | hosts/nixbox/hardware-configuration.nix | 29 | ||||
| -rw-r--r-- | modules/darwin/default.nix | 36 | ||||
| -rw-r--r-- | modules/darwin/homebrew.nix | 182 | ||||
| -rw-r--r-- | modules/darwin/macos.nix | 140 | ||||
| -rw-r--r-- | modules/home/claude.nix | 87 | ||||
| -rw-r--r-- | modules/home/default.nix | 52 | ||||
| -rw-r--r-- | modules/home/ghostty.nix | 14 | ||||
| -rw-r--r-- | modules/home/git.nix | 55 | ||||
| -rw-r--r-- | modules/home/lazygit.nix | 20 | ||||
| -rw-r--r-- | modules/home/starship.nix | 7 | ||||
| -rw-r--r-- | modules/home/tmux.nix | 33 | ||||
| -rw-r--r-- | modules/home/twm.nix | 38 | ||||
| -rw-r--r-- | modules/home/zsh.nix | 186 | ||||
| -rw-r--r-- | modules/nixos/default.nix | 59 | ||||
| -rwxr-xr-x | scripts/cleanup.sh | 218 |
20 files changed, 1463 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store Binary files differnew file mode 100644 index 0000000..ee14173 --- /dev/null +++ b/.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6b130c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.claude/settings.local.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..41ec958 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# dotfiles + +My Development Setup, managed with [Nix](https://nixos.org), [nix-darwin](https://github.com/LnL7/nix-darwin), and [home-manager](https://github.com/nix-community/home-manager). Works on macOS, NixOS, and standalone Linux. + +## Quickstart + +### 1. Install Nix + +```shell +curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install +``` + +### 2. Clone the repository + +```shell +git clone git@github.com:simenandre/dotfiles.git ~/dotfiles +cd ~/dotfiles +``` + +### 3. Apply the configuration + +#### macOS + +On first run, bootstrap nix-darwin: + +```shell +nix run nix-darwin -- switch --flake .#default +``` + +On subsequent runs: + +```shell +darwin-rebuild switch --flake .#default +``` + +#### NixOS + +Generate hardware config and rebuild: + +```shell +sudo nixos-generate-config --show-hardware-config > modules/nixos/hardware-configuration.nix +sudo nixos-rebuild switch --flake .#nixbox +``` + +#### Linux (standalone home-manager) + +```shell +# x86_64 +home-manager switch --flake .#simenandre + +# aarch64 +home-manager switch --flake .#simenandre@aarch64-linux +``` + +## Structure + +``` +flake.nix # Entry point – inputs & all configurations +modules/ + darwin/ + default.nix # System-level configuration (nix settings, core packages) + homebrew.nix # Homebrew packages (taps, brews, casks, Mac App Store) + macos.nix # macOS system defaults + home/ + default.nix # home-manager entry point (platform-aware home directory) + git.nix # Git configuration (platform-aware SSH signing path) + zsh.nix # Zsh configuration (zshenv, zshrc, aliases, path, extras) + starship.nix # Starship prompt + tmux.nix # Tmux configuration + ghostty.nix # Ghostty terminal configuration + nixos/ + default.nix # NixOS system configuration +scripts/ + cleanup.sh # macOS cleanup utility +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..913419f --- /dev/null +++ b/flake.lock @@ -0,0 +1,141 @@ +{ + "nodes": { + "config-nvim": { + "flake": false, + "locked": { + "lastModified": 1774097143, + "narHash": "sha256-NTNEkaZa5CdMrsycC+tqqFBUqa2J9r0GTwFWeU3gWog=", + "owner": "simenandre", + "repo": "config.nvim", + "rev": "83cd13c60ba2b8de7b5a50affa0e110c2a608cd6", + "type": "github" + }, + "original": { + "owner": "simenandre", + "repo": "config.nvim", + "type": "github" + } + }, + "dina": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775217257, + "narHash": "sha256-3m79jjgeNoK7JxhPrmNLGWI0vrZ1gtBCe3qWmijplYU=", + "owner": "dinacomputer", + "repo": "cli", + "rev": "4ddd97559fa6dfd2e4b3a4ccea634c16a40a0274", + "type": "github" + }, + "original": { + "owner": "dinacomputer", + "repo": "cli", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774007980, + "narHash": "sha256-FOnZjElEI8pqqCvB6K/1JRHTE8o4rer8driivTpq2uo=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "9670de2921812bc4e0452f6e3efd8c859696c183", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773000227, + "narHash": "sha256-zm3ftUQw0MPumYi91HovoGhgyZBlM4o3Zy0LhPNwzXE=", + "owner": "LnL7", + "repo": "nix-darwin", + "rev": "da529ac9e46f25ed5616fd634079a5f3c579135f", + "type": "github" + }, + "original": { + "owner": "LnL7", + "repo": "nix-darwin", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1773840656, + "narHash": "sha256-9tpvMGFteZnd3gRQZFlRCohVpqooygFuy9yjuyRL2C0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9cf7092bdd603554bd8b63c216e8943cf9b12512", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "config-nvim": "config-nvim", + "dina": "dina", + "home-manager": "home-manager", + "nix-darwin": "nix-darwin", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..db317d9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,90 @@ +{ + description = "Simen Andre's dotfiles configuration"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + nix-darwin = { + url = "github:LnL7/nix-darwin"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + config-nvim = { + url = "github:simenandre/config.nvim"; + flake = false; + }; + + dina = { + url = "github:dinacomputer/cli"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + nix-darwin, + home-manager, + config-nvim, + dina, + }: + let + mkHomeModules = username: [ + ./modules/home + { + home.username = username; + home.file.".config/nvim".source = config-nvim; + } + ]; + in + { + darwinConfigurations."default" = nix-darwin.lib.darwinSystem { + system = "aarch64-darwin"; + modules = [ + ./modules/darwin + home-manager.darwinModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.backupFileExtension = "bak"; + home-manager.extraSpecialArgs = { inherit dina; }; + home-manager.users.cobraz = { imports = mkHomeModules "cobraz"; }; + } + ]; + }; + + nixosConfigurations."nixbox" = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + ./hosts/nixbox/hardware-configuration.nix + ./modules/nixos + home-manager.nixosModules.home-manager + { + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.backupFileExtension = "bak"; + home-manager.extraSpecialArgs = { inherit dina; }; + home-manager.users.simenandre = { imports = mkHomeModules "simenandre"; }; + } + ]; + }; + + homeConfigurations."simenandre" = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages."x86_64-linux"; + extraSpecialArgs = { inherit dina; }; + modules = mkHomeModules "simenandre"; + }; + + homeConfigurations."simenandre@aarch64-linux" = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages."aarch64-linux"; + extraSpecialArgs = { inherit dina; }; + modules = mkHomeModules "simenandre"; + }; + }; +} diff --git a/hosts/nixbox/hardware-configuration.nix b/hosts/nixbox/hardware-configuration.nix new file mode 100644 index 0000000..2c91a21 --- /dev/null +++ b/hosts/nixbox/hardware-configuration.nix @@ -0,0 +1,29 @@ +# Generated by 'nixos-generate-config' for nixbox (QEMU/KVM guest). +# Regenerate with: nixos-generate-config --show-hardware-config +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = + [ (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = + { device = "/dev/disk/by-uuid/096adbf8-2498-4f70-b661-c699dbc4f257"; + fsType = "ext4"; + }; + + fileSystems."/boot" = + { device = "/dev/disk/by-uuid/AE22-C56D"; + fsType = "vfat"; + options = [ "fmask=0022" "dmask=0022" ]; + }; + + swapDevices = [ ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; +} diff --git a/modules/darwin/default.nix b/modules/darwin/default.nix new file mode 100644 index 0000000..5b487ef --- /dev/null +++ b/modules/darwin/default.nix @@ -0,0 +1,36 @@ +{ pkgs, ... }: + +{ + imports = [ + ./homebrew.nix + ./macos.nix + ]; + + # Nix is managed by Determinate installer + nix.enable = false; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # System state version – do not change after first installation + system.stateVersion = 5; + + # Primary user for system defaults and homebrew + system.primaryUser = "cobraz"; + + # Enable Touch ID for sudo + security.pam.services.sudo_local.touchIdAuth = true; + + # Remap Caps Lock to Escape + system.keyboard = { + enableKeyMapping = true; + remapCapsLockToEscape = true; + }; + + # Core system packages available to all users + environment.systemPackages = with pkgs; [ + curl + git + wget + ]; +} diff --git a/modules/darwin/homebrew.nix b/modules/darwin/homebrew.nix new file mode 100644 index 0000000..ada2229 --- /dev/null +++ b/modules/darwin/homebrew.nix @@ -0,0 +1,182 @@ +{ ... }: + +{ + homebrew = { + enable = true; + + onActivation = { + autoUpdate = true; + upgrade = true; + cleanup = "zap"; + }; + + taps = [ + "oven-sh/bun" + "pulumi/tap" + "go-task/tap" + "tursodatabase/tap" + "simenandre/tools" + "anomalyco/tap" + "siderolabs/tap" + ]; + + brews = [ + # Editors and terminal multiplexers + "neovim" + "tmux" + + # Shells and shell utilities + "zsh" + "bash" + "bash-completion" + "ghq" + "moreutils" + + # GNU tools + "findutils" + "gnu-sed" + "grep" + + # Networking tools + "openssh" + "nmap" + "wget" + "cloudflared" + + # Compression libraries and tools + "lz4" + "zlib" + "zstd" + "libffi" + "p7zip" + "pigz" + + # Programming languages and runtimes + "golangci-lint" + "rust" + "zig" + "typst" + + # JavaScript runtimes and package managers + "bun" + "pnpm" + "volta" + "deno" + + # Version control + "git" + "git-lfs" + "git-delta" + "lazygit" + + # Search tools + "ripgrep" + "fzf" + + # JSON/YAML processing tools + "jq" + "yq" + + # Protobuf and gRPC + "protobuf" + "protoc-gen-go" + "protoc-gen-go-grpc" + + # Cloud and IaC tools + "pulumi" + "pulumictl" + "azure-cli" + "kubectl" + "helm" + "cilium-cli" + "doctl" + "flyctl" + "talosctl" + "tektoncd-cli" + "runpodctl" + # Container tools + "docker" + "docker-buildx" + "docker-compose" + "docker-completion" + "colima" + "podman" + "podman-compose" + + # Task runners and automation + "go-task" + "act" + + # Databases and database tools + "turso" + "libpq" + "sqlc" + + # Terminal tools + "pv" + "rename" + "tree" + "tldr" + "starship" + "gh" + "htop" + + # HTTP and web servers + "caddy" + "goreleaser" + + # Document and media tools + "pandoc" + "ffmpeg" + "tesseract" + "ghostscript" + "graphviz" + + # Python tools + "pipx" + + # Specific toolsets + "rclone" + + # Mac App Store CLI + "mas" + ]; + + casks = [ + "1password" + "1password-cli" + "arc" + "basictex" + "claude" + "discord" + "elgato-camera-hub" + "figma" + "font-jetbrains-mono" + "font-symbols-only-nerd-font" + "gcloud-cli" + "ghostty" + "google-chrome" + "google-chrome@canary" + "google-drive" + "libreoffice" + "loom" + "microsoft-teams" + "obsidian" + "postico" + "proton-mail" + "protonvpn" + "raycast" + "signal" + "slack" + "sonos" + "spotify" + "visual-studio-code" + "zoom" + ]; + + masApps = { + "Tailscale" = 1475387142; + "Jojo" = 1659864300; + }; + }; +} diff --git a/modules/darwin/macos.nix b/modules/darwin/macos.nix new file mode 100644 index 0000000..0dd120a --- /dev/null +++ b/modules/darwin/macos.nix @@ -0,0 +1,140 @@ +{ ... }: + +{ + system.defaults = { + NSGlobalDomain = { + # Sidebar icon size: medium + NSTableViewDefaultSizeMode = 2; + # Always show scrollbars + AppleShowScrollBars = "Always"; + # Expand save panel by default + NSNavPanelExpandedStateForSaveMode = true; + NSNavPanelExpandedStateForSaveMode2 = true; + # Expand print panel by default + PMPrintingExpandedStateForPrint = true; + PMPrintingExpandedStateForPrint2 = true; + # Save to disk (not to iCloud) by default + NSDocumentSaveNewDocumentsToCloud = false; + # Disable auto-correct + NSAutomaticSpellingCorrectionEnabled = false; + # Font smoothing + AppleFontSmoothing = 1; + }; + + dock = { + # Minimize windows into their application's icon + minimize-to-application = true; + # Show indicator lights for open applications in the Dock + show-process-indicators = true; + # Wipe all default app icons from the Dock + persistent-apps = [ ]; + # Speed up Mission Control animations + expose-animation-duration = 0.1; + # Remove the auto-hiding Dock delay + autohide-delay = 0.0; + # Remove the animation when hiding/showing the Dock + autohide-time-modifier = 0.0; + # Automatically hide and show the Dock + autohide = true; + # Make Dock icons of hidden applications translucent + showhidden = true; + # Don't show recent applications in Dock + show-recents = false; + }; + + finder = { + # Show status bar + ShowStatusBar = true; + # Show path bar + ShowPathbar = true; + # Search the current folder by default + FXDefaultSearchScope = "SCcf"; + # Disable warning when changing a file extension + FXEnableExtensionChangeWarning = false; + }; + + screencapture = { + # Save screenshots to ~/Screenshots + location = "~/Screenshots"; + # Save screenshots as JPG + type = "jpg"; + }; + + trackpad = { + # Enable tap to click for this user and the login screen + Clicking = true; + }; + + + ActivityMonitor = { + # Show the main window when launching Activity Monitor + OpenMainWindow = true; + # Visualize CPU usage in the Activity Monitor Dock icon + IconType = 5; + # Show all processes in Activity Monitor + ShowCategory = 100; + # Sort Activity Monitor results by CPU usage + SortColumn = "CPUUsage"; + SortDirection = 0; + }; + + LaunchServices = { + # Disable the "Are you sure you want to open this application?" dialog + LSQuarantine = false; + }; + + CustomUserPreferences = { + "com.apple.print.PrintingPrefs" = { + # Automatically quit printer app once the print jobs complete + "Quit When Finished" = true; + }; + "com.apple.desktopservices" = { + # Avoid creating .DS_Store files on network or USB volumes + DSDontWriteNetworkStores = true; + DSDontWriteUSBStores = true; + }; + "com.apple.Safari" = { + # Privacy: don't send search queries to Apple + UniversalSearchEnabled = false; + SuppressSearchSuggestions = true; + }; + "com.apple.TimeMachine" = { + # Prevent Time Machine from prompting to use new hard drives + DoNotOfferNewDisksForBackup = true; + }; + "com.apple.TextEdit" = { + PlainTextEncoding = 4; + PlainTextEncodingForWrite = 4; + }; + "com.apple.SoftwareUpdate" = { + AutomaticCheckEnabled = true; + ScheduleFrequency = 1; + AutomaticDownload = 1; + CriticalUpdateInstall = 1; + }; + "com.apple.commerce" = { + AutoUpdate = true; + AutoUpdateRestartRequired = true; + }; + "com.apple.helpviewer" = { + DevMode = true; + }; + "com.apple.BluetoothAudioAgent" = { + "Apple Bitpool Min (editable)" = 40; + }; + "com.google.Chrome" = { + # Disable the all too sensitive backswipe on trackpads and Magic Mouse + AppleEnableSwipeNavigateWithScrolls = false; + AppleEnableMouseSwipeNavigateWithScrolls = false; + # Expand the print dialog by default + PMPrintingExpandedStateForPrint2 = true; + }; + "com.apple.messageshelper.MessageController" = { + SOInputLineSettings = { + automaticQuoteSubstitutionEnabled = false; + continuousSpellCheckingEnabled = false; + }; + }; + }; + }; +} diff --git a/modules/home/claude.nix b/modules/home/claude.nix new file mode 100644 index 0000000..5895f03 --- /dev/null +++ b/modules/home/claude.nix @@ -0,0 +1,87 @@ +{ ... }: + +{ + home.file.".claude/CLAUDE.md".text = '' + # Code Style + + - ONLY use comments to explain weird code or non-obvious behaviour, never to explain simple code. + + # Git & PRs + + - ALWAYS write short and concise git commit messages and pull request messages. + - NEVER include a Co-Authored-By: line in commits. + - NEVER include "Generated with [Claude Code]" in PRs. + + # Docker & Runtimes + + - ALWAYS use the latest LTS version for base Docker images, Node.js engine requirements and similar. + - ALWAYS strive to use base Docker images with common ancestors in multi-stage builds: a stage based on `debian:trixie-slim` strongly suggests using `node:24-trixie-slim`. + + # Nix + + - If a command is not available and there is a `flake.nix` in the project, use `nix develop` or `nix run` to run commands from the flake's dev shell instead of installing tools manually. + ''; + + home.file.".claude/skills/push/SKILL.md".text = '' + --- + name: push + description: Create a commit and push changes. Optionally creates a branch and opens a PR with an intelligently chosen reviewer. + disable-model-invocation: true + --- + + # /push — Commit & Push Workflow + + You are executing the /push skill. Follow these steps precisely. + + ## Step 1: Analyze Changes + + Run `git status` and `git diff` (staged + unstaged) to understand all current changes. + If there are no changes to commit, inform the user and stop. + + ## Step 2: Stage Files + + Stage the relevant files. Prefer `git add <specific files>` over `git add -A`. + Never stage files that look like secrets (.env, credentials, tokens). + + ## Step 3: Ask — Branch or Main? + + Use the **AskUserQuestion** tool to ask the user how to push. Example: + + - question: "How should this be pushed?" + - header: "Push target" + - options: + - label: "Current branch", description: "Push directly to the current branch" + - label: "New branch + PR", description: "Create a new branch and open a pull request" + + ### If "Current branch": + + - Run `git push` (with `-u` if needed). + - Done. + + ### If "New branch + PR": + + 1. Create a descriptive branch name based on the changes (e.g., `feat/add-logging`, `fix/null-check-user`). + 2. Check out the new branch and push with `-u`. + 3. **Choose a reviewer:** + - Run `git log --format='%an' -50 | sort | uniq -c | sort -rn | head -10` to find active contributors. + - Cross-reference with files changed: run `git log --format='%an' -- <changed-files> | sort | uniq -c | sort -rn | head -5` to find who has context on the modified code. + - Pick the most relevant reviewer (not the current user — check with `git config user.name`). + - Build a list of up to 4 candidate reviewers and use **AskUserQuestion** to let the user pick. Example: + - question: "Who should review this PR?" + - header: "Reviewer" + - options: one per candidate, with description showing their commit count / relevance to changed files. + 4. Create the PR using `gh pr create`: + - Short title (under 70 chars). + - Body with a `## Summary` section (2-3 bullet points) and a `## Test plan` section. + - Add the confirmed reviewer with `--reviewer`. + 5. Return the PR URL to the user. + + ## Step 4: Create Commit + + - Analyze the staged diff. + - Write a concise commit message using **conventional commit format** (e.g., `feat:`, `fix:`, `refactor:`, `chore:`, `docs:`, `test:`, `ci:`). Include a scope when it adds clarity (e.g., `feat(auth):`, `fix(api):`). + - Output ONLY the raw commit message — no markdown, no code blocks, no backticks, no explanations. + - **Do NOT add a Co-Authored-By line. Never include any co-author trailer.** + - Create the commit using a HEREDOC for the message. + ''; +} diff --git a/modules/home/default.nix b/modules/home/default.nix new file mode 100644 index 0000000..70b1174 --- /dev/null +++ b/modules/home/default.nix @@ -0,0 +1,52 @@ +{ pkgs, lib, config, dina, ... }: + +{ + imports = [ + ./claude.nix + ./git.nix + ./ghostty.nix + ./starship.nix + ./tmux.nix + ./lazygit.nix + ./twm.nix + ./zsh.nix + ]; + + home = { + homeDirectory = lib.mkForce ( + if pkgs.stdenv.isDarwin + then "/Users/${config.home.username}" + else "/home/${config.home.username}" + ); + # Do not change this after the first installation + stateVersion = "24.11"; + }; + + home.packages = with pkgs; [ + claude-code + dina.packages.${pkgs.stdenv.hostPlatform.system}.default + delta + fzf + gh + ghq + git-get + htop + jq + kubectl + lazygit + nodejs_22 + ripgrep + starship + talosctl + tmux + tree + twm + yq + opencode + ]; + + programs.go.enable = true; + + # Let home-manager manage itself + programs.home-manager.enable = true; +} diff --git a/modules/home/ghostty.nix b/modules/home/ghostty.nix new file mode 100644 index 0000000..1d42944 --- /dev/null +++ b/modules/home/ghostty.nix @@ -0,0 +1,14 @@ +{ ... }: + +{ + home.file.".config/ghostty/config".text = '' + theme = tokyonight-storm + + window-padding-x = 12 + window-padding-y = 12 + window-decoration = true + window-theme = "dark" + window-inherit-working-directory = true + window-inherit-font-size = true + ''; +} diff --git a/modules/home/git.nix b/modules/home/git.nix new file mode 100644 index 0000000..1915304 --- /dev/null +++ b/modules/home/git.nix @@ -0,0 +1,55 @@ +{ pkgs, ... }: + +{ + programs.git = { + enable = true; + + signing = if pkgs.stdenv.isDarwin then { + key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMCyUn5BfmAnd/6xi0X1mYF/+jPOmCS0bpNUePF55gCY"; + signByDefault = true; + } else { + signByDefault = false; + }; + + settings = { + user = { + name = "Simen A. W. Olsen"; + email = "hello@simenandre.no"; + }; + apply.whitespace = "fix"; + core = { + trustctime = false; + editor = "nvim"; + excludesfile = "~/.gitignore_global"; + }; + color.ui = "auto"; + "color \"branch\"" = { + current = "yellow reverse"; + local = "yellow"; + remote = "green"; + }; + "color \"diff\"" = { + meta = "yellow bold"; + frag = "magenta bold"; + old = "red"; + new = "green"; + }; + "color \"status\"" = { + added = "yellow"; + changed = "green"; + untracked = "cyan"; + }; + diff.renames = "copies"; + help.autocorrect = 1; + merge.log = true; + push.autoSetupRemote = true; + github.user = "simenandre"; + pull.rebase = true; + } // (if pkgs.stdenv.isDarwin then { + gpg.format = "ssh"; + "gpg \"ssh\"".program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign"; + "url \"ssh://git@github.com/\"".insteadOf = "https://github.com/"; + commit.gpgsign = true; + } else {}); + }; +} diff --git a/modules/home/lazygit.nix b/modules/home/lazygit.nix new file mode 100644 index 0000000..da8996a --- /dev/null +++ b/modules/home/lazygit.nix @@ -0,0 +1,20 @@ +{ ... }: + +{ + home.file.".config/lazygit/config.yml".text = '' + git: + pagers: + - colorArg: always + pager: delta --dark --paging=never + customCommands: + - key: "C" + context: "files" + description: "Generate commit message with Claude Code" + command: > + git diff --staged | $HOME/.local/bin/claude -p 'Generate a concise git commit message for these staged changes. + Output ONLY the raw commit message with no markdown, no code blocks, no backticks, no explanations. + Use conventional commit format.' --model haiku --output-format text > /tmp/commit_msg && + GIT_EDITOR=nvim git commit -e -F /tmp/commit_msg + output: terminal + ''; +} diff --git a/modules/home/starship.nix b/modules/home/starship.nix new file mode 100644 index 0000000..4f3cb61 --- /dev/null +++ b/modules/home/starship.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + programs.starship = { + enable = true; + }; +} diff --git a/modules/home/tmux.nix b/modules/home/tmux.nix new file mode 100644 index 0000000..f084f35 --- /dev/null +++ b/modules/home/tmux.nix @@ -0,0 +1,33 @@ +{ ... }: + +{ + home.file.".tmux.conf".text = '' + unbind r + bind r source-file ~/.tmux.conf \; display-message "Config reloaded" + + set -g default-terminal "tmux-256color" + set -ag terminal-overrides ",xterm-256color:RGB" + + set -g default-command "$SHELL -l" + + set -g prefix C-s + + set -g mouse on + + set -g base-index 1 + set-window-option -g mode-keys vi + + bind g new-window -c "#{pane_current_path}" -n "lazygit" lazygit + + bind f run-shell "tmux neww twm" + bind F run-shell "tmux neww twm -l" + bind s run-shell "tmux neww twm -e" # i rebind the original `s` to `S` so I can still use it + bind e run-shell "tmux switch -t $TWM_DEFAULT" # i set TWM_DEFAULT in my shellrc, just a session that is always available as a scratch area + + set -g @plugin 'tmux-plugins/tpm' + set -g @plugin 'christoomey/vim-tmux-navigator' + set -g @plugin 'tmux-plugins/tmux-sensible' + + run '~/.tmux/plugins/tpm/tpm' + ''; +} diff --git a/modules/home/twm.nix b/modules/home/twm.nix new file mode 100644 index 0000000..f5609d7 --- /dev/null +++ b/modules/home/twm.nix @@ -0,0 +1,38 @@ +{ config, ... }: + +{ + home.file.".config/twm/twm.yaml".text = '' + search_paths: + - ${config.home.homeDirectory}/ghq + - ${config.home.homeDirectory}/repositories + - ~/.config/nvim/ + + exclude_path_components: + - .git + - .direnv + - node_modules + - venv + - target + + workspace_definitions: + - name: catchall + default_layout: catchall-dev + + - name: go + has_all_files: + - go.mod + - go.sum + + - name: docker-compose + has_any_file: + - docker-compose.yml + - docker-compose.yaml + has_all_files: + - Dockerfile + - .git + + - name: pulumi + has_all_files: + - Pulumi.yaml + ''; +} diff --git a/modules/home/zsh.nix b/modules/home/zsh.nix new file mode 100644 index 0000000..678e70d --- /dev/null +++ b/modules/home/zsh.nix @@ -0,0 +1,186 @@ +{ pkgs, ... }: + +let + zsh-autocomplete = pkgs.fetchFromGitHub { + owner = "marlonrichert"; + repo = "zsh-autocomplete"; + rev = "main"; + hash = "sha256-KYey9sidz8VzgfM9YVaCsVXpKKdCjSHk3tuadQz9Re0="; + }; + zsh-autosuggestions = pkgs.fetchFromGitHub { + owner = "zsh-users"; + repo = "zsh-autosuggestions"; + rev = "master"; + hash = "sha256-KmkXgK1J6iAyb1FtF/gOa0adUnh1pgFsgQOUnNngBaE="; + }; + zsh-vi-mode = pkgs.fetchFromGitHub { + owner = "jeffreytse"; + repo = "zsh-vi-mode"; + rev = "master"; + hash = "sha256-WdZHCVxVVOs4HyG5f56vAA17UWYOvb9Yf6v7M1RIdU4="; + }; +in +{ + home.file = { + ".zsh_repos/marlonrichert/zsh-autocomplete".source = zsh-autocomplete; + ".zsh_repos/zsh-users/zsh-autosuggestions".source = zsh-autosuggestions; + ".zsh_repos/jeffreytse/zsh-vi-mode".source = zsh-vi-mode; + ".zshenv".text = '' + ##!/bin/zsh + # + # .zshenv - Zsh environment file, loaded always. + # + + # Set ZDOTDIR if you want to re-home Zsh. + export XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config} + export XDG_DATA_HOME=''${XDG_DATA_HOME:-$HOME/.local/share} + export XDG_CACHE_HOME=''${XDG_CACHE_HOME:-$HOME/.cache} + export ZDOTDIR=''${ZDOTDIR:-$XDG_CONFIG_HOME/zsh} + + # Ensure path arrays do not contain duplicates. + typeset -gU path fpath + + # Set the list of directories that zsh searches for commands. + path=( + $HOME/{,s}bin(N) + $HOME/.local/{,s}bin(N) + /opt/{homebrew,local}/{,s}bin(N) + /usr/local/{,s}bin(N) + $path + ) + '' + ( + if pkgs.stdenv.isDarwin then '' + eval "$(/opt/homebrew/bin/brew shellenv)" + '' else "" + ) + '' + [ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env" + ''; + + ".hushlogin".text = ""; + + ".config/zsh/.zshrc".text = '' + #!/bin/bash + # ^----- get shellcheck hints based on bash + # https://github.com/koalaman/shellcheck/issues/809 + # shellcheck disable=SC1090 # sourced filenames with variables + + # Remove stale compiled zsh files that shadow nix-managed configs + rm -f ~/.config/zsh/.zshrc.zwc ~/.config/zsh/{aliases,path,extras}.zwc + + eval "$(starship init zsh)" + + source ~/.zsh_repos/marlonrichert/zsh-autocomplete/zsh-autocomplete.plugin.zsh + source ~/.zsh_repos/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh + # zsh-vi-mode clobbers all keybindings on init, so anything + # that needs to survive (fzf, autosuggest) goes in zvm_after_init. + function zvm_after_init() { + eval "$(fzf --zsh)" + bindkey '^I^I' autosuggest-accept + } + + source ~/.zsh_repos/jeffreytse/zsh-vi-mode/zsh-vi-mode.plugin.zsh + + for file in ~/.config/zsh/{aliases,path,extras}; do + if [[ -r "$file" ]] && [[ -f "$file" ]]; then + # shellcheck source=/dev/null + source "$file" + fi + done + + command -v twm &>/dev/null && eval "$(twm --print-zsh-completion)" + ''; + + ".config/zsh/aliases".text = '' + # Git aliases + alias git-unpushed='git log --branches --not --remotes --no-walk --decorate --oneline' + alias gb='git checkout -b' + alias gfr='git fetch upstream && git rebase upstream/main' + alias git-undo='git reset --soft HEAD~1' + alias git-clean-branches="git for-each-ref --format '%(refname:short)' refs/heads | grep -v 'master\|main' | xargs git branch -D" + alias :q='exit' + alias ls='ls --color' + '' + (if pkgs.stdenv.isDarwin then '' + alias tailscale='/Applications/Tailscale.app/Contents/MacOS/Tailscale' + '' else "") + '' + + # Generic / Random / Utility + alias rg="rg --hidden --glob '!.git' --glob '!node_modules'" + alias r='cd $REPOS/github.com/' + alias c='clear' + '' + (if pkgs.stdenv.isDarwin then '' + alias afk='pmset displaysleepnow' + '' else "") + '' + alias dl="cd ~/Downloads" + alias l='ls -l' + alias todo='nt' + alias tasks="open 'obsidian://open?file=-%20Oppgaver'" + alias todos="mt ~/ghq/github.com/simenandre/notes/" + alias today="open 'obsidian://advanced-uri?daily=true'" + alias tre="tree -I '.git|node_modules'" + alias timeliste="open 'https://tripletex.no/execute/updateHourlist?contextId=11208568'" + alias t="open 'https://tripletex.no/execute/updateHourlist?contextId=11208568'" + alias fork-cleaner="fork-cleaner --token \$(cat ~/.secrets/github-token 2>/dev/null)" + alias vi="nvim" + alias vim="nvim" + alias v="nvim" + + alias dump-issues="getBjerkIssues > ~/Downloads/issues-$(date -I).json" + ''; + + ".config/zsh/path".text = '' + #!/bin/bash + + # Add directories to the PATH and prevent to add the same directory multiple times upon shell reload. + add_to_path() { + if [[ -d "$1" ]] && [[ ":$PATH:" != *":$1:"* ]]; then + export PATH="$1:$PATH" + fi + } + + add_to_path "$HOME/.volta/bin" + add_to_path "/Library/Frameworks/Firebird.framework/Resources/bin" + add_to_path "/opt/homebrew/opt/libpq/bin" + ''; + + ".config/zsh/extras".text = '' + if [[ "$(uname)" == "Darwin" ]]; then + export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock" + fi + + NOTES_DIR=$HOME/ghq/github.com/simenandre/notes/ + function note() { + if [ $1 ]; then + FILE=$NOTES_DIR/$(date +%Y-%m-%d).md + + if [ $2 ]; then + FILE=$2 + fi + + echo $1 >> $FILE + fi + } + + function nt() { + if [ $1 ]; then + CONTENT="- [ ] $1" + note $CONTENT $2 + fi + } + + function getBjerkIssues() { + gh issue list --repo bjerkio/internal \ + --search "-label:backlog -label:kind/system" \ + --json title,body,url,createdAt,assignees,number,labels,comments \ + --jq 'map({url, createdAt, title, labels: [.labels[].name], assignees: [.assignees[].login], body, number: "#\(.number)", comments: .comments | map({ body: .body, createdAt: .createdAt })})' + } + + function g() { + export GOOGLE_CLOUD_PROJECT="sokkel-sandbox" + export GOOGLE_CLOUD_LOCATION="europe-north1" + export GOOGLE_GENAI_USE_VERTEXAI=true + + gemini + } + ''; + }; +} diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix new file mode 100644 index 0000000..0d51dc4 --- /dev/null +++ b/modules/nixos/default.nix @@ -0,0 +1,59 @@ +{ pkgs, lib, ... }: + +{ + imports = [ ]; + + # Enable experimental Nix features + nix.settings.experimental-features = "nix-command flakes"; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # Bootloader + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # Networking + networking.hostName = "nixbox"; + networking.networkmanager.enable = true; + + # Timezone / locale + time.timeZone = "Europe/Oslo"; + i18n.defaultLocale = "en_US.UTF-8"; + + # User account + users.users.simenandre = { + isNormalUser = true; + extraGroups = [ "wheel" "networkmanager" "docker" ]; + shell = pkgs.zsh; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDXfT23Ne7Rl/rdiqrLf4vWfQqgUCo26+NcL41PVSag" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJwHftCdnV/pscQ4qCK/biIBsKk/G0A3hLVI1o62CJF4 so@bjerk.io" + ]; + }; + programs.zsh.enable = true; + + + # Core system packages + environment.systemPackages = with pkgs; [ + curl + gcc + git + wget + neovim + ghostty.terminfo + ]; + + # 1Password (for SSH signing) + programs._1password.enable = true; + programs._1password-gui.enable = true; + + # Docker + virtualisation.docker.enable = true; + + # SSH + services.openssh.enable = true; + + # System state version – do not change after first installation + system.stateVersion = "24.11"; +} diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh new file mode 100755 index 0000000..82afe4d --- /dev/null +++ b/scripts/cleanup.sh @@ -0,0 +1,218 @@ +#!/bin/bash + +# Mac Cleanup Script +# This script safely cleans up various caches, logs, and temporary files +# Run with: chmod +x cleanup.sh && ./cleanup.sh + +set -e # Exit on any error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to get directory size +get_size() { + if [ -d "$1" ]; then + du -sh "$1" 2>/dev/null | cut -f1 + else + echo "0B" + fi +} + +# Function to safely remove directory contents +safe_cleanup() { + local dir="$1" + local desc="$2" + + if [ -d "$dir" ]; then + local size=$(get_size "$dir") + print_status "Cleaning $desc ($size)..." + + # Only remove contents, not the directory itself + find "$dir" -mindepth 1 -delete 2>/dev/null || true + print_success "Cleaned $desc" + else + print_warning "$desc directory not found: $dir" + fi +} + +# Function to clean up files matching pattern +clean_pattern() { + local pattern="$1" + local desc="$2" + local count + + count=$(find . -name "$pattern" -type f 2>/dev/null | wc -l | tr -d ' ') + if [ "$count" -gt 0 ]; then + print_status "Removing $count $desc files..." + find . -name "$pattern" -type f -delete 2>/dev/null || true + print_success "Removed $desc files" + fi +} + +echo "🧹 Mac Cleanup Script" +echo "====================" +echo + +# Check if running as root (not recommended) +if [ "$EUID" -eq 0 ]; then + print_error "Please don't run this script as root/sudo" + exit 1 +fi + +print_status "Starting cleanup process..." +echo + +# 1. System Caches +# print_status "Cleaning system caches..." +# safe_cleanup "$HOME/Library/Caches" "user caches" +# safe_cleanup "/tmp" "temporary files" + +# 2. Browser Caches (be careful with user data) +# print_status "Cleaning browser caches..." +# safe_cleanup "$HOME/Library/Caches/Google/Chrome" "Chrome cache" +# safe_cleanup "$HOME/Library/Application Support/Google/Chrome/Default/Application Cache" "Chrome app cache" + +# 3. Development-related cleanup +print_status "Cleaning development caches..." + +# Node.js and npm +safe_cleanup "$HOME/.npm/_cacache" "npm cache" +if command -v npm &> /dev/null; then + print_status "Cleaning npm cache..." + npm cache clean --force 2>/dev/null || true +fi + +# Yarn cache +if command -v yarn &> /dev/null; then + print_status "Cleaning Yarn cache..." + yarn cache clean 2>/dev/null || true +fi + +# pnpm cache (you have this installed) +if command -v pnpm &> /dev/null; then + print_status "Cleaning pnpm cache..." + pnpm store prune 2>/dev/null || true +fi + +# Bun cache (you have this installed) +if command -v bun &> /dev/null; then + print_status "Cleaning Bun cache..." + rm -rf "$HOME/.bun/install/cache" 2>/dev/null || true +fi + +# Go module cache +safe_cleanup "$HOME/go/pkg/mod/cache" "Go module cache" + +# Rust cargo cache +safe_cleanup "$HOME/.cargo/registry/cache" "Rust cargo cache" + +# 4. Docker cleanup (you have Docker installed) +if command -v docker &> /dev/null && docker info &> /dev/null; then + print_status "Cleaning Docker..." + docker system prune -f 2>/dev/null || true + docker image prune -f 2>/dev/null || true + print_success "Docker cleanup completed" +fi + +# 5. Podman cleanup (you have Podman installed) +if command -v podman &> /dev/null; then + print_status "Cleaning Podman..." + podman system prune -f 2>/dev/null || true + podman image prune -f 2>/dev/null || true + print_success "Podman cleanup completed" +fi + +# 6. Homebrew cleanup (you're clearly using Homebrew extensively) +if command -v brew &> /dev/null; then + print_status "Cleaning Homebrew..." + brew cleanup --prune=all 2>/dev/null || true + brew autoremove 2>/dev/null || true + print_success "Homebrew cleanup completed" +fi + +# 7. System logs (older than 7 days) +print_status "Cleaning old system logs..." +sudo log collect --last 7d --output /dev/null 2>/dev/null || true + +# 8. Xcode derived data (if Xcode is installed) +if [ -d "$HOME/Library/Developer/Xcode/DerivedData" ]; then + safe_cleanup "$HOME/Library/Developer/Xcode/DerivedData" "Xcode derived data" +fi + +# 9. Clean up common temporary file patterns in current directory and subdirectories +# print_status "Cleaning temporary files in current directory..." +# clean_pattern "*.tmp" "temporary" +# clean_pattern "*.temp" "temporary" +# clean_pattern ".DS_Store" "DS_Store" +# clean_pattern "Thumbs.db" "Windows thumbnail" +# clean_pattern "*.log" "log" +# clean_pattern "*.swp" "Vim swap" +# clean_pattern "*.swo" "Vim swap" +# clean_pattern "*~" "backup" + +# 10. Node.js node_modules cleanup (be careful - only clean if package.json exists) +if [ -f "package.json" ]; then + print_status "Found package.json - looking for node_modules to clean..." + find . -name "node_modules" -type d -prune | while read -r dir; do + size=$(get_size "$dir") + print_status "Removing node_modules at $dir ($size)..." + rm -rf "$dir" + done + print_success "Node modules cleanup completed" +fi + +# 11. Empty trash +print_status "Emptying trash..." +osascript -e 'tell application "Finder" to empty trash' 2>/dev/null || true +print_success "Trash emptied" + +# 12. Clear font caches (since you work with fonts based on your Brewfile) +# print_status "Clearing font caches..." +# sudo atsutil databases -remove 2>/dev/null || true + +# 13. Repair disk permissions +# print_status "Repairing disk permissions..." +# sudo diskutil repairPermissions / 2>/dev/null || true + +echo +print_success "🎉 Cleanup completed!" + +# Show some system stats +echo +print_status "System Information:" +echo "📊 Disk Usage:" +df -h / | tail -1 | awk '{print " Available: " $4 " (" $5 " used)"}' + +echo "💾 Memory Usage:" +vm_stat | head -5 + +echo +print_status "💡 Additional recommendations:" +echo " • Restart your Mac to clear memory caches" +echo " • Run 'brew doctor' to check Homebrew health" +echo " • Consider using 'ncdu' to find large directories" +echo " • Check Activity Monitor for memory-hungry apps" + +echo +print_success "All done! 🚀" |
