Initial commit

This commit is contained in:
Peder Bergebakken Sundt 2025-02-23 00:04:10 +01:00
commit bdc8ae889d
7 changed files with 409 additions and 0 deletions

1
.envrc Normal file

@ -0,0 +1 @@
use flake

1
.gitignore vendored Normal file

@ -0,0 +1 @@
.direnv/

54
README.md Normal file

@ -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",
]
```

59
flake.lock generated Normal file

@ -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
}

44
flake.nix Normal file

@ -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";
};
});
};
}

219
nixq.sh Executable file

@ -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

31
package.nix Normal file

@ -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;
};
}