Initial commit
This commit is contained in:
commit
bdc8ae889d
1
.envrc
Normal file
1
.envrc
Normal file
@ -0,0 +1 @@
|
||||
use flake
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.direnv/
|
54
README.md
Normal file
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
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
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
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
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;
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user