A structured-data shell in Gleam, inspired by Nushell
5.5 kB
175 lines
1{
2 description = "gleshell — structured-data shell in Gleam, inspired by Nushell";
3
4 inputs = {
5 nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6 systems.url = "github:nix-systems/default";
7 devenv.url = "github:cachix/devenv";
8 devenv.inputs.nixpkgs.follows = "nixpkgs";
9 };
10
11 nixConfig = {
12 extra-substituters = [ "https://devenv.cachix.org" ];
13 extra-trusted-public-keys = [
14 "devenv.cachix.org-1:ppRjvZf4JrQqfA3n7eHkSxX4z1M0RKZ77N8D66zCFAI="
15 ];
16 };
17
18 outputs =
19 {
20 self,
21 nixpkgs,
22 devenv,
23 systems,
24 ...
25 }@inputs:
26 let
27 forEachSystem = nixpkgs.lib.genAttrs (import systems);
28 in
29 {
30 packages = forEachSystem (
31 system:
32 let
33 pkgs = nixpkgs.legacyPackages.${system};
34 inherit (pkgs) lib;
35
36 gleamToml = lib.importTOML ./gleam.toml;
37 manifestToml = lib.importTOML ./manifest.toml;
38
39 hexPackages = builtins.filter (p: p.source == "hex") manifestToml.packages;
40
41 packagesTOML = lib.concatStringsSep "\n" (
42 [ "[packages]" ] ++ map (p: "${p.name} = \"${p.version}\"") hexPackages
43 );
44
45 # Self-contained store package: compiles to Erlang shipment, wraps with
46 # store-path Erlang. No source checkout / GLESHELL_ROOT required at runtime.
47 gleshell = pkgs.stdenv.mkDerivation {
48 pname = "gleshell";
49 version = gleamToml.version;
50
51 src = lib.cleanSourceWith {
52 src = ./.;
53 filter =
54 path: type:
55 let
56 base = baseNameOf path;
57 in
58 lib.cleanSourceFilter path type
59 && base != "build"
60 && base != "result"
61 && base != ".devenv"
62 && base != ".direnv"
63 && base != "erl_crash.dump"
64 # Root escript is a regular file; keep the src/gleshell/ directory.
65 && !(type == "regular" && base == "gleshell")
66 && !(lib.hasSuffix ".escript" base);
67 };
68
69 nativeBuildInputs = [
70 pkgs.gleam
71 pkgs.beamPackages.erlang
72 pkgs.rebar3
73 pkgs.beamPackages.hex
74 pkgs.rsync
75 ];
76
77 configurePhase = ''
78 runHook preConfigure
79
80 mkdir -p build/packages
81 cat <<EOF > build/packages/packages.toml
82 ${packagesTOML}
83 EOF
84
85 # Vendor Hex deps so the pure Nix build never hits the network.
86 ${lib.concatMapStringsSep "\n" (p: ''
87 rsync --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r -r ${
88 pkgs.fetchHex {
89 pkg = p.name;
90 version = p.version;
91 sha256 = p.outer_checksum;
92 }
93 }/* build/packages/${p.name}/
94 '') hexPackages}
95
96 runHook postConfigure
97 '';
98
99 buildPhase = ''
100 runHook preBuild
101 export REBAR_CACHE_DIR="$TMP/.rebar-cache"
102 gleam export erlang-shipment
103 runHook postBuild
104 '';
105
106 installPhase = ''
107 runHook preInstall
108
109 mkdir -p $out/{bin,lib/gleshell}
110 rsync --exclude=entrypoint.sh --exclude=entrypoint.ps1 \
111 -r build/erlang-shipment/* $out/lib/gleshell/
112
113 # Bake -pa paths at install time (each ebin needs its own -pa).
114 ebin_args=()
115 for d in $out/lib/gleshell/*/ebin; do
116 ebin_args+=(-pa "$d")
117 done
118
119 # Runtime wrapper: fixed store paths, works from any cwd.
120 # +Bc: Ctrl+C cancels the line; do not open the Erlang BREAK menu.
121 {
122 echo "#!${pkgs.runtimeShell}"
123 echo "set -eu"
124 echo 'export ERL_AFLAGS="+Bc ''${ERL_AFLAGS:-}"'
125 echo "exec ${pkgs.beamPackages.erlang}/bin/erl ''${ebin_args[*]} -eval \"gleshell@@main:run(gleshell)\" -noshell -extra \"\$@\""
126 } > $out/bin/gle
127 chmod +x $out/bin/gle
128 ln -s gle $out/bin/gleshell
129
130 runHook postInstall
131 '';
132
133 meta = {
134 description = gleamToml.description;
135 mainProgram = "gle";
136 license = lib.licenses.asl20;
137 };
138 };
139
140 # Prompt icons (``, ``, …). Install so the REPL glyph set renders:
141 # nix profile install .#nerd-fonts
142 # On NixOS/home-manager prefer fonts.packages instead.
143 nerd-fonts = pkgs.nerd-fonts.symbols-only;
144 in
145 {
146 default = gleshell;
147 inherit gleshell nerd-fonts;
148 }
149 );
150
151 apps = forEachSystem (system: {
152 default = {
153 type = "app";
154 program = "${self.packages.${system}.gleshell}/bin/gle";
155 };
156 });
157
158 devShells = forEachSystem (
159 system:
160 let
161 pkgs = nixpkgs.legacyPackages.${system};
162 in
163 {
164 # --no-pure-eval (or direnv) is required so devenv can resolve
165 # devenv.root from $PWD; pure eval would point ./. at the store.
166 default = devenv.lib.mkShell {
167 inherit inputs pkgs;
168 modules = [ ./devenv.nix ];
169 };
170 }
171 );
172
173 formatter = forEachSystem (system: nixpkgs.legacyPackages.${system}.nixfmt-rfc-style);
174 };
175}