Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
template: add env() function
Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
  • Loading branch information
chawyehsu committed Mar 14, 2026
commit 406c41ca4cf592b91e1f1d423fb5fbba826e451c
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* [Diff colors](docs/config.md#diff-colors-and-styles) can now be configured
differently for each format.

* Add `env(name)` template function that looks up the value of a given
environment variable.

### Fixed bugs

## [0.39.0] - 2026-03-04
Expand Down
9 changes: 9 additions & 0 deletions cli/src/template_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,15 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
// .decorated("", "") to trim leading/trailing whitespace
Ok(Literal(value.map(|v| v.decorated("", ""))).into_dyn_wrapped())
});
map.insert("env", |_language, diagnostics, _build_ctx, function| {
let [var_name_node] = function.expect_exact_arguments()?;
let var_name =
template_parser::catch_aliases(diagnostics, var_name_node, |_diagnostics, node| {
template_parser::expect_string_literal(node)
})?;
let out_property = std::env::var(var_name).unwrap_or_default();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we pass HashMap<String, String> as evaluation context instead? I think it's better to not access std::env::var() in templater. For example, the current working directory is specified by path_converter: &'repo RepoPathUiConverter

Perhaps, we can reuse ConfigEnv::environment.

Copy link
Author

@chawyehsu chawyehsu Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fairly reasonable. I think reusing ConfigEnv's environment would be a good idea for deterministic evaluation, but as of now I definitely need a deeper understanding of jj's codebase to understand how to pass it into the templater's context and use it, considering this is my first contribution. ;p

Ok(Literal(out_property).into_dyn_wrapped())
});
map
}

Expand Down
22 changes: 22 additions & 0 deletions cli/tests/test_templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,28 @@ fn test_templater_config_function() {
insta::assert_snapshot!(render("config('unknown')"), @"");
}

#[test]
fn test_templater_env_function() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "repo"]).success();
let work_dir = test_env.work_dir("repo");
let render = |template| get_template_output(&work_dir, "@-", template);
insta::assert_snapshot!(render("env('JJ_TEMPLATE_ENV')"), @"");

let output = work_dir.run_jj_with(|cmd| {
cmd.args([
"log",
"--no-graph",
"-r",
"@-",
"-T",
"env('JJ_TEMPLATE_ENV')",
])
.env("JJ_TEMPLATE_ENV", "template-env-value")
});
insta::assert_snapshot!(output, @"template-env-value[EOF]");
}

#[must_use]
fn get_template_output(work_dir: &TestWorkDir, rev: &str, template: &str) -> CommandOutput {
work_dir.run_jj(["log", "--no-graph", "-r", rev, "-T", template])
Expand Down
2 changes: 2 additions & 0 deletions docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ The following functions are defined.
* `git_web_url([remote: String]) -> String`: Best-effort conversion of a git
remote URL to an HTTPS web URL. Defaults to the "origin" remote. Returns an
empty string on failure. SSH host alias resolution is currently unsupported.
* `env(name: StringLiteral) -> String`:
Look up the value of a given environment variable by `name`.

## Types

Expand Down