Skip to content

Commit 7145362

Browse files
authored
feat(hashing): support $TURBO_DEFAULT$ in inputs (#7113)
### Description Adds support for a `$TURBO_DEFAULT$` in `inputs`. This will expand to all files that `turbo` would normally include (all non-gitignored files in the workspace), but also allow additional globs (negative as well). For example: ```json "inputs": ["$TURBO_DEFAULT$", "ignored-file.json", "!./app/layout.tsx"] ``` This allows all package files to be included, but add an additional git ignored file: `ignored-file.json`, and exclude a file that is part of `$TURBO_DEFAULT$` (`!./app/layout.tsx`). This reduces the downsides of using `inputs`, and makes this key more flexible, and less error-prone. Closes #6814 Closes TURBO-2161
1 parent 550126e commit 7145362

File tree

2 files changed

+273
-30
lines changed

2 files changed

+273
-30
lines changed

crates/turborepo-scm/src/manual.rs

+63-3
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,16 @@ pub(crate) fn hash_files(
5656
Ok(hashes)
5757
}
5858

59-
pub(crate) fn get_package_file_hashes_from_processing_gitignore<S: AsRef<str>>(
59+
pub(crate) fn get_package_file_hashes_without_git<S: AsRef<str>>(
6060
turbo_root: &AbsoluteSystemPath,
6161
package_path: &AnchoredSystemPath,
6262
inputs: &[S],
63+
include_default_files: bool,
6364
) -> Result<GitHashes, Error> {
6465
let full_package_path = turbo_root.resolve(package_path);
6566
let mut hashes = GitHashes::new();
67+
let mut default_file_hashes = GitHashes::new();
68+
let mut excluded_file_hashes = GitHashes::new();
6669

6770
let mut walker_builder = WalkBuilder::new(&full_package_path);
6871
let mut includes = Vec::new();
@@ -102,6 +105,7 @@ pub(crate) fn get_package_file_hashes_from_processing_gitignore<S: AsRef<str>>(
102105
} else {
103106
Some(any(excludes)?)
104107
};
108+
105109
let walker = walker_builder
106110
.follow_links(false)
107111
// if inputs have been provided manually, we shouldn't skip ignored files to mimic the
@@ -110,6 +114,7 @@ pub(crate) fn get_package_file_hashes_from_processing_gitignore<S: AsRef<str>>(
110114
.require_git(false)
111115
.hidden(false) // this results in yielding hidden files (e.g. .gitignore)
112116
.build();
117+
113118
for dirent in walker {
114119
let dirent = dirent?;
115120
let metadata = dirent.metadata()?;
@@ -118,26 +123,80 @@ pub(crate) fn get_package_file_hashes_from_processing_gitignore<S: AsRef<str>>(
118123
if metadata.is_dir() {
119124
continue;
120125
}
126+
121127
let path = AbsoluteSystemPath::from_std_path(dirent.path())?;
122128
let relative_path = full_package_path.anchor(path)?;
123129
let relative_path = relative_path.to_unix();
130+
131+
// if we have includes, and this path doesn't match any of them, skip it
124132
if let Some(include_pattern) = include_pattern.as_ref() {
125133
if !include_pattern.is_match(relative_path.as_str()) {
126134
continue;
127135
}
128136
}
137+
138+
// if we have excludes, and this path matches one of them, skip it
129139
if let Some(exclude_pattern) = exclude_pattern.as_ref() {
130140
if exclude_pattern.is_match(relative_path.as_str()) {
131141
continue;
132142
}
133143
}
144+
134145
// FIXME: we don't hash symlinks...
135146
if metadata.is_symlink() {
136147
continue;
137148
}
138149
let hash = git_like_hash_file(path)?;
139150
hashes.insert(relative_path, hash);
140151
}
152+
153+
// If we're including default files, we need to walk again, but this time with
154+
// git_ignore enabled
155+
if include_default_files {
156+
let walker = walker_builder
157+
.follow_links(false)
158+
.git_ignore(true)
159+
.require_git(false)
160+
.hidden(false) // this results in yielding hidden files (e.g. .gitignore)
161+
.build();
162+
163+
for dirent in walker {
164+
let dirent = dirent?;
165+
let metadata = dirent.metadata()?;
166+
// We need to do this here, rather than as a filter, because the root
167+
// directory is always yielded and not subject to the supplied filter.
168+
if metadata.is_dir() {
169+
continue;
170+
}
171+
172+
let path = AbsoluteSystemPath::from_std_path(dirent.path())?;
173+
let relative_path = full_package_path.anchor(path)?;
174+
let relative_path = relative_path.to_unix();
175+
176+
if let Some(exclude_pattern) = exclude_pattern.as_ref() {
177+
if exclude_pattern.is_match(relative_path.as_str()) {
178+
// track excludes so we can exclude them to the hash map later
179+
if !metadata.is_symlink() {
180+
let hash = git_like_hash_file(path)?;
181+
excluded_file_hashes.insert(relative_path.clone(), hash);
182+
}
183+
}
184+
}
185+
186+
// FIXME: we don't hash symlinks...
187+
if metadata.is_symlink() {
188+
continue;
189+
}
190+
let hash = git_like_hash_file(path)?;
191+
default_file_hashes.insert(relative_path, hash);
192+
}
193+
}
194+
195+
// merge default with all hashes
196+
hashes.extend(default_file_hashes);
197+
// remove excluded files
198+
hashes.retain(|key, _| !excluded_file_hashes.contains_key(key));
199+
141200
Ok(hashes)
142201
}
143202

@@ -353,7 +412,7 @@ mod tests {
353412
);
354413

355414
let hashes =
356-
get_package_file_hashes_from_processing_gitignore::<&str>(&turbo_root, &pkg_path, &[])
415+
get_package_file_hashes_without_git::<&str>(&turbo_root, &pkg_path, &[], false)
357416
.unwrap();
358417
assert_eq!(hashes, expected);
359418

@@ -384,10 +443,11 @@ mod tests {
384443
}
385444
}
386445

387-
let hashes = get_package_file_hashes_from_processing_gitignore(
446+
let hashes = get_package_file_hashes_without_git(
388447
&turbo_root,
389448
&pkg_path,
390449
&["**/*file", "!some-dir/excluded-file"],
450+
false,
391451
)
392452
.unwrap();
393453

0 commit comments

Comments
 (0)