From ee5313c9bf08dbd7bbee8ea7d48d084b3a6426ec Mon Sep 17 00:00:00 2001 From: Lukas Wurzinger Date: Sun, 1 Jun 2025 16:47:14 +0200 Subject: [PATCH] init --- .envrc | 2 + flake.lock | 142 ++++++++++++++++++++++++++++++++ flake.nix | 78 ++++++++++++++++++ package.nix | 19 +++++ zap | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 471 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package.nix create mode 100755 zap diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..0f94eed --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +# shellcheck shell=bash +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f3686c3 --- /dev/null +++ b/flake.lock @@ -0,0 +1,142 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1747372754, + "narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1748460289, + "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "hooks": "hooks", + "nixpkgs": "nixpkgs", + "treefmt": "treefmt" + } + }, + "treefmt": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748243702, + "narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5e03b4d --- /dev/null +++ b/flake.nix @@ -0,0 +1,78 @@ +{ + description = "Disk zapper"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + hooks = { + url = "github:cachix/git-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + treefmt = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + flake-parts, + hooks, + treefmt, + }@inputs: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + hooks.flakeModule + treefmt.flakeModule + ]; + + systems = nixpkgs.lib.systems.flakeExposed; + + perSystem = + { + config, + pkgs, + self', + ... + }: + { + treefmt = { + projectRootFile = "flake.nix"; + + programs = { + nixfmt = { + enable = true; + package = pkgs.nixfmt-rfc-style; + }; + + shfmt = { + enable = true; + includes = [ + "zap" + ]; + }; + + shellcheck.enable = true; + }; + }; + + pre-commit.settings.hooks = { + treefmt.enable = true; + }; + + devShells.default = pkgs.mkShellNoCC { + packages = [ + self'.packages.default + ]; + + shellHook = '' + ${config.pre-commit.installationScript} + ''; + }; + + packages.default = pkgs.callPackage ./package.nix { }; + }; + }; +} diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..c970ee3 --- /dev/null +++ b/package.nix @@ -0,0 +1,19 @@ +{ + writeShellApplication, + util-linux, + jq, + e2fsprogs, + dosfstools, +}: +writeShellApplication { + name = "zap"; + + runtimeInputs = [ + util-linux + jq + e2fsprogs + dosfstools + ]; + + text = builtins.readFile ./zap; +} diff --git a/zap b/zap new file mode 100755 index 0000000..6aba9cd --- /dev/null +++ b/zap @@ -0,0 +1,230 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +progname="$0" + +warn() { + local line + for line in "$@"; do + echo "$progname: $line" 1>&2 + done +} + +error() { + warn "$@" + + exit 1 +} + +skips=() + +skip() { + if (($# < 1)); then + error 'name of value to be skipped is required' + fi + + if (($# > 1)); then + error 'too many arguments' + fi + + local skip=$1 + + for s in "${skips[@]}"; do + if [[ $s == "$skip" ]]; then + return 0 + fi + done + + return 1 +} + +args=$( + getopt \ + --options v \ + --longoptions mount:,create-boot,label:,boot-label:,crypt-label:,mapping:,mount-options:,boot-mount-options:,verbose \ + --name "$progname" \ + -- "$@" +) + +eval set -- "$args" + +mount=/mnt +mkboot=0 +bootlbl=BOOT +lbl=main +cryptlbl=cryptmain +mapping=main +bootmntflags=() +mntflags=() +fatflags=() +ext4flags=() +while true; do + case "$1" in + --mount) + mount=$2 + shift 2 + ;; + --create-boot) + skips+=(mkboot) + mkboot=1 + shift + ;; + --label) + skips+=(lbl) + lbl=$2 + shift 2 + ;; + --boot-label) + skips+=(bootlbl) + bootlbl=${2^^} + shift 2 + ;; + --crypt-label) + skips+=(cryptlbl) + cryptlbl=$2 + shift 2 + ;; + --mapping) + skips+=(mapping) + mapping=$2 + shift 2 + ;; + --mount-options) + mntflags+=(--options "$2") + shift 2 + ;; + --boot-mount-options) + bootmntflags+=(--options "$2") + shift 2 + ;; + -v | --verbose) + fatflags+=(-v) + ext4flags+=(-v) + shift + ;; + --) + shift + break + ;; + esac +done + +if (($# < 1)); then + error 'an argument specifying the block device is required' +fi + +if (($# > 1)); then + error 'too many arguments' +fi + +blkdev=$1 + +if ! skip mkboot; then + while true; do + read -rep 'Do you want to create a boot partition? [y/N] ' input + case "$input" in + [Yy]*) + mkboot=1 + break + ;; + '' | [Nn]*) + mkboot=0 + break + ;; + *) warn 'Please answer with yes or no' ;; + esac + done +fi + +{ + if ((mkboot)); then + echo ',512M,U;' + fi + echo ',,L;' +} | sfdisk --label gpt --quiet -- "$blkdev" + +parts=() +json=$(sfdisk --json -- "$blkdev") +while IFS= read -r k; do + parts+=("$(jq --argjson k "$k" --raw-output '.partitiontable.partitions[$k].node' <<<"$json")") +done < <(jq '.partitiontable.partitions | keys[]' <<<"$json") + +if ((mkboot)); then + bootfs="${parts[0]}" + blkdev="${parts[1]}" +else + blkdev="${parts[0]}" +fi + +if ((mkboot)); then + if ! skip bootlbl; then + read -rep "Which label should the boot file system have? [$bootlbl] " input + if [[ -n $input ]]; then + bootlbl=$input + fi + fi + + mkfs.fat -F 32 -n "$bootlbl" "${fatflags[@]}" -- "$bootfs" >/dev/null +fi + +while true; do + read -rep 'Do you want this disk to be encrypted? [y/N] ' input + case "$input" in + [Yy]*) + while true; do + read -rsp 'Enter password: ' password + echo >&2 + read -rsp 'Re-enter password: ' repassword + echo >&2 + if [[ $password == "$repassword" ]]; then + break + fi + done + + if ! skip cryptlbl; then + read -rep "Which label should the LUKS partition have? [$cryptlbl] " input + if [[ -n $input ]]; then + cryptlbl=$input + fi + fi + + cryptsetup luksFormat --batch-mode --label "$cryptlbl" -- "$blkdev" <<<"$password" + + if ! skip mapping; then + read -rep "Which name should the LUKS mapping have? [$mapping] " input + if [[ -n $input ]]; then + mapping=$input + fi + fi + + cryptsetup open -- "$blkdev" "$mapping" <<<"$password" + + fs=/dev/mapper/$mapping + break + ;; + '' | [Nn]*) + fs=$blkdev + break + ;; + *) warn 'Please answer with yes or no' ;; + esac +done + +if ! skip lbl; then + read -rep "Which label should the file system have? [$lbl] " input + if [[ -n $input ]]; then + lbl=$input + fi +fi + +mkfs.ext4 -qFL "$lbl" "${ext4flags[@]}" -- "$fs" +mkdir --parents -- "$mount" +mount "${mntflags[@]}" -- "$fs" "$mount" + +if ((mkboot)); then + mkdir -- "$mount/boot" + mount "${bootmntflags[@]}" -- "$bootfs" "$mount/boot" +fi