Skip to content

Commit 53d1431

Browse files
committed
cli: init with template
1 parent 6bde927 commit 53d1431

File tree

6 files changed

+142
-56
lines changed

6 files changed

+142
-56
lines changed

devenv/src/cli.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,11 @@ impl GlobalOptions {
182182
#[derive(Subcommand, Clone)]
183183
pub enum Commands {
184184
#[command(about = "Scaffold devenv.yaml, devenv.nix, .gitignore and .envrc.")]
185-
Init { target: Option<PathBuf> },
185+
Init {
186+
target: Option<PathBuf>,
187+
#[arg(long, help = "Use template")]
188+
template: Option<String>,
189+
},
186190

187191
#[command(about = "Generate devenv.yaml and devenv.nix using AI")]
188192
Generate {

devenv/src/cnix.rs

+16
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,22 @@ impl Nix {
270270

271271
Ok(())
272272
}
273+
274+
pub async fn flake_clone(&self, template: &str) -> Result<String> {
275+
// TODO: generate temp directory
276+
let temp_dir = "/tmp/devenv-clone";
277+
self.run_nix("nix", &["flake", "clone", template, "--dest", temp_dir], &self.options).await?;
278+
Ok(temp_dir.to_string())
279+
}
280+
281+
pub async fn init(&self, path: &Path, template: &String) -> Result<()> {
282+
let mut cmd =
283+
self.prepare_command("nix", &["flake", "init", "-t", template], &self.options)?;
284+
cmd.current_dir(path);
285+
self.run_nix_command(cmd, &self.options).await?;
286+
287+
Ok(())
288+
}
273289

274290
pub async fn metadata(&self) -> Result<String> {
275291
let options = Options {

devenv/src/devenv.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl Devenv {
144144
self.devenv_dotfile.join("processes.pid")
145145
}
146146

147-
pub fn init(&self, target: &Option<PathBuf>) -> Result<()> {
147+
pub async fn init(&mut self, target: &Option<PathBuf>, template: &Option<String>) -> Result<()> {
148148
let target = target
149149
.clone()
150150
.unwrap_or_else(|| fs::canonicalize(".").expect("Failed to get current directory"));
@@ -154,6 +154,21 @@ impl Devenv {
154154
std::fs::create_dir_all(&target).expect("Failed to create target directory");
155155
}
156156

157+
if let Some(template) = template {
158+
let temp_dir = self.nix.flake_clone(template).await?;
159+
let options = DevenvOptions {
160+
devenv_root: Some(Path::new(&temp_dir).to_path_buf()),
161+
..DevenvOptions::default()
162+
};
163+
let mut devenv = Devenv::new(options).await;
164+
devenv.assemble(false).await?;
165+
// Required, so that the directory is not viewed as a Git
166+
// repository, otherwise the .devenv.flake.nix is ignored.
167+
let flake_ref = format!("path:{}", &temp_dir);
168+
devenv.nix.init(&target, &flake_ref).await?;
169+
return Ok(());
170+
}
171+
157172
for filename in REQUIRED_FILES {
158173
info!("Creating {}", filename);
159174

devenv/src/flake.tmpl.nix

+1
Original file line numberDiff line numberDiff line change
@@ -135,5 +135,6 @@
135135
});
136136
devenv = config;
137137
build = build project.options project.config;
138+
templates = config.templates;
138139
};
139140
}

devenv/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ async fn main() -> Result<()> {
133133
};
134134
Ok(())
135135
}
136-
Commands::Init { target } => devenv.init(&target),
136+
Commands::Init { target, template } => devenv.init(&target, &template).await,
137137
Commands::Generate { .. } => match which::which("devenv-generate") {
138138
Ok(devenv_generate) => {
139139
let error = Command::new(devenv_generate)

src/modules/top-level.nix

+103-53
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
{ config, pkgs, lib, bootstrapPkgs ? null, ... }:
1+
{
2+
config,
3+
pkgs,
4+
lib,
5+
bootstrapPkgs ? null,
6+
...
7+
}:
28
let
39
types = lib.types;
410
# Returns a list of all the entries in a folder
5-
listEntries = path:
6-
map (name: path + "/${name}") (builtins.attrNames (builtins.readDir path));
11+
listEntries = path: map (name: path + "/${name}") (builtins.attrNames (builtins.readDir path));
712

8-
drvOrPackageToPaths = drvOrPackage:
13+
drvOrPackageToPaths =
14+
drvOrPackage:
915
if drvOrPackage ? outputs then
1016
builtins.map (output: drvOrPackage.${output}) drvOrPackage.outputs
1117
else
@@ -16,23 +22,44 @@ let
1622
ignoreCollisions = true;
1723
};
1824

19-
failedAssertions = builtins.map (x: x.message) (builtins.filter (x: !x.assertion) config.assertions);
25+
failedAssertions = builtins.map (x: x.message) (
26+
builtins.filter (x: !x.assertion) config.assertions
27+
);
2028

2129
performAssertions =
2230
let
23-
formatAssertionMessage = message:
31+
formatAssertionMessage =
32+
message:
2433
let
2534
lines = lib.splitString "\n" message;
2635
in
2736
"- ${lib.concatStringsSep "\n " lines}";
2837
in
29-
if failedAssertions != [ ]
30-
then
38+
if failedAssertions != [ ] then
3139
throw ''
3240
Failed assertions:
3341
${lib.concatStringsSep "\n" (builtins.map formatAssertionMessage failedAssertions)}
3442
''
35-
else lib.trivial.showWarnings config.warnings;
43+
else
44+
lib.trivial.showWarnings config.warnings;
45+
templateType = types.submodule (
46+
{ name, config, ... }:
47+
{
48+
options = {
49+
path = lib.mkOption {
50+
type = lib.types.path;
51+
};
52+
description = lib.mkOption {
53+
type = types.str;
54+
description = "description";
55+
};
56+
welcomeText = lib.mkOption {
57+
type = types.str;
58+
description = "description";
59+
};
60+
};
61+
}
62+
);
3663
in
3764
{
3865
options = {
@@ -89,15 +116,14 @@ in
89116

90117
# Remove the default apple-sdk on macOS.
91118
# Allow users to specify an optional SDK in `apple.sdk`.
92-
apply = stdenv:
93-
if stdenv.isDarwin
94-
then
95-
stdenv.override
96-
(prev: {
97-
extraBuildInputs =
98-
builtins.filter (x: !lib.hasPrefix "apple-sdk" x.pname) prev.extraBuildInputs;
99-
})
100-
else stdenv;
119+
apply =
120+
stdenv:
121+
if stdenv.isDarwin then
122+
stdenv.override (prev: {
123+
extraBuildInputs = builtins.filter (x: !lib.hasPrefix "apple-sdk" x.pname) prev.extraBuildInputs;
124+
})
125+
else
126+
stdenv;
101127

102128
};
103129

@@ -170,7 +196,12 @@ in
170196
type = types.listOf types.unspecified;
171197
internal = true;
172198
default = [ ];
173-
example = [{ assertion = false; message = "you can't enable this for that reason"; }];
199+
example = [
200+
{
201+
assertion = false;
202+
message = "you can't enable this for that reason";
203+
}
204+
];
174205
description = ''
175206
This option allows modules to express conditions that must
176207
hold for the evaluation of the configuration to succeed,
@@ -201,6 +232,11 @@ in
201232
'';
202233
};
203234

235+
templates = lib.mkOption {
236+
type = lib.types.attrsOf templateType;
237+
description = "A set of templates.";
238+
};
239+
204240
devenv = {
205241
root = lib.mkOption {
206242
type = types.str;
@@ -243,7 +279,12 @@ in
243279
xdg = builtins.getEnv "XDG_RUNTIME_DIR";
244280
tmp = builtins.getEnv "TMPDIR";
245281
in
246-
if xdg != "" then xdg else if tmp != "" then tmp else "/tmp";
282+
if xdg != "" then
283+
xdg
284+
else if tmp != "" then
285+
tmp
286+
else
287+
"/tmp";
247288
};
248289

249290
profile = lib.mkOption {
@@ -253,26 +294,26 @@ in
253294
};
254295
};
255296

256-
imports = [
257-
./info.nix
258-
./outputs.nix
259-
./files.nix
260-
./processes.nix
261-
./outputs.nix
262-
./scripts.nix
263-
./update-check.nix
264-
./containers.nix
265-
./debug.nix
266-
./lib.nix
267-
./tests.nix
268-
./cachix.nix
269-
./tasks.nix
270-
]
271-
++ (listEntries ./languages)
272-
++ (listEntries ./services)
273-
++ (listEntries ./integrations)
274-
++ (listEntries ./process-managers)
275-
;
297+
imports =
298+
[
299+
./info.nix
300+
./outputs.nix
301+
./files.nix
302+
./processes.nix
303+
./outputs.nix
304+
./scripts.nix
305+
./update-check.nix
306+
./containers.nix
307+
./debug.nix
308+
./lib.nix
309+
./tests.nix
310+
./cachix.nix
311+
./tasks.nix
312+
]
313+
++ (listEntries ./languages)
314+
++ (listEntries ./services)
315+
++ (listEntries ./integrations)
316+
++ (listEntries ./process-managers);
276317

277318
config = {
278319
assertions = [
@@ -285,7 +326,10 @@ in
285326
'';
286327
}
287328
{
288-
assertion = config.devenv.flakesIntegration || config.overlays == [ ] || lib.versionAtLeast config.devenv.cliVersion "1.4.2";
329+
assertion =
330+
config.devenv.flakesIntegration
331+
|| config.overlays == [ ]
332+
|| lib.versionAtLeast config.devenv.cliVersion "1.4.2";
289333
message = ''
290334
Using overlays requires devenv 1.4.2 or higher, while your current version is ${config.devenv.cliVersion}.
291335
'';
@@ -305,8 +349,7 @@ in
305349
packages = [
306350
# needed to make sure we can load libs
307351
pkgs.pkg-config
308-
]
309-
++ lib.optional (config.apple.sdk != null) config.apple.sdk;
352+
] ++ lib.optional (config.apple.sdk != null) config.apple.sdk;
310353

311354
enterShell = lib.mkBefore ''
312355
export PS1="\[\e[0;34m\](devenv)\[\e[0m\] ''${PS1-}"
@@ -357,19 +400,26 @@ in
357400
nativeBuildInputs = partitionedPkgs.wrong;
358401
in
359402
performAssertions (
360-
(pkgs.mkShell.override { stdenv = config.stdenv; }) ({
361-
name = "devenv-shell";
362-
hardeningDisable = config.hardeningDisable;
363-
inherit buildInputs nativeBuildInputs;
364-
shellHook = ''
365-
${lib.optionalString config.devenv.debug "set -x"}
366-
${config.enterShell}
367-
'';
368-
} // config.env)
403+
(pkgs.mkShell.override { stdenv = config.stdenv; }) (
404+
{
405+
name = "devenv-shell";
406+
hardeningDisable = config.hardeningDisable;
407+
inherit buildInputs nativeBuildInputs;
408+
shellHook = ''
409+
${lib.optionalString config.devenv.debug "set -x"}
410+
${config.enterShell}
411+
'';
412+
}
413+
// config.env
414+
)
369415
);
370416

371417
infoSections."env" = lib.mapAttrsToList (name: value: "${name}: ${toString value}") config.env;
372-
infoSections."packages" = builtins.map (package: package.name) (builtins.filter (package: !(builtins.elem package.name (builtins.attrNames config.scripts))) config.packages);
418+
infoSections."packages" = builtins.map (package: package.name) (
419+
builtins.filter (
420+
package: !(builtins.elem package.name (builtins.attrNames config.scripts))
421+
) config.packages
422+
);
373423

374424
_module.args.pkgs = bootstrapPkgs.appendOverlays config.overlays;
375425

0 commit comments

Comments
 (0)