Compare commits
14 Commits
master
...
attribute-
Author | SHA1 | Date | |
---|---|---|---|
|
393dbabc9b | ||
|
38873dcb74 | ||
|
3ddd1b1683 | ||
|
0eb2a0678c | ||
|
3c40c0c6eb | ||
|
70d1e1b3af | ||
|
af249dbce1 | ||
|
7c30fb4944 | ||
|
6fe068c432 | ||
|
b5ad133b93 | ||
|
8d3d88fc13 | ||
|
f2201d8684 | ||
|
d38c1add13 | ||
|
d2b10e04d3 |
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@ -30,12 +30,6 @@ jobs:
|
|||||||
- name: Format
|
- name: Format
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
- name: Completion Scripts
|
|
||||||
run: |
|
|
||||||
./bin/update-completions
|
|
||||||
git diff --no-ext-diff --quiet --exit-code
|
|
||||||
./tests/completions/just.bash
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
@ -73,6 +73,16 @@ jobs:
|
|||||||
id: ref-type
|
id: ref-type
|
||||||
run: cargo run --package ref-type -- --reference ${{ github.ref }} >> $GITHUB_OUTPUT
|
run: cargo run --package ref-type -- --reference ${{ github.ref }} >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- 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
|
- name: Package
|
||||||
id: package
|
id: package
|
||||||
env:
|
env:
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
|||||||
/fuzz/artifacts
|
/fuzz/artifacts
|
||||||
/fuzz/corpus
|
/fuzz/corpus
|
||||||
/fuzz/target
|
/fuzz/target
|
||||||
|
/man
|
||||||
/target
|
/target
|
||||||
/test-utilities/Cargo.lock
|
/test-utilities/Cargo.lock
|
||||||
/test-utilities/target
|
/test-utilities/target
|
||||||
|
34
CHANGELOG.md
34
CHANGELOG.md
@ -1,6 +1,40 @@
|
|||||||
Changelog
|
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
|
[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]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.0.3"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
|
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@ -505,12 +505,6 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@ -593,7 +587,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "just"
|
name = "just"
|
||||||
version = "1.27.0"
|
version = "1.28.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"blake3",
|
"blake3",
|
||||||
@ -782,9 +776,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.83"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -963,18 +957,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.202"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.202"
|
version = "1.0.203"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1090,11 +1084,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum_macros"
|
name = "strum_macros"
|
||||||
version = "0.26.2"
|
version = "0.26.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
@ -1125,9 +1119,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target"
|
name = "target"
|
||||||
version = "2.0.1"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4df6b0340c7cc29eb3b955cc588d145ed60651bf1ab939083295d19ec8cc282"
|
checksum = "1e8f05f774b2db35bdad5a8237a90be1102669f8ea013fea9777b366d34ab145"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
@ -1224,9 +1218,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "update-contributors"
|
name = "update-contributors"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "just"
|
name = "just"
|
||||||
version = "1.27.0"
|
version = "1.28.0"
|
||||||
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
||||||
autotests = false
|
autotests = false
|
||||||
categories = ["command-line-utilities", "development-tools"]
|
categories = ["command-line-utilities", "development-tools"]
|
||||||
|
61
README.md
61
README.md
@ -812,6 +812,7 @@ foo:
|
|||||||
| `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. |
|
| `dotenv-filename` | string | - | Load a `.env` file with a custom name, if present. |
|
||||||
| `dotenv-load` | boolean | `false` | Load a `.env` file, if present. |
|
| `dotenv-load` | boolean | `false` | Load a `.env` file, if present. |
|
||||||
| `dotenv-path` | string | - | Load a `.env` file from a custom path and error if not present. Overrides `dotenv-filename`. |
|
| `dotenv-path` | string | - | Load a `.env` file from a custom path and error if not present. Overrides `dotenv-filename`. |
|
||||||
|
| `dotenv-required` | boolean | `false` | Error if a `.env` file isn't found. |
|
||||||
| `export` | boolean | `false` | Export all variables as environment variables. |
|
| `export` | boolean | `false` | Export all variables as environment variables. |
|
||||||
| `fallback` | boolean | `false` | Search `justfile` in parent directory if the first recipe on the command line is not found. |
|
| `fallback` | boolean | `false` | Search `justfile` in parent directory if the first recipe on the command line is not found. |
|
||||||
| `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. |
|
| `ignore-comments` | boolean | `false` | Ignore recipe lines beginning with `#`. |
|
||||||
@ -877,17 +878,25 @@ bar
|
|||||||
|
|
||||||
#### Dotenv Settings
|
#### Dotenv Settings
|
||||||
|
|
||||||
If `dotenv-load`, `dotenv-filename` or `dotenv-path` is set, `just` will load
|
If any of `dotenv-load`, `dotenv-filename`, `dotenv-path`, or `dotenv-required`
|
||||||
environment variables from a file.
|
are set, `just` will try to load environment variables from a file.
|
||||||
|
|
||||||
If `dotenv-path` is set, `just` will look for a file at the given path. It is
|
If `dotenv-path` is set, `just` will look for a file at the given path, which
|
||||||
an error if a dotenv file is not found at `dotenv-path`, but not an error if a
|
may be absolute, or relative to the working directory.
|
||||||
dotenv file is not found with `dotenv-filename`.
|
|
||||||
|
|
||||||
Otherwise, `just` looks for a file named `.env` by default, unless
|
If `dotenv-filename` is set `just` will look for a file at the given path,
|
||||||
`dotenv-filename` set, in which case the value of `dotenv-filename` is used.
|
relative to the working directory and each of its ancestors.
|
||||||
This file can be located in the same directory as your `justfile` or in a
|
|
||||||
parent directory.
|
If `dotenv-filename` is not set, but `dotenv-load` or `dotenv-required` are
|
||||||
|
set, just will look for a file named `.env`, relative to the working directory
|
||||||
|
and each of its ancestors.
|
||||||
|
|
||||||
|
`dotenv-filename` and `dotenv-path` and similar, but `dotenv-path` is only
|
||||||
|
checked relative to the working directory, whereas `dotenv-filename` is checked
|
||||||
|
relative to the working directory and each of its ancestors.
|
||||||
|
|
||||||
|
It is not an error if an environment file is not found, unless
|
||||||
|
`dotenv-required` is set.
|
||||||
|
|
||||||
The loaded variables are environment variables, not `just` variables, and so
|
The loaded variables are environment variables, not `just` variables, and so
|
||||||
must be accessed using `$VARIABLE_NAME` in recipes and backticks.
|
must be accessed using `$VARIABLE_NAME` in recipes and backticks.
|
||||||
@ -990,7 +999,7 @@ $ just test foo "bar baz"
|
|||||||
#### Shell
|
#### Shell
|
||||||
|
|
||||||
The `shell` setting controls the command used to invoke recipe lines and
|
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
|
```just
|
||||||
# use python3 to execute recipe lines and backticks
|
# use python3 to execute recipe lines and backticks
|
||||||
@ -1776,7 +1785,7 @@ js-lint:
|
|||||||
[group('rust recipes')]
|
[group('rust recipes')]
|
||||||
[group('lint')]
|
[group('lint')]
|
||||||
rust-lint:
|
rust-lint:
|
||||||
echo 'Runninng Rust linter…'
|
echo 'Running Rust linter…'
|
||||||
|
|
||||||
[group('lint')]
|
[group('lint')]
|
||||||
cpp-lint:
|
cpp-lint:
|
||||||
@ -2044,6 +2053,23 @@ a $A $B=`echo $A`:
|
|||||||
When [export](#export) is set, all `just` variables are exported as environment
|
When [export](#export) is set, all `just` variables are exported as environment
|
||||||
variables.
|
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
|
#### Getting Environment Variables from the environment
|
||||||
|
|
||||||
Environment variables from the environment are passed automatically to the
|
Environment variables from the environment are passed automatically to the
|
||||||
@ -3463,18 +3489,18 @@ complete -F _just -o bashdefault -o default j
|
|||||||
|
|
||||||
### Shell Completion Scripts
|
### Shell Completion Scripts
|
||||||
|
|
||||||
Shell completion scripts for Bash, Zsh, Fish, PowerShell, and Elvish are
|
Shell completion scripts for Bash, Elvish, Fish, Nushell, PowerShell, and Zsh
|
||||||
available in the
|
are available [release archives](https://github.com/casey/just/releases).
|
||||||
[completions](https://github.com/casey/just/tree/master/completions) directory.
|
|
||||||
Please refer to your shell's documentation for how to install them.
|
|
||||||
|
|
||||||
The `just` binary can also generate the same completion scripts at runtime,
|
The `just` binary can also generate the same completion scripts at runtime
|
||||||
using the `--completions` command:
|
using `just --completions SHELL`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ just --completions zsh > just.zsh
|
$ just --completions zsh > just.zsh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Please refer to your shell's documentation for how to install them.
|
||||||
|
|
||||||
*macOS Note:* Recent versions of macOS use zsh as the default shell. If you use
|
*macOS Note:* Recent versions of macOS use zsh as the default shell. If you use
|
||||||
Homebrew to install `just`, it will automatically install the most recent copy
|
Homebrew to install `just`, it will automatically install the most recent copy
|
||||||
of the zsh completion script in the Homebrew zsh directory, which the built-in
|
of the zsh completion script in the Homebrew zsh directory, which the built-in
|
||||||
@ -3664,7 +3690,6 @@ Release x.y.z
|
|||||||
- Update changelog
|
- Update changelog
|
||||||
- Update changelog contributor credits
|
- Update changelog contributor credits
|
||||||
- Update dependencies
|
- Update dependencies
|
||||||
- Update man page
|
|
||||||
- Update version references in readme
|
- 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` 后面出现的配方覆盖之前的同名配方 |
|
| `allow-duplicate-recipes` | boolean | False | 允许在 `justfile` 后面出现的配方覆盖之前的同名配方 |
|
||||||
| `dotenv-load` | boolean | False | 如果有`.env` 环境变量文件的话,则将其加载 |
|
| `allow-duplicate-variables` | boolean | False | 允许在 `justfile` 后面出现的变量覆盖之前的同名变量 |
|
||||||
| `export` | boolean | False | 将所有变量导出为环境变量 |
|
| `dotenv-filename` | string | - | 如果有自定义名称的 `.env` 环境变量文件的话,则将其加载 |
|
||||||
| `fallback` | boolean | False | 如果命令行中的第一个配方没有找到,则在父目录中搜索 `justfile` |
|
| `dotenv-load` | boolean | False | 如果有`.env` 环境变量文件的话,则将其加载 |
|
||||||
| `ignore-comments` | boolean | False | 忽略以`#`开头的配方行 |
|
| `dotenv-path` | string | - | 从自定义路径中加载 `.env` 环境变量文件, 文件不存在将会报错。可以覆盖 `dotenv-filename` |
|
||||||
| `positional-arguments` | boolean | False | 传递位置参数 |
|
| `dotenv-required` | boolean | False | 如果 `.env` 环境变量文件不存在的话,需要报错 |
|
||||||
| `shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
| `export` | boolean | False | 将所有变量导出为环境变量 |
|
||||||
| `tempdir` | string | - | 在 `tempdir` 位置创建临时目录,而不是系统默认的临时目录 |
|
| `fallback` | boolean | False | 如果命令行中的第一个配方没有找到,则在父目录中搜索 `justfile` |
|
||||||
| `windows-powershell` | boolean | False | 在 Windows 上使用 PowerShell 作为默认 Shell(废弃,建议使用 `windows-shell`) |
|
| `ignore-comments` | boolean | False | 忽略以`#`开头的配方行 |
|
||||||
| `windows-shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
| `positional-arguments` | boolean | False | 传递位置参数 |
|
||||||
|
| `shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
||||||
|
| `tempdir` | string | - | 在 `tempdir` 位置创建临时目录,而不是系统默认的临时目录 |
|
||||||
|
| `windows-powershell` | boolean | False | 在 Windows 上使用 PowerShell 作为默认 Shell(废弃,建议使用 `windows-shell`) |
|
||||||
|
| `windows-shell` | `[COMMAND, ARGS…]` | - | 设置用于调用配方和评估反引号内包裹内容的命令 |
|
||||||
|
|
||||||
Bool 类型设置可以写成:
|
Bool 类型设置可以写成:
|
||||||
|
|
||||||
@ -685,9 +689,69 @@ $ just foo
|
|||||||
bar
|
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
|
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` 文件加载环境变量
|
#### 从 `.env` 文件加载环境变量
|
||||||
|
|
||||||
如果 [dotenv-load](#环境变量加载) 被设置,`just` 将从 `.env` 文件中加载环境变量。该文件中的变量将作为环境变量提供给配方。参见 [环境变量集成](#env-集成) 以获得更多信息。
|
如果 [dotenv-load](#环境变量加载) 被设置,`just` 将从 `.env` 文件中加载环境变量。该文件中的变量将作为环境变量提供给配方。参见 [环境变量集成](#环境变量加载) 以获得更多信息。
|
||||||
|
|
||||||
#### 从环境变量中设置 `just` 变量
|
#### 从环境变量中设置 `just` 变量
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euxo pipefail
|
|
||||||
|
|
||||||
cargo build
|
|
||||||
|
|
||||||
for script in completions/*; do
|
|
||||||
shell=${script##*.}
|
|
||||||
if [ $shell == nu ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
./target/debug/just --completions $shell > $script
|
|
||||||
done
|
|
@ -1,165 +0,0 @@
|
|||||||
_just() {
|
|
||||||
local i cur prev words cword opts cmd
|
|
||||||
COMPREPLY=()
|
|
||||||
|
|
||||||
# Modules use "::" as the separator, which is considered a wordbreak character in bash.
|
|
||||||
# The _get_comp_words_by_ref function is a hack to allow for exceptions to this rule without
|
|
||||||
# modifying the global COMP_WORDBREAKS environment variable.
|
|
||||||
if type _get_comp_words_by_ref &>/dev/null; then
|
|
||||||
_get_comp_words_by_ref -n : cur prev words cword
|
|
||||||
else
|
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
||||||
words=$COMP_WORDS
|
|
||||||
cword=$COMP_CWORD
|
|
||||||
fi
|
|
||||||
|
|
||||||
cmd=""
|
|
||||||
opts=""
|
|
||||||
|
|
||||||
for i in ${words[@]}
|
|
||||||
do
|
|
||||||
case "${cmd},${i}" in
|
|
||||||
",$1")
|
|
||||||
cmd="just"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
case "${cmd}" in
|
|
||||||
just)
|
|
||||||
opts="-n -f -q -u -v -d -c -e -l -s -E -g -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --groups --man --show --summary --variables --dotenv-filename --dotenv-path --global-justfile --timestamp --timestamp-format --help --version [ARGUMENTS]..."
|
|
||||||
if [[ ${cur} == -* ]] ; then
|
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
|
||||||
return 0
|
|
||||||
elif [[ ${cword} -eq 1 ]]; then
|
|
||||||
local recipes=$(just --summary 2> /dev/null)
|
|
||||||
|
|
||||||
if echo "${cur}" | \grep -qF '/'; then
|
|
||||||
local path_prefix=$(echo "${cur}" | sed 's/[/][^/]*$/\//')
|
|
||||||
local recipes=$(just --summary 2> /dev/null -- "${path_prefix}")
|
|
||||||
local recipes=$(printf "${path_prefix}%s\t" $recipes)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $? -eq 0 ]]; then
|
|
||||||
COMPREPLY=( $(compgen -W "${recipes}" -- "${cur}") )
|
|
||||||
if type __ltrim_colon_completions &>/dev/null; then
|
|
||||||
__ltrim_colon_completions "$cur"
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
case "${prev}" in
|
|
||||||
--chooser)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--color)
|
|
||||||
COMPREPLY=($(compgen -W "auto always never" -- "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--command-color)
|
|
||||||
COMPREPLY=($(compgen -W "black blue cyan green purple red yellow" -- "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--dump-format)
|
|
||||||
COMPREPLY=($(compgen -W "just json" -- "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--list-heading)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--list-prefix)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--justfile)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
-f)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--set)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--shell)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--shell-arg)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--working-directory)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
-d)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--command)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
-c)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--completions)
|
|
||||||
COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--list)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
-l)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--show)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
-s)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--dotenv-filename)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--dotenv-path)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
-E)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--timestamp-format)
|
|
||||||
COMPREPLY=($(compgen -f "${cur}"))
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
COMPREPLY=()
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
|
|
||||||
complete -F _just -o nosort -o bashdefault -o default just
|
|
||||||
else
|
|
||||||
complete -F _just -o bashdefault -o default just
|
|
||||||
fi
|
|
@ -1,84 +0,0 @@
|
|||||||
use builtin;
|
|
||||||
use str;
|
|
||||||
|
|
||||||
set edit:completion:arg-completer[just] = {|@words|
|
|
||||||
fn spaces {|n|
|
|
||||||
builtin:repeat $n ' ' | str:join ''
|
|
||||||
}
|
|
||||||
fn cand {|text desc|
|
|
||||||
edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc
|
|
||||||
}
|
|
||||||
var command = 'just'
|
|
||||||
for word $words[1..-1] {
|
|
||||||
if (str:has-prefix $word '-') {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
set command = $command';'$word
|
|
||||||
}
|
|
||||||
var completions = [
|
|
||||||
&'just'= {
|
|
||||||
cand --chooser 'Override binary invoked by `--choose`'
|
|
||||||
cand --color 'Print colorful output'
|
|
||||||
cand --command-color 'Echo recipe lines in <COMMAND-COLOR>'
|
|
||||||
cand --dump-format 'Dump justfile as <FORMAT>'
|
|
||||||
cand --list-heading 'Print <TEXT> before list'
|
|
||||||
cand --list-prefix 'Print <TEXT> before each list item'
|
|
||||||
cand -f 'Use <JUSTFILE> as justfile'
|
|
||||||
cand --justfile 'Use <JUSTFILE> as justfile'
|
|
||||||
cand --set 'Override <VARIABLE> with <VALUE>'
|
|
||||||
cand --shell 'Invoke <SHELL> to run recipes'
|
|
||||||
cand --shell-arg 'Invoke shell with <SHELL-ARG> as an argument'
|
|
||||||
cand -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
|
||||||
cand --working-directory 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
|
||||||
cand -c 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
|
||||||
cand --command 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
|
||||||
cand --completions 'Print shell completion script for <SHELL>'
|
|
||||||
cand -l 'List available recipes'
|
|
||||||
cand --list 'List available recipes'
|
|
||||||
cand -s 'Show recipe at <PATH>'
|
|
||||||
cand --show 'Show recipe at <PATH>'
|
|
||||||
cand --dotenv-filename 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
|
||||||
cand -E 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
|
||||||
cand --dotenv-path 'Load <DOTENV-PATH> as environment file instead of searching for one'
|
|
||||||
cand --timestamp-format 'Timestamp format string'
|
|
||||||
cand --check 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
|
|
||||||
cand --yes 'Automatically confirm all recipes.'
|
|
||||||
cand -n 'Print what just would do without doing it'
|
|
||||||
cand --dry-run 'Print what just would do without doing it'
|
|
||||||
cand --highlight 'Highlight echoed recipe lines in bold'
|
|
||||||
cand --no-aliases 'Don''t show aliases in list'
|
|
||||||
cand --no-deps 'Don''t run recipe dependencies'
|
|
||||||
cand --no-dotenv 'Don''t load `.env` file'
|
|
||||||
cand --no-highlight 'Don''t highlight echoed recipe lines in bold'
|
|
||||||
cand -q 'Suppress all output'
|
|
||||||
cand --quiet 'Suppress all output'
|
|
||||||
cand --shell-command 'Invoke <COMMAND> with the shell used to run recipe lines and backticks'
|
|
||||||
cand --clear-shell-args 'Clear shell arguments'
|
|
||||||
cand -u 'Return list and summary entries in source order'
|
|
||||||
cand --unsorted 'Return list and summary entries in source order'
|
|
||||||
cand --unstable 'Enable unstable features'
|
|
||||||
cand -v 'Use verbose output'
|
|
||||||
cand --verbose 'Use verbose output'
|
|
||||||
cand --changelog 'Print changelog'
|
|
||||||
cand --choose '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`'
|
|
||||||
cand --dump 'Print justfile'
|
|
||||||
cand -e 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
|
|
||||||
cand --edit 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
|
|
||||||
cand --evaluate 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.'
|
|
||||||
cand --fmt 'Format and overwrite justfile'
|
|
||||||
cand --init 'Initialize new justfile in project root'
|
|
||||||
cand --groups 'List recipe groups'
|
|
||||||
cand --man 'Print man page'
|
|
||||||
cand --summary 'List names of available recipes'
|
|
||||||
cand --variables 'List names of variables'
|
|
||||||
cand -g 'Use global justfile'
|
|
||||||
cand --global-justfile 'Use global justfile'
|
|
||||||
cand --timestamp 'Print recipe command timestamps'
|
|
||||||
cand -h 'Print help'
|
|
||||||
cand --help 'Print help'
|
|
||||||
cand -V 'Print version'
|
|
||||||
cand --version 'Print version'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
$completions[$command]
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
function __fish_just_complete_recipes
|
|
||||||
just --list 2> /dev/null | tail -n +2 | awk '{
|
|
||||||
command = $1;
|
|
||||||
args = $0;
|
|
||||||
desc = "";
|
|
||||||
delim = "";
|
|
||||||
sub(/^[[:space:]]*[^[:space:]]*/, "", args);
|
|
||||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", args);
|
|
||||||
|
|
||||||
if (match(args, /#.*/)) {
|
|
||||||
desc = substr(args, RSTART+2, RLENGTH);
|
|
||||||
args = substr(args, 0, RSTART-1);
|
|
||||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
gsub(/\+|=[`\'"][^`\'"]*[`\'"]/, "", args);
|
|
||||||
gsub(/ /, ",", args);
|
|
||||||
|
|
||||||
if (args != ""){
|
|
||||||
args = "Args: " args;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args != "" && desc != "") {
|
|
||||||
delim = "; ";
|
|
||||||
}
|
|
||||||
|
|
||||||
print command "\t" args delim desc
|
|
||||||
}'
|
|
||||||
end
|
|
||||||
|
|
||||||
# don't suggest files right off
|
|
||||||
complete -c just -n "__fish_is_first_arg" --no-files
|
|
||||||
|
|
||||||
# complete recipes
|
|
||||||
complete -c just -a '(__fish_just_complete_recipes)'
|
|
||||||
|
|
||||||
# autogenerated completions
|
|
||||||
complete -c just -l chooser -d 'Override binary invoked by `--choose`' -r
|
|
||||||
complete -c just -l color -d 'Print colorful output' -r -f -a "{auto '',always '',never ''}"
|
|
||||||
complete -c just -l command-color -d 'Echo recipe lines in <COMMAND-COLOR>' -r -f -a "{black '',blue '',cyan '',green '',purple '',red '',yellow ''}"
|
|
||||||
complete -c just -l dump-format -d 'Dump justfile as <FORMAT>' -r -f -a "{just '',json ''}"
|
|
||||||
complete -c just -l list-heading -d 'Print <TEXT> before list' -r
|
|
||||||
complete -c just -l list-prefix -d 'Print <TEXT> before each list item' -r
|
|
||||||
complete -c just -s f -l justfile -d 'Use <JUSTFILE> as justfile' -r -F
|
|
||||||
complete -c just -l set -d 'Override <VARIABLE> with <VALUE>' -r
|
|
||||||
complete -c just -l shell -d 'Invoke <SHELL> to run recipes' -r
|
|
||||||
complete -c just -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument' -r
|
|
||||||
complete -c just -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set' -r -F
|
|
||||||
complete -c just -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set' -r
|
|
||||||
complete -c just -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "{bash '',elvish '',fish '',powershell '',zsh ''}"
|
|
||||||
complete -c just -s l -l list -d 'List available recipes' -r
|
|
||||||
complete -c just -s s -l show -d 'Show recipe at <PATH>' -r
|
|
||||||
complete -c just -l dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`' -r
|
|
||||||
complete -c just -s E -l dotenv-path -d 'Load <DOTENV-PATH> as environment file instead of searching for one' -r -F
|
|
||||||
complete -c just -l timestamp-format -d 'Timestamp format string' -r
|
|
||||||
complete -c just -l check -d 'Run `--fmt` in \'check\' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.'
|
|
||||||
complete -c just -l yes -d 'Automatically confirm all recipes.'
|
|
||||||
complete -c just -s n -l dry-run -d 'Print what just would do without doing it'
|
|
||||||
complete -c just -l highlight -d 'Highlight echoed recipe lines in bold'
|
|
||||||
complete -c just -l no-aliases -d 'Don\'t show aliases in list'
|
|
||||||
complete -c just -l no-deps -d 'Don\'t run recipe dependencies'
|
|
||||||
complete -c just -l no-dotenv -d 'Don\'t load `.env` file'
|
|
||||||
complete -c just -l no-highlight -d 'Don\'t highlight echoed recipe lines in bold'
|
|
||||||
complete -c just -s q -l quiet -d 'Suppress all output'
|
|
||||||
complete -c just -l shell-command -d 'Invoke <COMMAND> with the shell used to run recipe lines and backticks'
|
|
||||||
complete -c just -l clear-shell-args -d 'Clear shell arguments'
|
|
||||||
complete -c just -s u -l unsorted -d 'Return list and summary entries in source order'
|
|
||||||
complete -c just -l unstable -d 'Enable unstable features'
|
|
||||||
complete -c just -s v -l verbose -d 'Use verbose output'
|
|
||||||
complete -c just -l changelog -d 'Print changelog'
|
|
||||||
complete -c just -l choose -d '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`'
|
|
||||||
complete -c just -l dump -d 'Print justfile'
|
|
||||||
complete -c just -s e -l edit -d 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`'
|
|
||||||
complete -c just -l evaluate -d 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable\'s value.'
|
|
||||||
complete -c just -l fmt -d 'Format and overwrite justfile'
|
|
||||||
complete -c just -l init -d 'Initialize new justfile in project root'
|
|
||||||
complete -c just -l groups -d 'List recipe groups'
|
|
||||||
complete -c just -l man -d 'Print man page'
|
|
||||||
complete -c just -l summary -d 'List names of available recipes'
|
|
||||||
complete -c just -l variables -d 'List names of variables'
|
|
||||||
complete -c just -s g -l global-justfile -d 'Use global justfile'
|
|
||||||
complete -c just -l timestamp -d 'Print recipe command timestamps'
|
|
||||||
complete -c just -s h -l help -d 'Print help'
|
|
||||||
complete -c just -s V -l version -d 'Print version'
|
|
@ -1,110 +0,0 @@
|
|||||||
using namespace System.Management.Automation
|
|
||||||
using namespace System.Management.Automation.Language
|
|
||||||
|
|
||||||
Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|
||||||
param($wordToComplete, $commandAst, $cursorPosition)
|
|
||||||
|
|
||||||
$commandElements = $commandAst.CommandElements
|
|
||||||
$command = @(
|
|
||||||
'just'
|
|
||||||
for ($i = 1; $i -lt $commandElements.Count; $i++) {
|
|
||||||
$element = $commandElements[$i]
|
|
||||||
if ($element -isnot [StringConstantExpressionAst] -or
|
|
||||||
$element.StringConstantType -ne [StringConstantType]::BareWord -or
|
|
||||||
$element.Value.StartsWith('-') -or
|
|
||||||
$element.Value -eq $wordToComplete) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
$element.Value
|
|
||||||
}) -join ';'
|
|
||||||
|
|
||||||
$completions = @(switch ($command) {
|
|
||||||
'just' {
|
|
||||||
[CompletionResult]::new('--chooser', 'chooser', [CompletionResultType]::ParameterName, 'Override binary invoked by `--choose`')
|
|
||||||
[CompletionResult]::new('--color', 'color', [CompletionResultType]::ParameterName, 'Print colorful output')
|
|
||||||
[CompletionResult]::new('--command-color', 'command-color', [CompletionResultType]::ParameterName, 'Echo recipe lines in <COMMAND-COLOR>')
|
|
||||||
[CompletionResult]::new('--dump-format', 'dump-format', [CompletionResultType]::ParameterName, 'Dump justfile as <FORMAT>')
|
|
||||||
[CompletionResult]::new('--list-heading', 'list-heading', [CompletionResultType]::ParameterName, 'Print <TEXT> before list')
|
|
||||||
[CompletionResult]::new('--list-prefix', 'list-prefix', [CompletionResultType]::ParameterName, 'Print <TEXT> before each list item')
|
|
||||||
[CompletionResult]::new('-f', 'f', [CompletionResultType]::ParameterName, 'Use <JUSTFILE> as justfile')
|
|
||||||
[CompletionResult]::new('--justfile', 'justfile', [CompletionResultType]::ParameterName, 'Use <JUSTFILE> as justfile')
|
|
||||||
[CompletionResult]::new('--set', 'set', [CompletionResultType]::ParameterName, 'Override <VARIABLE> with <VALUE>')
|
|
||||||
[CompletionResult]::new('--shell', 'shell', [CompletionResultType]::ParameterName, 'Invoke <SHELL> to run recipes')
|
|
||||||
[CompletionResult]::new('--shell-arg', 'shell-arg', [CompletionResultType]::ParameterName, 'Invoke shell with <SHELL-ARG> as an argument')
|
|
||||||
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set')
|
|
||||||
[CompletionResult]::new('--working-directory', 'working-directory', [CompletionResultType]::ParameterName, 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set')
|
|
||||||
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
|
||||||
[CompletionResult]::new('--command', 'command', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
|
||||||
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
|
||||||
[CompletionResult]::new('-l', 'l', [CompletionResultType]::ParameterName, 'List available recipes')
|
|
||||||
[CompletionResult]::new('--list', 'list', [CompletionResultType]::ParameterName, 'List available recipes')
|
|
||||||
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show recipe at <PATH>')
|
|
||||||
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show recipe at <PATH>')
|
|
||||||
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
|
|
||||||
[CompletionResult]::new('-E', 'E ', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
|
||||||
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load <DOTENV-PATH> as environment file instead of searching for one')
|
|
||||||
[CompletionResult]::new('--timestamp-format', 'timestamp-format', [CompletionResultType]::ParameterName, 'Timestamp format string')
|
|
||||||
[CompletionResult]::new('--check', 'check', [CompletionResultType]::ParameterName, 'Run `--fmt` in ''check'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.')
|
|
||||||
[CompletionResult]::new('--yes', 'yes', [CompletionResultType]::ParameterName, 'Automatically confirm all recipes.')
|
|
||||||
[CompletionResult]::new('-n', 'n', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
|
|
||||||
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
|
|
||||||
[CompletionResult]::new('--highlight', 'highlight', [CompletionResultType]::ParameterName, 'Highlight echoed recipe lines in bold')
|
|
||||||
[CompletionResult]::new('--no-aliases', 'no-aliases', [CompletionResultType]::ParameterName, 'Don''t show aliases in list')
|
|
||||||
[CompletionResult]::new('--no-deps', 'no-deps', [CompletionResultType]::ParameterName, 'Don''t run recipe dependencies')
|
|
||||||
[CompletionResult]::new('--no-dotenv', 'no-dotenv', [CompletionResultType]::ParameterName, 'Don''t load `.env` file')
|
|
||||||
[CompletionResult]::new('--no-highlight', 'no-highlight', [CompletionResultType]::ParameterName, 'Don''t highlight echoed recipe lines in bold')
|
|
||||||
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Suppress all output')
|
|
||||||
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Suppress all output')
|
|
||||||
[CompletionResult]::new('--shell-command', 'shell-command', [CompletionResultType]::ParameterName, 'Invoke <COMMAND> with the shell used to run recipe lines and backticks')
|
|
||||||
[CompletionResult]::new('--clear-shell-args', 'clear-shell-args', [CompletionResultType]::ParameterName, 'Clear shell arguments')
|
|
||||||
[CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'Return list and summary entries in source order')
|
|
||||||
[CompletionResult]::new('--unsorted', 'unsorted', [CompletionResultType]::ParameterName, 'Return list and summary entries in source order')
|
|
||||||
[CompletionResult]::new('--unstable', 'unstable', [CompletionResultType]::ParameterName, 'Enable unstable features')
|
|
||||||
[CompletionResult]::new('-v', 'v', [CompletionResultType]::ParameterName, 'Use verbose output')
|
|
||||||
[CompletionResult]::new('--verbose', 'verbose', [CompletionResultType]::ParameterName, 'Use verbose output')
|
|
||||||
[CompletionResult]::new('--changelog', 'changelog', [CompletionResultType]::ParameterName, 'Print changelog')
|
|
||||||
[CompletionResult]::new('--choose', 'choose', [CompletionResultType]::ParameterName, '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`')
|
|
||||||
[CompletionResult]::new('--dump', 'dump', [CompletionResultType]::ParameterName, 'Print justfile')
|
|
||||||
[CompletionResult]::new('-e', 'e', [CompletionResultType]::ParameterName, 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`')
|
|
||||||
[CompletionResult]::new('--edit', 'edit', [CompletionResultType]::ParameterName, 'Edit justfile with editor given by $VISUAL or $EDITOR, falling back to `vim`')
|
|
||||||
[CompletionResult]::new('--evaluate', 'evaluate', [CompletionResultType]::ParameterName, 'Evaluate and print all variables. If a variable name is given as an argument, only print that variable''s value.')
|
|
||||||
[CompletionResult]::new('--fmt', 'fmt', [CompletionResultType]::ParameterName, 'Format and overwrite justfile')
|
|
||||||
[CompletionResult]::new('--init', 'init', [CompletionResultType]::ParameterName, 'Initialize new justfile in project root')
|
|
||||||
[CompletionResult]::new('--groups', 'groups', [CompletionResultType]::ParameterName, 'List recipe groups')
|
|
||||||
[CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page')
|
|
||||||
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
|
|
||||||
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
|
|
||||||
[CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, 'Use global justfile')
|
|
||||||
[CompletionResult]::new('--global-justfile', 'global-justfile', [CompletionResultType]::ParameterName, 'Use global justfile')
|
|
||||||
[CompletionResult]::new('--timestamp', 'timestamp', [CompletionResultType]::ParameterName, 'Print recipe command timestamps')
|
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
|
||||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
|
||||||
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function Get-JustFileRecipes([string[]]$CommandElements) {
|
|
||||||
$justFileIndex = $commandElements.IndexOf("--justfile");
|
|
||||||
|
|
||||||
if ($justFileIndex -ne -1 && $justFileIndex + 1 -le $commandElements.Length) {
|
|
||||||
$justFileLocation = $commandElements[$justFileIndex + 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
$justArgs = @("--summary")
|
|
||||||
|
|
||||||
if (Test-Path $justFileLocation) {
|
|
||||||
$justArgs += @("--justfile", $justFileLocation)
|
|
||||||
}
|
|
||||||
|
|
||||||
$recipes = $(just @justArgs) -split ' '
|
|
||||||
return $recipes | ForEach-Object { [CompletionResult]::new($_) }
|
|
||||||
}
|
|
||||||
|
|
||||||
$elementValues = $commandElements | Select-Object -ExpandProperty Value
|
|
||||||
$recipes = Get-JustFileRecipes -CommandElements $elementValues
|
|
||||||
$completions += $recipes
|
|
||||||
$completions.Where{ $_.CompletionText -like "$wordToComplete*" } |
|
|
||||||
Sort-Object -Property ListItemText
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
#compdef just
|
|
||||||
|
|
||||||
autoload -U is-at-least
|
|
||||||
|
|
||||||
_just() {
|
|
||||||
typeset -A opt_args
|
|
||||||
typeset -a _arguments_options
|
|
||||||
local ret=1
|
|
||||||
|
|
||||||
if is-at-least 5.2; then
|
|
||||||
_arguments_options=(-s -S -C)
|
|
||||||
else
|
|
||||||
_arguments_options=(-s -C)
|
|
||||||
fi
|
|
||||||
|
|
||||||
local context curcontext="$curcontext" state line
|
|
||||||
local common=(
|
|
||||||
'--chooser=[Override binary invoked by \`--choose\`]: : ' \
|
|
||||||
'--color=[Print colorful output]: :(auto always never)' \
|
|
||||||
'--command-color=[Echo recipe lines in <COMMAND-COLOR>]: :(black blue cyan green purple red yellow)' \
|
|
||||||
'--dump-format=[Dump justfile as <FORMAT>]:FORMAT:(just json)' \
|
|
||||||
'--list-heading=[Print <TEXT> before list]:TEXT: ' \
|
|
||||||
'--list-prefix=[Print <TEXT> before each list item]:TEXT: ' \
|
|
||||||
'-f+[Use <JUSTFILE> as justfile]: :_files' \
|
|
||||||
'--justfile=[Use <JUSTFILE> as justfile]: :_files' \
|
|
||||||
'*--set=[Override <VARIABLE> with <VALUE>]: :(_just_variables)' \
|
|
||||||
'--shell=[Invoke <SHELL> to run recipes]: : ' \
|
|
||||||
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]: : ' \
|
|
||||||
'-d+[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]: :_files' \
|
|
||||||
'--working-directory=[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]: :_files' \
|
|
||||||
'*-c+[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
|
||||||
'*--command=[Run an arbitrary command with the working directory, \`.env\`, overrides, and exports set]: : ' \
|
|
||||||
'*--completions=[Print shell completion script for <SHELL>]:SHELL:(bash elvish fish powershell zsh)' \
|
|
||||||
'()-l+[List available recipes]' \
|
|
||||||
'()--list=[List available recipes]' \
|
|
||||||
'-s+[Show recipe at <PATH>]: :(_just_commands)' \
|
|
||||||
'--show=[Show recipe at <PATH>]: :(_just_commands)' \
|
|
||||||
'(-E --dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of \`.env\`]: : ' \
|
|
||||||
'-E+[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
|
||||||
'--dotenv-path=[Load <DOTENV-PATH> as environment file instead of searching for one]: :_files' \
|
|
||||||
'--timestamp-format=[Timestamp format string]: : ' \
|
|
||||||
'--check[Run \`--fmt\` in '\''check'\'' mode. Exits with 0 if justfile is formatted correctly. Exits with 1 and prints a diff if formatting is required.]' \
|
|
||||||
'--yes[Automatically confirm all recipes.]' \
|
|
||||||
'(-q --quiet)-n[Print what just would do without doing it]' \
|
|
||||||
'(-q --quiet)--dry-run[Print what just would do without doing it]' \
|
|
||||||
'--highlight[Highlight echoed recipe lines in bold]' \
|
|
||||||
'--no-aliases[Don'\''t show aliases in list]' \
|
|
||||||
'--no-deps[Don'\''t run recipe dependencies]' \
|
|
||||||
'--no-dotenv[Don'\''t load \`.env\` file]' \
|
|
||||||
'--no-highlight[Don'\''t highlight echoed recipe lines in bold]' \
|
|
||||||
'(-n --dry-run)-q[Suppress all output]' \
|
|
||||||
'(-n --dry-run)--quiet[Suppress all output]' \
|
|
||||||
'--shell-command[Invoke <COMMAND> with the shell used to run recipe lines and backticks]' \
|
|
||||||
'--clear-shell-args[Clear shell arguments]' \
|
|
||||||
'-u[Return list and summary entries in source order]' \
|
|
||||||
'--unsorted[Return list and summary entries in source order]' \
|
|
||||||
'--unstable[Enable unstable features]' \
|
|
||||||
'*-v[Use verbose output]' \
|
|
||||||
'*--verbose[Use verbose output]' \
|
|
||||||
'--changelog[Print changelog]' \
|
|
||||||
'--choose[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\`]' \
|
|
||||||
'--dump[Print justfile]' \
|
|
||||||
'-e[Edit justfile with editor given by \$VISUAL or \$EDITOR, falling back to \`vim\`]' \
|
|
||||||
'--edit[Edit justfile with editor given by \$VISUAL or \$EDITOR, falling back to \`vim\`]' \
|
|
||||||
'--evaluate[Evaluate and print all variables. If a variable name is given as an argument, only print that variable'\''s value.]' \
|
|
||||||
'--fmt[Format and overwrite justfile]' \
|
|
||||||
'--init[Initialize new justfile in project root]' \
|
|
||||||
'--groups[List recipe groups]' \
|
|
||||||
'--man[Print man page]' \
|
|
||||||
'--summary[List names of available recipes]' \
|
|
||||||
'--variables[List names of variables]' \
|
|
||||||
'(-f --justfile -d --working-directory)-g[Use global justfile]' \
|
|
||||||
'(-f --justfile -d --working-directory)--global-justfile[Use global justfile]' \
|
|
||||||
'--timestamp[Print recipe command timestamps]' \
|
|
||||||
'-h[Print help]' \
|
|
||||||
'--help[Print help]' \
|
|
||||||
'-V[Print version]' \
|
|
||||||
'--version[Print version]' \
|
|
||||||
)
|
|
||||||
|
|
||||||
_arguments "${_arguments_options[@]}" $common \
|
|
||||||
'1: :_just_commands' \
|
|
||||||
'*: :->args' \
|
|
||||||
&& ret=0
|
|
||||||
|
|
||||||
case $state in
|
|
||||||
args)
|
|
||||||
curcontext="${curcontext%:*}-${words[2]}:"
|
|
||||||
|
|
||||||
local lastarg=${words[${#words}]}
|
|
||||||
local recipe
|
|
||||||
|
|
||||||
local cmds; cmds=(
|
|
||||||
${(s: :)$(_call_program commands just --summary)}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Find first recipe name
|
|
||||||
for ((i = 2; i < $#words; i++ )) do
|
|
||||||
if [[ ${cmds[(I)${words[i]}]} -gt 0 ]]; then
|
|
||||||
recipe=${words[i]}
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ $lastarg = */* ]]; then
|
|
||||||
# Arguments contain slash would be recognised as a file
|
|
||||||
_arguments -s -S $common '*:: :_files'
|
|
||||||
elif [[ $lastarg = *=* ]]; then
|
|
||||||
# Arguments contain equal would be recognised as a variable
|
|
||||||
_message "value"
|
|
||||||
elif [[ $recipe ]]; then
|
|
||||||
# Show usage message
|
|
||||||
_message "`just --show $recipe`"
|
|
||||||
# Or complete with other commands
|
|
||||||
#_arguments -s -S $common '*:: :_just_commands'
|
|
||||||
else
|
|
||||||
_arguments -s -S $common '*:: :_just_commands'
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[_just_commands] )) ||
|
|
||||||
_just_commands() {
|
|
||||||
[[ $PREFIX = -* ]] && return 1
|
|
||||||
integer ret=1
|
|
||||||
local variables; variables=(
|
|
||||||
${(s: :)$(_call_program commands just --variables)}
|
|
||||||
)
|
|
||||||
local commands; commands=(
|
|
||||||
${${${(M)"${(f)$(_call_program commands just --list)}":# *}/ ##/}/ ##/:Args: }
|
|
||||||
)
|
|
||||||
|
|
||||||
if compset -P '*='; then
|
|
||||||
case "${${words[-1]%=*}#*=}" in
|
|
||||||
*) _message 'value' && ret=0 ;;
|
|
||||||
esac
|
|
||||||
else
|
|
||||||
_describe -t variables 'variables' variables -qS "=" && ret=0
|
|
||||||
_describe -t commands 'just commands' commands "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$funcstack[1]" = "_just" ]; then
|
|
||||||
(( $+functions[_just_variables] )) ||
|
|
||||||
_just_variables() {
|
|
||||||
[[ $PREFIX = -* ]] && return 1
|
|
||||||
integer ret=1
|
|
||||||
local variables; variables=(
|
|
||||||
${(s: :)$(_call_program commands just --variables)}
|
|
||||||
)
|
|
||||||
|
|
||||||
if compset -P '*='; then
|
|
||||||
case "${${words[-1]%=*}#*=}" in
|
|
||||||
*) _message 'value' && ret=0 ;;
|
|
||||||
esac
|
|
||||||
else
|
|
||||||
_describe -t variables 'variables' variables && ret=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
_just "$@"
|
|
||||||
else
|
|
||||||
compdef _just just
|
|
||||||
fi
|
|
1
justfile
1
justfile
@ -43,6 +43,7 @@ shellcheck:
|
|||||||
shellcheck www/install.sh
|
shellcheck www/install.sh
|
||||||
|
|
||||||
man:
|
man:
|
||||||
|
mkdir -p man
|
||||||
cargo run -- --man > man/just.1
|
cargo run -- --man > man/just.1
|
||||||
|
|
||||||
view-man: man
|
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 modules: Table<Justfile> = Table::new();
|
||||||
|
|
||||||
|
let mut unexports: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
let mut definitions: HashMap<&str, (&'static str, Name)> = HashMap::new();
|
let mut definitions: HashMap<&str, (&'static str, Name)> = HashMap::new();
|
||||||
|
|
||||||
let mut define = |name: Name<'src>,
|
let mut define = |name: Name<'src>,
|
||||||
@ -98,6 +100,13 @@ impl<'src> Analyzer<'src> {
|
|||||||
self.analyze_set(set)?;
|
self.analyze_set(set)?;
|
||||||
self.sets.insert(set.clone());
|
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();
|
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
|
||||||
|
|
||||||
for assignment in assignments {
|
for assignment in assignments {
|
||||||
if !settings.allow_duplicate_variables
|
let variable = assignment.name.lexeme();
|
||||||
&& self.assignments.contains_key(assignment.name.lexeme())
|
|
||||||
{
|
if !settings.allow_duplicate_variables && self.assignments.contains_key(variable) {
|
||||||
return Err(assignment.name.token.error(DuplicateVariable {
|
return Err(assignment.name.token.error(DuplicateVariable { variable }));
|
||||||
variable: assignment.name.lexeme(),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.assignments
|
.assignments
|
||||||
.get(assignment.name.lexeme())
|
.get(variable)
|
||||||
.map_or(true, |original| assignment.depth <= original.depth)
|
.map_or(true, |original| assignment.depth <= original.depth)
|
||||||
{
|
{
|
||||||
self.assignments.insert(assignment.clone());
|
self.assignments.insert(assignment.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if unexports.contains(variable) {
|
||||||
|
return Err(assignment.name.token.error(ExportUnexported { variable }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssignmentResolver::resolve_assignments(&self.assignments)?;
|
AssignmentResolver::resolve_assignments(&self.assignments)?;
|
||||||
@ -167,6 +178,7 @@ impl<'src> Analyzer<'src> {
|
|||||||
recipes,
|
recipes,
|
||||||
settings,
|
settings,
|
||||||
source: root.into(),
|
source: root.into(),
|
||||||
|
unexports,
|
||||||
warnings,
|
warnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ impl AttributeDiscriminant {
|
|||||||
impl<'src> Attribute<'src> {
|
impl<'src> Attribute<'src> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
name: Name<'src>,
|
name: Name<'src>,
|
||||||
argument: Option<StringLiteral<'src>>,
|
mut arguments: Vec<StringLiteral<'src>>,
|
||||||
) -> CompileResult<'src, Self> {
|
) -> CompileResult<'src, Self> {
|
||||||
use AttributeDiscriminant::*;
|
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();
|
let range = discriminant.argument_range();
|
||||||
|
|
||||||
if !range.contains(&found) {
|
if !range.contains(&found) {
|
||||||
@ -72,9 +71,9 @@ impl<'src> Attribute<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(match discriminant {
|
Ok(match discriminant {
|
||||||
Confirm => Self::Confirm(argument),
|
Confirm => Self::Confirm(arguments.pop()),
|
||||||
Doc => Self::Doc(argument),
|
Doc => Self::Doc(arguments.pop()),
|
||||||
Group => Self::Group(argument.unwrap()),
|
Group => Self::Group(arguments.pop().unwrap()),
|
||||||
Linux => Self::Linux,
|
Linux => Self::Linux,
|
||||||
Macos => Self::Macos,
|
Macos => Self::Macos,
|
||||||
NoCd => Self::NoCd,
|
NoCd => Self::NoCd,
|
||||||
|
@ -1,25 +1,41 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(crate) trait CommandExt {
|
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 {
|
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 {
|
for (name, value) in dotenv {
|
||||||
self.env(name, value);
|
self.env(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent) = scope.parent() {
|
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() {
|
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() {
|
for binding in scope.bindings() {
|
||||||
|
@ -131,6 +131,9 @@ impl Display for CompileError<'_> {
|
|||||||
DuplicateVariable { variable } => {
|
DuplicateVariable { variable } => {
|
||||||
write!(f, "Variable `{variable}` has multiple definitions")
|
write!(f, "Variable `{variable}` has multiple definitions")
|
||||||
}
|
}
|
||||||
|
DuplicateUnexport { variable } => {
|
||||||
|
write!(f, "Variable `{variable}` is unexported multiple times")
|
||||||
|
}
|
||||||
ExpectedKeyword { expected, found } => {
|
ExpectedKeyword { expected, found } => {
|
||||||
let expected = List::or_ticked(expected);
|
let expected = List::or_ticked(expected);
|
||||||
if found.kind == TokenKind::Identifier {
|
if found.kind == TokenKind::Identifier {
|
||||||
@ -143,6 +146,9 @@ impl Display for CompileError<'_> {
|
|||||||
write!(f, "Expected keyword {expected} but found `{}`", found.kind)
|
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"),
|
ExtraLeadingWhitespace => write!(f, "Recipe line has extra leading whitespace"),
|
||||||
FunctionArgumentCountMismatch {
|
FunctionArgumentCountMismatch {
|
||||||
function,
|
function,
|
||||||
|
@ -52,10 +52,16 @@ pub(crate) enum CompileErrorKind<'src> {
|
|||||||
DuplicateVariable {
|
DuplicateVariable {
|
||||||
variable: &'src str,
|
variable: &'src str,
|
||||||
},
|
},
|
||||||
|
DuplicateUnexport {
|
||||||
|
variable: &'src str,
|
||||||
|
},
|
||||||
ExpectedKeyword {
|
ExpectedKeyword {
|
||||||
expected: Vec<Keyword>,
|
expected: Vec<Keyword>,
|
||||||
found: Token<'src>,
|
found: Token<'src>,
|
||||||
},
|
},
|
||||||
|
ExportUnexported {
|
||||||
|
variable: &'src str,
|
||||||
|
},
|
||||||
ExtraLeadingWhitespace,
|
ExtraLeadingWhitespace,
|
||||||
FunctionArgumentCountMismatch {
|
FunctionArgumentCountMismatch {
|
||||||
function: &'src str,
|
function: &'src str,
|
||||||
|
@ -32,6 +32,7 @@ pub(crate) struct Config {
|
|||||||
pub(crate) invocation_directory: PathBuf,
|
pub(crate) invocation_directory: PathBuf,
|
||||||
pub(crate) list_heading: String,
|
pub(crate) list_heading: String,
|
||||||
pub(crate) list_prefix: String,
|
pub(crate) list_prefix: String,
|
||||||
|
pub(crate) list_submodules: bool,
|
||||||
pub(crate) load_dotenv: bool,
|
pub(crate) load_dotenv: bool,
|
||||||
pub(crate) no_aliases: bool,
|
pub(crate) no_aliases: bool,
|
||||||
pub(crate) no_dependencies: bool,
|
pub(crate) no_dependencies: bool,
|
||||||
@ -97,11 +98,12 @@ mod arg {
|
|||||||
pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH";
|
pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH";
|
||||||
pub(crate) const DRY_RUN: &str = "DRY-RUN";
|
pub(crate) const DRY_RUN: &str = "DRY-RUN";
|
||||||
pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT";
|
pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT";
|
||||||
pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL_JUSTFILE";
|
pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL-JUSTFILE";
|
||||||
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
|
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
|
||||||
pub(crate) const JUSTFILE: &str = "JUSTFILE";
|
pub(crate) const JUSTFILE: &str = "JUSTFILE";
|
||||||
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
|
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
|
||||||
pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX";
|
pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX";
|
||||||
|
pub(crate) const LIST_SUBMODULES: &str = "LIST-SUBMODULES";
|
||||||
pub(crate) const NO_ALIASES: &str = "NO-ALIASES";
|
pub(crate) const NO_ALIASES: &str = "NO-ALIASES";
|
||||||
pub(crate) const NO_DEPS: &str = "NO-DEPS";
|
pub(crate) const NO_DEPS: &str = "NO-DEPS";
|
||||||
pub(crate) const NO_DOTENV: &str = "NO-DOTENV";
|
pub(crate) const NO_DOTENV: &str = "NO-DOTENV";
|
||||||
@ -112,7 +114,7 @@ mod arg {
|
|||||||
pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
|
pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
|
||||||
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
||||||
pub(crate) const TIMESTAMP: &str = "TIMESTAMP";
|
pub(crate) const TIMESTAMP: &str = "TIMESTAMP";
|
||||||
pub(crate) const TIMESTAMP_FORMAT: &str = "TIMESTAMP_FORMAT";
|
pub(crate) const TIMESTAMP_FORMAT: &str = "TIMESTAMP-FORMAT";
|
||||||
pub(crate) const UNSORTED: &str = "UNSORTED";
|
pub(crate) const UNSORTED: &str = "UNSORTED";
|
||||||
pub(crate) const UNSTABLE: &str = "UNSTABLE";
|
pub(crate) const UNSTABLE: &str = "UNSTABLE";
|
||||||
pub(crate) const VERBOSE: &str = "VERBOSE";
|
pub(crate) const VERBOSE: &str = "VERBOSE";
|
||||||
@ -236,6 +238,13 @@ impl Config {
|
|||||||
.value_name("TEXT")
|
.value_name("TEXT")
|
||||||
.action(ArgAction::Set),
|
.action(ArgAction::Set),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(arg::LIST_SUBMODULES)
|
||||||
|
.long("list-submodules")
|
||||||
|
.help("List recipes in submodules")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.env("JUST_LIST_SUBMODULES"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(arg::NO_ALIASES)
|
Arg::new(arg::NO_ALIASES)
|
||||||
.long("no-aliases")
|
.long("no-aliases")
|
||||||
@ -754,6 +763,7 @@ impl Config {
|
|||||||
list_prefix: matches
|
list_prefix: matches
|
||||||
.get_one::<String>(arg::LIST_PREFIX)
|
.get_one::<String>(arg::LIST_PREFIX)
|
||||||
.map_or_else(|| " ".into(), Into::into),
|
.map_or_else(|| " ".into(), Into::into),
|
||||||
|
list_submodules: matches.get_flag(arg::LIST_SUBMODULES),
|
||||||
load_dotenv: !matches.get_flag(arg::NO_DOTENV),
|
load_dotenv: !matches.get_flag(arg::NO_DOTENV),
|
||||||
no_aliases: matches.get_flag(arg::NO_ALIASES),
|
no_aliases: matches.get_flag(arg::NO_ALIASES),
|
||||||
no_dependencies: matches.get_flag(arg::NO_DEPS),
|
no_dependencies: matches.get_flag(arg::NO_DEPS),
|
||||||
|
12
src/error.rs
12
src/error.rs
@ -20,7 +20,7 @@ pub(crate) enum Error<'src> {
|
|||||||
token: Token<'src>,
|
token: Token<'src>,
|
||||||
output_error: OutputError,
|
output_error: OutputError,
|
||||||
},
|
},
|
||||||
CacheDirIo {
|
RuntimeDirIo {
|
||||||
io_error: io::Error,
|
io_error: io::Error,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
},
|
},
|
||||||
@ -79,6 +79,7 @@ pub(crate) enum Error<'src> {
|
|||||||
Dotenv {
|
Dotenv {
|
||||||
dotenv_error: dotenvy::Error,
|
dotenv_error: dotenvy::Error,
|
||||||
},
|
},
|
||||||
|
DotenvRequired,
|
||||||
DumpJson {
|
DumpJson {
|
||||||
serde_json_error: serde_json::Error,
|
serde_json_error: serde_json::Error,
|
||||||
},
|
},
|
||||||
@ -286,9 +287,6 @@ impl<'src> ColorDisplay for Error<'src> {
|
|||||||
}?,
|
}?,
|
||||||
OutputError::Utf8(utf8_error) => write!(f, "Backtick succeeded but stdout was not utf8: {utf8_error}")?,
|
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} => {
|
ChooserInvoke { shell_binary, shell_arguments, chooser, io_error} => {
|
||||||
let chooser = chooser.to_string_lossy();
|
let chooser = chooser.to_string_lossy();
|
||||||
write!(f, "Chooser `{shell_binary} {shell_arguments} {chooser}` invocation failed: {io_error}")?;
|
write!(f, "Chooser `{shell_binary} {shell_arguments} {chooser}` invocation failed: {io_error}")?;
|
||||||
@ -347,6 +345,9 @@ impl<'src> ColorDisplay for Error<'src> {
|
|||||||
Dotenv { dotenv_error } => {
|
Dotenv { dotenv_error } => {
|
||||||
write!(f, "Failed to load environment file: {dotenv_error}")?;
|
write!(f, "Failed to load environment file: {dotenv_error}")?;
|
||||||
}
|
}
|
||||||
|
DotenvRequired => {
|
||||||
|
write!(f, "Dotenv file not found")?;
|
||||||
|
}
|
||||||
DumpJson { serde_json_error } => {
|
DumpJson { serde_json_error } => {
|
||||||
write!(f, "Failed to dump JSON to stdout: {serde_json_error}")?;
|
write!(f, "Failed to dump JSON to stdout: {serde_json_error}")?;
|
||||||
}
|
}
|
||||||
@ -403,6 +404,9 @@ impl<'src> ColorDisplay for Error<'src> {
|
|||||||
write!(f, "Recipe `{recipe}` was not confirmed")?;
|
write!(f, "Recipe `{recipe}` was not confirmed")?;
|
||||||
}
|
}
|
||||||
RegexCompile { source } => write!(f, "{source}")?,
|
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)?,
|
Search { search_error } => Display::fmt(search_error, f)?,
|
||||||
Shebang { recipe, command, argument, io_error} => {
|
Shebang { recipe, command, argument, io_error} => {
|
||||||
if let Some(argument) = argument {
|
if let Some(argument) = argument {
|
||||||
|
@ -8,6 +8,7 @@ pub(crate) struct Evaluator<'src: 'run, 'run> {
|
|||||||
pub(crate) scope: Scope<'src, 'run>,
|
pub(crate) scope: Scope<'src, 'run>,
|
||||||
pub(crate) search: &'run Search,
|
pub(crate) search: &'run Search,
|
||||||
pub(crate) settings: &'run Settings<'run>,
|
pub(crate) settings: &'run Settings<'run>,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src, 'run> Evaluator<'src, 'run> {
|
impl<'src, 'run> Evaluator<'src, 'run> {
|
||||||
@ -19,6 +20,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: Scope<'src, 'run>,
|
scope: Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
settings: &'run Settings<'run>,
|
settings: &'run Settings<'run>,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
) -> RunResult<'src, Scope<'src, 'run>> {
|
) -> RunResult<'src, Scope<'src, 'run>> {
|
||||||
let mut evaluator = Self {
|
let mut evaluator = Self {
|
||||||
assignments: Some(assignments),
|
assignments: Some(assignments),
|
||||||
@ -28,6 +30,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope,
|
scope,
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
|
unsets,
|
||||||
};
|
};
|
||||||
|
|
||||||
for assignment in assignments.values() {
|
for assignment in assignments.values() {
|
||||||
@ -217,7 +220,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
cmd.arg(command);
|
cmd.arg(command);
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
cmd.current_dir(&self.search.working_directory);
|
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.stdin(Stdio::inherit());
|
||||||
cmd.stderr(if self.config.verbosity.quiet() {
|
cmd.stderr(if self.config.verbosity.quiet() {
|
||||||
Stdio::null()
|
Stdio::null()
|
||||||
@ -261,6 +264,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
settings: &'run Settings,
|
settings: &'run Settings,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
||||||
let mut evaluator = Self {
|
let mut evaluator = Self {
|
||||||
assignments: None,
|
assignments: None,
|
||||||
@ -270,6 +274,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: scope.child(),
|
scope: scope.child(),
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
|
unsets,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scope = scope.child();
|
let mut scope = scope.child();
|
||||||
@ -316,6 +321,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
settings: &'run Settings,
|
settings: &'run Settings,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
assignments: None,
|
assignments: None,
|
||||||
@ -325,6 +331,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: Scope::child(scope),
|
scope: Scope::child(scope),
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
|
unsets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ pub(crate) enum Item<'src> {
|
|||||||
},
|
},
|
||||||
Recipe(UnresolvedRecipe<'src>),
|
Recipe(UnresolvedRecipe<'src>),
|
||||||
Set(Set<'src>),
|
Set(Set<'src>),
|
||||||
|
Unexport {
|
||||||
|
name: Name<'src>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Display for Item<'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::Recipe(recipe) => write!(f, "{}", recipe.color_display(Color::never())),
|
||||||
Self::Set(set) => write!(f, "{set}"),
|
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>,
|
pub(crate) settings: Settings<'src>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub(crate) source: PathBuf,
|
pub(crate) source: PathBuf,
|
||||||
|
pub(crate) unexports: HashSet<String>,
|
||||||
pub(crate) warnings: Vec<Warning>,
|
pub(crate) warnings: Vec<Warning>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +114,7 @@ impl<'src> Justfile<'src> {
|
|||||||
scope,
|
scope,
|
||||||
search,
|
search,
|
||||||
&self.settings,
|
&self.settings,
|
||||||
|
&self.unexports,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +165,7 @@ impl<'src> Justfile<'src> {
|
|||||||
|
|
||||||
let scope = scope.child();
|
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| {
|
let status = InterruptHandler::guard(|| command.status()).map_err(|io_error| {
|
||||||
Error::CommandInvoke {
|
Error::CommandInvoke {
|
||||||
@ -286,6 +288,7 @@ impl<'src> Justfile<'src> {
|
|||||||
scope: invocation.scope,
|
scope: invocation.scope,
|
||||||
search,
|
search,
|
||||||
settings: invocation.settings,
|
settings: invocation.settings,
|
||||||
|
unexports: &self.unexports,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::run_recipe(
|
Self::run_recipe(
|
||||||
@ -441,6 +444,7 @@ impl<'src> Justfile<'src> {
|
|||||||
context.scope,
|
context.scope,
|
||||||
search,
|
search,
|
||||||
context.settings,
|
context.settings,
|
||||||
|
context.unexports,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let scope = outer.child();
|
let scope = outer.child();
|
||||||
@ -452,6 +456,7 @@ impl<'src> Justfile<'src> {
|
|||||||
&scope,
|
&scope,
|
||||||
search,
|
search,
|
||||||
context.settings,
|
context.settings,
|
||||||
|
context.unexports,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !context.config.no_dependencies {
|
if !context.config.no_dependencies {
|
||||||
|
@ -10,6 +10,7 @@ pub(crate) enum Keyword {
|
|||||||
DotenvFilename,
|
DotenvFilename,
|
||||||
DotenvLoad,
|
DotenvLoad,
|
||||||
DotenvPath,
|
DotenvPath,
|
||||||
|
DotenvRequired,
|
||||||
Else,
|
Else,
|
||||||
Export,
|
Export,
|
||||||
Fallback,
|
Fallback,
|
||||||
@ -24,6 +25,7 @@ pub(crate) enum Keyword {
|
|||||||
Shell,
|
Shell,
|
||||||
Tempdir,
|
Tempdir,
|
||||||
True,
|
True,
|
||||||
|
Unexport,
|
||||||
WindowsPowershell,
|
WindowsPowershell,
|
||||||
WindowsShell,
|
WindowsShell,
|
||||||
X,
|
X,
|
||||||
|
@ -42,7 +42,7 @@ pub(crate) use {
|
|||||||
std::{
|
std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp,
|
cmp,
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
||||||
env,
|
env,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
fmt::{self, Debug, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const DEFAULT_DOTENV_FILENAME: &str = ".env";
|
|
||||||
|
|
||||||
pub(crate) fn load_dotenv(
|
pub(crate) fn load_dotenv(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
@ -17,16 +15,21 @@ pub(crate) fn load_dotenv(
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.or(settings.dotenv_path.as_ref());
|
.or(settings.dotenv_path.as_ref());
|
||||||
|
|
||||||
if !settings.dotenv_load.unwrap_or_default() && dotenv_filename.is_none() && dotenv_path.is_none()
|
if !settings.dotenv_load
|
||||||
|
&& dotenv_filename.is_none()
|
||||||
|
&& dotenv_path.is_none()
|
||||||
|
&& !settings.dotenv_required
|
||||||
{
|
{
|
||||||
return Ok(BTreeMap::new());
|
return Ok(BTreeMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = dotenv_path {
|
if let Some(path) = dotenv_path {
|
||||||
return load_from_file(&working_directory.join(path));
|
if path.is_file() {
|
||||||
|
return load_from_file(&working_directory.join(path));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = dotenv_filename.map_or(DEFAULT_DOTENV_FILENAME, |s| s.as_str());
|
let filename = dotenv_filename.map_or(".env", |s| s.as_str());
|
||||||
|
|
||||||
for directory in working_directory.ancestors() {
|
for directory in working_directory.ancestors() {
|
||||||
let path = directory.join(filename);
|
let path = directory.join(filename);
|
||||||
@ -35,7 +38,11 @@ pub(crate) fn load_dotenv(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(BTreeMap::new())
|
if settings.dotenv_required {
|
||||||
|
Err(Error::DotenvRequired)
|
||||||
|
} else {
|
||||||
|
Ok(BTreeMap::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_file(path: &Path) -> RunResult<'static, BTreeMap<String, String>> {
|
fn load_from_file(path: &Path) -> RunResult<'static, BTreeMap<String, String>> {
|
||||||
|
@ -7,6 +7,13 @@ impl<'src> Namepath<'src> {
|
|||||||
pub(crate) fn join(&self, name: Name<'src>) -> Self {
|
pub(crate) fn join(&self, name: Name<'src>) -> Self {
|
||||||
Self(self.0.iter().copied().chain(iter::once(name)).collect())
|
Self(self.0.iter().copied().chain(iter::once(name)).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn spaced(&self) -> ModulePath {
|
||||||
|
ModulePath {
|
||||||
|
path: self.0.iter().map(|name| name.lexeme().into()).collect(),
|
||||||
|
spaced: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Display for Namepath<'src> {
|
impl<'src> Display for Namepath<'src> {
|
||||||
|
@ -54,6 +54,11 @@ impl<'src> Node<'src> for Item<'src> {
|
|||||||
}
|
}
|
||||||
Self::Recipe(recipe) => recipe.tree(),
|
Self::Recipe(recipe) => recipe.tree(),
|
||||||
Self::Set(set) => set.tree(),
|
Self::Set(set) => set.tree(),
|
||||||
|
Self::Unexport { name } => {
|
||||||
|
let mut unexport = Tree::atom(Keyword::Unexport.lexeme());
|
||||||
|
unexport.push_mut(name.lexeme().replace('-', "_"));
|
||||||
|
unexport
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,6 +289,7 @@ impl<'src> Node<'src> for Set<'src> {
|
|||||||
Setting::AllowDuplicateRecipes(value)
|
Setting::AllowDuplicateRecipes(value)
|
||||||
| Setting::AllowDuplicateVariables(value)
|
| Setting::AllowDuplicateVariables(value)
|
||||||
| Setting::DotenvLoad(value)
|
| Setting::DotenvLoad(value)
|
||||||
|
| Setting::DotenvRequired(value)
|
||||||
| Setting::Export(value)
|
| Setting::Export(value)
|
||||||
| Setting::Fallback(value)
|
| Setting::Fallback(value)
|
||||||
| Setting::PositionalArguments(value)
|
| Setting::PositionalArguments(value)
|
||||||
|
@ -340,6 +340,11 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
self.presume_keyword(Keyword::Export)?;
|
self.presume_keyword(Keyword::Export)?;
|
||||||
items.push(Item::Assignment(self.parse_assignment(true)?));
|
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)
|
Some(Keyword::Import)
|
||||||
if self.next_are(&[Identifier, StringToken])
|
if self.next_are(&[Identifier, StringToken])
|
||||||
|| self.next_are(&[Identifier, Identifier, StringToken])
|
|| self.next_are(&[Identifier, Identifier, StringToken])
|
||||||
@ -917,6 +922,7 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
Some(Setting::AllowDuplicateVariables(self.parse_set_bool()?))
|
Some(Setting::AllowDuplicateVariables(self.parse_set_bool()?))
|
||||||
}
|
}
|
||||||
Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)),
|
Keyword::DotenvLoad => Some(Setting::DotenvLoad(self.parse_set_bool()?)),
|
||||||
|
Keyword::DotenvRequired => Some(Setting::DotenvRequired(self.parse_set_bool()?)),
|
||||||
Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)),
|
Keyword::Export => Some(Setting::Export(self.parse_set_bool()?)),
|
||||||
Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)),
|
Keyword::Fallback => Some(Setting::Fallback(self.parse_set_bool()?)),
|
||||||
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
|
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
|
||||||
@ -981,15 +987,29 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
loop {
|
loop {
|
||||||
let name = self.parse_name()?;
|
let name = self.parse_name()?;
|
||||||
|
|
||||||
let argument = if self.accepted(ParenL)? {
|
let arguments: Vec<StringLiteral> = if self.next_is(Colon) {
|
||||||
let argument = self.parse_string_literal()?;
|
self.presume(Colon)?;
|
||||||
self.expect(ParenR)?;
|
let single_arg = self.parse_string_literal()?;
|
||||||
Some(argument)
|
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 {
|
} else {
|
||||||
None
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let attribute = Attribute::new(name, argument)?;
|
let attribute = Attribute::new(name, arguments)?;
|
||||||
|
|
||||||
if let Some(line) = attributes.get(&attribute) {
|
if let Some(line) = attributes.get(&attribute) {
|
||||||
return Err(name.error(CompileErrorKind::DuplicateAttribute {
|
return Err(name.error(CompileErrorKind::DuplicateAttribute {
|
||||||
@ -1152,6 +1172,18 @@ mod tests {
|
|||||||
tree: (justfile (alias t test)),
|
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! {
|
test! {
|
||||||
name: aliases_multiple,
|
name: aliases_multiple,
|
||||||
text: "alias t := test\nalias b := build",
|
text: "alias t := test\nalias b := build",
|
||||||
|
@ -169,6 +169,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
scope,
|
scope,
|
||||||
context.search,
|
context.search,
|
||||||
context.settings,
|
context.settings,
|
||||||
|
context.unexports,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.shebang {
|
if self.shebang {
|
||||||
@ -279,7 +280,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
cmd.stdout(Stdio::null());
|
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()) {
|
match InterruptHandler::guard(|| cmd.status()) {
|
||||||
Ok(exit_status) => {
|
Ok(exit_status) => {
|
||||||
@ -353,9 +354,9 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
let tempdir = match &context.settings.tempdir {
|
let tempdir = match &context.settings.tempdir {
|
||||||
Some(tempdir) => tempdir_builder.tempdir_in(context.search.working_directory.join(tempdir)),
|
Some(tempdir) => tempdir_builder.tempdir_in(context.search.working_directory.join(tempdir)),
|
||||||
None => {
|
None => {
|
||||||
if let Some(cache_dir) = dirs::cache_dir() {
|
if let Some(runtime_dir) = dirs::runtime_dir() {
|
||||||
let path = cache_dir.join("just");
|
let path = runtime_dir.join("just");
|
||||||
fs::create_dir_all(&path).map_err(|io_error| Error::CacheDirIo {
|
fs::create_dir_all(&path).map_err(|io_error| Error::RuntimeDirIo {
|
||||||
io_error,
|
io_error,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
})?;
|
})?;
|
||||||
@ -425,7 +426,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
command.args(positional);
|
command.args(positional);
|
||||||
}
|
}
|
||||||
|
|
||||||
command.export(context.settings, context.dotenv, scope);
|
command.export(context.settings, context.dotenv, scope, context.unexports);
|
||||||
|
|
||||||
// run it!
|
// run it!
|
||||||
match InterruptHandler::guard(|| command.status()) {
|
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) scope: &'run Scope<'src, 'run>,
|
||||||
pub(crate) search: &'run Search,
|
pub(crate) search: &'run Search,
|
||||||
pub(crate) settings: &'run Settings<'src>,
|
pub(crate) settings: &'run Settings<'src>,
|
||||||
|
pub(crate) unexports: &'run HashSet<String>,
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ pub(crate) enum Setting<'src> {
|
|||||||
DotenvFilename(String),
|
DotenvFilename(String),
|
||||||
DotenvLoad(bool),
|
DotenvLoad(bool),
|
||||||
DotenvPath(String),
|
DotenvPath(String),
|
||||||
|
DotenvRequired(bool),
|
||||||
Export(bool),
|
Export(bool),
|
||||||
Fallback(bool),
|
Fallback(bool),
|
||||||
IgnoreComments(bool),
|
IgnoreComments(bool),
|
||||||
@ -24,6 +25,7 @@ impl<'src> Display for Setting<'src> {
|
|||||||
Self::AllowDuplicateRecipes(value)
|
Self::AllowDuplicateRecipes(value)
|
||||||
| Self::AllowDuplicateVariables(value)
|
| Self::AllowDuplicateVariables(value)
|
||||||
| Self::DotenvLoad(value)
|
| Self::DotenvLoad(value)
|
||||||
|
| Self::DotenvRequired(value)
|
||||||
| Self::Export(value)
|
| Self::Export(value)
|
||||||
| Self::Fallback(value)
|
| Self::Fallback(value)
|
||||||
| Self::IgnoreComments(value)
|
| Self::IgnoreComments(value)
|
||||||
|
@ -10,8 +10,9 @@ pub(crate) struct Settings<'src> {
|
|||||||
pub(crate) allow_duplicate_recipes: bool,
|
pub(crate) allow_duplicate_recipes: bool,
|
||||||
pub(crate) allow_duplicate_variables: bool,
|
pub(crate) allow_duplicate_variables: bool,
|
||||||
pub(crate) dotenv_filename: Option<String>,
|
pub(crate) dotenv_filename: Option<String>,
|
||||||
pub(crate) dotenv_load: Option<bool>,
|
pub(crate) dotenv_load: bool,
|
||||||
pub(crate) dotenv_path: Option<PathBuf>,
|
pub(crate) dotenv_path: Option<PathBuf>,
|
||||||
|
pub(crate) dotenv_required: bool,
|
||||||
pub(crate) export: bool,
|
pub(crate) export: bool,
|
||||||
pub(crate) fallback: bool,
|
pub(crate) fallback: bool,
|
||||||
pub(crate) ignore_comments: bool,
|
pub(crate) ignore_comments: bool,
|
||||||
@ -39,11 +40,14 @@ impl<'src> Settings<'src> {
|
|||||||
settings.dotenv_filename = Some(filename);
|
settings.dotenv_filename = Some(filename);
|
||||||
}
|
}
|
||||||
Setting::DotenvLoad(dotenv_load) => {
|
Setting::DotenvLoad(dotenv_load) => {
|
||||||
settings.dotenv_load = Some(dotenv_load);
|
settings.dotenv_load = dotenv_load;
|
||||||
}
|
}
|
||||||
Setting::DotenvPath(path) => {
|
Setting::DotenvPath(path) => {
|
||||||
settings.dotenv_path = Some(PathBuf::from(path));
|
settings.dotenv_path = Some(PathBuf::from(path));
|
||||||
}
|
}
|
||||||
|
Setting::DotenvRequired(dotenv_required) => {
|
||||||
|
settings.dotenv_required = dotenv_required;
|
||||||
|
}
|
||||||
Setting::Export(export) => {
|
Setting::Export(export) => {
|
||||||
settings.export = export;
|
settings.export = export;
|
||||||
}
|
}
|
||||||
|
@ -261,14 +261,15 @@ impl Subcommand {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for recipe in recipes {
|
for recipe in recipes {
|
||||||
if let Err(io_error) = child
|
writeln!(
|
||||||
.stdin
|
child.stdin.as_mut().unwrap(),
|
||||||
.as_mut()
|
"{}",
|
||||||
.expect("Child was created with piped stdio")
|
recipe.namepath.spaced()
|
||||||
.write_all(format!("{}\n", recipe.namepath).as_bytes())
|
)
|
||||||
{
|
.map_err(|io_error| Error::ChooserWrite {
|
||||||
return Err(Error::ChooserWrite { io_error, chooser });
|
io_error,
|
||||||
}
|
chooser: chooser.clone(),
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = match child.wait_with_output() {
|
let output = match child.wait_with_output() {
|
||||||
@ -487,6 +488,12 @@ impl Subcommand {
|
|||||||
.ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?;
|
.ok_or_else(|| Error::UnknownSubmodule { path: path.clone() })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::list_module(config, module, 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_module(config: &Config, module: &Justfile, depth: usize) {
|
||||||
let aliases = if config.no_aliases {
|
let aliases = if config.no_aliases {
|
||||||
BTreeMap::new()
|
BTreeMap::new()
|
||||||
} else {
|
} else {
|
||||||
@ -531,7 +538,11 @@ impl Subcommand {
|
|||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
print!("{}", config.list_heading);
|
let list_prefix = config.list_prefix.repeat(depth + 1);
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
print!("{}", config.list_heading);
|
||||||
|
}
|
||||||
|
|
||||||
let groups = {
|
let groups = {
|
||||||
let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
|
let mut groups = BTreeMap::<Option<String>, Vec<&Recipe>>::new();
|
||||||
@ -556,7 +567,7 @@ impl Subcommand {
|
|||||||
let no_groups = groups.contains_key(&None) && groups.len() == 1;
|
let no_groups = groups.contains_key(&None) && groups.len() == 1;
|
||||||
|
|
||||||
if !no_groups {
|
if !no_groups {
|
||||||
print!("{}", config.list_prefix);
|
print!("{list_prefix}");
|
||||||
if let Some(group_name) = group {
|
if let Some(group_name) = group {
|
||||||
println!("[{group_name}]");
|
println!("[{group_name}]");
|
||||||
} else {
|
} else {
|
||||||
@ -579,8 +590,7 @@ impl Subcommand {
|
|||||||
if doc.lines().count() > 1 {
|
if doc.lines().count() > 1 {
|
||||||
for line in doc.lines() {
|
for line in doc.lines() {
|
||||||
println!(
|
println!(
|
||||||
"{}{} {}",
|
"{list_prefix}{} {}",
|
||||||
config.list_prefix,
|
|
||||||
config.color.stdout().doc().paint("#"),
|
config.color.stdout().doc().paint("#"),
|
||||||
config.color.stdout().doc().paint(line),
|
config.color.stdout().doc().paint(line),
|
||||||
);
|
);
|
||||||
@ -589,8 +599,7 @@ impl Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print!(
|
print!(
|
||||||
"{}{}",
|
"{list_prefix}{}",
|
||||||
config.list_prefix,
|
|
||||||
RecipeSignature { name, recipe }.color_display(config.color.stdout())
|
RecipeSignature { name, recipe }.color_display(config.color.stdout())
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -610,11 +619,21 @@ impl Subcommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for submodule in module.modules(config) {
|
if config.list_submodules {
|
||||||
println!("{}{} ...", config.list_prefix, submodule.name(),);
|
for (i, submodule) in module.modules(config).into_iter().enumerate() {
|
||||||
}
|
if i + groups.len() > 0 {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
println!("{list_prefix}{}:", submodule.name());
|
||||||
|
|
||||||
|
Self::list_module(config, submodule, depth + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for submodule in module.modules(config) {
|
||||||
|
println!("{list_prefix}{} ...", submodule.name(),);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show<'src>(
|
fn show<'src>(
|
||||||
|
@ -72,7 +72,7 @@ fn multiple_attributes_one_line_error_message() {
|
|||||||
)
|
)
|
||||||
.stderr(
|
.stderr(
|
||||||
"
|
"
|
||||||
error: Expected ']', ',', or '(', but found identifier
|
error: Expected ']', ':', ',', or '(', but found identifier
|
||||||
——▶ justfile:1:17
|
——▶ justfile:1:17
|
||||||
│
|
│
|
||||||
1 │ [macos, windows linux]
|
1 │ [macos, windows linux]
|
||||||
|
@ -47,16 +47,21 @@ test! {
|
|||||||
status: 2,
|
status: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
#[test]
|
||||||
name: env_is_loaded,
|
fn env_is_loaded() {
|
||||||
justfile: "
|
Test::new()
|
||||||
set dotenv-load
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load
|
||||||
|
|
||||||
x:
|
x:
|
||||||
echo XYZ
|
echo XYZ
|
||||||
",
|
",
|
||||||
args: ("--command", "sh", "-c", "printf $DOTENV_KEY"),
|
)
|
||||||
stdout: "dotenv-value",
|
.args(["--command", "sh", "-c", "printf $DOTENV_KEY"])
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value")
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
|
@ -1,19 +1,38 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn output() {
|
#[cfg(target_os = "linux")]
|
||||||
let tempdir = tempdir();
|
fn bash() {
|
||||||
|
|
||||||
let output = Command::new(executable_path("just"))
|
let output = Command::new(executable_path("just"))
|
||||||
.arg("--completions")
|
.args(["--completions", "bash"])
|
||||||
.arg("bash")
|
|
||||||
.current_dir(tempdir.path())
|
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
|
|
||||||
let text = String::from_utf8_lossy(&output.stdout);
|
let script = str::from_utf8(&output.stdout).unwrap();
|
||||||
|
|
||||||
assert!(text.starts_with("_just() {"));
|
let tempdir = tempdir();
|
||||||
|
|
||||||
|
let path = tempdir.path().join("just.bash");
|
||||||
|
|
||||||
|
fs::write(&path, script).unwrap();
|
||||||
|
|
||||||
|
let status = Command::new("./tests/completions/just.bash")
|
||||||
|
.arg(path)
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(status.success());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn replacements() {
|
||||||
|
for shell in ["bash", "elvish", "fish", "powershell", "zsh"] {
|
||||||
|
let output = Command::new(executable_path("just"))
|
||||||
|
.args(["--completions", shell])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
assert!(output.status.success());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ reply_equals() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# --- Initial Setup ---
|
# --- Initial Setup ---
|
||||||
source ./completions/just.bash
|
source "$1"
|
||||||
cd tests/completions
|
cd tests/completions
|
||||||
cargo build
|
cargo build
|
||||||
PATH="$(git rev-parse --show-toplevel)/target/debug:$PATH"
|
PATH="$(git rev-parse --show-toplevel)/target/debug:$PATH"
|
||||||
|
@ -130,7 +130,7 @@ fn confirm_recipe_with_prompt_too_many_args() {
|
|||||||
echo confirmed
|
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("")
|
.stdout("")
|
||||||
.status(1)
|
.status(1)
|
||||||
.run();
|
.run();
|
||||||
|
223
tests/dotenv.rs
223
tests/dotenv.rs
@ -12,40 +12,54 @@ fn dotenv() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
#[test]
|
||||||
name: set_false,
|
fn set_false() {
|
||||||
justfile: r#"
|
Test::new()
|
||||||
set dotenv-load := false
|
.justfile(
|
||||||
|
r#"
|
||||||
|
set dotenv-load := false
|
||||||
|
|
||||||
foo:
|
@foo:
|
||||||
if [ -n "${DOTENV_KEY+1}" ]; then echo defined; else echo undefined; fi
|
if [ -n "${DOTENV_KEY+1}" ]; then echo defined; else echo undefined; fi
|
||||||
"#,
|
"#,
|
||||||
stdout: "undefined\n",
|
)
|
||||||
stderr: "if [ -n \"${DOTENV_KEY+1}\" ]; then echo defined; else echo undefined; fi\n",
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("undefined\n")
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
#[test]
|
||||||
name: set_implicit,
|
fn set_implicit() {
|
||||||
justfile: r#"
|
Test::new()
|
||||||
set dotenv-load
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
echo $DOTENV_KEY
|
echo $DOTENV_KEY
|
||||||
"#,
|
",
|
||||||
stdout: "dotenv-value\n",
|
)
|
||||||
stderr: "echo $DOTENV_KEY\n",
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value\n")
|
||||||
|
.stderr("echo $DOTENV_KEY\n")
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
#[test]
|
||||||
name: set_true,
|
fn set_true() {
|
||||||
justfile: r#"
|
Test::new()
|
||||||
set dotenv-load := true
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load := true
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
echo $DOTENV_KEY
|
echo $DOTENV_KEY
|
||||||
"#,
|
",
|
||||||
stdout: "dotenv-value\n",
|
)
|
||||||
stderr: "echo $DOTENV_KEY\n",
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value\n")
|
||||||
|
.stderr("echo $DOTENV_KEY\n")
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -53,32 +67,28 @@ fn no_warning() {
|
|||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
foo:
|
foo:
|
||||||
echo ${DOTENV_KEY:-unset}
|
echo ${DOTENV_KEY:-unset}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
.stdout("unset\n")
|
.stdout("unset\n")
|
||||||
.stderr("echo ${DOTENV_KEY:-unset}\n")
|
.stderr("echo ${DOTENV_KEY:-unset}\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_not_found() {
|
fn dotenv_required() {
|
||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
foo:
|
set dotenv-required
|
||||||
echo $JUST_TEST_VARIABLE
|
|
||||||
",
|
foo:
|
||||||
|
",
|
||||||
)
|
)
|
||||||
.args(["--dotenv-path", ".env.prod"])
|
.stderr("error: Dotenv file not found\n")
|
||||||
.stderr(if cfg!(windows) {
|
.status(1)
|
||||||
"error: Failed to load environment file: The system cannot find the file specified. (os \
|
|
||||||
error 2)\n"
|
|
||||||
} else {
|
|
||||||
"error: Failed to load environment file: No such file or directory (os error 2)\n"
|
|
||||||
})
|
|
||||||
.status(EXIT_FAILURE)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,9 +97,9 @@ fn path_resolves() {
|
|||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
foo:
|
foo:
|
||||||
@echo $JUST_TEST_VARIABLE
|
@echo $JUST_TEST_VARIABLE
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.tree(tree! {
|
.tree(tree! {
|
||||||
subdir: {
|
subdir: {
|
||||||
@ -107,9 +117,9 @@ fn filename_resolves() {
|
|||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
foo:
|
foo:
|
||||||
@echo $JUST_TEST_VARIABLE
|
@echo $JUST_TEST_VARIABLE
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.tree(tree! {
|
.tree(tree! {
|
||||||
".env.special": "JUST_TEST_VARIABLE=bar"
|
".env.special": "JUST_TEST_VARIABLE=bar"
|
||||||
@ -145,11 +155,11 @@ fn path_flag_overwrites_no_load() {
|
|||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
set dotenv-load := false
|
set dotenv-load := false
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
@echo $JUST_TEST_VARIABLE
|
@echo $JUST_TEST_VARIABLE
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.tree(tree! {
|
.tree(tree! {
|
||||||
subdir: {
|
subdir: {
|
||||||
@ -227,12 +237,12 @@ fn program_argument_has_priority_for_dotenv_filename() {
|
|||||||
fn program_argument_has_priority_for_dotenv_path() {
|
fn program_argument_has_priority_for_dotenv_path() {
|
||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
r#"
|
"
|
||||||
set dotenv-path := "subdir/.env"
|
set dotenv-path := 'subdir/.env'
|
||||||
|
|
||||||
foo:
|
foo:
|
||||||
@echo $JUST_TEST_VARIABLE
|
@echo $JUST_TEST_VARIABLE
|
||||||
"#,
|
",
|
||||||
)
|
)
|
||||||
.tree(tree! {
|
.tree(tree! {
|
||||||
subdir: {
|
subdir: {
|
||||||
@ -257,8 +267,111 @@ fn dotenv_path_is_relative_to_working_directory() {
|
|||||||
@echo $DOTENV_KEY
|
@echo $DOTENV_KEY
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
.tree(tree! { subdir: { } })
|
.tree(tree! { subdir: { } })
|
||||||
.current_dir("subdir")
|
.current_dir("subdir")
|
||||||
.stdout("dotenv-value\n")
|
.stdout("dotenv-value\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotenv_variable_in_recipe() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load
|
||||||
|
|
||||||
|
echo:
|
||||||
|
echo $DOTENV_KEY
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value\n")
|
||||||
|
.stderr("echo $DOTENV_KEY\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotenv_variable_in_backtick() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load
|
||||||
|
X:=`echo $DOTENV_KEY`
|
||||||
|
echo:
|
||||||
|
echo {{X}}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value\n")
|
||||||
|
.stderr("echo dotenv-value\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotenv_variable_in_function_in_recipe() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load
|
||||||
|
echo:
|
||||||
|
echo {{env_var_or_default('DOTENV_KEY', 'foo')}}
|
||||||
|
echo {{env_var('DOTENV_KEY')}}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value\ndotenv-value\n")
|
||||||
|
.stderr("echo dotenv-value\necho dotenv-value\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotenv_variable_in_function_in_backtick() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load
|
||||||
|
X:=env_var_or_default('DOTENV_KEY', 'foo')
|
||||||
|
Y:=env_var('DOTENV_KEY')
|
||||||
|
echo:
|
||||||
|
echo {{X}}
|
||||||
|
echo {{Y}}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.stdout("dotenv-value\ndotenv-value\n")
|
||||||
|
.stderr("echo dotenv-value\necho dotenv-value\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_dotenv() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
X:=env_var_or_default('DOTENV_KEY', 'DEFAULT')
|
||||||
|
echo:
|
||||||
|
echo {{X}}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.arg("--no-dotenv")
|
||||||
|
.stdout("DEFAULT\n")
|
||||||
|
.stderr("echo DEFAULT\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn dotenv_env_var_override() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
echo:
|
||||||
|
echo $DOTENV_KEY
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
|
.env("DOTENV_KEY", "not-the-dotenv-value")
|
||||||
|
.stdout("not-the-dotenv-value\n")
|
||||||
|
.stderr("echo $DOTENV_KEY\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -126,11 +126,10 @@ fn list_groups_with_custom_prefix() {
|
|||||||
Test::new()
|
Test::new()
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
[group('B')]
|
[group: 'B']
|
||||||
foo:
|
foo:
|
||||||
|
|
||||||
[group('A')]
|
[group: 'A', group: 'B']
|
||||||
[group('B')]
|
|
||||||
bar:
|
bar:
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
@ -46,8 +46,9 @@ fn alias() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"positional_arguments": false,
|
"positional_arguments": false,
|
||||||
@ -58,6 +59,7 @@ fn alias() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -84,8 +86,9 @@ fn assignment() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -96,6 +99,7 @@ fn assignment() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -136,8 +140,9 @@ fn body() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -148,6 +153,7 @@ fn body() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -200,8 +206,9 @@ fn dependencies() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -212,6 +219,7 @@ fn dependencies() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -302,8 +310,9 @@ fn dependency_argument() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -314,6 +323,7 @@ fn dependency_argument() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -366,8 +376,9 @@ fn duplicate_recipes() {
|
|||||||
"allow_duplicate_recipes": true,
|
"allow_duplicate_recipes": true,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -378,6 +389,7 @@ fn duplicate_recipes() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -408,8 +420,9 @@ fn duplicate_variables() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": true,
|
"allow_duplicate_variables": true,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -420,6 +433,7 @@ fn duplicate_variables() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -453,8 +467,9 @@ fn doc_comment() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -465,6 +480,7 @@ fn doc_comment() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -484,8 +500,9 @@ fn empty_justfile() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -496,6 +513,7 @@ fn empty_justfile() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -636,8 +654,9 @@ fn parameters() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -648,6 +667,7 @@ fn parameters() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -721,8 +741,9 @@ fn priors() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -733,6 +754,7 @@ fn priors() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -766,8 +788,9 @@ fn private() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -778,6 +801,7 @@ fn private() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -811,8 +835,9 @@ fn quiet() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -823,6 +848,7 @@ fn quiet() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -870,6 +896,7 @@ fn settings() {
|
|||||||
"dotenv_filename": "filename",
|
"dotenv_filename": "filename",
|
||||||
"dotenv_load": true,
|
"dotenv_load": true,
|
||||||
"dotenv_path": "path",
|
"dotenv_path": "path",
|
||||||
|
"dotenv_required": false,
|
||||||
"export": true,
|
"export": true,
|
||||||
"fallback": true,
|
"fallback": true,
|
||||||
"ignore_comments": true,
|
"ignore_comments": true,
|
||||||
@ -883,6 +910,7 @@ fn settings() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -919,8 +947,9 @@ fn shebang() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -931,6 +960,7 @@ fn shebang() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -964,8 +994,9 @@ fn simple() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"ignore_comments": false,
|
"ignore_comments": false,
|
||||||
@ -976,6 +1007,7 @@ fn simple() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1012,8 +1044,9 @@ fn attribute() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"positional_arguments": false,
|
"positional_arguments": false,
|
||||||
@ -1024,6 +1057,7 @@ fn attribute() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1073,8 +1107,9 @@ fn module() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"positional_arguments": false,
|
"positional_arguments": false,
|
||||||
@ -1085,6 +1120,7 @@ fn module() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1093,8 +1129,9 @@ fn module() {
|
|||||||
"allow_duplicate_recipes": false,
|
"allow_duplicate_recipes": false,
|
||||||
"allow_duplicate_variables": false,
|
"allow_duplicate_variables": false,
|
||||||
"dotenv_filename": null,
|
"dotenv_filename": null,
|
||||||
"dotenv_load": null,
|
"dotenv_load": false,
|
||||||
"dotenv_path": null,
|
"dotenv_path": null,
|
||||||
|
"dotenv_required": false,
|
||||||
"export": false,
|
"export": false,
|
||||||
"fallback": false,
|
"fallback": false,
|
||||||
"positional_arguments": false,
|
"positional_arguments": false,
|
||||||
@ -1105,6 +1142,7 @@ fn module() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}))
|
}))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -104,6 +104,7 @@ mod summary;
|
|||||||
mod tempdir;
|
mod tempdir;
|
||||||
mod timestamps;
|
mod timestamps;
|
||||||
mod undefined_variables;
|
mod undefined_variables;
|
||||||
|
mod unexport;
|
||||||
mod unstable;
|
mod unstable;
|
||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
mod windows_shell;
|
mod windows_shell;
|
||||||
|
130
tests/list.rs
130
tests/list.rs
@ -223,3 +223,133 @@ fn list_unknown_submodule() {
|
|||||||
.status(1)
|
.status(1)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_with_groups_in_modules() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
[group('FOO')]
|
||||||
|
foo:
|
||||||
|
|
||||||
|
mod bar
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.write("bar.just", "[group('BAZ')]\nbaz:")
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "--list-submodules"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
[FOO]
|
||||||
|
foo
|
||||||
|
|
||||||
|
bar:
|
||||||
|
[BAZ]
|
||||||
|
baz
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_displays_recipes_in_submodules() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "bar:\n @echo FOO")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "--list-submodules"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
foo:
|
||||||
|
bar
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn modules_are_space_separated_in_output() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "foo:")
|
||||||
|
.write("bar.just", "bar:")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
|
||||||
|
mod bar
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "--list-submodules"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
bar:
|
||||||
|
bar
|
||||||
|
|
||||||
|
foo:
|
||||||
|
foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_recipe_list_alignment_ignores_private_recipes() {
|
||||||
|
Test::new()
|
||||||
|
.write(
|
||||||
|
"foo.just",
|
||||||
|
"
|
||||||
|
# foos
|
||||||
|
foo:
|
||||||
|
@echo FOO
|
||||||
|
|
||||||
|
[private]
|
||||||
|
barbarbar:
|
||||||
|
@echo BAR
|
||||||
|
|
||||||
|
@_bazbazbaz:
|
||||||
|
@echo BAZ
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.justfile("mod foo")
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "--list-submodules"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
foo:
|
||||||
|
foo # foos
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_modules_are_properly_indented() {
|
||||||
|
Test::new()
|
||||||
|
.write("foo.just", "mod bar")
|
||||||
|
.write("bar.just", "baz:\n @echo FOO")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.args(["--unstable", "--list", "--list-submodules"])
|
||||||
|
.stdout(
|
||||||
|
"
|
||||||
|
Available recipes:
|
||||||
|
foo:
|
||||||
|
bar:
|
||||||
|
baz
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -884,7 +884,7 @@ _private-recipe:
|
|||||||
args: ("--list"),
|
args: ("--list"),
|
||||||
stdout: r#"
|
stdout: r#"
|
||||||
Available recipes:
|
Available recipes:
|
||||||
a Z="\t z" # something else
|
a Z="\t z" # something else
|
||||||
hello a b='B ' c='C' # this does a thing
|
hello a b='B ' c='C' # this does a thing
|
||||||
"#,
|
"#,
|
||||||
}
|
}
|
||||||
@ -1589,84 +1589,6 @@ echo:
|
|||||||
stderr: "echo 1\n",
|
stderr: "echo 1\n",
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
|
||||||
name: dotenv_variable_in_recipe,
|
|
||||||
justfile: "
|
|
||||||
#
|
|
||||||
set dotenv-load
|
|
||||||
|
|
||||||
echo:
|
|
||||||
echo $DOTENV_KEY
|
|
||||||
",
|
|
||||||
stdout: "dotenv-value\n",
|
|
||||||
stderr: "echo $DOTENV_KEY\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
test! {
|
|
||||||
name: dotenv_variable_in_backtick,
|
|
||||||
justfile: "
|
|
||||||
#
|
|
||||||
set dotenv-load
|
|
||||||
X:=`echo $DOTENV_KEY`
|
|
||||||
echo:
|
|
||||||
echo {{X}}
|
|
||||||
",
|
|
||||||
stdout: "dotenv-value\n",
|
|
||||||
stderr: "echo dotenv-value\n",
|
|
||||||
}
|
|
||||||
test! {
|
|
||||||
name: dotenv_variable_in_function_in_recipe,
|
|
||||||
justfile: "
|
|
||||||
#
|
|
||||||
set dotenv-load
|
|
||||||
echo:
|
|
||||||
echo {{env_var_or_default('DOTENV_KEY', 'foo')}}
|
|
||||||
echo {{env_var('DOTENV_KEY')}}
|
|
||||||
",
|
|
||||||
stdout: "dotenv-value\ndotenv-value\n",
|
|
||||||
stderr: "echo dotenv-value\necho dotenv-value\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
test! {
|
|
||||||
name: dotenv_variable_in_function_in_backtick,
|
|
||||||
justfile: "
|
|
||||||
#
|
|
||||||
set dotenv-load
|
|
||||||
X:=env_var_or_default('DOTENV_KEY', 'foo')
|
|
||||||
Y:=env_var('DOTENV_KEY')
|
|
||||||
echo:
|
|
||||||
echo {{X}}
|
|
||||||
echo {{Y}}
|
|
||||||
",
|
|
||||||
stdout: "dotenv-value\ndotenv-value\n",
|
|
||||||
stderr: "echo dotenv-value\necho dotenv-value\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
test! {
|
|
||||||
name: no_dotenv,
|
|
||||||
justfile: "
|
|
||||||
#
|
|
||||||
X:=env_var_or_default('DOTENV_KEY', 'DEFAULT')
|
|
||||||
echo:
|
|
||||||
echo {{X}}
|
|
||||||
",
|
|
||||||
args: ("--no-dotenv"),
|
|
||||||
stdout: "DEFAULT\n",
|
|
||||||
stderr: "echo DEFAULT\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
test! {
|
|
||||||
name: dotenv_env_var_override,
|
|
||||||
justfile: "
|
|
||||||
#
|
|
||||||
echo:
|
|
||||||
echo $DOTENV_KEY
|
|
||||||
",
|
|
||||||
env: {"DOTENV_KEY": "not-the-dotenv-value",},
|
|
||||||
stdout: "not-the-dotenv-value\n",
|
|
||||||
stderr: "echo $DOTENV_KEY\n",
|
|
||||||
}
|
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: invalid_escape_sequence_message,
|
name: invalid_escape_sequence_message,
|
||||||
justfile: r#"
|
justfile: r#"
|
||||||
|
@ -515,7 +515,6 @@ fn missing_optional_modules_do_not_conflict() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn root_dotenv_is_available_to_submodules() {
|
fn root_dotenv_is_available_to_submodules() {
|
||||||
Test::new()
|
Test::new()
|
||||||
.write("foo.just", "foo:\n @echo $DOTENV_KEY")
|
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
set dotenv-load
|
set dotenv-load
|
||||||
@ -523,10 +522,10 @@ fn root_dotenv_is_available_to_submodules() {
|
|||||||
mod foo
|
mod foo
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
.write("foo.just", "foo:\n @echo $DOTENV_KEY")
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
.test_round_trip(false)
|
.test_round_trip(false)
|
||||||
.arg("--unstable")
|
.args(["--unstable", "foo", "foo"])
|
||||||
.arg("foo")
|
|
||||||
.arg("foo")
|
|
||||||
.stdout("dotenv-value\n")
|
.stdout("dotenv-value\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
@ -534,10 +533,6 @@ fn root_dotenv_is_available_to_submodules() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn dotenv_settings_in_submodule_are_ignored() {
|
fn dotenv_settings_in_submodule_are_ignored() {
|
||||||
Test::new()
|
Test::new()
|
||||||
.write(
|
|
||||||
"foo.just",
|
|
||||||
"set dotenv-load := false\nfoo:\n @echo $DOTENV_KEY",
|
|
||||||
)
|
|
||||||
.justfile(
|
.justfile(
|
||||||
"
|
"
|
||||||
set dotenv-load
|
set dotenv-load
|
||||||
@ -545,10 +540,13 @@ fn dotenv_settings_in_submodule_are_ignored() {
|
|||||||
mod foo
|
mod foo
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
.write(
|
||||||
|
"foo.just",
|
||||||
|
"set dotenv-load := false\nfoo:\n @echo $DOTENV_KEY",
|
||||||
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
.test_round_trip(false)
|
.test_round_trip(false)
|
||||||
.arg("--unstable")
|
.args(["--unstable", "foo", "foo"])
|
||||||
.arg("foo")
|
|
||||||
.arg("foo")
|
|
||||||
.stdout("dotenv-value\n")
|
.stdout("dotenv-value\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@ fn shell_expanded_strings_can_be_used_in_settings() {
|
|||||||
echo $DOTENV_KEY
|
echo $DOTENV_KEY
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
|
.write(".env", "DOTENV_KEY=dotenv-value")
|
||||||
.env("JUST_TEST_VARIABLE", ".env")
|
.env("JUST_TEST_VARIABLE", ".env")
|
||||||
.stdout("dotenv-value\n")
|
.stdout("dotenv-value\n")
|
||||||
.run();
|
.run();
|
||||||
|
@ -5,8 +5,8 @@ pub(crate) fn tempdir() -> TempDir {
|
|||||||
|
|
||||||
builder.prefix("just-test-tempdir");
|
builder.prefix("just-test-tempdir");
|
||||||
|
|
||||||
if let Some(cache_dir) = dirs::cache_dir() {
|
if let Some(runtime_dir) = dirs::runtime_dir() {
|
||||||
let path = cache_dir.join("just");
|
let path = runtime_dir.join("just");
|
||||||
fs::create_dir_all(&path).unwrap();
|
fs::create_dir_all(&path).unwrap();
|
||||||
builder.tempdir_in(path)
|
builder.tempdir_in(path)
|
||||||
} else {
|
} else {
|
||||||
|
@ -201,9 +201,8 @@ impl Test {
|
|||||||
} else {
|
} else {
|
||||||
self.stdout.clone()
|
self.stdout.clone()
|
||||||
};
|
};
|
||||||
let stderr = unindent(&self.stderr);
|
|
||||||
|
|
||||||
fs::write(self.tempdir.path().join(".env"), "DOTENV_KEY=dotenv-value").unwrap();
|
let stderr = unindent(&self.stderr);
|
||||||
|
|
||||||
let mut command = Command::new(executable_path("just"));
|
let mut command = Command::new(executable_path("just"));
|
||||||
|
|
||||||
@ -258,7 +257,7 @@ impl Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !compare("status", output.status.code().unwrap(), self.status)
|
if !compare("status", output.status.code(), Some(self.status))
|
||||||
| (self.stdout_regex.is_none() && !compare("stdout", output_stdout, &stdout))
|
| (self.stdout_regex.is_none() && !compare("stdout", output_stdout, &stdout))
|
||||||
| (self.stderr_regex.is_none() && !compare("stderr", output_stderr, &stderr))
|
| (self.stderr_regex.is_none() && !compare("stderr", output_stderr, &stderr))
|
||||||
{
|
{
|
||||||
|
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