mirror of
https://github.com/casey/just
synced 2024-06-29 06:24:38 +00:00
Compare commits
6 Commits
0d83940a8a
...
fac177559a
Author | SHA1 | Date | |
---|---|---|---|
|
fac177559a | ||
|
95d51ab933 | ||
|
2964a62542 | ||
|
570d3058cf | ||
|
c900b6f478 | ||
|
af86a471e2 |
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
@ -95,7 +95,7 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
- name: Publish Archive
|
||||
uses: softprops/action-gh-release@v2.0.5
|
||||
uses: softprops/action-gh-release@v2.0.6
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
draft: false
|
||||
|
@ -105,7 +105,7 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish Changelog
|
||||
uses: softprops/action-gh-release@v2.0.5
|
||||
uses: softprops/action-gh-release@v2.0.6
|
||||
if: >-
|
||||
${{
|
||||
startsWith(github.ref, 'refs/tags/')
|
||||
|
|
|
@ -656,8 +656,8 @@ Available recipes:
|
|||
lint
|
||||
```
|
||||
|
||||
Recipes in submodules can be listed with `just --list PATH`, where `PATH` is a
|
||||
space- or `::`-separated module path:
|
||||
Recipes in [submodules](#modules1190) can be listed with `just --list PATH`,
|
||||
where `PATH` is a space- or `::`-separated module path:
|
||||
|
||||
```
|
||||
$ cat justfile
|
||||
|
@ -3145,7 +3145,7 @@ import? 'foo/bar.just'
|
|||
|
||||
Missing source files for optional imports do not produce an error.
|
||||
|
||||
### Modules <sup>1.19.0</sup>
|
||||
### Modules<sup>1.19.0</sup>
|
||||
|
||||
A `justfile` can declare modules using `mod` statements. `mod` statements are
|
||||
currently unstable, so you'll need to use the `--unstable` flag, or set the
|
||||
|
|
|
@ -149,7 +149,7 @@ impl<'src> Analyzer<'src> {
|
|||
}
|
||||
}
|
||||
|
||||
let recipes = RecipeResolver::resolve_recipes(recipe_table, &self.assignments)?;
|
||||
let recipes = RecipeResolver::resolve_recipes(&self.assignments, &settings, recipe_table)?;
|
||||
|
||||
let mut aliases = Table::new();
|
||||
while let Some(alias) = self.aliases.pop() {
|
||||
|
|
|
@ -1482,6 +1482,22 @@ mod tests {
|
|||
},
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_config_from_working_directory_and_justfile_long_stdin,
|
||||
args: ["--working-directory", "foo", "--justfile", "-"],
|
||||
search_config: SearchConfig::WithStdinAndWorkingDirectory {
|
||||
working_directory: PathBuf::from("foo"),
|
||||
},
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_config_from_working_directory_and_justfile_short_stdin,
|
||||
args: ["--working-directory", "foo", "-f", "-"],
|
||||
search_config: SearchConfig::WithStdinAndWorkingDirectory {
|
||||
working_directory: PathBuf::from("foo"),
|
||||
},
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_config_justfile_long,
|
||||
args: ["--justfile", "foo"],
|
||||
|
@ -1490,6 +1506,12 @@ mod tests {
|
|||
},
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_config_justfile_long_stdin,
|
||||
args: ["--justfile", "-"],
|
||||
search_config: SearchConfig::WithStdin,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_config_justfile_short,
|
||||
args: ["-f", "foo"],
|
||||
|
@ -1498,6 +1520,12 @@ mod tests {
|
|||
},
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_config_justfile_short_stdin,
|
||||
args: ["-f", "-"],
|
||||
search_config: SearchConfig::WithStdin,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: search_directory_parent,
|
||||
args: ["../"],
|
||||
|
|
|
@ -8,8 +8,9 @@ pub(crate) struct RecipeResolver<'src: 'run, 'run> {
|
|||
|
||||
impl<'src: 'run, 'run> RecipeResolver<'src, 'run> {
|
||||
pub(crate) fn resolve_recipes(
|
||||
unresolved_recipes: Table<'src, UnresolvedRecipe<'src>>,
|
||||
assignments: &'run Table<'src, Assignment<'src>>,
|
||||
settings: &Settings,
|
||||
unresolved_recipes: Table<'src, UnresolvedRecipe<'src>>,
|
||||
) -> CompileResult<'src, Table<'src, Rc<Recipe<'src>>>> {
|
||||
let mut resolver = Self {
|
||||
resolved_recipes: Table::new(),
|
||||
|
@ -39,6 +40,10 @@ impl<'src: 'run, 'run> RecipeResolver<'src, 'run> {
|
|||
}
|
||||
|
||||
for line in &recipe.body {
|
||||
if line.is_comment() && settings.ignore_comments {
|
||||
continue;
|
||||
}
|
||||
|
||||
for fragment in &line.fragments {
|
||||
if let Fragment::Interpolation { expression, .. } = fragment {
|
||||
for variable in expression.variables() {
|
||||
|
|
178
src/search.rs
178
src/search.rs
|
@ -1,14 +1,58 @@
|
|||
use {super::*, std::path::Component};
|
||||
use {
|
||||
super::*,
|
||||
std::{
|
||||
io::{stdin, Read},
|
||||
path::{Component, Display},
|
||||
},
|
||||
};
|
||||
|
||||
const DEFAULT_JUSTFILE_NAME: &str = JUSTFILE_NAMES[0];
|
||||
pub(crate) const JUSTFILE_NAMES: [&str; 2] = ["justfile", ".justfile"];
|
||||
const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
|
||||
|
||||
pub(crate) struct Search {
|
||||
pub(crate) justfile: PathBuf,
|
||||
pub(crate) justfile: JustfileKind,
|
||||
pub(crate) working_directory: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum JustfileKind {
|
||||
Path { path: PathBuf },
|
||||
Stdin { data: String },
|
||||
}
|
||||
|
||||
impl JustfileKind {
|
||||
pub fn to_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
JustfileKind::Path { path } => path.to_str(),
|
||||
JustfileKind::Stdin { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self) -> Display<'_> {
|
||||
match self {
|
||||
JustfileKind::Path { path } => path.display(),
|
||||
JustfileKind::Stdin { .. } => Path::new("<STDIN>").display(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&Path> {
|
||||
match self {
|
||||
JustfileKind::Path { path } => path.parent(),
|
||||
JustfileKind::Stdin { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PathBuf> for JustfileKind {
|
||||
fn eq(&self, other: &PathBuf) -> bool {
|
||||
match self {
|
||||
JustfileKind::Path { path } => path == other,
|
||||
JustfileKind::Stdin { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Search {
|
||||
fn global_justfile_paths() -> Vec<PathBuf> {
|
||||
let mut paths = Vec::new();
|
||||
|
@ -33,6 +77,7 @@ impl Search {
|
|||
paths
|
||||
}
|
||||
|
||||
/// Search for a Justfile
|
||||
pub(crate) fn find(
|
||||
search_config: &SearchConfig,
|
||||
invocation_directory: &Path,
|
||||
|
@ -41,24 +86,29 @@ impl Search {
|
|||
SearchConfig::FromInvocationDirectory => Self::find_next(invocation_directory),
|
||||
SearchConfig::FromSearchDirectory { search_directory } => {
|
||||
let search_directory = Self::clean(invocation_directory, search_directory);
|
||||
let justfile = Self::justfile(&search_directory)?;
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
let path = Self::justfile(&search_directory)?;
|
||||
let working_directory = Self::working_directory_from_justfile(&path)?;
|
||||
let justfile = JustfileKind::Path { path: path.clone() };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
SearchConfig::GlobalJustfile => Ok(Self {
|
||||
justfile: Self::global_justfile_paths()
|
||||
SearchConfig::GlobalJustfile => {
|
||||
let path = Self::global_justfile_paths()
|
||||
.iter()
|
||||
.find(|path| path.exists())
|
||||
.cloned()
|
||||
.ok_or(SearchError::GlobalJustfileNotFound)?,
|
||||
working_directory: Self::project_root(invocation_directory)?,
|
||||
}),
|
||||
.ok_or(SearchError::GlobalJustfileNotFound)?;
|
||||
Ok(Self {
|
||||
justfile: JustfileKind::Path { path },
|
||||
working_directory: Self::project_root(invocation_directory)?,
|
||||
})
|
||||
}
|
||||
SearchConfig::WithJustfile { justfile } => {
|
||||
let justfile = Self::clean(invocation_directory, justfile);
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
let path = Self::clean(invocation_directory, justfile);
|
||||
let justfile = JustfileKind::Path { path: path.clone() };
|
||||
let working_directory = Self::working_directory_from_justfile(&path)?;
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
|
@ -67,22 +117,58 @@ impl Search {
|
|||
SearchConfig::WithJustfileAndWorkingDirectory {
|
||||
justfile,
|
||||
working_directory,
|
||||
} => Ok(Self {
|
||||
justfile: Self::clean(invocation_directory, justfile),
|
||||
working_directory: Self::clean(invocation_directory, working_directory),
|
||||
}),
|
||||
} => {
|
||||
let path = Self::clean(invocation_directory, justfile);
|
||||
let justfile = JustfileKind::Path { path };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory: Self::clean(invocation_directory, working_directory),
|
||||
})
|
||||
}
|
||||
SearchConfig::WithStdin => {
|
||||
let working_directory = PathBuf::from(invocation_directory);
|
||||
let mut data = String::new();
|
||||
if let Err(err) = stdin().read_to_string(&mut data) {
|
||||
return Err(SearchError::Io {
|
||||
directory: working_directory,
|
||||
io_error: err,
|
||||
});
|
||||
}
|
||||
let justfile = JustfileKind::Stdin { data };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
SearchConfig::WithStdinAndWorkingDirectory { working_directory } => {
|
||||
let working_directory = working_directory.to_owned();
|
||||
let mut data = String::new();
|
||||
if let Err(err) = stdin().read_to_string(&mut data) {
|
||||
return Err(SearchError::Io {
|
||||
directory: working_directory,
|
||||
io_error: err,
|
||||
});
|
||||
}
|
||||
let justfile = JustfileKind::Stdin { data };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_next(starting_dir: &Path) -> SearchResult<Self> {
|
||||
let justfile = Self::justfile(starting_dir)?;
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
let path = Self::justfile(starting_dir)?;
|
||||
let justfile = JustfileKind::Path { path: path.clone() };
|
||||
let working_directory = Self::working_directory_from_justfile(&path)?;
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
|
||||
/// Search for a Justfile when running "init" subcommand
|
||||
pub(crate) fn init(
|
||||
search_config: &SearchConfig,
|
||||
invocation_directory: &Path,
|
||||
|
@ -90,7 +176,8 @@ impl Search {
|
|||
match search_config {
|
||||
SearchConfig::FromInvocationDirectory => {
|
||||
let working_directory = Self::project_root(invocation_directory)?;
|
||||
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
|
||||
let path = working_directory.join(DEFAULT_JUSTFILE_NAME);
|
||||
let justfile = JustfileKind::Path { path };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
|
@ -99,7 +186,8 @@ impl Search {
|
|||
SearchConfig::FromSearchDirectory { search_directory } => {
|
||||
let search_directory = Self::clean(invocation_directory, search_directory);
|
||||
let working_directory = Self::project_root(&search_directory)?;
|
||||
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
|
||||
let path = working_directory.join(DEFAULT_JUSTFILE_NAME);
|
||||
let justfile = JustfileKind::Path { path };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
|
@ -107,8 +195,9 @@ impl Search {
|
|||
}
|
||||
SearchConfig::GlobalJustfile => Err(SearchError::GlobalJustfileInit),
|
||||
SearchConfig::WithJustfile { justfile } => {
|
||||
let justfile = Self::clean(invocation_directory, justfile);
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
let path = Self::clean(invocation_directory, justfile);
|
||||
let working_directory = Self::working_directory_from_justfile(&path)?;
|
||||
let justfile = JustfileKind::Path { path };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
|
@ -117,14 +206,51 @@ impl Search {
|
|||
SearchConfig::WithJustfileAndWorkingDirectory {
|
||||
justfile,
|
||||
working_directory,
|
||||
} => Ok(Self {
|
||||
justfile: Self::clean(invocation_directory, justfile),
|
||||
working_directory: Self::clean(invocation_directory, working_directory),
|
||||
}),
|
||||
} => {
|
||||
let path = Self::clean(invocation_directory, justfile);
|
||||
let justfile = JustfileKind::Path { path };
|
||||
let working_directory = Self::clean(invocation_directory, working_directory);
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
|
||||
SearchConfig::WithStdin => {
|
||||
let working_directory = Self::project_root(invocation_directory)?;
|
||||
let mut data = String::new();
|
||||
if let Err(err) = io::stdin().read_to_string(&mut data) {
|
||||
return Err(SearchError::Io {
|
||||
directory: working_directory,
|
||||
io_error: err,
|
||||
});
|
||||
}
|
||||
let justfile = JustfileKind::Stdin { data };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
|
||||
SearchConfig::WithStdinAndWorkingDirectory { working_directory } => {
|
||||
let working_directory = working_directory.to_owned();
|
||||
let mut data = String::new();
|
||||
if let Err(err) = io::stdin().read_to_string(&mut data) {
|
||||
return Err(SearchError::Io {
|
||||
directory: working_directory,
|
||||
io_error: err,
|
||||
});
|
||||
}
|
||||
let justfile = JustfileKind::Stdin { data };
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn justfile(directory: &Path) -> SearchResult<PathBuf> {
|
||||
fn justfile(directory: &Path) -> SearchResult<PathBuf> {
|
||||
for directory in directory.ancestors() {
|
||||
let mut candidates = BTreeSet::new();
|
||||
|
||||
|
|
|
@ -19,4 +19,10 @@ pub(crate) enum SearchConfig {
|
|||
justfile: PathBuf,
|
||||
working_directory: PathBuf,
|
||||
},
|
||||
/// Use justfile loaded from stdin by using "-" as justfile name.
|
||||
WithStdin,
|
||||
/// Use justfile loaded from stdin and working directory.
|
||||
WithStdinAndWorkingDirectory {
|
||||
working_directory: PathBuf,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use {super::*, pretty_assertions::assert_eq};
|
||||
use {self::search::JustfileKind, super::*, pretty_assertions::assert_eq};
|
||||
|
||||
pub(crate) fn compile(src: &str) -> Justfile {
|
||||
Compiler::test_compile(src).expect("expected successful compilation")
|
||||
|
@ -17,7 +17,8 @@ pub(crate) fn config(args: &[&str]) -> Config {
|
|||
|
||||
pub(crate) fn search(config: &Config) -> Search {
|
||||
let working_directory = config.invocation_directory.clone();
|
||||
let justfile = working_directory.join("justfile");
|
||||
let path = working_directory.join("justfile");
|
||||
let justfile = JustfileKind::Path { path };
|
||||
|
||||
Search {
|
||||
justfile,
|
||||
|
|
|
@ -97,3 +97,41 @@ fn dont_evaluate_comments() {
|
|||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_analyze_comments() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
set ignore-comments
|
||||
|
||||
some_recipe:
|
||||
# {{ bar }}
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comments_still_must_be_parsable_when_ignored() {
|
||||
Test::new()
|
||||
.justfile(
|
||||
"
|
||||
set ignore-comments
|
||||
|
||||
some_recipe:
|
||||
# {{ foo bar }}
|
||||
",
|
||||
)
|
||||
.stderr(
|
||||
"
|
||||
error: Expected '}}', '(', '+', or '/', but found identifier
|
||||
——▶ justfile:4:12
|
||||
│
|
||||
4 │ # {{ foo bar }}
|
||||
│ ^^^
|
||||
",
|
||||
)
|
||||
.status(EXIT_FAILURE)
|
||||
.run();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user