Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions bin/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ open Ns.Cmd
open Ns.Util

let main
({ installables; target_info; printcmd; force_experimental_features } :
({ installables; target_info; printcmd; force_experimental_features; sh; _ } :
Cli.strategy)
=
let ({ entrypoint; attribute; subshell_dir } : Cli.target_info) = target_info in
Expand All @@ -17,19 +17,20 @@ let main
| Some entrypoint ->
if Unix.flake_exists_at entrypoint
then
( Cmd.nix_develop entrypoint attribute force_experimental_features
( Cmd.nix_develop entrypoint attribute force_experimental_features sh
, if Unix.shell_exists_at entrypoint
then Some (Cmd.legacy_nix_shell_from_entrypoint entrypoint attribute)
then Some (Cmd.legacy_nix_shell_from_entrypoint entrypoint attribute sh)
else None )
else if Unix.shell_exists_at entrypoint
then Cmd.legacy_nix_shell_from_entrypoint entrypoint attribute, None
then Cmd.legacy_nix_shell_from_entrypoint entrypoint attribute sh, None
else Error.handle_ns_error "no available devshell entrypoint: %s\n%!" entrypoint
| None ->
( Cmd.nix_shell
(List.map Uri.uri_to_string installables)
force_experimental_features
sh
, Option.map
(fun installables -> Cmd.legacy_nix_shell_from_installables installables)
(fun installables -> Cmd.legacy_nix_shell_from_installables installables sh)
(Uri.combine_installables_tr installables) )
in
{ workdir; primary; fallback }
Expand Down
1 change: 1 addition & 0 deletions default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(builtins.getFlake (toString ./.)).outputs.packages.${builtins.currentSystem}.default
30 changes: 28 additions & 2 deletions lib/cli.ml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ module Cli = struct
; target_info : target_info
; printcmd : bool
; force_experimental_features : string list option
; sh : string
; sharg : string list
; impure : bool
}

(** Positional URI arguments passed to the program via the command line. *)
Expand All @@ -39,9 +42,28 @@ module Cli = struct
Arg.(value & flag & info [ "force"; "f" ] ~doc)
;;

let sh =
let doc =
"The shell to use in the subshell. Accepts a package name (e.g. zsh), a nix-store \
path, or a local path (e.g. /bin/sh). Defaults to the current user's login shell \
or bash if no user is logged in."
in
Arg.(value & opt filepath (Unix.eval_sh (Unix.pw_shell ())) & info [ "sh" ] ~doc)
;;

let sharg =
let doc = "Supply arguments to the shell." in
Arg.(value & opt_all string [] & info [ "sharg" ] ~doc)
;;

let impure =
let doc = "Allow the current shell environment to bleed into the subshell." in
Arg.(value & flag & info [ "impure" ] ~doc)
;;

(** Processes the args so that the main function knows what to do with them. *)
let make_strategy =
let build original_args printcmd force_experimental_features =
let build original_args printcmd force_experimental_features sh sharg impure =
let installables, target_info =
let default_installables = []
and default_target =
Expand Down Expand Up @@ -111,9 +133,13 @@ module Cli = struct
; "nix-command"
]
else None)
; sh = Unix.eval_sh sh
; sharg
; impure
}
in
Term.(const build $ uris $ printcmd $ force_experimental_features)
Term.(
const build $ uris $ printcmd $ force_experimental_features $ sh $ sharg $ impure)
;;

let cmd entrypoint =
Expand Down
22 changes: 12 additions & 10 deletions lib/cmd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,34 @@ module Cmd = struct
| args -> List.fold_left (fun cmd arg -> Bos.Cmd.add_arg cmd arg) cmd args
;;

let nix_develop entrypoint attribute force_experimental_features =
(* TODO: nix develop --ignore-env behaves differently than nix-shell --pure
which is causing none of the packages to show up. *)
let nix_develop entrypoint attribute force_experimental_features sh =
Bos.Cmd.v "nix"
|>+ [ "develop" ] @ [ Uri.sprintf_uri_attr_opt entrypoint attribute ]
|>+ Option.value ~default:[] force_experimental_features
|>+ [ "--command"; Unix.shell () ]
(* |>+ [ "--ignore-env"; "-k"; "TERM"; "-k"; "TERMINFO"; "-k"; "HOME"; "-k"; "USER"; "-k"; "DISPLAY" ] *)
|>+ [ "--command"; sh ]
;;

let legacy_nix_shell_from_entrypoint entrypoint attribute =
let legacy_nix_shell_from_entrypoint entrypoint attribute sh =
Bos.Cmd.v "nix-shell"
|>+ Option.value ~default:[] (Option.map (fun attr -> [ "--attr"; attr ]) attribute)
|>+ [ entrypoint ]
|>+ [ "--command"; Unix.shell () ]
|>+ [ "--pure" ]
|>+ [ "--command"; sh ]
;;

let nix_shell installables force_experimental_features =
let nix_shell installables force_experimental_features sh =
Bos.Cmd.v "nix"
|>+ [ "shell" ]
|>+ installables
|>+ Option.value ~default:[] force_experimental_features
|>+ [ "--command"; sh ]
;;

let legacy_nix_shell_from_installables installables =
(* Need to combine/validate that the installables given are all Nixpkgs *)
Bos.Cmd.v "nix-shell"
|>+ [ "--packages" ] @ installables
|>+ [ "--command"; Unix.shell () ]
let legacy_nix_shell_from_installables installables sh =
Bos.Cmd.v "nix-shell" |>+ [ "--packages" ] @ installables |>+ [ "--command"; sh ]
;;

let print_strategy ({ workdir; primary; fallback } : strategy) =
Expand Down
35 changes: 34 additions & 1 deletion lib/util.ml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module Util = struct
;;

(** Get the user's login shell *)
let shell () = (Unix.getpwuid (Unix.getuid ())).Unix.pw_shell
let pw_shell () = (Unix.getpwuid (Unix.getuid ())).Unix.pw_shell

(** Whether a flake.nix file exists at the given directory *)
let flake_exists_at dir = Sys.file_exists (Filename.concat dir "flake.nix")
Expand All @@ -33,6 +33,39 @@ module Util = struct
Sys.file_exists (Filename.concat dir "shell.nix")
|| Sys.file_exists (Filename.concat dir "default.nix")
;;

(** Determines which shell to use. If the user gives a package
we build it and evaluate the main program to get the store path.
If there is no login shell, default to nixpkgs#bash. *)
let eval_sh sh =
let default = "bash"
and get_bin pkg =
let run_cmd cmd =
let ic = Unix.open_process_in cmd in
let result = Option.get (In_channel.input_line ic) in
(* TODO: Handle this potential error *)
let _ = Unix.close_process_in ic in
result
in
let store_path =
run_cmd (Printf.sprintf "nix-build '<nixpkgs>' -A %s --no-out-link --quiet" pkg)
and program =
run_cmd
(Printf.sprintf
"nix-instantiate '<nixpkgs>' --eval --raw -A %s.meta.mainProgram"
pkg)
in
let bin_path = Printf.sprintf "%s/bin/%s" store_path program in
bin_path
in
if sh = "/noshell" || sh = "/sbin/nologin"
then get_bin default
else (
match String.split_on_char '/' sh with
| [ "" ] -> Error.handle_ns_error "invalid argument: shell cannot be empty\n%!"
| [ pkg ] -> get_bin pkg
| _ -> sh)
;;
end

(** Types and functions for parsing, interacting and formatting URIs. *)
Expand Down
1 change: 1 addition & 0 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(builtins.getFlake (toString ./.)).outputs.devShells.${builtins.currentSystem}.default
Loading