Skip to content

Commit 92e9148

Browse files
allow multiple fallbacks for --affected base branch (#9045)
### Description We use `main` as our default for base branch, but a lot of repos are likely using `master`, we should fall back to that if `main` isn't a valid ref. ### Testing Instructions see unit tests https://github.com/vercel/turborepo/pull/9045/files#diff-db1296fb6f1a06e197e7cd6cdf5979b738c6b56b7700b048ab3b00fd767f8aecR734-R746 for situations to test manually. --------- Co-authored-by: Chris Olszewski <[email protected]>
1 parent f736816 commit 92e9148

File tree

10 files changed

+163
-85
lines changed

10 files changed

+163
-85
lines changed

crates/turborepo-lib/src/commands/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct ConfigOutput<'a> {
2323
package_manager: PackageManager,
2424
daemon: Option<bool>,
2525
env_mode: EnvMode,
26-
scm_base: &'a str,
26+
scm_base: Option<&'a str>,
2727
scm_head: &'a str,
2828
cache_dir: &'a Utf8Path,
2929
}

crates/turborepo-lib/src/config.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,8 @@ impl ConfigurationOptions {
298298
self.ui.unwrap_or(UIMode::Stream)
299299
}
300300

301-
pub fn scm_base(&self) -> &str {
302-
self.scm_base.as_deref().unwrap_or("main")
301+
pub fn scm_base(&self) -> Option<&str> {
302+
self.scm_base.as_deref()
303303
}
304304

305305
pub fn scm_head(&self) -> &str {

crates/turborepo-lib/src/opts.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ pub struct ScopeOpts {
307307
pub pkg_inference_root: Option<AnchoredSystemPathBuf>,
308308
pub global_deps: Vec<String>,
309309
pub filter_patterns: Vec<String>,
310-
pub affected_range: Option<(String, String)>,
310+
pub affected_range: Option<(Option<String>, String)>,
311311
}
312312

313313
impl<'a> TryFrom<OptsInputs<'a>> for ScopeOpts {
@@ -324,7 +324,7 @@ impl<'a> TryFrom<OptsInputs<'a>> for ScopeOpts {
324324
let affected_range = inputs.execution_args.affected.then(|| {
325325
let scm_base = inputs.config.scm_base();
326326
let scm_head = inputs.config.scm_head();
327-
(scm_base.to_string(), scm_head.to_string())
327+
(scm_base.map(|b| b.to_owned()), scm_head.to_string())
328328
});
329329

330330
Ok(Self {
@@ -513,7 +513,7 @@ mod test {
513513
pkg_inference_root: None,
514514
global_deps: vec![],
515515
filter_patterns: opts_input.filter_patterns,
516-
affected_range: opts_input.affected,
516+
affected_range: opts_input.affected.map(|(base, head)| (Some(base), head)),
517517
};
518518
let opts = Opts {
519519
run_opts,

crates/turborepo-lib/src/query.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ impl Query {
242242
base: Option<String>,
243243
head: Option<String>,
244244
) -> Result<Vec<Package>, Error> {
245-
let base = base.unwrap_or_else(|| "main".to_string());
245+
let base = base.map(|s| s.to_owned());
246246
let head = head.unwrap_or_else(|| "HEAD".to_string());
247247
let mut opts = self.run.opts().clone();
248248
opts.scope_opts.affected_range = Some((base, head));

crates/turborepo-lib/src/run/scope/change_detector.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
pub trait GitChangeDetector {
1818
fn changed_packages(
1919
&self,
20-
from_ref: &str,
20+
from_ref: Option<&str>,
2121
to_ref: Option<&str>,
2222
include_uncommitted: bool,
2323
allow_unknown_objects: bool,
@@ -55,7 +55,7 @@ impl<'a> ScopeChangeDetector<'a> {
5555
/// returns an empty lockfile change
5656
fn get_lockfile_contents(
5757
&self,
58-
from_ref: &str,
58+
from_ref: Option<&str>,
5959
changed_files: &HashSet<AnchoredSystemPathBuf>,
6060
) -> Option<LockfileChange> {
6161
let lockfile_path = self
@@ -88,13 +88,13 @@ impl<'a> ScopeChangeDetector<'a> {
8888
impl<'a> GitChangeDetector for ScopeChangeDetector<'a> {
8989
fn changed_packages(
9090
&self,
91-
from_ref: &str,
91+
from_ref: Option<&str>,
9292
to_ref: Option<&str>,
9393
include_uncommitted: bool,
9494
allow_unknown_objects: bool,
9595
) -> Result<HashSet<PackageName>, ResolutionError> {
9696
let mut changed_files = HashSet::new();
97-
if !from_ref.is_empty() {
97+
if !from_ref.map_or(false, |s| s.is_empty()) {
9898
changed_files = match self.scm.changed_files(
9999
self.turbo_root,
100100
from_ref,

crates/turborepo-lib/src/run/scope/filter.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl<'a, T: GitChangeDetector> FilterResolver<'a, T> {
163163
/// It applies the following rules:
164164
pub(crate) fn resolve(
165165
&self,
166-
affected: &Option<(String, String)>,
166+
affected: &Option<(Option<String>, String)>,
167167
patterns: &[String],
168168
) -> Result<(HashSet<PackageName>, bool), ResolutionError> {
169169
// inference is None only if we are in the root
@@ -185,7 +185,7 @@ impl<'a, T: GitChangeDetector> FilterResolver<'a, T> {
185185

186186
fn get_packages_from_patterns(
187187
&self,
188-
affected: &Option<(String, String)>,
188+
affected: &Option<(Option<String>, String)>,
189189
patterns: &[String],
190190
) -> Result<HashSet<PackageName>, ResolutionError> {
191191
let mut selectors = patterns
@@ -196,7 +196,7 @@ impl<'a, T: GitChangeDetector> FilterResolver<'a, T> {
196196
if let Some((from_ref, to_ref)) = affected {
197197
selectors.push(TargetSelector {
198198
git_range: Some(GitRange {
199-
from_ref: from_ref.to_string(),
199+
from_ref: from_ref.clone(),
200200
to_ref: Some(to_ref.to_string()),
201201
include_uncommitted: true,
202202
allow_unknown_objects: true,
@@ -549,7 +549,7 @@ impl<'a, T: GitChangeDetector> FilterResolver<'a, T> {
549549
git_range: &GitRange,
550550
) -> Result<HashSet<PackageName>, ResolutionError> {
551551
self.change_detector.changed_packages(
552-
&git_range.from_ref,
552+
git_range.from_ref.as_deref(),
553553
git_range.to_ref.as_deref(),
554554
git_range.include_uncommitted,
555555
git_range.allow_unknown_objects,
@@ -1123,7 +1123,7 @@ mod test {
11231123
#[test_case(
11241124
vec![
11251125
TargetSelector {
1126-
git_range: Some(GitRange { from_ref: "HEAD~1".to_string(), to_ref: None, ..Default::default() }),
1126+
git_range: Some(GitRange { from_ref: Some("HEAD~1".to_string()), to_ref: None, ..Default::default() }),
11271127
..Default::default()
11281128
}
11291129
],
@@ -1133,7 +1133,7 @@ mod test {
11331133
#[test_case(
11341134
vec![
11351135
TargetSelector {
1136-
git_range: Some(GitRange { from_ref: "HEAD~1".to_string(), to_ref: None, ..Default::default() }),
1136+
git_range: Some(GitRange { from_ref: Some("HEAD~1".to_string()), to_ref: None, ..Default::default() }),
11371137
parent_dir: Some(AnchoredSystemPathBuf::try_from(".").unwrap()),
11381138
..Default::default()
11391139
}
@@ -1144,7 +1144,7 @@ mod test {
11441144
#[test_case(
11451145
vec![
11461146
TargetSelector {
1147-
git_range: Some(GitRange { from_ref: "HEAD~1".to_string(), to_ref: None, ..Default::default() }),
1147+
git_range: Some(GitRange { from_ref: Some("HEAD~1".to_string()), to_ref: None, ..Default::default() }),
11481148
parent_dir: Some(AnchoredSystemPathBuf::try_from("package-2").unwrap()),
11491149
..Default::default()
11501150
}
@@ -1155,7 +1155,7 @@ mod test {
11551155
#[test_case(
11561156
vec![
11571157
TargetSelector {
1158-
git_range: Some(GitRange { from_ref: "HEAD~1".to_string(), to_ref: None, ..Default::default() }),
1158+
git_range: Some(GitRange { from_ref: Some("HEAD~1".to_string()), to_ref: None, ..Default::default() }),
11591159
name_pattern: "package-2*".to_string(),
11601160
..Default::default()
11611161
}
@@ -1166,7 +1166,7 @@ mod test {
11661166
#[test_case(
11671167
vec![
11681168
TargetSelector {
1169-
git_range: Some(GitRange { from_ref: "HEAD~1".to_string(), to_ref: None, ..Default::default() }),
1169+
git_range: Some(GitRange { from_ref: Some("HEAD~1".to_string()), to_ref: None, ..Default::default() }),
11701170
name_pattern: "package-1".to_string(),
11711171
match_dependencies: true,
11721172
..Default::default()
@@ -1178,7 +1178,7 @@ mod test {
11781178
#[test_case(
11791179
vec![
11801180
TargetSelector {
1181-
git_range: Some(GitRange { from_ref: "HEAD~2".to_string(), to_ref: None, ..Default::default() }),
1181+
git_range: Some(GitRange { from_ref: Some("HEAD~2".to_string()), to_ref: None, ..Default::default() }),
11821182
..Default::default()
11831183
}
11841184
],
@@ -1188,7 +1188,7 @@ mod test {
11881188
#[test_case(
11891189
vec![
11901190
TargetSelector {
1191-
git_range: Some(GitRange { from_ref: "HEAD~2".to_string(), to_ref: Some("HEAD~1".to_string()), ..Default::default() }),
1191+
git_range: Some(GitRange { from_ref: Some("HEAD~2".to_string()), to_ref: Some("HEAD~1".to_string()), ..Default::default() }),
11921192
..Default::default()
11931193
}
11941194
],
@@ -1198,7 +1198,7 @@ mod test {
11981198
#[test_case(
11991199
vec![
12001200
TargetSelector {
1201-
git_range: Some(GitRange { from_ref: "HEAD~1".to_string(), to_ref: None, ..Default::default() }),
1201+
git_range: Some(GitRange { from_ref: Some("HEAD~1".to_string()), to_ref: None, ..Default::default() }),
12021202
parent_dir: Some(AnchoredSystemPathBuf::try_from("package-*").unwrap()),
12031203
match_dependencies: true, ..Default::default()
12041204
}
@@ -1250,14 +1250,14 @@ mod test {
12501250
impl<'a> GitChangeDetector for TestChangeDetector<'a> {
12511251
fn changed_packages(
12521252
&self,
1253-
from: &str,
1253+
from: Option<&str>,
12541254
to: Option<&str>,
12551255
_include_uncommitted: bool,
12561256
_allow_unknown_objects: bool,
12571257
) -> Result<HashSet<PackageName>, ResolutionError> {
12581258
Ok(self
12591259
.0
1260-
.get(&(from, to))
1260+
.get(&(from.expect("expected base branch"), to))
12611261
.map(|h| h.to_owned())
12621262
.expect("unsupported range"))
12631263
}

crates/turborepo-lib/src/run/scope/target_selector.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use turbopath::AnchoredSystemPathBuf;
66

77
#[derive(Debug, Default, PartialEq)]
88
pub struct GitRange {
9-
pub from_ref: String,
9+
pub from_ref: Option<String>,
1010
pub to_ref: Option<String>,
1111
pub include_uncommitted: bool,
1212
// Allow unknown objects to be included in the range, without returning an error.
@@ -155,7 +155,7 @@ impl FromStr for TargetSelector {
155155
));
156156
}
157157
GitRange {
158-
from_ref: a.to_string(),
158+
from_ref: Some(a.to_string()),
159159
to_ref: Some(b.to_string()),
160160
include_uncommitted: false,
161161
allow_unknown_objects: false,
@@ -164,7 +164,7 @@ impl FromStr for TargetSelector {
164164
// If only the start of the range is specified, we assume that
165165
// we want to include uncommitted changes
166166
GitRange {
167-
from_ref: commits_str.to_string(),
167+
from_ref: Some(commits_str.to_string()),
168168
to_ref: None,
169169
include_uncommitted: true,
170170
allow_unknown_objects: false,
@@ -252,17 +252,17 @@ mod test {
252252
#[test_case("...{./foo}", TargetSelector { raw: "...{./foo}".to_string(), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), include_dependents: true, ..Default::default() }; "dot dot dot curly bracket foo")]
253253
#[test_case(".", TargetSelector { raw: ".".to_string(), parent_dir: Some(AnchoredSystemPathBuf::try_from(".").unwrap()), ..Default::default() }; "parent dir dot")]
254254
#[test_case("..", TargetSelector { raw: "..".to_string(), parent_dir: Some(AnchoredSystemPathBuf::try_from("..").unwrap()), ..Default::default() }; "parent dir dot dot")]
255-
#[test_case("[master]", TargetSelector { raw: "[master]".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), ..Default::default() }; "square brackets master")]
256-
#[test_case("[from...to]", TargetSelector { raw: "[from...to]".to_string(), git_range: Some(GitRange { from_ref: "from".to_string(), to_ref: Some("to".to_string()), ..Default::default() }), ..Default::default() }; "[from...to]")]
257-
#[test_case("{foo}[master]", TargetSelector { raw: "{foo}[master]".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), ..Default::default() }; "{foo}[master]")]
258-
#[test_case("pattern{foo}[master]", TargetSelector { raw: "pattern{foo}[master]".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), name_pattern: "pattern".to_string(), ..Default::default() }; "pattern{foo}[master]")]
259-
#[test_case("[master]...", TargetSelector { raw: "[master]...".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), include_dependencies: true, ..Default::default() }; "square brackets master dot dot dot")]
260-
#[test_case("...[master]", TargetSelector { raw: "...[master]".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), include_dependents: true, ..Default::default() }; "dot dot dot master square brackets")]
261-
#[test_case("...[master]...", TargetSelector { raw: "...[master]...".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), include_dependencies: true, include_dependents: true, ..Default::default() }; "dot dot dot master square brackets dot dot dot")]
262-
#[test_case("...[from...to]...", TargetSelector { raw: "...[from...to]...".to_string(), git_range: Some(GitRange { from_ref: "from".to_string(), to_ref: Some("to".to_string()), ..Default::default() }), include_dependencies: true, include_dependents: true, ..Default::default() }; "dot dot dot [from...to] dot dot dot")]
263-
#[test_case("foo...[master]", TargetSelector { raw: "foo...[master]".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), name_pattern: "foo".to_string(), match_dependencies: true, ..Default::default() }; "foo...[master]")]
264-
#[test_case("foo...[master]...", TargetSelector { raw: "foo...[master]...".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), name_pattern: "foo".to_string(), match_dependencies: true, include_dependencies: true, ..Default::default() }; "foo...[master] dot dot dot")]
265-
#[test_case("{foo}...[master]", TargetSelector { raw: "{foo}...[master]".to_string(), git_range: Some(GitRange { from_ref: "master".to_string(), to_ref: None, include_uncommitted: true, ..Default::default() }), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), match_dependencies: true, ..Default::default() }; " curly brackets foo...[master]")]
255+
#[test_case("[master]", TargetSelector { raw: "[master]".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), ..Default::default() }; "square brackets master")]
256+
#[test_case("[from...to]", TargetSelector { raw: "[from...to]".to_string(), git_range: Some(GitRange { from_ref: Some("from".to_string()), to_ref: Some("to".to_string()), ..Default::default() }), ..Default::default() }; "[from...to]")]
257+
#[test_case("{foo}[master]", TargetSelector { raw: "{foo}[master]".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), ..Default::default() }; "{foo}[master]")]
258+
#[test_case("pattern{foo}[master]", TargetSelector { raw: "pattern{foo}[master]".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), name_pattern: "pattern".to_string(), ..Default::default() }; "pattern{foo}[master]")]
259+
#[test_case("[master]...", TargetSelector { raw: "[master]...".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), include_dependencies: true, ..Default::default() }; "square brackets master dot dot dot")]
260+
#[test_case("...[master]", TargetSelector { raw: "...[master]".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), include_dependents: true, ..Default::default() }; "dot dot dot master square brackets")]
261+
#[test_case("...[master]...", TargetSelector { raw: "...[master]...".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), include_dependencies: true, include_dependents: true, ..Default::default() }; "dot dot dot master square brackets dot dot dot")]
262+
#[test_case("...[from...to]...", TargetSelector { raw: "...[from...to]...".to_string(), git_range: Some(GitRange { from_ref: Some("from".to_string()), to_ref: Some("to".to_string()), ..Default::default() }), include_dependencies: true, include_dependents: true, ..Default::default() }; "dot dot dot [from...to] dot dot dot")]
263+
#[test_case("foo...[master]", TargetSelector { raw: "foo...[master]".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), name_pattern: "foo".to_string(), match_dependencies: true, ..Default::default() }; "foo...[master]")]
264+
#[test_case("foo...[master]...", TargetSelector { raw: "foo...[master]...".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), name_pattern: "foo".to_string(), match_dependencies: true, include_dependencies: true, ..Default::default() }; "foo...[master] dot dot dot")]
265+
#[test_case("{foo}...[master]", TargetSelector { raw: "{foo}...[master]".to_string(), git_range: Some(GitRange { from_ref: Some("master".to_string()), to_ref: None, include_uncommitted: true, ..Default::default() }), parent_dir: Some(AnchoredSystemPathBuf::try_from("foo").unwrap()), match_dependencies: true, ..Default::default() }; " curly brackets foo...[master]")]
266266
fn parse_target_selector(raw_selector: &str, want: TargetSelector) {
267267
let result = TargetSelector::from_str(raw_selector);
268268

0 commit comments

Comments
 (0)