diff options
| author | Kleidi Bujari <mail@4kb.net> | 2024-12-17 23:03:27 -0500 |
|---|---|---|
| committer | Kleidi Bujari <mail@4kb.net> | 2024-12-17 23:03:27 -0500 |
| commit | eb13a9a552f9e50a66faba985521ba8e3483c447 (patch) | |
| tree | 209193a91b0db20fb1bb30007e72e321f0293696 /machines | |
| parent | f5ed731abbc38d765333b005b9815a5f6d3e59c2 (diff) | |
| download | depot-eb13a9a552f9e50a66faba985521ba8e3483c447.tar.gz depot-eb13a9a552f9e50a66faba985521ba8e3483c447.tar.bz2 depot-eb13a9a552f9e50a66faba985521ba8e3483c447.zip | |
Push configuration out of flake
Projects are not necessarily all exported by the system flake, and
should freely support other derivations. The new "mod" directory will be
used for most projects, and it can be imported by machine
configurations.
That said, the tooling around flakes is pretty good and will still be
used until the dependency is lessened.
Diffstat (limited to 'machines')
| -rw-r--r-- | machines/flake-module.nix | 50 | ||||
| -rw-r--r-- | machines/iridium/default.nix | 12 | ||||
| -rw-r--r-- | machines/xnet/README | 18 | ||||
| -rw-r--r-- | machines/xnet/default.nix | 38 | ||||
| -rw-r--r-- | machines/xnet/desktop/default.nix | 124 | ||||
| -rw-r--r-- | machines/xnet/disk.nix | 139 | ||||
| -rw-r--r-- | machines/xnet/gitserver/default.nix | 54 | ||||
| -rw-r--r-- | machines/xnet/gitserver/gitweb.nix | 75 | ||||
| -rw-r--r-- | machines/xnet/monitoring/default.nix | 38 | ||||
| -rw-r--r-- | machines/xnet/monitoring/grafana.nix | 51 | ||||
| -rw-r--r-- | machines/xnet/monitoring/prometheus.nix | 15 | ||||
| -rw-r--r-- | machines/xnet/net/default.nix | 44 | ||||
| -rw-r--r-- | machines/xnet/net/sshd.nix | 46 | ||||
| -rw-r--r-- | machines/xnet/nginx.nix | 28 | ||||
| -rw-r--r-- | machines/xnet/users.nix | 74 |
15 files changed, 753 insertions, 53 deletions
diff --git a/machines/flake-module.nix b/machines/flake-module.nix deleted file mode 100644 index 8d9dd2c..0000000 --- a/machines/flake-module.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ lib, self, ... }: -let - inherit (lib) nixosSystem; - inherit (self.nixosModules) xnet xlib; - inherit (self) inputs; -in -{ - flake.nixosConfigurations = { - t480 = nixosSystem { - system = "x86_64-linux"; - specialArgs = { inherit xlib; }; - modules = [ ./t480 xnet ]; - }; - - iso = nixosSystem { - system = "x86_64-linux"; - modules = [ ./iso ]; - }; - - t1 = nixosSystem { - system = "x86_64-linux"; - modules = [ ./t1 xnet ]; - }; - - iridium = nixosSystem { - system = "x86_64-linux"; - specialArgs = { inherit inputs; }; - modules = [ ./iridium xnet ]; - }; - }; - - perSystem = { pkgs, inputs', ... }: { - packages.xinstall = - let - disko = inputs'.disko.packages.disko-install; - - configs = builtins.concatStringsSep " " - (builtins.filter (x: x != "iso") - (builtins.attrNames self.nixosConfigurations)); - in - pkgs.writeShellScriptBin "xinstall" '' - set -euo pipefail - - FLAKE="github:kbujari/depot" - TARGET=$(${pkgs.gum}/bin/gum choose ${configs}) - - # ${disko}/bin/disko-install --help - ''; - }; -} diff --git a/machines/iridium/default.nix b/machines/iridium/default.nix index 8a3edd0..640554b 100644 --- a/machines/iridium/default.nix +++ b/machines/iridium/default.nix @@ -1,4 +1,11 @@ -{ inputs, lib, ... }: { +{ inputs, lib, ... }: +let + gitKeys = builtins.fetchurl { + url = "https://github.com/kbujari.keys"; + sha256 = "0fpa679zkrpx77vangzf3gnidwvmky8ifivn8411xx6albrikaqx"; + }; +in +{ system.stateVersion = "24.11"; users.mutableUsers = false; @@ -18,8 +25,7 @@ gitServer = { enable = true; gitweb.enable = true; - keys = builtins.filter (x: x != "") - (lib.strings.splitString "\n" (builtins.readFile inputs.sshKeys)); + keys = lib.splitString "\n" (builtins.readFile gitKeys); }; }; diff --git a/machines/xnet/README b/machines/xnet/README new file mode 100644 index 0000000..ab1c28c --- /dev/null +++ b/machines/xnet/README @@ -0,0 +1,18 @@ +xnet +==== + +Base configuration for any machines running a standard xnet +configuration. + +Notable features: + + - Root filesystem running in RAM + - Automated ZFS partitioning of disk + - Hardened SSH and web services + +Usage +===== + +The entire module is exposed from the top-level flake as a nixosModule, +but it changes too often to be considered stable. Options are made +available when imported under the 'xnet' attribute set. diff --git a/machines/xnet/default.nix b/machines/xnet/default.nix new file mode 100644 index 0000000..5a6c587 --- /dev/null +++ b/machines/xnet/default.nix @@ -0,0 +1,38 @@ +{ lib, pkgs, ... }: +let + inherit (lib) mkDefault; +in +{ + imports = [ + ./disk.nix + ./users.nix + ./nginx.nix + ./net + ./desktop + ./gitserver + # ./monitoring + ]; + + i18n.defaultLocale = mkDefault "en_US.UTF-8"; + time.timeZone = mkDefault "America/Toronto"; + + nix = { + settings = { + auto-optimise-store = true; + experimental-features = [ "nix-command" "flakes" ]; + warn-dirty = false; + + # timeout fast from binary cache + connect-timeout = 5; + }; + gc = { + automatic = true; + options = mkDefault "--delete-older-than 30d"; + }; + }; + + documentation = { + doc.enable = mkDefault false; + info.enable = mkDefault false; + }; +} diff --git a/machines/xnet/desktop/default.nix b/machines/xnet/desktop/default.nix new file mode 100644 index 0000000..1e37497 --- /dev/null +++ b/machines/xnet/desktop/default.nix @@ -0,0 +1,124 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.xnet.desktop; + inherit (lib) mkDefault mkOption mkIf types; +in +{ + options.xnet.desktop = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable graphical desktop."; + }; + }; + + config = mkIf cfg.enable { + security.rtkit.enable = true; + services.pipewire = { + enable = true; + pulse.enable = true; + alsa = { + enable = true; + }; + }; + + programs.sway = { + enable = true; + wrapperFeatures = { + gtk = true; + base = true; + }; + extraPackages = with pkgs; [ + foot + fuzzel + imv + mako + mpv + playerctl + pop-icon-theme + pwvucontrol + sway-contrib.grimshot + swayidle + swaylock + tigervnc + waybar + wl-clipboard + xwayland + zathura + ]; + extraSessionCommands = '' + export MOZ_ENABLE_WAYLAND=1 + export MOZ_USE_XINPUT2=1 + export MOZ_WEBRENDER=1 + export XDG_CURRENT_DESKTOP=sway + export XDG_SESSION_TYPE=wayland + ''; + }; + + programs.light.enable = mkDefault true; + + qt = { + enable = true; + style = "adwaita-dark"; + platformTheme = "gnome"; + }; + + fonts.packages = with pkgs; [ + departure-mono + noto-fonts + noto-fonts-cjk-sans + noto-fonts-emoji + (nerdfonts.override { fonts = [ "NerdFontsSymbolsOnly" ]; }) + ]; + + xdg.portal = { + enable = true; + extraPortals = with pkgs; [ + xdg-desktop-portal-wlr + xdg-desktop-portal-gtk + ]; + }; + + programs.firefox = { + enable = true; + policies = { + DisableTelemetry = true; + DisableFirefoxStudies = true; + EnableTrackingProtection = { + Value = true; + Locked = true; + Cryptomining = true; + Fingerprinting = true; + }; + DisablePocket = true; + DisableFirefoxAccounts = true; + DisableAccounts = true; + DisableFirefoxScreenshots = true; + OverrideFirstRunPage = ""; + OverridePostUpdatePage = ""; + DontCheckDefaultBrowser = true; + DisplayBookmarksToolbar = "never"; + DisplayMenuBar = "default-off"; + SearchBar = "unified"; + ExtensionSettings = { + "*".installation_mode = "blocked"; # blocks all addons except the ones specified below + # uBlock Origin: + "uBlock0@raymondhill.net" = { + install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi"; + installation_mode = "force_installed"; + }; + # Bitwarden: + "{446900e4-71c2-419f-a6a7-df9c091e268b}" = { + install_url = "https://addons.mozilla.org/firefox/downloads/latest/bitwarden-password-manager/latest.xpi"; + installation_mode = "force_installed"; + }; + # Dark Reader: + "addon@darkreader.org" = { + install_url = "https://addons.mozilla.org/firefox/downloads/latest/darkreader/latest.xpi"; + installation_mode = "force_installed"; + }; + }; + }; + }; + }; +} diff --git a/machines/xnet/disk.nix b/machines/xnet/disk.nix new file mode 100644 index 0000000..caef61d --- /dev/null +++ b/machines/xnet/disk.nix @@ -0,0 +1,139 @@ +{ config, lib, modulesPath, ... }: +let + cfg = config.xnet.disk; + commonOpts = { + acltype = "posixacl"; + atime = "off"; + compression = "on"; + normalization = "formD"; + relatime = "off"; + xattr = "sa"; + "com.sun:auto-snapshot" = "false"; + }; + + inherit (lib) mkOption mkDefault mkIf types; +in +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + options.xnet.disk = { + enable = mkOption { + type = types.bool; + default = false; + description = "Apply xnet-standard ZFS disk layout."; + }; + + device = mkOption { + type = types.str; + description = "Device used for zroot ZFS pool."; + }; + }; + + config = mkIf cfg.enable { + networking.hostId = builtins.substring 0 8 + (builtins.hashString "md5" config.networking.hostName); + + services.zfs = { + autoScrub.enable = true; + trim.enable = true; + }; + + # With root running in memory, swap should be required unless + # otherwise specified + zramSwap.enable = mkDefault true; + + boot = { + kernelParams = [ "nohibernate" "elevator=none" ]; + supportedFilesystems = [ "vfat" "zfs" ]; + zfs.devNodes = "/dev/disk/by-partuuid"; + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + initrd = { + systemd.enable = true; + availableKernelModules = [ + "xhci_pci" + "ahci" + "nvme" + "usb_storage" + "sd_mod" + "sdhci_pci" + ]; + }; + tmp.cleanOnBoot = mkDefault true; + }; + + disko.devices.disk.main = { + type = "disk"; + device = cfg.device; + content = { + type = "gpt"; + partitions.ESP = { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + partitions.ZFS = { + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; + }; + }; + }; + }; + + disko.devices = { + nodev."/" = { + fsType = "tmpfs"; + mountOptions = [ "defaults" "size=2G" "mode=755" ]; + }; + + zpool.zroot = { + type = "zpool"; + options = { + ashift = "12"; + autotrim = "on"; + }; + + datasets = { + "local" = { + type = "zfs_fs"; + options = commonOpts // { + mountpoint = "none"; + }; + }; + + "local/nix" = { + type = "zfs_fs"; + mountpoint = "/nix"; + options.mountpoint = "legacy"; + }; + + "local/reserved" = { + type = "zfs_fs"; + options = { + refreservation = "10G"; + mountpoint = "none"; + }; + }; + + "persist" = { + type = "zfs_fs"; + mountpoint = "/persist"; + options = commonOpts // { + mountpoint = "legacy"; + }; + }; + }; + }; + }; + }; +} diff --git a/machines/xnet/gitserver/default.nix b/machines/xnet/gitserver/default.nix new file mode 100644 index 0000000..da83f73 --- /dev/null +++ b/machines/xnet/gitserver/default.nix @@ -0,0 +1,54 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.xnet.gitServer; + inherit (lib) mkOption mkIf types; +in +{ + imports = [ ./gitweb.nix ]; + + options.xnet.gitServer = { + enable = mkOption { + type = types.bool; + default = false; + description = "Serve git repos over SSH."; + }; + + path = mkOption { + type = types.path; + default = "/persist/repo/git"; + description = "Directory where repos will be stored."; + }; + + keys = mkOption { + type = types.listOf types.str; + description = "SSH public keys used for git operations."; + }; + }; + + config = mkIf cfg.enable { + users.users.git = { + group = "git"; + initialPassword = ""; + isSystemUser = true; + home = cfg.path; + homeMode = "755"; + createHome = true; + shell = "${pkgs.git}/bin/git-shell"; + openssh.authorizedKeys.keys = cfg.keys; + }; + + users.groups.git = { }; + + programs.git = { + enable = true; + config = { + init = { + defaultBranch = "master"; + }; + safe = { + directory = "*"; + }; + }; + }; + }; +} diff --git a/machines/xnet/gitserver/gitweb.nix b/machines/xnet/gitserver/gitweb.nix new file mode 100644 index 0000000..bfd593d --- /dev/null +++ b/machines/xnet/gitserver/gitweb.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.xnet.gitServer.gitweb; + inherit (lib) mkOption mkIf types; +in +{ + options.xnet.gitServer.gitweb = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable web interface to git repos."; + }; + + hostName = mkOption { + type = types.str; + default = "src.web.4kb.net"; + description = "Hostname the webUI is served from."; + }; + }; + + config = mkIf cfg.enable { + xnet.nginx.enable = true; + + users.users.nginx.extraGroups = [ "git" ]; + services.cgit.main = { + enable = true; + scanPath = config.xnet.gitServer.path; + package = pkgs.cgit-pink; + nginx = { + virtualHost = cfg.hostName; + location = "/"; + }; + extraConfig = '' + mimetype.gif=image/gif + mimetype.html=text/html + mimetype.jpeg=image/jpeg + mimetype.jpg=image/jpeg + mimetype.pdf=application/pdf + mimetype.png=image/png + mimetype.svg=image/svg+xml + readme=:readme + readme=:readme.md + readme=:readme.txt + readme=:README + readme=:README.md + readme=:README.txt + ''; + settings = { + about-filter = "${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh"; + clone-url = "https://${cfg.hostName}/$CGIT_REPO_URL git@${cfg.hostName}:$CGIT_REPO_URL"; + enable-commit-graph = true; + enable-http-clone = false; + enable-index-links = true; + enable-remote-branches = true; + remove-suffix = true; + robots = "noindex, nofollow"; + root-desc = "What I cannot create, I do not understand"; + root-title = cfg.hostName; + section-from-path = true; + snapshots = "tar.gz tar.bz2 zip"; + }; + }; + + # required for rendering markdown readme + environment.systemPackages = with pkgs; [ + python312 + python312Packages.markdown + ]; + + # services.nginx.virtualHosts."${cfg.gitweb.hostName}" = { + # useACMEHost = "4kb.net"; + # addSSL = true; + # }; + }; +} diff --git a/machines/xnet/monitoring/default.nix b/machines/xnet/monitoring/default.nix new file mode 100644 index 0000000..87ae724 --- /dev/null +++ b/machines/xnet/monitoring/default.nix @@ -0,0 +1,38 @@ +{ config, ... }: +let + inherit (config.networking) hostName; + inherit (config.services.prometheus) exporters; +in +{ + imports = [ ./grafana.nix ./prometheus.nix ]; + + exporters = { + node = { + enable = true; + enabledCollectors = [ "processes" "systemd" ]; + }; + + systemd = { + enable = true; + extraFlags = [ + "--systemd.collector.enable-ip-accounting" + "--systemd.collector.enable-restart-count" + ]; + }; + }; + + services.prometheus.scrapeConfigs = [ + { + job_name = "node"; + static_configs = [{ + targets = [ "${hostName}:${toString exporters.node.port}" ]; + }]; + } + { + job_name = "systemd"; + static_configs = [{ + targets = [ "${hostName}:${toString exporters.systemd.port}" ]; + }]; + } + ]; +} diff --git a/machines/xnet/monitoring/grafana.nix b/machines/xnet/monitoring/grafana.nix new file mode 100644 index 0000000..6a37ce1 --- /dev/null +++ b/machines/xnet/monitoring/grafana.nix @@ -0,0 +1,51 @@ +{ config, pkgs, ... }: { + services.grafana.provision = { + enable = true; + datasources.settings.datasources = [{ + name = "Prometheus"; + type = "prometheus"; + url = "http://localhost:9090"; + access = "proxy"; + editable = false; + }]; + + dashboards.settings.providers = [{ + name = "Fetched Dashboards"; + options.path = "/etc/grafana/dashboards"; + }]; + }; + + environment.etc = { + "grafana/dashboards/node-exporter.json" = { + user = "grafana"; + group = "grafana"; + source = pkgs.fetchurl { + url = "https://grafana.com/api/dashboards/1860/revisions/37/download"; + hash = "sha256-1DE1aaanRHHeCOMWDGdOS1wBXxOF84UXAjJzT5Ek6mM="; + }; + }; + }; + + services.grafana = { + enable = true; + settings.server = { + domain = "grafana.web.4kb.net"; + protocol = "socket"; + }; + settings."auth.anonymous" = { + enabled = true; + org_role = "Admin"; + }; + }; + + users.groups.grafana.members = [ "nginx" ]; + systemd.services.nginx.serviceConfig.ProtectHome = false; + + services.nginx.virtualHosts."${config.services.grafana.settings.server.domain}" = { + useACMEHost = "4kb.net"; + addSSL = true; + locations."/" = { + proxyPass = "http://unix:/${toString config.services.grafana.settings.server.socket}"; + }; + }; +} diff --git a/machines/xnet/monitoring/prometheus.nix b/machines/xnet/monitoring/prometheus.nix new file mode 100644 index 0000000..e32e078 --- /dev/null +++ b/machines/xnet/monitoring/prometheus.nix @@ -0,0 +1,15 @@ +{ config, ... }: { + services.prometheus = { + enable = true; + globalConfig.scrape_interval = "1m"; + # scrapeConfigs = [{ + # job_name = "node"; + # relabel_configs = [{ + # source_labels = [ "__address__" ]; + # regex = "(.*):[0-9]+"; + # target_label = "instance"; + # replacement = "$1"; + # }]; + # }]; + }; +} diff --git a/machines/xnet/net/default.nix b/machines/xnet/net/default.nix new file mode 100644 index 0000000..2255f53 --- /dev/null +++ b/machines/xnet/net/default.nix @@ -0,0 +1,44 @@ +{ config, lib, ... }: +let + cfg = config.xnet.net; + inherit (lib) mkOption mkIf types; + prefix = "10.26.4"; +in +{ + imports = [ + ./sshd.nix + ]; + + options.xnet.net = { + interface = mkOption { + type = types.str; + default = ""; + description = "Network interface connecting to xnet."; + }; + + addr = mkOption { + type = types.ints.between 0 255; + description = "Final octet for xnet address."; + example = 4; + }; + }; + + # TODO: + # - Add assertion that each address is only used once across config + # - Add each host to each other hosts dns configuration + config = mkIf (builtins.stringLength cfg.interface > 0) { + networking.vlans = { + "${cfg.interface}.4" = { + inherit (cfg) interface; + id = 4; + }; + }; + + networking.interfaces = { + "${cfg.interface}.4".ipv4.addresses = [{ + address = "${prefix}.${toString cfg.addr}"; + prefixLength = 24; + }]; + }; + }; +} diff --git a/machines/xnet/net/sshd.nix b/machines/xnet/net/sshd.nix new file mode 100644 index 0000000..ef225db --- /dev/null +++ b/machines/xnet/net/sshd.nix @@ -0,0 +1,46 @@ +{ config, lib, ... }: +let + cfg = config.xnet.net.sshd; + inherit (lib) mkOption mkIf types; +in +{ + options.xnet.net.sshd = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable hardened SSH service."; + }; + }; + + config = mkIf cfg.enable { + services.openssh = { + enable = true; + startWhenNeeded = true; + openFirewall = true; + hostKeys = [{ + path = "/persist/certs/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + }]; + settings = { + UsePAM = true; + X11Forwarding = false; + PermitRootLogin = "no"; + PasswordAuthentication = false; + Ciphers = [ "chacha20-poly1305@openssh.com" ]; + Macs = [ "hmac-sha2-512-etm@openssh.com" ]; + KexAlgorithms = [ "curve25519-sha256@libssh.org" ]; + }; + sftpServerExecutable = "internal-sftp"; + sftpFlags = [ "-f AUTHPRIV" "-l INFO" ]; + extraConfig = + let + pubkeyTypes = lib.strings.concatStringsSep "," [ + "sk-ssh-ed25519-cert-v01@openssh.com" + "ssh-ed25519-cert-v01@openssh.com" + "ssh-ed25519" + ]; + in + "PubkeyAcceptedKeyTypes ${pubkeyTypes}"; + }; + }; +} diff --git a/machines/xnet/nginx.nix b/machines/xnet/nginx.nix new file mode 100644 index 0000000..64c1d65 --- /dev/null +++ b/machines/xnet/nginx.nix @@ -0,0 +1,28 @@ +{ config, lib, ... }: +let + cfg = config.xnet.nginx; + + inherit (lib) mkOption mkIf types; +in +{ + + options.xnet.nginx = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable optimized nginx."; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + }; + }; +} diff --git a/machines/xnet/users.nix b/machines/xnet/users.nix new file mode 100644 index 0000000..53aa15b --- /dev/null +++ b/machines/xnet/users.nix @@ -0,0 +1,74 @@ +{ pkgs, lib, config, ... }: +let + cfg = config.xnet.users; + inherit (lib) mkOption mkIf types; +in +{ + + options.xnet.users = { + enable = mkOption { + type = types.listOf (types.enum [ "kle" ]); + default = [ ]; + description = "Users to enable."; + }; + }; + + config = { + users = { + mutableUsers = false; + users.kle = mkIf (builtins.elem "kle" cfg.enable) { + hashedPassword = "$6$R4dDhaftX.vapGMd$.An36hlp3DXfkIC7bPZ0MDPo6Zvpk8JRrhy2LES.lZZj6JDa74oJkcMW3DCsIySvLJxOPXSShos0TpgJ/w0fH/"; + isNormalUser = true; + createHome = true; + extraGroups = [ "wheel" "users" "networkmanager" "video" ]; + packages = with pkgs; [ + btop + curl + fzf + jq + lynx + neovim + ranger + rsync + sshfs + tree + zip + ]; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP7T2uWJFUu8aFZZgQusGKyEMocb2pKbHLDad2eIJus9" + ]; + }; + }; + + security.sudo = { + execWheelOnly = true; + extraConfig = "Defaults lecture = never"; + }; + + programs.ssh = { + knownHosts = { + "github.com".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl"; + }; + extraConfig = '' + Host github + HostName github.com + User git + PreferredAuthentications publickey + ''; + }; + + programs.git = { + config = { + init.defaultBranch = "master"; + fetch.prune = true; + core.excludesFile = pkgs.writeText "gitignore" '' + # dev shell caching + .direnv/ + .envrc + ''; + push.default = "upstream"; + push.autoSetupRemote = true; + }; + }; + }; +} |
