commit bdc8ae889dd6dc0539e479baa72373fffdc6ae5c Author: Peder Bergebakken Sundt <pbsds@hotmail.com> Date: Sun Feb 23 00:04:10 2025 +0100 Initial commit diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b42106 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.direnv/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3453fd5 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# nixq + +Like `jq`, `yq` and `htmlq`, but for nix :snowflake::snowflake::snowflake: +Basically just a shell interface for [nix-select](https://git.clan.lol/clan/nix-select/). + +## Examples + +``` +$ nixq . 'packages.{x86_64-linux,aarch64-linux}.*' --yaml +aarch64-linux: + default: /nix/store/6nrvhk2viafsy84sg29jrzkf4xdap529-nixq + nixq: /nix/store/6nrvhk2viafsy84sg29jrzkf4xdap529-nixq + nixq-lix: /nix/store/2m7z3kjf76mqaldh40r3zfg39vfbrg93-nixq + nixq-nix: /nix/store/q8jqlz75hs6hlpzsbjhr994w2ffzyply-nixq +x86_64-linux: + default: /nix/store/fx8n5krq20y6z1p97iv7ra77zzq0lvf6-nixq + nixq: /nix/store/fx8n5krq20y6z1p97iv7ra77zzq0lvf6-nixq + nixq-lix: /nix/store/a2znjf47hhfd2749ff0lwy7ljz3nsz43-nixq + nixq-nix: /nix/store/046dkmci916zgy1690pqvcb0wijrij98-nixq +``` + +``` +$ nixq -f '<nixpkgs>' 'pkgs.{spade,sus-compiler,pagefind}.meta.maintainers.*.github' +{ + "pagefind": [ + "pbsds" + ], + "spade": [ + "pbsds" + ], + "sus-compiler": [ + "pbsds" + ] +} +``` + +``` +$ nixq flake:nixpkgs 'legacyPackages.x86_64-linux.{disko,pagefind}.meta.available' +{ + "disko": true, + "pagefind": true +} +``` + +``` +$ nixq github:NixOS/nixpkgs/nixos-unstable 'legacyPackages.x86_64-linux.python313Packages' --keys | jq .[:5] +[ + "acompressor", + "autocrop", + "autodeint", + "autoload", + "autosub", +] +``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..30ef198 --- /dev/null +++ b/flake.lock @@ -0,0 +1,59 @@ +{ + "nodes": { + "nix-select": { + "locked": { + "lastModified": 1739677887, + "narHash": "sha256-VIGgoWnzXzlwRHfJt3YRNQsRyX1I1dPdVm61hnyKJZo=", + "ref": "refs/heads/main", + "rev": "d2aa1f72b6e1ac318f4125d6eebee256bbeb05a3", + "revCount": 11, + "type": "git", + "url": "https://git.clan.lol/clan/nix-select.git" + }, + "original": { + "type": "git", + "url": "https://git.clan.lol/clan/nix-select.git" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1739866667, + "narHash": "sha256-EO1ygNKZlsAC9avfcwHkKGMsmipUk1Uc0TbrEZpkn64=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "73cf49b8ad837ade2de76f87eb53fc85ed5d4680", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1739667890, + "narHash": "sha256-7QtSNdCEbYG1v+ZVrFWhBkhlo2GWehPffWC0BP1VZSo=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "9b883b6d4d3bd580734ddb4b5bfde8ebffd26559", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "nix-select": "nix-select", + "nixpkgs": "nixpkgs", + "nixpkgs-lib": "nixpkgs-lib" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9e87bbd --- /dev/null +++ b/flake.nix @@ -0,0 +1,44 @@ +{ + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + inputs.nix-select.url = "git+https://git.clan.lol/clan/nix-select.git"; + inputs.nixpkgs-lib.url = "github:nix-community/nixpkgs.lib"; + + outputs = inputs: + let + forAllSystems = f: inputs.nixpkgs.lib.genAttrs inputs.nixpkgs.lib.systems.flakeExposed (system: f rec { + inherit system; + pkgs = inputs.nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + }); + in { + inherit inputs; + + overlays.default = prev: { + nixq = prev.callPackage ./package.nix { inherit (inputs) nix-select nixpkgs-lib; }; + }; + + packages = forAllSystems ({ pkgs, ... }: rec { + default = nixq; + nixq = pkgs.callPackage ./package.nix { inherit (inputs) nix-select nixpkgs-lib; }; + nixq-nix = pkgs.callPackage ./package.nix { inherit (inputs) nix-select nixpkgs-lib; extraRuntimeDeps = [ pkgs.nix ]; }; + nixq-lix = pkgs.callPackage ./package.nix { inherit (inputs) nix-select nixpkgs-lib; extraRuntimeDeps = [ pkgs.lix ]; }; + }); + + devShells = forAllSystems ({ lib, pkgs, ... }: rec { + default = pkgs.mkShellNoCC { + packages = builtins.attrValues { + inherit (pkgs) + lix + jq + yq + bat + ; + }; + env.NIXQ_NIX_SELECT_PATH = inputs.nix-select.outPath; + env.NIXQ_NIXPKGS_LIB_PATH = inputs.nixpkgs.outPath; + env.UV_PYTHON_DOWNLOADS = "never"; + }; + }); + + }; +} diff --git a/nixq.sh b/nixq.sh new file mode 100755 index 0000000..a5ad4c7 --- /dev/null +++ b/nixq.sh @@ -0,0 +1,219 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -z "${NIXQ_NIX_SELECT_PATH:-}" ]; then + >&2 echo "nix-select not found! Please set NIXQ_NIX_SELECT_PATH!" +fi +if [ -z "${NIXQ_NIXPKGS_LIB_PATH:-}" ]; then + >&2 echo "nixpkgs-lib not found! Please set NIXQ_NIXPKGS_LIB_PATH!" +fi + +die() { + >&2 echo "ERROR:" "$@" + exit 1 +} + +print_help() { + echo "nixq: A nix tool to query elements/lists/attrs from nested list/attrs," + echo " using the nix-select library: https://git.clan.lol/clan/nix-select/" + echo "" + echo "Usage:" + echo " $0 [-f] <file> [-q] <query> [-k] [-v] [-D] [-y] [--apply <expr>] ..." + echo "" + echo " -f, --file <file> Load a file, typically named default.nix" + echo " -f, --file <file> Load a file, typically named default.nix" + echo " [--flake] <file> Load a flake, does not support attributes after '#'" + echo " [-q, --query] <query> The select query to perform" + echo " -k, --keys, --names Will '--apply builtins.attrName'" + echo " -v, --values Will '--apply builtins.attrValue'" + echo " -t, --try-values Will tryEval the attribute set values" + # echo " -D, --rename-outPath Rename \"outPath\" keys to \"outPath'\"" + echo " -y, --yaml Output as yaml" + echo " --raw Output raw" + echo " --apply <expr> Apply custom expression with nixpkgs 'lib' in scope, can be used multiple times" + echo " ... Passed on to 'nix eval'" + # PAGER="" nix --extra-experimental-features "nix-command flakes" eval --help +} + +nix_str() { + local out="$1" + out="${out/\"/\\\"}" + out="${out/\$/\\\$}" + printf '"%s"\n' "$out" +} + +lib_expr="let lib = import $NIXQ_NIXPKGS_LIB_PATH/lib; in" + +if [[ $# -eq 0 ]]; then + print_help + exit 0 +fi + +file_expr="" +query="" +output_raw=false +output_yaml=false + +set_file_expr_normal() { + if [[ -z "${1:-}" ]]; then + die "No file provided" + fi + local path + if [[ $1 =~ \<[a-zA-Z0-9_-]+\> ]]; then + path="$1" + else + path="$(nix_str "$(realpath "$1")")" + fi + file_expr="(let f = import $path; in if builtins.isFunction f then f {} else f)" +} +set_file_expr_flake() { + if [[ -z "${1:-}" ]]; then + die "No flake provided" + fi + file_expr="(builtins.getFlake $(nix_str "$1"))" +} + +posargs=() +filtered_args=() + +while [[ "$#" -gt 0 ]]; do + arg="$1" + case "$1" in + -[^-][^-]*) + shift + # shellcheck disable=SC2046 + set -- $(echo "${arg:1:99999}" | sed -e 's/\(.\)/ -\1/g') "$@" + ;; + --help) + print_help + exit 0 + ;; + -f | --file) + shift + set_file_expr_normal "$1" + shift + ;; + --flake) + shift + set_file_expr_flake "$1" + shift + ;; + -q | --query) + shift + query="$1" + shift + ;; + -y | --yaml) + output_yaml=true + shift + ;; + --raw) + output_raw=true + filtered_args+=("$1") + shift + ;; + -k | --keys | --names) + filtered_args+=("--apply" "builtins.attrNames") + shift + ;; + -v | --values) + filtered_args+=("--apply" "builtins.attrValues") + shift + ;; + #-D | --rename-out-path) + # filtered_args+=("--apply" "$lib_expr lib.mapAttrs' (k: v: { name= { outPath=\"outPath'\"; }.\${k} or k; value = v; })"); + # shift + # ;; + -t | --try-values) + # TODO: figure out how to force the full value + # filtered_args+=("--apply" "builtins.mapAttrs (k: v: if (builtins.tryEval (builtins.toJSON v == \"\")).success then v else null)"); + filtered_args+=("--apply" "builtins.mapAttrs (k: v: if (builtins.tryEval (v == null)).success then v else null)") + shift + ;; + #-d | --max-depth) + # filtered_args+=("--apply" ""); + # shift + # ;; + --apply) + filtered_args+=("$1") + shift + filtered_args+=("$lib_expr $1") + shift + ;; + [^-]*) + posargs+=("$1") + shift + ;; + *) + filtered_args+=("$1") + shift + ;; + esac +done + +for arg in "${posargs[@]}"; do + if [[ -z "$file_expr" ]]; then + set_file_expr_flake "$arg" + elif [[ -z "$query" ]]; then + query="$arg" + else + die "Too many positional arguments!" + fi +done + +if [[ -z "$file_expr" ]]; then + die "No file provided" +fi +if [[ -z "$query" ]]; then + die "No query provided" +fi + +if ! $output_raw; then + # TODO: figure out why this is slow + #filtered_args+=( + # "--apply" + # ' + # let + # mapTree = x: + # if !(builtins.tryEval (x==null)).success then + # null + # else if builtins.isAttrs x then + # builtins.mapAttrs (k: v: mapTree v) x + # else if builtins.isList x then + # builtins.map mapTree x + # else if builtins.isFunction x then + # "<lambda>" + # else + # x; + # in mapTree + # ' + #); + filtered_args+=(--json) +fi + +nix_cmd=( + nix + --extra-experimental-features "nix-command flakes" + eval --impure + --expr "(import $NIXQ_NIX_SELECT_PATH/select.nix).select $(nix_str "$query") $file_expr" + "${filtered_args[@]}" +) + +output="$( + # set -x + "${nix_cmd[@]}" +)" + +if $output_raw; then + cat <<<"$output" +elif $output_yaml; then + if [[ -t 1 ]] && >/dev/null command -v bat; then + yq . -y <<<"$output" | bat --language yaml --style plain --paging never + else + yq . -y <<<"$output" + fi +elif [[ -t 1 ]] && >/dev/null command -v jq; then + jq . <<<"$output" +else + cat <<<"$output" +fi diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..7f31bd1 --- /dev/null +++ b/package.nix @@ -0,0 +1,31 @@ +{ + writeShellApplication, + coreutils, + gnused, + jq, + yq, + bat, + + nix-select ? "", + nixpkgs-lib ? "", + extraRuntimeDeps ? [ ], +}: +writeShellApplication { + name = "nixq"; + + text = builtins.readFile ./nixq.sh; + extraShellCheckFlags = [ "--severity" "info" ]; + + runtimeInputs = [ + coreutils + gnused + jq + yq + bat + ] ++ extraRuntimeDeps; + + runtimeEnv = { + NIXQ_NIX_SELECT_PATH = nix-select; + NIXQ_NIXPKGS_LIB_PATH = nixpkgs-lib; + }; +}