From 329dcaf7aef65676c691f86122449cac78d17c9d Mon Sep 17 00:00:00 2001 From: atagen Date: Tue, 19 May 2026 23:39:49 +1000 Subject: [PATCH] nu --- bin/.ocamlformat | 0 bin/dune | 4 - bin/main.ml | 21 --- dune-project | 26 ---- flake.lock | 26 ++-- flake.nix | 60 ++------- lib/.ocamlformat | 0 lib/commands/cook.ml | 9 -- lib/commands/fresh.ml | 17 --- lib/commands/gut.ml | 5 - lib/commands/help.ml | 1 - lib/commands/hunt.ml | 61 --------- lib/commands/look.ml | 114 ----------------- lib/commands/poke.ml | 9 -- lib/commands/ritual.ml | 39 ------ lib/commands/trade.ml | 8 -- lib/commands/yum.ml | 7 - lib/common.ml | 61 --------- lib/dune | 4 - lib/http.ml | 232 --------------------------------- lib/json.ml | 8 -- lib/meat.ml | 11 -- meat.nu | 285 +++++++++++++++++++++++++++++++++++++++++ meat.opam | 31 ----- nix/default.nix | 38 ++++-- nix/module.nix | 13 +- test/dune | 2 - test/test_meat.ml | 0 28 files changed, 351 insertions(+), 741 deletions(-) delete mode 100644 bin/.ocamlformat delete mode 100644 bin/dune delete mode 100644 bin/main.ml delete mode 100644 dune-project delete mode 100644 lib/.ocamlformat delete mode 100644 lib/commands/cook.ml delete mode 100644 lib/commands/fresh.ml delete mode 100644 lib/commands/gut.ml delete mode 100644 lib/commands/help.ml delete mode 100644 lib/commands/hunt.ml delete mode 100644 lib/commands/look.ml delete mode 100644 lib/commands/poke.ml delete mode 100644 lib/commands/ritual.ml delete mode 100644 lib/commands/trade.ml delete mode 100644 lib/commands/yum.ml delete mode 100644 lib/common.ml delete mode 100644 lib/dune delete mode 100644 lib/http.ml delete mode 100644 lib/json.ml delete mode 100644 lib/meat.ml create mode 100644 meat.nu delete mode 100644 meat.opam delete mode 100644 test/dune delete mode 100644 test/test_meat.ml diff --git a/bin/.ocamlformat b/bin/.ocamlformat deleted file mode 100644 index e69de29..0000000 diff --git a/bin/dune b/bin/dune deleted file mode 100644 index b1f555a..0000000 --- a/bin/dune +++ /dev/null @@ -1,4 +0,0 @@ -(executable - (public_name meat) - (name main) - (libraries meat)) diff --git a/bin/main.ml b/bin/main.ml deleted file mode 100644 index 11c0cca..0000000 --- a/bin/main.ml +++ /dev/null @@ -1,21 +0,0 @@ -open Meat - -let () = - match Sys.getenv_opt "MEATS" with - | Some _ -> - if Array.length Sys.argv >= 2 then - match String.lowercase_ascii (Array.get Sys.argv 1) with - | "yum" -> yum () - | "cook" -> cook () - | "poke" -> poke () - | "gut" -> gut () - | "trade" -> trade () - | "look" -> look () - | "fresh" -> fresh () - | "hunt" -> hunt () - | "ritual" -> ritual () - | _ -> help () - else help () - | None -> - meat_print "NO PATH TO RUNESTONE FOUND!"; - help () diff --git a/dune-project b/dune-project deleted file mode 100644 index 256991c..0000000 --- a/dune-project +++ /dev/null @@ -1,26 +0,0 @@ -(lang dune 3.16) - -(name meat) - -(generate_opam_files true) - -(source - (github username/reponame)) - -(authors "Author Name ") - -(maintainers "Maintainer Name ") - -(license LICENSE) - -(documentation https://url/to/documentation) - -(package - (name meat) - (synopsis "A short synopsis") - (description "A longer description") - (depends ocaml dune) - (tags - ("add topics" "to describe" your project))) - -; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/flake.lock b/flake.lock index 843a72b..863dc62 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1773478949, - "narHash": "sha256-8rMpSs2OWGaDlDFO5FS6Pf9WutHBXxM2omPr6hfKydI=", + "lastModified": 1779030999, + "narHash": "sha256-PLR0pNxIN3JPs/rSVnXTIPXzkPLaGAdt/wjPq7+k1PE=", "owner": "feel-co", "repo": "ndg", - "rev": "c3bc1541668e6f6632a7005c7e4963c0a5dedc7b", + "rev": "b363612b524436520c85100192ec2bdab9a675c0", "type": "github" }, "original": { @@ -35,11 +35,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1774078191, - "narHash": "sha256-nyxxxW1/2ouu9dU0I02ul5pHrmUrE1JVFhfFlmYe3Lw=", + "lastModified": 1778869304, + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "09061f748ee21f68a089cd5d91ec1859cd93d0be", + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", "type": "github" }, "original": { @@ -51,11 +51,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1773282481, - "narHash": "sha256-oFe06TmOy8UUT1f7xMHqDpSYq2Fy1mkIsXZUvdnyfeY=", - "rev": "fe416aaedd397cacb33a610b33d60ff2b431b127", + "lastModified": 1775036866, + "narHash": "sha256-ByAX1LkhCwZ94+KnFAmnJSMAvui7kgCxjHgUHsWAbfI=", + "rev": "6201e203d09599479a3b3450ed24fa81537ebc4e", "type": "tarball", - "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre962285.fe416aaedd39/nixexprs.tar.xz?lastModified=1773282481&rev=fe416aaedd397cacb33a610b33d60ff2b431b127" + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre972949.6201e203d095/nixexprs.tar.xz?lastModified=1775036866&rev=6201e203d09599479a3b3450ed24fa81537ebc4e" }, "original": { "type": "tarball", @@ -64,11 +64,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1774078191, - "narHash": "sha256-nyxxxW1/2ouu9dU0I02ul5pHrmUrE1JVFhfFlmYe3Lw=", + "lastModified": 1778869304, + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "09061f748ee21f68a089cd5d91ec1859cd93d0be", + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 33d7c3a..263e27e 100644 --- a/flake.nix +++ b/flake.nix @@ -10,67 +10,26 @@ with inputs; let version = builtins.toString self.lastModified; - collectPkgs = builtins.attrValues; forEachSystem = function: nixpkgs.lib.genAttrs (import nix-systems) ( system: function nixpkgs.legacyPackages.${system} system ); - deps = forEachSystem ( - pkgs: _: { - build = collectPkgs { - inherit (pkgs.ocamlPackages) - dune_3 - ocaml - yojson - ssl - ; - inherit (pkgs.openssl) dev; - }; - dev = collectPkgs { - inherit (pkgs.ocamlPackages) - utop - ocaml-lsp - ocamlformat - ocamlformat-rpc-lib - ; - }; - } - ); in { devShells = forEachSystem ( - pkgs: sys: { + pkgs: _: { default = pkgs.mkShell { - packages = pkgs.lib.mapAttrsToList (_: v: v) deps.${sys}; - shellHook = - let - justFile = '' - default: - @just --list - - @build: - nix build .#debug --offline - - @release: - nix build --offline - - @test: - printf "\\n\\n\\t************ running nix+dune tests ************\\n\\n\\n" - nix flake check --offline - ''; - in - '' - printf '${justFile}' > justfile - ''; + packages = [ + pkgs.nushell + ]; }; } ); packages = forEachSystem ( - pkgs: sys: { + pkgs: _: { default = pkgs.callPackage ./nix/default.nix { - buildInputs = deps.${sys}.build; inherit version; }; docs = pkgs.callPackage unf.lib.pak-chooie { @@ -88,11 +47,18 @@ { pkgs, lib, + config, ... }: + let + cfg = config.programs.meat; + in { imports = [ ./nix/module.nix ]; - programs.meat.package = self.packages.${pkgs.stdenv.hostPlatform.system}.default; + programs.meat.package = self.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs { + differ = cfg.differ; + monitor = cfg.monitor; + }; }; }; diff --git a/lib/.ocamlformat b/lib/.ocamlformat deleted file mode 100644 index e69de29..0000000 diff --git a/lib/commands/cook.ml b/lib/commands/cook.ml deleted file mode 100644 index 4c79b1c..0000000 --- a/lib/commands/cook.ml +++ /dev/null @@ -1,9 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "PREPARING DELICIOUS MEATS.."; - let build_target = - Unix.getenv "MEATS" ^ "#nixosConfigurations." ^ Unix.gethostname () - ^ ".config.system.build.toplevel" - in - Common.do_cmd @@ "nix build --no-link" ^ build_target |> ignore; - print_string Common.footer diff --git a/lib/commands/fresh.ml b/lib/commands/fresh.ml deleted file mode 100644 index d4c4a52..0000000 --- a/lib/commands/fresh.ml +++ /dev/null @@ -1,17 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "HUNTING FRESH MEATS.."; - let argv_len = Array.length Sys.argv in - let root = Sys.getenv "MEATS" in - if argv_len >= 3 then - let open Array in - let flakes = sub Sys.argv 2 (argv_len - 2) in - flakes - |> iter (fun f -> - if Common.all_low f = "meat" then Common.meat_print "PROCESSING REAL MEAT.." - else Common.meat_print ("PROCESSING FRESH MEAT " ^ Common.all_caps f ^ ".."); - Common.do_cmd ~args:false @@ "nix flake update " ^ f ^ " --flake " ^ root - |> ignore) - else Common.do_cmd @@ "nix flake update --flake " ^ root |> ignore; - print_string Common.footer; - print_newline () diff --git a/lib/commands/gut.ml b/lib/commands/gut.ml deleted file mode 100644 index 8a5594c..0000000 --- a/lib/commands/gut.ml +++ /dev/null @@ -1,5 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "CLEANING MEAT STORES.."; - Common.do_cmd "nh clean all" |> ignore; - print_string Common.footer diff --git a/lib/commands/help.ml b/lib/commands/help.ml deleted file mode 100644 index f29a0e7..0000000 --- a/lib/commands/help.ml +++ /dev/null @@ -1 +0,0 @@ -let run () = print_string (Common.header ^ Common.help_text ^ Common.footer ^ "\n") diff --git a/lib/commands/hunt.ml b/lib/commands/hunt.ml deleted file mode 100644 index 655feee..0000000 --- a/lib/commands/hunt.ml +++ /dev/null @@ -1,61 +0,0 @@ -let es_host = "nixos-search-7-1733963800.us-east-1.bonsaisearch.net" - -let es_query term = - Printf.sprintf - {|{"from":0,"size":20,"sort":[{"_score":"desc","package_attr_name":"desc","package_pversion":"desc"}],"collapse":{"field":"package_attr_name"},"query":{"bool":{"must":[{"term":{"type":"package"}},{"multi_match":{"type":"cross_fields","query":"%s","fields":["package_attr_name^9","package_pname^6","package_description^1.3","package_longDescription^1"]}}]}}}|} - term - -let truncate n s = if String.length s > n then String.sub s 0 n ^ ".." else s - -let run () = - print_string Common.header; - let argv_len = Array.length Sys.argv in - if argv_len < 3 then ( - Common.meat_print "WHAT MEAT ARE YOU HUNTING?"; - print_string Common.footer) - else - let query = - Array.sub Sys.argv 2 (argv_len - 2) - |> Array.to_list |> String.concat " " - in - Common.meat_print ("HUNTING FOR " ^ Common.all_caps query ^ ".."); - let body = es_query query in - let path = "/nixos-*-unstable-*/_search" in - (try - let resp = Http.https_post ~host:es_host ~path ~body in - let json = Json.parse_json resp in - let hits = - match Option.bind (Json.jfield "hits" json) (fun h -> Json.jfield "hits" h) with - | Some (`List items) -> items - | _ -> [] - in - if hits = [] then - Common.meat_print "NO MEATS FOUND!" - else - List.iter - (fun hit -> - let src = - match Json.jfield "_source" hit with Some s -> s | None -> `Null - in - let name = - match Option.bind (Json.jfield "package_attr_name" src) Json.jstring with - | Some s -> s - | None -> "?" - in - let version = - match Option.bind (Json.jfield "package_pversion" src) Json.jstring with - | Some s -> s - | None -> "" - in - let desc = - match Option.bind (Json.jfield "package_description" src) Json.jstring with - | Some s -> truncate 60 s - | None -> "" - in - let ver_str = if version <> "" then " (" ^ version ^ ")" else "" in - Printf.printf " \tnixpkgs#%s%s\n" name ver_str; - if desc <> "" then Printf.printf " \t %s\n" desc) - hits - with e -> - Common.meat_print ("HUNT FAILED: " ^ Printexc.to_string e)); - print_string Common.footer diff --git a/lib/commands/look.ml b/lib/commands/look.ml deleted file mode 100644 index c1df668..0000000 --- a/lib/commands/look.ml +++ /dev/null @@ -1,114 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "LOOKING FOR FRESHER MEATS.."; - let lockfile = Http.read_file (Unix.getenv "MEATS" ^ "/flake.lock") in - let json = Json.parse_json lockfile in - let nodes = - match Json.jfield "nodes" json with Some n -> n | None -> failwith "no nodes" - in - let root_node = - match Json.jfield "root" nodes with Some n -> n | None -> failwith "no root" - in - let root_inputs = - match Json.jfield "inputs" root_node with - | Some n -> n - | None -> failwith "no root inputs" - in - let input_pairs = - match Json.jassoc root_inputs with - | Some p -> p - | None -> failwith "root inputs not object" - in - let any_stale = ref false in - List.iter - (fun (name, node_ref) -> - let node_name = - match Json.jstring node_ref with Some s -> s | None -> name - in - let node = - match Json.jfield node_name nodes with - | Some n -> n - | None -> failwith ("no node: " ^ node_name) - in - let locked = - match Json.jfield "locked" node with - | Some n -> n - | None -> failwith "no locked" - in - let original = - match Json.jfield "original" node with - | Some n -> n - | None -> failwith "no original" - in - let locked_rev = - match Option.bind (Json.jfield "rev" locked) Json.jstring with - | Some s -> s - | None -> failwith "no rev" - in - let locked_type = - match Option.bind (Json.jfield "type" locked) Json.jstring with - | Some s -> s - | None -> failwith "no type" - in - let original_ref = Option.bind (Json.jfield "ref" original) Json.jstring in - let host, path = - match locked_type with - | "github" -> - let owner = - match Option.bind (Json.jfield "owner" locked) Json.jstring with - | Some s -> s - | None -> failwith "no owner" - in - let repo = - match Option.bind (Json.jfield "repo" locked) Json.jstring with - | Some s -> s - | None -> failwith "no repo" - in - ( "github.com", - "/" ^ owner ^ "/" ^ repo - ^ ".git/info/refs?service=git-upload-pack" ) - | "git" -> - let url = - match Option.bind (Json.jfield "url" original) Json.jstring with - | Some s -> s - | None -> failwith "no url" - in - let prefix = "https://" in - let plen = String.length prefix in - if String.length url > plen && String.sub url 0 plen = prefix then - let after = - String.sub url plen (String.length url - plen) - in - let slash = - match String.index_opt after '/' with - | Some i -> i - | None -> failwith "no path in url" - in - let h = String.sub after 0 slash in - let p = - String.sub after slash (String.length after - slash) - in - (h, p ^ "/info/refs?service=git-upload-pack") - else failwith ("unsupported url: " ^ url) - | t -> failwith ("unsupported type: " ^ t) - in - try - let body = Http.https_get ~host ~path in - let refs = Http.parse_pktline body in - let target_ref = - match original_ref with - | Some r -> "refs/heads/" ^ r - | None -> "HEAD" - in - let latest_rev = - match List.assoc_opt target_ref refs with - | Some rev -> rev - | None -> failwith ("ref not found: " ^ target_ref) - in - if latest_rev <> locked_rev then ( - any_stale := true; - Common.meat_print (Common.all_caps name ^ " HAS FRESHER MEAT!")) - with _ -> Common.meat_print ("COULD NOT REACH " ^ Common.all_caps name ^ "..")) - input_pairs; - if not !any_stale then Common.meat_print "ALL MEATS ARE FRESH!"; - print_string Common.footer diff --git a/lib/commands/poke.ml b/lib/commands/poke.ml deleted file mode 100644 index 800e39d..0000000 --- a/lib/commands/poke.ml +++ /dev/null @@ -1,9 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "PREPARING SUSPICIOUS MEATS.."; - let build_target = - Unix.getenv "MEATS" ^ "#nixosConfigurations." ^ Unix.gethostname () - ^ ".config.system.build.toplevel" - in - Common.do_cmd @@ "nix build --no-link " ^ build_target ^ " --show-trace" |> ignore; - print_string Common.footer diff --git a/lib/commands/ritual.ml b/lib/commands/ritual.ml deleted file mode 100644 index 739995f..0000000 --- a/lib/commands/ritual.ml +++ /dev/null @@ -1,39 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "PREPARING RITUAL GROUND.."; - let ( >>= ) = Result.bind in - let ( >|= ) = Fun.flip Result.map in - let tmpdir = Filename.temp_dir "meat-chew" "" in - let meats = Unix.getenv "MEATS" in - let hostname = Unix.gethostname () in - let nix_conf_target = - meats ^ "/entry.nix -A nixosConfigurations." ^ hostname - ^ {|'.config.environment.etc."nix/nix.conf"'|} - in - let build_target = - meats ^ "/entry.nix -A nixosConfigurations." ^ hostname - ^ ".config.system.build.toplevel" - in - ( Common.do_cmd ~args:false - @@ "nix-build --log-format internal-json -v --out-link " ^ tmpdir - ^ "/nix.conf " ^ nix_conf_target ^ " |& nom --json" - >>= fun () -> - Common.meat_print "CONSUMING MEATS.."; - Common.do_cmd ~args:false - @@ "NIX_USER_CONF_FILES=" ^ tmpdir - ^ "/nix.conf nix-build --log-format internal-json -v --out-link " ^ tmpdir - ^ "/build " ^ build_target ^ " |& nom --json" - >>= fun () -> - Common.do_cmd ~args:false @@ "dix /nix/var/nix/profiles/system " ^ tmpdir - ^ "/build" - >>= fun () -> - Common.do_cmd ~args:false - @@ "sudo sh -c 'nix-env --set -p /nix/var/nix/profiles/system " ^ tmpdir - ^ "/build && " ^ tmpdir ^ "/build/bin/switch-to-configuration switch'" - >|= fun () -> - Unix.unlink @@ tmpdir ^ "/nix-conf"; - Unix.unlink @@ tmpdir ^ "/build" ) - |> ( function - | Error _ -> print_string "FAILED TO CONSUME MEATS." - | _ -> () ); - print_string Common.footer diff --git a/lib/commands/trade.ml b/lib/commands/trade.ml deleted file mode 100644 index 80e6dfa..0000000 --- a/lib/commands/trade.ml +++ /dev/null @@ -1,8 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "TRADING FOREIGN MEATS.."; - Common.do_remote () |> function - | Error _ -> print_string "FAILED TO TRADE MEATS." - | _ -> - (); - print_string Common.footer diff --git a/lib/commands/yum.ml b/lib/commands/yum.ml deleted file mode 100644 index 08514cc..0000000 --- a/lib/commands/yum.ml +++ /dev/null @@ -1,7 +0,0 @@ -let run () = - print_string Common.header; - Common.meat_print "CONSUMING DELICIOUS MEATS.."; - ( Common.do_build () |> function - | Error _ -> print_string "FAILED TO CONSUME MEATS." - | _ -> () ); - print_string Common.footer diff --git a/lib/common.ml b/lib/common.ml deleted file mode 100644 index ca7ead8..0000000 --- a/lib/common.ml +++ /dev/null @@ -1,61 +0,0 @@ -let header = "\n ----- MEAT ----------------------------------------\n" -let footer = "\n ---------------------------------------------------\n" - -let help_text = - {| - YUM - CONSUME DELICIOUS MEATS - COOK - ONLY PREPARE MEATS - POKE - TASTE SUSPICIOUS MEATS - GUT - CLEAN MEAT STORES - FRESH - HUNT FRESH MEATS - LOOK - LOOK FOR FRESHER MEATS - HUNT - HUNT FOR MEATS IN NIXPKGS - RITUAL - PERFORM RITUAL THEN CONSUME - TRADE - SEND MEATS FAR AWAY - ..-A - ..ALL MEATS|} -open Sys - -let pass_args () = - let len = Array.length argv and sconcat acc el = acc ^ " " ^ el in - match len with - | 3 -> argv.(2) - | n when n > 3 -> - print_int (n - 1); - Array.fold_left sconcat " " (Array.sub argv 2 (n - 2)) - | _ -> "" - -let do_cmd ?(args = true) cmd = - match command (if args then cmd ^ " " ^ pass_args () else cmd) with - | 0 -> Ok () - | e -> Error e - -let meat_print text = print_endline ("\n \t" ^ text ^ "\n") - -let do_build () = - let ( >>= ) = Result.bind in - let ( >|= ) = Fun.flip Result.map in - let tmpdir = Filename.temp_dir "meat-build" "" in - let build_target = - Unix.getenv "MEATS" ^ "/entry.nix -A nixosConfigurations." - ^ Unix.gethostname () ^ ".config.system.build.toplevel" - in - do_cmd @@ "nix-build --log-format internal-json -v --out-link " ^ tmpdir - ^ "/build " ^ build_target ^ " |& nom --json" - >>= fun () -> - do_cmd @@ "dix /nix/var/nix/profiles/system " ^ tmpdir ^ "/build" - >>= fun () -> - do_cmd @@ "sudo sh -c 'nix-env --set -p /nix/var/nix/profiles/system " ^ tmpdir - ^ "/build && " ^ tmpdir ^ "/build/bin/switch-to-configuration switch'" - >|= fun () -> Unix.unlink @@ tmpdir ^ "/build" - -let do_remote () = - meat_print "tbd"; - Ok () - -let all_flag () = - if Array.length argv >= 3 then - match Array.get argv 2 with "-a" | "--all" -> true | _ -> false - else false - -let all_caps s = s |> String.map (fun c -> Char.uppercase_ascii c) -let all_low s = s |> String.map (fun c -> Char.lowercase_ascii c) diff --git a/lib/dune b/lib/dune deleted file mode 100644 index 016c372..0000000 --- a/lib/dune +++ /dev/null @@ -1,4 +0,0 @@ -(include_subdirs qualified) -(library - (name meat) - (libraries unix ssl yojson)) diff --git a/lib/http.ml b/lib/http.ml deleted file mode 100644 index f681505..0000000 --- a/lib/http.ml +++ /dev/null @@ -1,232 +0,0 @@ -let read_file path = - let ic = open_in path in - let n = in_channel_length ic in - let s = really_input_string ic n in - close_in ic; - s - -let contains_sub haystack needle = - let hl = String.length haystack and nl = String.length needle in - if nl > hl then false - else - let rec check i = - if i + nl > hl then false - else if String.sub haystack i nl = needle then true - else check (i + 1) - in - check 0 - -let decode_chunked body = - let len = String.length body in - let buf = Buffer.create (len / 2) in - let i = ref 0 in - (try - while !i < len do - let line_end = - let rec find j = - if j >= len then raise Exit else if body.[j] = '\r' then j - else find (j + 1) - in - find !i - in - let size_str = String.sub body !i (line_end - !i) in - let size_str = - match String.index_opt size_str ';' with - | Some idx -> String.sub size_str 0 idx - | None -> size_str - in - let size = int_of_string ("0x" ^ String.trim size_str) in - if size = 0 then raise Exit; - i := line_end + 2; - if !i + size <= len then - Buffer.add_string buf (String.sub body !i size); - i := !i + size + 2 - done - with Exit -> ()); - Buffer.contents buf - -let b64_encode src = - let tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" in - let len = String.length src in - let buf = Buffer.create (len * 4 / 3 + 4) in - let i = ref 0 in - while !i + 2 < len do - let a = Char.code src.[!i] and b = Char.code src.[!i+1] and c = Char.code src.[!i+2] in - Buffer.add_char buf tbl.[(a lsr 2) land 0x3f]; - Buffer.add_char buf tbl.[((a lsl 4) lor (b lsr 4)) land 0x3f]; - Buffer.add_char buf tbl.[((b lsl 2) lor (c lsr 6)) land 0x3f]; - Buffer.add_char buf tbl.[c land 0x3f]; - i := !i + 3 - done; - (match len - !i with - | 2 -> - let a = Char.code src.[!i] and b = Char.code src.[!i+1] in - Buffer.add_char buf tbl.[(a lsr 2) land 0x3f]; - Buffer.add_char buf tbl.[((a lsl 4) lor (b lsr 4)) land 0x3f]; - Buffer.add_char buf tbl.[(b lsl 2) land 0x3f]; - Buffer.add_char buf '=' - | 1 -> - let a = Char.code src.[!i] in - Buffer.add_char buf tbl.[(a lsr 2) land 0x3f]; - Buffer.add_char buf tbl.[(a lsl 4) land 0x3f]; - Buffer.add_string buf "==" - | _ -> ()); - Buffer.contents buf - -let ssl_inited = ref false - -let https_request ~meth ~host ~path ?(headers=[]) ?(body="") () = - if not !ssl_inited then ( - Ssl.init (); - ssl_inited := true); - let he = Unix.gethostbyname host in - let addr = he.h_addr_list.(0) in - let sockaddr = Unix.ADDR_INET (addr, 443) in - let ctx = Ssl.create_context Ssl.TLSv1_2 Ssl.Client_context in - let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in - Unix.connect sock sockaddr; - let ssl = Ssl.embed_socket sock ctx in - Ssl.set_client_SNI_hostname ssl host; - Ssl.connect ssl; - let extra_headers = - List.fold_left (fun acc (k, v) -> acc ^ k ^ ": " ^ v ^ "\r\n") "" headers - in - let content_len = if body <> "" then - Printf.sprintf "Content-Length: %d\r\n" (String.length body) - else "" in - let req = - Printf.sprintf "%s %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: meat/1.0\r\nConnection: close\r\n%s%s\r\n%s" - meth path host extra_headers content_len body - in - ignore (Ssl.write ssl (Bytes.of_string req) 0 (String.length req)); - let buf = Buffer.create 8192 in - let chunk = Bytes.create 4096 in - (try - while true do - let n = Ssl.read ssl chunk 0 4096 in - if n = 0 then raise Exit; - Buffer.add_subbytes buf chunk 0 n - done - with _ -> ()); - (try Ssl.shutdown_connection ssl with _ -> ()); - Unix.close sock; - let response = Buffer.contents buf in - let rlen = String.length response in - let header_end = - let rec find pos = - if pos + 3 >= rlen then rlen - else if - response.[pos] = '\r' - && response.[pos + 1] = '\n' - && response.[pos + 2] = '\r' - && response.[pos + 3] = '\n' - then pos - else find (pos + 1) - in - find 0 - in - let resp_headers = String.sub response 0 header_end in - let body_start = min (header_end + 4) rlen in - let resp_body = String.sub response body_start (rlen - body_start) in - if - contains_sub - (String.lowercase_ascii resp_headers) - "transfer-encoding: chunked" - then decode_chunked resp_body - else resp_body - -let https_get ~host ~path = - https_request ~meth:"GET" ~host ~path - ~headers:[("User-Agent", "git/2.0")] () - -let https_post ~host ~path ~body = - https_request ~meth:"POST" ~host ~path - ~headers:[ - ("Content-Type", "application/json"); - ("Accept", "application/json"); - ("Authorization", "Basic " ^ b64_encode "aWVSALXpZv:X8gPHnzL52wFEekuxsfQ9cSh"); - ] ~body () - -let parse_pktline body = - let len = String.length body in - let i = ref 0 in - let refs = ref [] in - let hex_val c = - match c with - | '0' .. '9' -> Char.code c - Char.code '0' - | 'a' .. 'f' -> Char.code c - Char.code 'a' + 10 - | 'A' .. 'F' -> Char.code c - Char.code 'A' + 10 - | _ -> 0 - in - let read_pkt_len () = - if !i + 4 > len then 0 - else - let a = hex_val body.[!i] - and b = hex_val body.[!i + 1] - and c = hex_val body.[!i + 2] - and d = hex_val body.[!i + 3] in - i := !i + 4; - (a * 4096) + (b * 256) + (c * 16) + d - in - let plen = read_pkt_len () in - if plen > 4 then i := !i + (plen - 4); - ignore (read_pkt_len ()); - let continue = ref true in - while !continue do - let plen = read_pkt_len () in - if plen = 0 then continue := false - else - let payload_len = plen - 4 in - if !i + payload_len <= len && payload_len >= 41 then ( - let payload = String.sub body !i payload_len in - i := !i + payload_len; - let sha = String.sub payload 0 40 in - let rest = String.sub payload 41 (String.length payload - 41) in - let refname = - let s = - match String.index_opt rest '\000' with - | Some idx -> String.sub rest 0 idx - | None -> rest - in - match String.index_opt s '\n' with - | Some idx -> String.sub s 0 idx - | None -> s - in - refs := (refname, sha) :: !refs) - else i := !i + max 0 payload_len - done; - List.rev !refs - -let derelativise base = List.map (fun a -> base ^ "/" ^ a) - -let filter_dirs fullpath dirs = - dirs |> derelativise fullpath - |> List.filter (fun d -> (Unix.stat d).st_kind = Unix.S_DIR) - -let readdir d = try Sys.readdir d with Sys_error _ -> [||] - -let walk entry = - let open List in - let rec loop dir : string list = - let contents = readdir dir |> Array.to_list in - let is_flake = mem "flake.nix" contents in - if dir = entry then - let subdirs = contents |> filter_dirs dir in - flatten (map loop subdirs) - else if is_flake then - let subdirs = contents |> filter_dirs dir in - let children = flatten (map loop subdirs) in - [ dir ] @ children - else [] - in - loop entry - -let countdepth s = - s |> String.fold_left (fun acc el -> acc + if el = '/' then 1 else 0) 0 - -let compdepth a b = - let ad = countdepth a and bd = countdepth b in - let dif = ad - bd in - match dif with 0 -> 0 | _ -> dif / abs dif - -let fmt_dir d = String.split_on_char '/' d |> List.rev |> List.hd |> Common.all_caps diff --git a/lib/json.ml b/lib/json.ml deleted file mode 100644 index 5dd5297..0000000 --- a/lib/json.ml +++ /dev/null @@ -1,8 +0,0 @@ -let parse_json src = Yojson.Basic.from_string src - -let jfield key = function - | `Assoc pairs -> List.assoc_opt key pairs - | _ -> None - -let jstring = function `String s -> Some s | _ -> None -let jassoc = function `Assoc pairs -> Some pairs | _ -> None diff --git a/lib/meat.ml b/lib/meat.ml deleted file mode 100644 index 29a35d1..0000000 --- a/lib/meat.ml +++ /dev/null @@ -1,11 +0,0 @@ -let yum = Commands.Yum.run -let cook = Commands.Cook.run -let poke = Commands.Poke.run -let gut = Commands.Gut.run -let trade = Commands.Trade.run -let look = Commands.Look.run -let fresh = Commands.Fresh.run -let hunt = Commands.Hunt.run -let ritual = Commands.Ritual.run -let help = Commands.Help.run -let meat_print = Common.meat_print diff --git a/meat.nu b/meat.nu new file mode 100644 index 0000000..c0ecbfb --- /dev/null +++ b/meat.nu @@ -0,0 +1,285 @@ +#!/usr/bin/env nu + +const HEADER = " + ----- MEAT ---------------------------------------- +" + +const FOOTER = " + --------------------------------------------------- +" + +const HELP_TEXT = " + YUM - CONSUME DELICIOUS MEATS + COOK - ONLY PREPARE MEATS + POKE - TASTE SUSPICIOUS MEATS + GUT - CLEAN MEAT STORES + FRESH - HUNT FRESH MEATS + LOOK - LOOK FOR FRESHER MEATS + HUNT - HUNT FOR MEATS IN NIXPKGS + RITUAL - PERFORM RITUAL THEN CONSUME + TRADE - SEND MEATS FAR AWAY + ..-A - ..ALL MEATS" + +def meat-print [text: string] { + print $"\n \t($text)\n" +} + +def with-frame [body: closure] { + print -n $HEADER + do $body + print -n $FOOTER +} + +def hn []: nothing -> string { + ^hostname | str trim +} + +def flake-target []: nothing -> string { + $"($env.MEATS)#nixosConfigurations.(hn).config.system.build.toplevel" +} + +def system-attr []: nothing -> string { + $"nixosConfigurations.(hn).config.system.build.toplevel" +} + +def nix-conf-attr []: nothing -> string { + $"nixosConfigurations.(hn).config.environment.etc.\"nix/nix.conf\"" +} + +def nix-build-nom [out: string, source: string, attr: string, extras: list = []] { + ^nix-build --log-format internal-json -v --out-link $out $source -A $attr ...$extras out+err>| ^$env.MONITOR --json +} + +def activate [build_path: string] { + ^sudo $nu.current-exe -c $"nix-env --set -p /nix/var/nix/profiles/system ($build_path); ($build_path)/bin/switch-to-configuration switch" +} + +def do-build [extras: list = []] { + let tmpdir = ^mktemp -d -t "meat-build.XXXXXX" | str trim + let build = $"($tmpdir)/build" + nix-build-nom $build $"($env.MEATS)/entry.nix" (system-attr) $extras + ^$env.DIFFER /nix/var/nix/profiles/system $build + activate $build + try { rm $build } +} + +def cmd-help [] { + print -n $"($HEADER)($HELP_TEXT)($FOOTER)\n" +} + +def cmd-yum [...args: string] { + with-frame { + meat-print "CONSUMING DELICIOUS MEATS.." + try { do-build $args } catch { print "FAILED TO CONSUME MEATS." } + } +} + +def cmd-cook [...args: string] { + with-frame { + meat-print "PREPARING DELICIOUS MEATS.." + try { ^nix build --no-link (flake-target) ...$args } + } +} + +def cmd-poke [...args: string] { + with-frame { + meat-print "PREPARING SUSPICIOUS MEATS.." + try { ^nix build --no-link (flake-target) --show-trace ...$args } + } +} + +def cmd-gut [...args: string] { + with-frame { + meat-print "CLEANING MEAT STORES.." + try { ^nh clean all ...$args } + } +} + +def cmd-trade [] { + with-frame { + meat-print "TRADING FOREIGN MEATS.." + meat-print "tbd" + } +} + +def cmd-fresh [...flakes: string] { + with-frame { + meat-print "HUNTING FRESH MEATS.." + let root = $env.MEATS + if ($flakes | is-empty) { + try { ^nix flake update --flake $root } + } else { + for f in $flakes { + if ($f | str downcase) == "meat" { + meat-print "PROCESSING REAL MEAT.." + } else { + meat-print $"PROCESSING FRESH MEAT ($f | str upcase).." + } + try { ^nix flake update $f --flake $root } + } + } + } + print "" +} + +def lookup-input [nodes: record, name: string, node_ref: any] { + let node_name = if (($node_ref | describe) == "string") { $node_ref } else { $name } + let node = $nodes | get $node_name + let locked = $node.locked + let original = $node.original + let url = match $locked.type { + "github" => $"https://github.com/($locked.owner)/($locked.repo).git" + "git" => $original.url + _ => { error make { msg: $"unsupported type: ($locked.type)" } } + } + let target_ref = if ('ref' in $original) { $"refs/heads/($original.ref)" } else { "HEAD" } + let out = ^git ls-remote $url $target_ref | complete + if $out.exit_code != 0 { + error make { msg: "git ls-remote failed" } + } + let first_line = $out.stdout | str trim | lines | get 0? + if ($first_line | is-empty) { + error make { msg: "no refs returned" } + } + let latest = $first_line | split row "\t" | first + { name: $name, locked: $locked.rev, latest: $latest } +} + +def cmd-look [] { + with-frame { + meat-print "LOOK FOR NEW MEATS.." + let lock = open --raw $"($env.MEATS)/flake.lock" | from json + let nodes = $lock.nodes + let root_inputs = $nodes.root.inputs + let stale = $root_inputs | transpose name node_ref | each { |row| + try { + let r = lookup-input $nodes $row.name $row.node_ref + if $r.latest != $r.locked { + meat-print $"($r.name | str upcase) MEAT RIPE!" + $r.name + } else { null } + } catch { + meat-print $"NO FIND ($row.name | str upcase).." + null + } + } | compact + if ($stale | is-empty) { + meat-print "NO MEAT FRESHER" + } + } +} + +def truncate-desc [s: string, n: int]: nothing -> string { + if (($s | str length) > $n) { + ($s | str substring 0..$n) + ".." + } else { + $s + } +} + +def cmd-hunt [...query: string] { + with-frame { + if ($query | is-empty) { + meat-print "WHAT MEAT YOU SEEK?" + return + } + let q = $query | str join " " + meat-print $"HUNTING FOR ($q | str upcase).." + let body = { + from: 0, + size: 20, + sort: [{ _score: "desc", package_attr_name: "desc", package_pversion: "desc" }], + collapse: { field: "package_attr_name" }, + query: { + bool: { + must: [ + { term: { type: "package" } }, + { multi_match: { + type: "cross_fields", + query: $q, + fields: ["package_attr_name^9", "package_pname^6", "package_description^1.3", "package_longDescription^1"] + } } + ] + } + } + } + try { + let resp = ( + http post + --user "aWVSALXpZv" + --password "X8gPHnzL52wFEekuxsfQ9cSh" + --content-type "application/json" + "https://nixos-search-7-1733963800.us-east-1.bonsaisearch.net/nixos-*-unstable-*/_search" + $body + ) + let hits = $resp.hits?.hits? | default [] + if ($hits | is-empty) { + meat-print "NO MEATS FOUND!" + } else { + for hit in $hits { + let src = $hit._source + let name = $src.package_attr_name? | default "?" + let ver = $src.package_pversion? | default "" + let desc = $src.package_description? | default "" + let ver_str = if ($ver | is-empty) { "" } else { $" \(($ver)\)" } + print $" \tnixpkgs#($name)($ver_str)" + if not ($desc | is-empty) { + print $" \t (truncate-desc $desc 60)" + } + } + } + } catch { |e| + meat-print $"HUNT FAILED: ($e.msg)" + } + } +} + +def cmd-ritual [] { + with-frame { + meat-print "PREPARING RITUAL GROUND.." + let tmpdir = ^mktemp -d -t "meat-chew.XXXXXX" | str trim + let meats = $env.MEATS + let nix_conf = $"($tmpdir)/nix.conf" + let build = $"($tmpdir)/build" + try { + nix-build-nom $nix_conf $"($meats)/entry.nix" (nix-conf-attr) + meat-print "CONSUMING MEATS.." + with-env { NIX_USER_CONF_FILES: $nix_conf } { + nix-build-nom $build $"($meats)/entry.nix" (system-attr) + } + ^$env.DIFFER /nix/var/nix/profiles/system $build + activate $build + try { rm $nix_conf } + try { rm $build } + } catch { + print "FAILED TO CONSUME MEATS." + } + } +} + +def cmd-shelter [new_closure: string] { + activate $new_closure +} + +def main [...args: string] { + if ($env.MEATS? | is-empty) { + meat-print "NO PATH TO RUNESTONE FOUND!" + cmd-help + return + } + let sub = $args | get 0? | default "" | str downcase + let rest = $args | skip 1 + match $sub { + "yum" => { cmd-yum ...$rest } + "cook" => { cmd-cook ...$rest } + "poke" => { cmd-poke ...$rest } + "gut" => { cmd-gut ...$rest } + "trade" => { cmd-trade } + "look" => { cmd-look } + "fresh" => { cmd-fresh ...$rest } + "hunt" => { cmd-hunt ...$rest } + "ritual" => { cmd-ritual } + "shelter" => { cmd-shelter $rest } + _ => { cmd-help } + } +} diff --git a/meat.opam b/meat.opam deleted file mode 100644 index f89d30f..0000000 --- a/meat.opam +++ /dev/null @@ -1,31 +0,0 @@ -# This file is generated by dune, edit dune-project instead -opam-version: "2.0" -synopsis: "A short synopsis" -description: "A longer description" -maintainer: ["Maintainer Name "] -authors: ["Author Name "] -license: "LICENSE" -tags: ["add topics" "to describe" "your" "project"] -homepage: "https://github.com/username/reponame" -doc: "https://url/to/documentation" -bug-reports: "https://github.com/username/reponame/issues" -depends: [ - "ocaml" - "dune" {>= "3.16"} - "odoc" {with-doc} -] -build: [ - ["dune" "subst"] {dev} - [ - "dune" - "build" - "-p" - name - "-j" - jobs - "@install" - "@runtest" {with-test} - "@doc" {with-doc} - ] -] -dev-repo: "git+https://github.com/username/reponame.git" diff --git a/nix/default.nix b/nix/default.nix index a6004a4..2215000 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,17 +1,39 @@ { - ocamlPackages, - buildInputs, - git, + lib, + stdenvNoCC, + nushell, + makeBinaryWrapper, version, + differ, + monitor, ... }: -ocamlPackages.buildDunePackage { +stdenvNoCC.mkDerivation { pname = "meat"; version = "0.1-delicious-${version}"; - minimalOCamlVersion = "5.2"; - src = ./..; - nativeBuildInputs = [ git ]; - buildInputs = buildInputs; + + nativeBuildInputs = [ makeBinaryWrapper ]; + + dontBuild = true; + + installPhase = '' + runHook preInstall + install -Dm644 meat.nu $out/share/meat/meat.nu + mkdir -p $out/bin + makeWrapper ${nushell}/bin/nu $out/bin/meat \ + --add-flags "$out/share/meat/meat.nu" \ + --set DIFFER ${ + lib.makeBinPath [ + differ + ] + } + --set MONITOR ${ + lib.makeBinPath [ + monitor + ] + } + runHook postInstall + ''; } diff --git a/nix/module.nix b/nix/module.nix index a45e9ef..b2d6079 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -19,14 +19,21 @@ in type = types.package; description = "your ideal meat"; }; + differ = mkOption { + type = types.package; + description = "diffing tool to use"; + default = pkgs.dix; + }; + monitor = mkOption { + type = types.package; + description = "nix monitoring tool to use"; + default = pkgs.nix-output-monitor; + }; }; config = lib.mkIf cfg.enable { environment.sessionVariables.MEATS = cfg.flake; environment.systemPackages = [ cfg.package - pkgs.nh # for now.. - pkgs.nix-output-monitor - pkgs.dix ]; }; } diff --git a/test/dune b/test/dune deleted file mode 100644 index 249a645..0000000 --- a/test/dune +++ /dev/null @@ -1,2 +0,0 @@ -(test - (name test_meat)) diff --git a/test/test_meat.ml b/test/test_meat.ml deleted file mode 100644 index e69de29..0000000