Compare commits
10 Commits
8d3d88fc13
...
393dbabc9b
Author | SHA1 | Date | |
---|---|---|---|
|
393dbabc9b | ||
|
38873dcb74 | ||
|
3ddd1b1683 | ||
|
0eb2a0678c | ||
|
3c40c0c6eb | ||
|
70d1e1b3af | ||
|
af249dbce1 | ||
|
7c30fb4944 | ||
|
6fe068c432 | ||
|
b5ad133b93 |
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@ -73,13 +73,15 @@ jobs:
|
||||
id: ref-type
|
||||
run: cargo run --package ref-type -- --reference ${{ github.ref }} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate Completion Scripts
|
||||
- name: Generate Completion Scripts and Manpage
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
cargo build
|
||||
for shell in bash elvish fish powershell zsh; do
|
||||
./target/debug/just --completions $shell > completions/just.$shell
|
||||
done
|
||||
mkdir -p man
|
||||
./target/debug/just --man > man/just.1
|
||||
|
||||
- name: Package
|
||||
id: package
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
||||
/fuzz/artifacts
|
||||
/fuzz/corpus
|
||||
/fuzz/target
|
||||
/man
|
||||
/target
|
||||
/test-utilities/Cargo.lock
|
||||
/test-utilities/target
|
||||
|
34
CHANGELOG.md
34
CHANGELOG.md
@ -1,6 +1,40 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
[1.28.0](https://github.com/casey/just/releases/tag/1.28.0) - 2024-06-05
|
||||
------------------------------------------------------------------------
|
||||
|
||||
### Changed
|
||||
- Write shebang recipes to $XDG_RUNTIME_DIR ([#2128](https://github.com/casey/just/pull/2128))
|
||||
- Add `set dotenv-required` to require an environment file ([#2116](https://github.com/casey/just/pull/2116))
|
||||
- Don't display submodule recipes in `--list` ([#2112](https://github.com/casey/just/pull/2112))
|
||||
|
||||
### Added
|
||||
- Allow listing recipes in submodules with `--list-submodules` ([#2113](https://github.com/casey/just/pull/2113))
|
||||
- Show recipes in submodules with `--show RECIPE::PATH` ([#2111](https://github.com/casey/just/pull/2111))
|
||||
- Add `--timestamp-format` ([#2106](https://github.com/casey/just/pull/2106) by [neunenak](https://github.com/neunenak))
|
||||
- Allow listing submodule recipes with `--list PATH` ([#2108](https://github.com/casey/just/pull/2108))
|
||||
- Print recipe command timestamps with `--timestamps` ([#2084](https://github.com/casey/just/pull/2084) by [neunenak](https://github.com/neunenak))
|
||||
- Add `module_file()` and `module_directory()` functions ([#2105](https://github.com/casey/just/pull/2105))
|
||||
|
||||
### Fixed
|
||||
- Use space-separated recipe paths in `--choose` ([#2115](https://github.com/casey/just/pull/2115))
|
||||
- Fix bash completion for aliases ([#2104](https://github.com/casey/just/pull/2104) by [laniakea64](https://github.com/laniakea64))
|
||||
|
||||
### Misc
|
||||
- Don't check in manpage ([#2130](https://github.com/casey/just/pull/2130))
|
||||
- Document default shell ([#2129](https://github.com/casey/just/pull/2129))
|
||||
- Remove duplicate section in Chinese readme ([#2127](https://github.com/casey/just/pull/2127) by [potterxu](https://github.com/potterxu))
|
||||
- Update Chinese readme ([#2124](https://github.com/casey/just/pull/2124) by [potterxu](https://github.com/potterxu))
|
||||
- Fix typo in readme ([#2122](https://github.com/casey/just/pull/2122) by [potterxu](https://github.com/potterxu))
|
||||
- Don't check in auto-generated completion scripts ([#2120](https://github.com/casey/just/pull/2120))
|
||||
- Document when dependencies run in readme ([#2103](https://github.com/casey/just/pull/2103))
|
||||
- Build aarch64-pc-windows-msvc release binaries ([#2100](https://github.com/casey/just/pull/2100) by [alshdavid](https://github.com/alshdavid))
|
||||
- Clarify that `dotenv-path`-given env file is required ([#2099](https://github.com/casey/just/pull/2099))
|
||||
- Print multi-line doc comments before recipe in `--list` ([#2090](https://github.com/casey/just/pull/2090))
|
||||
- List unsorted imported recipes by import depth and offset ([#2092](https://github.com/casey/just/pull/2092))
|
||||
- Update README.md ([#2091](https://github.com/casey/just/pull/2091) by [laniakea64](https://github.com/laniakea64))
|
||||
|
||||
[1.27.0](https://github.com/casey/just/releases/tag/1.27.0) - 2024-05-25
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
38
Cargo.lock
generated
38
Cargo.lock
generated
@ -67,9 +67,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.3"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
|
||||
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@ -505,12 +505,6 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@ -593,7 +587,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "just"
|
||||
version = "1.27.0"
|
||||
version = "1.28.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"blake3",
|
||||
@ -782,9 +776,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.83"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -963,18 +957,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.202"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.202"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1090,11 +1084,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.2"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
@ -1125,9 +1119,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "target"
|
||||
version = "2.0.1"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4df6b0340c7cc29eb3b955cc588d145ed60651bf1ab939083295d19ec8cc282"
|
||||
checksum = "1e8f05f774b2db35bdad5a8237a90be1102669f8ea013fea9777b366d34ab145"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
@ -1224,9 +1218,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "update-contributors"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "just"
|
||||
version = "1.27.0"
|
||||
version = "1.28.0"
|
||||
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
||||
autotests = false
|
||||
categories = ["command-line-utilities", "development-tools"]
|
||||
|
22
README.md
22
README.md
@ -999,7 +999,7 @@ $ just test foo "bar baz"
|
||||
#### Shell
|
||||
|
||||
The `shell` setting controls the command used to invoke recipe lines and
|
||||
backticks. Shebang recipes are unaffected.
|
||||
backticks. Shebang recipes are unaffected. The default shell is `sh -cu`.
|
||||
|
||||
```just
|
||||
# use python3 to execute recipe lines and backticks
|
||||
@ -1785,7 +1785,7 @@ js-lint:
|
||||
[group('rust recipes')]
|
||||
[group('lint')]
|
||||
rust-lint:
|
||||
echo 'Runninng Rust linter…'
|
||||
echo 'Running Rust linter…'
|
||||
|
||||
[group('lint')]
|
||||
cpp-lint:
|
||||
@ -2053,6 +2053,23 @@ a $A $B=`echo $A`:
|
||||
When [export](#export) is set, all `just` variables are exported as environment
|
||||
variables.
|
||||
|
||||
#### Unexporting Environment Variables<sup>master</sup>
|
||||
|
||||
Environment variables can be unexported with the `unexport keyword`:
|
||||
|
||||
```just
|
||||
unexport FOO
|
||||
|
||||
@foo:
|
||||
echo $FOO
|
||||
```
|
||||
|
||||
```
|
||||
$ export FOO=bar
|
||||
$ just foo
|
||||
sh: FOO: unbound variable
|
||||
```
|
||||
|
||||
#### Getting Environment Variables from the environment
|
||||
|
||||
Environment variables from the environment are passed automatically to the
|
||||
@ -3673,7 +3690,6 @@ Release x.y.z
|
||||
- Update changelog
|
||||
- Update changelog contributor credits
|
||||
- Update dependencies
|
||||
- Update man page
|
||||
- Update version references in readme
|
||||
```
|
||||
|
||||
|
124
README.中文.md
124
README.中文.md
@ -50,7 +50,7 @@ Yay, all your tests passed!
|
||||
|
||||
- 错误会尽可能被静态地解决。未知的配方和循环依赖关系会在运行之前被报告。
|
||||
|
||||
- `just` 可以 [加载`.env`文件](#env-集成),简化环境变量注入。
|
||||
- `just` 可以 [加载`.env`文件](#环境变量加载),简化环境变量注入。
|
||||
|
||||
- 配方可以在 [命令行中列出](#列出可用的配方)。
|
||||
|
||||
@ -641,18 +641,22 @@ foo:
|
||||
|
||||
#### 设置一览表
|
||||
|
||||
| 名称 | 值 | 默认 | 描述 |
|
||||
| ------------------------- | ------------------ | --------|------------------------------------------------------------------------------- |
|
||||
| `allow-duplicate-recipes` | boolean | False | 允许在 `justfile` 后面出现的配方覆盖之前的同名配方 |
|
||||
| `dotenv-load` | boolean | False | 如果有`.env` 环境变量文件的话,则将其加载 |
|
||||
| `export` | boolean | False | 将所有变量导出为环境变量 |
|
||||
| `fallback` | boolean | False | 如果命令行中的第一个配方没有找到,则在父目录中搜索 `justfile` |
|
||||
| `ignore-comments` | boolean | False | 忽略以`#`开头的配方行 |
|
||||
| `positional-arguments` | boolean | False | 传递位置参数 |
|
||||
| `shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
||||
| `tempdir` | string | - | 在 `tempdir` 位置创建临时目录,而不是系统默认的临时目录 |
|
||||
| `windows-powershell` | boolean | False | 在 Windows 上使用 PowerShell 作为默认 Shell(废弃,建议使用 `windows-shell`) |
|
||||
| `windows-shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
||||
| 名称 | 值 | 默认 | 描述 |
|
||||
| --------------------------- | ------------------ | ----- | --------------------------------------------------------------------------------------- |
|
||||
| `allow-duplicate-recipes` | boolean | False | 允许在 `justfile` 后面出现的配方覆盖之前的同名配方 |
|
||||
| `allow-duplicate-variables` | boolean | False | 允许在 `justfile` 后面出现的变量覆盖之前的同名变量 |
|
||||
| `dotenv-filename` | string | - | 如果有自定义名称的 `.env` 环境变量文件的话,则将其加载 |
|
||||
| `dotenv-load` | boolean | False | 如果有`.env` 环境变量文件的话,则将其加载 |
|
||||
| `dotenv-path` | string | - | 从自定义路径中加载 `.env` 环境变量文件, 文件不存在将会报错。可以覆盖 `dotenv-filename` |
|
||||
| `dotenv-required` | boolean | False | 如果 `.env` 环境变量文件不存在的话,需要报错 |
|
||||
| `export` | boolean | False | 将所有变量导出为环境变量 |
|
||||
| `fallback` | boolean | False | 如果命令行中的第一个配方没有找到,则在父目录中搜索 `justfile` |
|
||||
| `ignore-comments` | boolean | False | 忽略以`#`开头的配方行 |
|
||||
| `positional-arguments` | boolean | False | 传递位置参数 |
|
||||
| `shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
||||
| `tempdir` | string | - | 在 `tempdir` 位置创建临时目录,而不是系统默认的临时目录 |
|
||||
| `windows-powershell` | boolean | False | 在 Windows 上使用 PowerShell 作为默认 Shell(废弃,建议使用 `windows-shell`) |
|
||||
| `windows-shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
||||
|
||||
Bool 类型设置可以写成:
|
||||
|
||||
@ -685,9 +689,69 @@ $ just foo
|
||||
bar
|
||||
```
|
||||
|
||||
#### 允许重复的变量
|
||||
如果 `allow-duplicate-variables` 被设置为 `true`,那么定义多个同名的变量将不会报错。默认为 `false`。
|
||||
|
||||
```just
|
||||
set allow-duplicate-variables
|
||||
|
||||
a := "foo"
|
||||
a := "bar"
|
||||
|
||||
@foo:
|
||||
echo $a
|
||||
```
|
||||
|
||||
```sh
|
||||
$ just foo
|
||||
bar
|
||||
```
|
||||
|
||||
#### 环境变量加载
|
||||
|
||||
如果将 `dotenv-load` 设置为 `true`,并且存在 `.env` 文件,则该环境配置文件将被加载。默认为 `false`。
|
||||
如果 `dotenv-load`, `dotenv-filename`, `dotenv-path`, or `dotenv-required`
|
||||
中任意一项被设置, `just` 会尝试从文件中加载环境变量
|
||||
|
||||
如果设置了 `dotenv-path`, `just` 会在指定的路径下搜索文件,该路径可以是绝对路径,
|
||||
也可以是基于当前工作路径的相对路径
|
||||
|
||||
如果设置了 `dotenv-filename`,`just` 会在指定的相对路径,以及其所有的上层目录中,搜索指定文件
|
||||
|
||||
如果没有设置 `dotenv-filename`,但是设置了 `dotenv-load` 或 `dotenv-required`,
|
||||
`just` 会在当前工作路径,以及其所有的上层目录中,寻找名为 `.env` 的文件。
|
||||
|
||||
`dotenv-filename` 和 `dotenv-path` 很相似,但是 `dotenv-path` 只会检查指定的目录
|
||||
而 `dotenv-filename` 会检查指定目录以及其所有的上层目录。
|
||||
|
||||
如果没有找到环境变量文件也不会报错,除非设置了 `dotenv-required`。
|
||||
|
||||
从文件中加载的变量是环境变量,而非 `just` 变量,所以在配方和反引号中需要必须通过 `$VARIABLE_NAME` 来调用。
|
||||
|
||||
比如,如果你的 `.env` 文件包含以下内容:
|
||||
|
||||
```sh
|
||||
# a comment, will be ignored
|
||||
DATABASE_ADDRESS=localhost:6379
|
||||
SERVER_PORT=1337
|
||||
```
|
||||
|
||||
并且你的 `justfile` 包含:
|
||||
|
||||
```just
|
||||
set dotenv-load
|
||||
|
||||
serve:
|
||||
@echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT…"
|
||||
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
|
||||
```
|
||||
|
||||
`just serve` 将会输出:
|
||||
|
||||
```sh
|
||||
$ just serve
|
||||
Starting server with database localhost:6379 on port 1337…
|
||||
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
|
||||
```
|
||||
|
||||
#### 导出
|
||||
|
||||
@ -853,36 +917,6 @@ Available recipes:
|
||||
test # test stuff
|
||||
```
|
||||
|
||||
### `.env` 集成
|
||||
|
||||
如果 [`dotenv-load`](#环境变量加载) 被设置,`just` 将从一个名为 `.env` 的文件中加载环境变量。这个文件可以和你的 `justfile` 位于同一目录下,或者位于其父目录下。这些变量是环境变量,而不是 `just` 的变量,因此必须使用 `$VARIABLE_NAME` 在配方和反引号中访问。
|
||||
|
||||
例如,假如你的 `.env` 文件包含:
|
||||
|
||||
```sh
|
||||
# 注释,将被忽略
|
||||
DATABASE_ADDRESS=localhost:6379
|
||||
SERVER_PORT=1337
|
||||
```
|
||||
|
||||
而你的 `justfile` 包含:
|
||||
|
||||
```just
|
||||
set dotenv-load
|
||||
|
||||
serve:
|
||||
@echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT…"
|
||||
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
|
||||
```
|
||||
|
||||
`just serve` 将会输出:
|
||||
|
||||
```sh
|
||||
$ just serve
|
||||
Starting server with database localhost:6379 on port 1337…
|
||||
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
|
||||
```
|
||||
|
||||
### 变量和替换
|
||||
|
||||
支持在变量、字符串、拼接、路径连接和替换中使用 `{{…}}` :
|
||||
@ -1463,7 +1497,7 @@ HOME is '/home/myuser'
|
||||
|
||||
#### 从 `.env` 文件加载环境变量
|
||||
|
||||
如果 [dotenv-load](#环境变量加载) 被设置,`just` 将从 `.env` 文件中加载环境变量。该文件中的变量将作为环境变量提供给配方。参见 [环境变量集成](#env-集成) 以获得更多信息。
|
||||
如果 [dotenv-load](#环境变量加载) 被设置,`just` 将从 `.env` 文件中加载环境变量。该文件中的变量将作为环境变量提供给配方。参见 [环境变量集成](#环境变量加载) 以获得更多信息。
|
||||
|
||||
#### 从环境变量中设置 `just` 变量
|
||||
|
||||
|
1
justfile
1
justfile
@ -43,6 +43,7 @@ shellcheck:
|
||||
shellcheck www/install.sh
|
||||
|
||||
man:
|
||||
mkdir -p man
|
||||
cargo run -- --man > man/just.1
|
||||
|
||||
view-man: man
|
||||
|
195
man/just.1
195
man/just.1
@ -1,195 +0,0 @@
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.TH just 1 "just 1.27.0"
|
||||
.SH NAME
|
||||
just \- 🤖 Just a command runner \- https://github.com/casey/just
|
||||
.SH SYNOPSIS
|
||||
\fBjust\fR [\fB\-\-check\fR] [\fB\-\-chooser\fR] [\fB\-\-color\fR] [\fB\-\-command\-color\fR] [\fB\-\-yes\fR] [\fB\-n\fR|\fB\-\-dry\-run\fR] [\fB\-\-dump\-format\fR] [\fB\-\-highlight\fR] [\fB\-\-list\-heading\fR] [\fB\-\-list\-prefix\fR] [\fB\-\-no\-aliases\fR] [\fB\-\-no\-deps\fR] [\fB\-\-no\-dotenv\fR] [\fB\-\-no\-highlight\fR] [\fB\-f\fR|\fB\-\-justfile\fR] [\fB\-q\fR|\fB\-\-quiet\fR] [\fB\-\-set\fR] [\fB\-\-shell\fR] [\fB\-\-shell\-arg\fR] [\fB\-\-shell\-command\fR] [\fB\-\-clear\-shell\-args\fR] [\fB\-u\fR|\fB\-\-unsorted\fR] [\fB\-\-unstable\fR] [\fB\-v\fR|\fB\-\-verbose\fR]... [\fB\-d\fR|\fB\-\-working\-directory\fR] [\fB\-\-changelog\fR] [\fB\-\-choose\fR] [\fB\-c\fR|\fB\-\-command\fR] [\fB\-\-completions\fR] [\fB\-\-dump\fR] [\fB\-e\fR|\fB\-\-edit\fR] [\fB\-\-evaluate\fR] [\fB\-\-fmt\fR] [\fB\-\-init\fR] [\fB\-l\fR|\fB\-\-list\fR] [\fB\-\-groups\fR] [\fB\-\-man\fR] [\fB\-s\fR|\fB\-\-show\fR] [\fB\-\-summary\fR] [\fB\-\-variables\fR] [\fB\-\-dotenv\-filename\fR] [\fB\-E\fR|\fB\-\-dotenv\-path\fR] [\fB\-g\fR|\fB\-\-global\-justfile\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fIARGUMENTS\fR]
|
||||
.SH DESCRIPTION
|
||||
🤖 Just a command runner \- https://github.com/casey/just
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-\-check\fR
|
||||
Run `\-\-fmt` in \*(Aqcheck\*(Aq mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.
|
||||
.TP
|
||||
\fB\-\-chooser\fR
|
||||
Override binary invoked by `\-\-choose`
|
||||
.RS
|
||||
May also be specified with the \fBJUST_CHOOSER\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-\-color\fR [default: auto]
|
||||
Print colorful output
|
||||
.br
|
||||
|
||||
.br
|
||||
[\fIpossible values: \fRauto, always, never]
|
||||
.RS
|
||||
May also be specified with the \fBJUST_COLOR\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-\-command\-color\fR
|
||||
Echo recipe lines in <COMMAND\-COLOR>
|
||||
.br
|
||||
|
||||
.br
|
||||
[\fIpossible values: \fRblack, blue, cyan, green, purple, red, yellow]
|
||||
.RS
|
||||
May also be specified with the \fBJUST_COMMAND_COLOR\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-\-yes\fR
|
||||
Automatically confirm all recipes.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-dry\-run\fR
|
||||
Print what just would do without doing it
|
||||
.RS
|
||||
May also be specified with the \fBJUST_DRY_RUN\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-\-dump\-format\fR=\fIFORMAT\fR [default: just]
|
||||
Dump justfile as <FORMAT>
|
||||
.br
|
||||
|
||||
.br
|
||||
[\fIpossible values: \fRjust, json]
|
||||
.TP
|
||||
\fB\-\-highlight\fR
|
||||
Highlight echoed recipe lines in bold
|
||||
.TP
|
||||
\fB\-\-list\-heading\fR=\fITEXT\fR
|
||||
Print <TEXT> before list
|
||||
.TP
|
||||
\fB\-\-list\-prefix\fR=\fITEXT\fR
|
||||
Print <TEXT> before each list item
|
||||
.TP
|
||||
\fB\-\-no\-aliases\fR
|
||||
Don\*(Aqt show aliases in list
|
||||
.TP
|
||||
\fB\-\-no\-deps\fR
|
||||
Don\*(Aqt run recipe dependencies
|
||||
.TP
|
||||
\fB\-\-no\-dotenv\fR
|
||||
Don\*(Aqt load `.env` file
|
||||
.TP
|
||||
\fB\-\-no\-highlight\fR
|
||||
Don\*(Aqt highlight echoed recipe lines in bold
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-justfile\fR
|
||||
Use <JUSTFILE> as justfile
|
||||
.RS
|
||||
May also be specified with the \fBJUST_JUSTFILE\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
Suppress all output
|
||||
.RS
|
||||
May also be specified with the \fBJUST_QUIET\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-\-set\fR=\fIVARIABLE VALUE\fR
|
||||
Override <VARIABLE> with <VALUE>
|
||||
.TP
|
||||
\fB\-\-shell\fR
|
||||
Invoke <SHELL> to run recipes
|
||||
.TP
|
||||
\fB\-\-shell\-arg\fR
|
||||
Invoke shell with <SHELL\-ARG> as an argument
|
||||
.TP
|
||||
\fB\-\-shell\-command\fR
|
||||
Invoke <COMMAND> with the shell used to run recipe lines and backticks
|
||||
.TP
|
||||
\fB\-\-clear\-shell\-args\fR
|
||||
Clear shell arguments
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-unsorted\fR
|
||||
Return list and summary entries in source order
|
||||
.TP
|
||||
\fB\-\-unstable\fR
|
||||
Enable unstable features
|
||||
.RS
|
||||
May also be specified with the \fBJUST_UNSTABLE\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Use verbose output
|
||||
.RS
|
||||
May also be specified with the \fBJUST_VERBOSE\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-working\-directory\fR
|
||||
Use <WORKING\-DIRECTORY> as working directory. \-\-justfile must also be set
|
||||
.RS
|
||||
May also be specified with the \fBJUST_WORKING_DIRECTORY\fR environment variable.
|
||||
.RE
|
||||
.TP
|
||||
\fB\-\-changelog\fR
|
||||
Print changelog
|
||||
.TP
|
||||
\fB\-\-choose\fR
|
||||
Select one or more recipes to run using a binary chooser. If `\-\-chooser` is not passed the chooser defaults to the value of $JUST_CHOOSER, falling back to `fzf`
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-command\fR
|
||||
Run an arbitrary command with the working directory, `.env`, overrides, and exports set
|
||||
.TP
|
||||
\fB\-\-completions\fR=\fISHELL\fR
|
||||
Print shell completion script for <SHELL>
|
||||
.br
|
||||
|
||||
.br
|
||||
[\fIpossible values: \fRbash, elvish, fish, powershell, zsh]
|
||||
.TP
|
||||
\fB\-\-dump\fR
|
||||
Print justfile
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-edit\fR
|
||||
Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`
|
||||
.TP
|
||||
\fB\-\-evaluate\fR
|
||||
Evaluate and print all variables. If a variable name is given as an argument, only print that variable\*(Aqs value.
|
||||
.TP
|
||||
\fB\-\-fmt\fR
|
||||
Format and overwrite justfile
|
||||
.TP
|
||||
\fB\-\-init\fR
|
||||
Initialize new justfile in project root
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-list\fR
|
||||
List available recipes and their arguments
|
||||
.TP
|
||||
\fB\-\-groups\fR
|
||||
List recipe groups
|
||||
.TP
|
||||
\fB\-\-man\fR
|
||||
Print man page
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-show\fR=\fIRECIPE\fR
|
||||
Show information about <RECIPE>
|
||||
.TP
|
||||
\fB\-\-summary\fR
|
||||
List names of available recipes
|
||||
.TP
|
||||
\fB\-\-variables\fR
|
||||
List names of variables
|
||||
.TP
|
||||
\fB\-\-dotenv\-filename\fR
|
||||
Search for environment file named <DOTENV\-FILENAME> instead of `.env`
|
||||
.TP
|
||||
\fB\-E\fR, \fB\-\-dotenv\-path\fR
|
||||
Load <DOTENV\-PATH> as environment file instead of searching for one
|
||||
.TP
|
||||
\fB\-g\fR, \fB\-\-global\-justfile\fR
|
||||
Use global justfile
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Print help
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Print version
|
||||
.TP
|
||||
[\fIARGUMENTS\fR]
|
||||
Overrides and recipe(s) to run, defaulting to the first recipe in the justfile
|
||||
.SH VERSION
|
||||
v1.27.0
|
||||
.SH AUTHORS
|
||||
Casey Rodarmor <casey@rodarmor.com>
|
@ -37,6 +37,8 @@ impl<'src> Analyzer<'src> {
|
||||
|
||||
let mut modules: Table<Justfile> = Table::new();
|
||||
|
||||
let mut unexports: HashSet<String> = HashSet::new();
|
||||
|
||||
let mut definitions: HashMap<&str, (&'static str, Name)> = HashMap::new();
|
||||
|
||||
let mut define = |name: Name<'src>,
|
||||
@ -98,6 +100,13 @@ impl<'src> Analyzer<'src> {
|
||||
self.analyze_set(set)?;
|
||||
self.sets.insert(set.clone());
|
||||
}
|
||||
Item::Unexport { name } => {
|
||||
if !unexports.insert(name.lexeme().to_string()) {
|
||||
return Err(name.token.error(DuplicateUnexport {
|
||||
variable: name.lexeme(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,21 +118,23 @@ impl<'src> Analyzer<'src> {
|
||||
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
|
||||
|
||||
for assignment in assignments {
|
||||
if !settings.allow_duplicate_variables
|
||||
&& self.assignments.contains_key(assignment.name.lexeme())
|
||||
{
|
||||
return Err(assignment.name.token.error(DuplicateVariable {
|
||||
variable: assignment.name.lexeme(),
|
||||
}));
|
||||
let variable = assignment.name.lexeme();
|
||||
|
||||
if !settings.allow_duplicate_variables && self.assignments.contains_key(variable) {
|
||||
return Err(assignment.name.token.error(DuplicateVariable { variable }));
|
||||
}
|
||||
|
||||
if self
|
||||
.assignments
|
||||
.get(assignment.name.lexeme())
|
||||
.get(variable)
|
||||
.map_or(true, |original| assignment.depth <= original.depth)
|
||||
{
|
||||
self.assignments.insert(assignment.clone());
|
||||
}
|
||||
|
||||
if unexports.contains(variable) {
|
||||
return Err(assignment.name.token.error(ExportUnexported { variable }));
|
||||
}
|
||||
}
|
||||
|
||||
AssignmentResolver::resolve_assignments(&self.assignments)?;
|
||||
@ -167,6 +178,7 @@ impl<'src> Analyzer<'src> {
|
||||
recipes,
|
||||
settings,
|
||||
source: root.into(),
|
||||
unexports,
|
||||
warnings,
|
||||
})
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ impl AttributeDiscriminant {
|
||||
impl<'src> Attribute<'src> {
|
||||
pub(crate) fn new(
|
||||
name: Name<'src>,
|
||||
argument: Option<StringLiteral<'src>>,
|
||||
mut arguments: Vec<StringLiteral<'src>>,
|
||||
) -> CompileResult<'src, Self> {
|
||||
use AttributeDiscriminant::*;
|
||||
|
||||
@ -56,8 +56,7 @@ impl<'src> Attribute<'src> {
|
||||
})
|
||||
})?;
|
||||
|
||||
let found = argument.as_ref().iter().count();
|
||||
|
||||
let found = arguments.len();
|
||||
let range = discriminant.argument_range();
|
||||
|
||||
if !range.contains(&found) {
|
||||
@ -72,9 +71,9 @@ impl<'src> Attribute<'src> {
|
||||
}
|
||||
|
||||
Ok(match discriminant {
|
||||
Confirm => Self::Confirm(argument),
|
||||
Doc => Self::Doc(argument),
|
||||
Group => Self::Group(argument.unwrap()),
|
||||
Confirm => Self::Confirm(arguments.pop()),
|
||||
Doc => Self::Doc(arguments.pop()),
|
||||
Group => Self::Group(arguments.pop().unwrap()),
|
||||
Linux => Self::Linux,
|
||||
Macos => Self::Macos,
|
||||
NoCd => Self::NoCd,
|
||||
|
@ -1,25 +1,41 @@
|
||||
use super::*;
|
||||
|
||||
pub(crate) trait CommandExt {
|
||||
fn export(&mut self, settings: &Settings, dotenv: &BTreeMap<String, String>, scope: &Scope);
|
||||
fn export(
|
||||
&mut self,
|
||||
settings: &Settings,
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
scope: &Scope,
|
||||
unexports: &HashSet<String>,
|
||||
);
|
||||
|
||||
fn export_scope(&mut self, settings: &Settings, scope: &Scope);
|
||||
fn export_scope(&mut self, settings: &Settings, scope: &Scope, unexports: &HashSet<String>);
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn export(&mut self, settings: &Settings, dotenv: &BTreeMap<String, String>, scope: &Scope) {
|
||||
fn export(
|
||||
&mut self,
|
||||
settings: &Settings,
|
||||
dotenv: &BTreeMap<String, String>,
|
||||
scope: &Scope,
|
||||
unexports: &HashSet<String>,
|
||||
) {
|
||||
for (name, value) in dotenv {
|
||||
self.env(name, value);
|
||||
}
|
||||
|
||||
if let Some(parent) = scope.parent() {
|
||||
self.export_scope(settings, parent);
|
||||
self.export_scope(settings, parent, unexports);
|
||||
}
|
||||
}
|
||||
|
||||
fn export_scope(&mut self, settings: &Settings, scope: &Scope) {
|
||||
fn export_scope(&mut self, settings: &Settings, scope: &Scope, unexports: &HashSet<String>) {
|
||||
if let Some(parent) = scope.parent() {
|
||||
self.export_scope(settings, parent);
|
||||
self.export_scope(settings, parent, unexports);
|
||||
}
|
||||
|
||||
for unexport in unexports {
|
||||
self.env_remove(unexport);
|
||||
}
|
||||
|
||||
for binding in scope.bindings() {
|
||||
|
@ -131,6 +131,9 @@ impl Display for CompileError<'_> {
|
||||
DuplicateVariable { variable } => {
|
||||
write!(f, "Variable `{variable}` has multiple definitions")
|
||||
}
|
||||
DuplicateUnexport { variable } => {
|
||||
write!(f, "Variable `{variable}` is unexported multiple times")
|
||||
}
|
||||
ExpectedKeyword { expected, found } => {
|
||||
let expected = List::or_ticked(expected);
|
||||
if found.kind == TokenKind::Identifier {
|
||||
@ -143,6 +146,9 @@ impl Display for CompileError<'_> {
|
||||
write!(f, "Expected keyword {expected} but found `{}`", found.kind)
|
||||
}
|
||||
}
|
||||
ExportUnexported { variable } => {
|
||||
write!(f, "Variable {variable} is both exported and unexported")
|
||||
}
|
||||
ExtraLeadingWhitespace => write!(f, "Recipe line has extra leading whitespace"),
|
||||
FunctionArgumentCountMismatch {
|
||||
function,
|
||||
|
@ -52,10 +52,16 @@ pub(crate) enum CompileErrorKind<'src> {
|
||||
DuplicateVariable {
|
||||
variable: &'src str,
|
||||
},
|
||||
DuplicateUnexport {
|
||||
variable: &'src str,
|
||||
},
|
||||
ExpectedKeyword {
|
||||
expected: Vec<Keyword>,
|
||||
found: Token<'src>,
|
||||
},
|
||||
ExportUnexported {
|
||||
variable: &'src str,
|
||||
},
|
||||
ExtraLeadingWhitespace,
|
||||
FunctionArgumentCountMismatch {
|
||||
function: &'src str,
|
||||
|
@ -20,7 +20,7 @@ pub(crate) enum Error<'src> {
|
||||
token: Token<'src>,
|
||||
output_error: OutputError,
|
||||
},
|
||||
CacheDirIo {
|
||||
RuntimeDirIo {
|
||||
io_error: io::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
@ -287,9 +287,6 @@ impl<'src> ColorDisplay for Error<'src> {
|
||||
}?,
|
||||
OutputError::Utf8(utf8_error) => write!(f, "Backtick succeeded but stdout was not utf8: {utf8_error}")?,
|
||||
}
|
||||
CacheDirIo { io_error, path } => {
|
||||
write!(f, "I/O error in cache dir `{}`: {io_error}", path.display())?;
|
||||
}
|
||||
ChooserInvoke { shell_binary, shell_arguments, chooser, io_error} => {
|
||||
let chooser = chooser.to_string_lossy();
|
||||
write!(f, "Chooser `{shell_binary} {shell_arguments} {chooser}` invocation failed: {io_error}")?;
|
||||
@ -407,6 +404,9 @@ impl<'src> ColorDisplay for Error<'src> {
|
||||
write!(f, "Recipe `{recipe}` was not confirmed")?;
|
||||
}
|
||||
RegexCompile { source } => write!(f, "{source}")?,
|
||||
RuntimeDirIo { io_error, path } => {
|
||||
write!(f, "I/O error in runtime dir `{}`: {io_error}", path.display())?;
|
||||
}
|
||||
Search { search_error } => Display::fmt(search_error, f)?,
|
||||
Shebang { recipe, command, argument, io_error} => {
|
||||
if let Some(argument) = argument {
|
||||
|
@ -8,6 +8,7 @@ pub(crate) struct Evaluator<'src: 'run, 'run> {
|
||||
pub(crate) scope: Scope<'src, 'run>,
|
||||
pub(crate) search: &'run Search,
|
||||
pub(crate) settings: &'run Settings<'run>,
|
||||
unsets: &'run HashSet<String>,
|
||||
}
|
||||
|
||||
impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
@ -19,6 +20,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
scope: Scope<'src, 'run>,
|
||||
search: &'run Search,
|
||||
settings: &'run Settings<'run>,
|
||||
unsets: &'run HashSet<String>,
|
||||
) -> RunResult<'src, Scope<'src, 'run>> {
|
||||
let mut evaluator = Self {
|
||||
assignments: Some(assignments),
|
||||
@ -28,6 +30,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
scope,
|
||||
search,
|
||||
settings,
|
||||
unsets,
|
||||
};
|
||||
|
||||
for assignment in assignments.values() {
|
||||
@ -217,7 +220,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
cmd.arg(command);
|
||||
cmd.args(args);
|
||||
cmd.current_dir(&self.search.working_directory);
|
||||
cmd.export(self.settings, self.dotenv, &self.scope);
|
||||
cmd.export(self.settings, self.dotenv, &self.scope, self.unsets);
|
||||
cmd.stdin(Stdio::inherit());
|
||||
cmd.stderr(if self.config.verbosity.quiet() {
|
||||
Stdio::null()
|
||||
@ -261,6 +264,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
scope: &'run Scope<'src, 'run>,
|
||||
search: &'run Search,
|
||||
settings: &'run Settings,
|
||||
unsets: &'run HashSet<String>,
|
||||
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
||||
let mut evaluator = Self {
|
||||
assignments: None,
|
||||
@ -270,6 +274,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
scope: scope.child(),
|
||||
search,
|
||||
settings,
|
||||
unsets,
|
||||
};
|
||||
|
||||
let mut scope = scope.child();
|
||||
@ -316,6 +321,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
scope: &'run Scope<'src, 'run>,
|
||||
search: &'run Search,
|
||||
settings: &'run Settings,
|
||||
unsets: &'run HashSet<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
assignments: None,
|
||||
@ -325,6 +331,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
||||
scope: Scope::child(scope),
|
||||
search,
|
||||
settings,
|
||||
unsets,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ pub(crate) enum Item<'src> {
|
||||
},
|
||||
Recipe(UnresolvedRecipe<'src>),
|
||||
Set(Set<'src>),
|
||||
Unexport {
|
||||
name: Name<'src>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'src> Display for Item<'src> {
|
||||
@ -61,6 +64,7 @@ impl<'src> Display for Item<'src> {
|
||||
}
|
||||
Self::Recipe(recipe) => write!(f, "{}", recipe.color_display(Color::never())),
|
||||
Self::Set(set) => write!(f, "{set}"),
|
||||
Self::Unexport { name } => write!(f, "unexport {name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ pub(crate) struct Justfile<'src> {
|
||||
pub(crate) settings: Settings<'src>,
|
||||
#[serde(skip)]
|
||||
pub(crate) source: PathBuf,
|
||||
pub(crate) unexports: HashSet<String>,
|
||||
pub(crate) warnings: Vec<Warning>,
|
||||
}
|
||||
|
||||
@ -113,6 +114,7 @@ impl<'src> Justfile<'src> {
|
||||
scope,
|
||||
search,
|
||||
&self.settings,
|
||||
&self.unexports,
|
||||
)
|
||||
}
|
||||
|
||||
@ -163,7 +165,7 @@ impl<'src> Justfile<'src> {
|
||||
|
||||
let scope = scope.child();
|
||||
|
||||
command.export(&self.settings, &dotenv, &scope);
|
||||
command.export(&self.settings, &dotenv, &scope, &self.unexports);
|
||||
|
||||
let status = InterruptHandler::guard(|| command.status()).map_err(|io_error| {
|
||||
Error::CommandInvoke {
|
||||
@ -286,6 +288,7 @@ impl<'src> Justfile<'src> {
|
||||
scope: invocation.scope,
|
||||
search,
|
||||
settings: invocation.settings,
|
||||
unexports: &self.unexports,
|
||||
};
|
||||
|
||||
Self::run_recipe(
|
||||
@ -441,6 +444,7 @@ impl<'src> Justfile<'src> {
|
||||
context.scope,
|
||||
search,
|
||||
context.settings,
|
||||
context.unexports,
|
||||
)?;
|
||||
|
||||
let scope = outer.child();
|
||||
@ -452,6 +456,7 @@ impl<'src> Justfile<'src> {
|
||||
&scope,
|
||||
search,
|
||||
context.settings,
|
||||
context.unexports,
|
||||
);
|
||||
|
||||
if !context.config.no_dependencies {
|
||||
|
@ -25,6 +25,7 @@ pub(crate) enum Keyword {
|
||||
Shell,
|
||||
Tempdir,
|
||||
True,
|
||||
Unexport,
|
||||
WindowsPowershell,
|
||||
WindowsShell,
|
||||
X,
|
||||
|
@ -42,7 +42,7 @@ pub(crate) use {
|
||||
std::{
|
||||
borrow::Cow,
|
||||
cmp,
|
||||
collections::{BTreeMap, BTreeSet, HashMap},
|
||||
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
||||
env,
|
||||
ffi::OsString,
|
||||
fmt::{self, Debug, Display, Formatter},
|
||||
|
@ -54,6 +54,11 @@ impl<'src> Node<'src> for Item<'src> {
|
||||
}
|
||||
Self::Recipe(recipe) => recipe.tree(),
|
||||
Self::Set(set) => set.tree(),
|
||||
Self::Unexport { name } => {
|
||||
let mut unexport = Tree::atom(Keyword::Unexport.lexeme());
|
||||
unexport.push_mut(name.lexeme().replace('-', "_"));
|
||||
unexport
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -340,6 +340,11 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
self.presume_keyword(Keyword::Export)?;
|
||||
items.push(Item::Assignment(self.parse_assignment(true)?));
|
||||
}
|
||||
Some(Keyword::Unexport) => {
|
||||
self.presume_keyword(Keyword::Unexport)?;
|
||||
let name = self.parse_name()?;
|
||||
items.push(Item::Unexport { name });
|
||||
}
|
||||
Some(Keyword::Import)
|
||||
if self.next_are(&[Identifier, StringToken])
|
||||
|| self.next_are(&[Identifier, Identifier, StringToken])
|
||||
@ -982,15 +987,29 @@ impl<'run, 'src> Parser<'run, 'src> {
|
||||
loop {
|
||||
let name = self.parse_name()?;
|
||||
|
||||
let argument = if self.accepted(ParenL)? {
|
||||
let argument = self.parse_string_literal()?;
|
||||
self.expect(ParenR)?;
|
||||
Some(argument)
|
||||
let arguments: Vec<StringLiteral> = if self.next_is(Colon) {
|
||||
self.presume(Colon)?;
|
||||
let single_arg = self.parse_string_literal()?;
|
||||
vec![single_arg]
|
||||
} else if self.next_is(ParenL) {
|
||||
self.presume(ParenL)?;
|
||||
let mut args = Vec::new();
|
||||
loop {
|
||||
args.push(self.parse_string_literal()?);
|
||||
if self.next_is(ParenR) {
|
||||
self.presume(ParenR)?;
|
||||
break;
|
||||
}
|
||||
if self.next_is(Comma) {
|
||||
self.presume(Comma)?;
|
||||
}
|
||||
}
|
||||
args
|
||||
} else {
|
||||
None
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let attribute = Attribute::new(name, argument)?;
|
||||
let attribute = Attribute::new(name, arguments)?;
|
||||
|
||||
if let Some(line) = attributes.get(&attribute) {
|
||||
return Err(name.error(CompileErrorKind::DuplicateAttribute {
|
||||
@ -1153,6 +1172,18 @@ mod tests {
|
||||
tree: (justfile (alias t test)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: single_argument_attribute_shorthand,
|
||||
text: "[group: 'some-group']\nalias t := test",
|
||||
tree: (justfile (alias t test)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: single_argument_attribute_shorthand_multiple_same_line,
|
||||
text: "[group: 'some-group', group: 'some-other-group']\nalias t := test",
|
||||
tree: (justfile (alias t test)),
|
||||
}
|
||||
|
||||
test! {
|
||||
name: aliases_multiple,
|
||||
text: "alias t := test\nalias b := build",
|
||||
|
@ -169,6 +169,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
scope,
|
||||
context.search,
|
||||
context.settings,
|
||||
context.unexports,
|
||||
);
|
||||
|
||||
if self.shebang {
|
||||
@ -279,7 +280,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
cmd.stdout(Stdio::null());
|
||||
}
|
||||
|
||||
cmd.export(context.settings, context.dotenv, scope);
|
||||
cmd.export(context.settings, context.dotenv, scope, context.unexports);
|
||||
|
||||
match InterruptHandler::guard(|| cmd.status()) {
|
||||
Ok(exit_status) => {
|
||||
@ -353,9 +354,9 @@ impl<'src, D> Recipe<'src, D> {
|
||||
let tempdir = match &context.settings.tempdir {
|
||||
Some(tempdir) => tempdir_builder.tempdir_in(context.search.working_directory.join(tempdir)),
|
||||
None => {
|
||||
if let Some(cache_dir) = dirs::cache_dir() {
|
||||
let path = cache_dir.join("just");
|
||||
fs::create_dir_all(&path).map_err(|io_error| Error::CacheDirIo {
|
||||
if let Some(runtime_dir) = dirs::runtime_dir() {
|
||||
let path = runtime_dir.join("just");
|
||||
fs::create_dir_all(&path).map_err(|io_error| Error::RuntimeDirIo {
|
||||
io_error,
|
||||
path: path.clone(),
|
||||
})?;
|
||||
@ -425,7 +426,7 @@ impl<'src, D> Recipe<'src, D> {
|
||||
command.args(positional);
|
||||
}
|
||||
|
||||
command.export(context.settings, context.dotenv, scope);
|
||||
command.export(context.settings, context.dotenv, scope, context.unexports);
|
||||
|
||||
// run it!
|
||||
match InterruptHandler::guard(|| command.status()) {
|
||||
|
@ -7,4 +7,5 @@ pub(crate) struct RecipeContext<'src: 'run, 'run> {
|
||||
pub(crate) scope: &'run Scope<'src, 'run>,
|
||||
pub(crate) search: &'run Search,
|
||||
pub(crate) settings: &'run Settings<'src>,
|
||||
pub(crate) unexports: &'run HashSet<String>,
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ fn multiple_attributes_one_line_error_message() {
|
||||
)
|
||||
.stderr(
|
||||
"
|
||||
error: Expected ']', ',', or '(', but found identifier
|
||||
error: Expected ']', ':', ',', or '(', but found identifier
|
||||
——▶ justfile:1:17
|
||||
│
|
||||
1 │ [macos, windows linux]
|
||||
|
@ -29,10 +29,10 @@ fn bash() {
|
||||
#[test]
|
||||
fn replacements() {
|
||||
for shell in ["bash", "elvish", "fish", "powershell", "zsh"] {
|
||||
let status = Command::new(executable_path("just"))
|
||||
let output = Command::new(executable_path("just"))
|
||||
.args(["--completions", shell])
|
||||
.status()
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
assert!(output.status.success());
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ fn confirm_recipe_with_prompt_too_many_args() {
|
||||
echo confirmed
|
||||
",
|
||||
)
|
||||
.stderr("error: Expected ')', but found ','\n ——▶ justfile:1:64\n │\n1 │ [confirm(\"This is dangerous - are you sure you want to run it?\",\"this second argument is not supported\")]\n │ ^\n")
|
||||
.stderr("error: Attribute `confirm` got 2 arguments but takes at most 1 argument\n ——▶ justfile:1:2\n │\n1 │ [confirm(\"This is dangerous - are you sure you want to run it?\",\"this second argument is not supported\")]\n │ ^^^^^^^\n")
|
||||
.stdout("")
|
||||
.status(1)
|
||||
.run();
|
||||
|
@ -126,11 +126,10 @@ fn list_groups_with_custom_prefix() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
[group('B')]
|
||||
[group: 'B']
|
||||
foo:
|
||||
|
||||
[group('A')]
|
||||
[group('B')]
|
||||
[group: 'A', group: 'B']
|
||||
bar:
|
||||
",
|
||||
)
|
||||
|
@ -59,6 +59,7 @@ fn alias() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -98,6 +99,7 @@ fn assignment() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -151,6 +153,7 @@ fn body() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -216,6 +219,7 @@ fn dependencies() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -319,6 +323,7 @@ fn dependency_argument() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -384,6 +389,7 @@ fn duplicate_recipes() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -427,6 +433,7 @@ fn duplicate_variables() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -473,6 +480,7 @@ fn doc_comment() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -505,6 +513,7 @@ fn empty_justfile() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -658,6 +667,7 @@ fn parameters() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -744,6 +754,7 @@ fn priors() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -790,6 +801,7 @@ fn private() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -836,6 +848,7 @@ fn quiet() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -897,6 +910,7 @@ fn settings() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -946,6 +960,7 @@ fn shebang() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -992,6 +1007,7 @@ fn simple() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -1041,6 +1057,7 @@ fn attribute() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}),
|
||||
);
|
||||
@ -1103,6 +1120,7 @@ fn module() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
},
|
||||
},
|
||||
@ -1124,6 +1142,7 @@ fn module() {
|
||||
"windows_powershell": false,
|
||||
"windows_shell": null,
|
||||
},
|
||||
"unexports": [],
|
||||
"warnings": [],
|
||||
}))
|
||||
.unwrap()
|
||||
|
@ -104,6 +104,7 @@ mod summary;
|
||||
mod tempdir;
|
||||
mod timestamps;
|
||||
mod undefined_variables;
|
||||
mod unexport;
|
||||
mod unstable;
|
||||
#[cfg(target_family = "windows")]
|
||||
mod windows_shell;
|
||||
|
@ -884,7 +884,7 @@ _private-recipe:
|
||||
args: ("--list"),
|
||||
stdout: r#"
|
||||
Available recipes:
|
||||
a Z="\t z" # something else
|
||||
a Z="\t z" # something else
|
||||
hello a b='B ' c='C' # this does a thing
|
||||
"#,
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ pub(crate) fn tempdir() -> TempDir {
|
||||
|
||||
builder.prefix("just-test-tempdir");
|
||||
|
||||
if let Some(cache_dir) = dirs::cache_dir() {
|
||||
let path = cache_dir.join("just");
|
||||
if let Some(runtime_dir) = dirs::runtime_dir() {
|
||||
let path = runtime_dir.join("just");
|
||||
fs::create_dir_all(&path).unwrap();
|
||||
builder.tempdir_in(path)
|
||||
} else {
|
||||
|
104
tests/unexport.rs
Normal file
104
tests/unexport.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unexport_environment_variable_linewise() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
unexport JUST_TEST_VARIABLE
|
||||
|
||||
@recipe:
|
||||
echo ${JUST_TEST_VARIABLE:-unset}
|
||||
",
|
||||
)
|
||||
.env("JUST_TEST_VARIABLE", "foo")
|
||||
.stdout("unset\n")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unexport_environment_variable_shebang() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
unexport JUST_TEST_VARIABLE
|
||||
|
||||
recipe:
|
||||
#!/usr/bin/env bash
|
||||
echo ${JUST_TEST_VARIABLE:-unset}
|
||||
",
|
||||
)
|
||||
.env("JUST_TEST_VARIABLE", "foo")
|
||||
.stdout("unset\n")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_unexport_fails() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
unexport JUST_TEST_VARIABLE
|
||||
|
||||
recipe:
|
||||
echo \"variable: $JUST_TEST_VARIABLE\"
|
||||
|
||||
unexport JUST_TEST_VARIABLE
|
||||
",
|
||||
)
|
||||
.env("JUST_TEST_VARIABLE", "foo")
|
||||
.stderr(
|
||||
"
|
||||
error: Variable `JUST_TEST_VARIABLE` is unexported multiple times
|
||||
——▶ justfile:6:10
|
||||
│
|
||||
6 │ unexport JUST_TEST_VARIABLE
|
||||
│ ^^^^^^^^^^^^^^^^^^
|
||||
",
|
||||
)
|
||||
.status(1)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_unexport_conflict() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
unexport JUST_TEST_VARIABLE
|
||||
|
||||
recipe:
|
||||
echo variable: $JUST_TEST_VARIABLE
|
||||
|
||||
export JUST_TEST_VARIABLE := 'foo'
|
||||
",
|
||||
)
|
||||
.stderr(
|
||||
"
|
||||
error: Variable JUST_TEST_VARIABLE is both exported and unexported
|
||||
——▶ justfile:6:8
|
||||
│
|
||||
6 │ export JUST_TEST_VARIABLE := 'foo'
|
||||
│ ^^^^^^^^^^^^^^^^^^
|
||||
",
|
||||
)
|
||||
.status(1)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unexport_doesnt_override_local_recipe_export() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
unexport JUST_TEST_VARIABLE
|
||||
|
||||
recipe $JUST_TEST_VARIABLE:
|
||||
@echo \"variable: $JUST_TEST_VARIABLE\"
|
||||
",
|
||||
)
|
||||
.args(["recipe", "value"])
|
||||
.stdout("variable: value\n")
|
||||
.status(0)
|
||||
.run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user