Skip to content

Commit af3c783

Browse files
authored
Fix .pants.bootstrap handling. (#108)
A re-exec approach is now used to ensure pants runs in a bash subprocess for complete fidelity with the `./pants` bash script case. Fixes #107
1 parent e31d05f commit af3c783

File tree

10 files changed

+134
-164
lines changed

10 files changed

+134
-164
lines changed

.pants.bootstrap

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
function should_be_ignored() {
22
# Normally `set` would print information about functions as well as variables; causing scie-pants
33
# to emit warnings about skipping un-parseable env vars. This function stresses that corner in
4-
# integration tests.
4+
# integration tests. Although `set` is no longer used to evaluated `.pants.bootstrap` scripts,
5+
# this shell function still serves to prevent a back-slide.
56
return
67
}
78

89
# Ensure GIT_COMMIT is set
9-
: ${GIT_COMMIT:=$(git rev-parse HEAD)}
10+
export GIT_COMMIT="$(git rev-parse HEAD)"
11+
12+
# Exercise https://github.com/pantsbuild/scie-pants/issues/107
13+
export PANTS_DOCKER_TOOLS="+['aws-oidc', 'open']"
14+
export COLUMNS="$(tput cols)"
15+
export LINES="$(tput lines)"
1016

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release Notes
22

3+
## 0.4.2
4+
5+
This release fixes `.pants.bootstrap` handling to robustly mimic handling by the `./pants` script.
6+
The `scie-pants` binary now re-execs itself through a bash shell when `.pants.bootstrap` needs to
7+
be sourced.
8+
39
## 0.4.1
410

511
This release supports using a released Pants version in the Pants repo when a Pants version to use

Cargo.lock

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ members = [
66
[package]
77
name = "scie-pants"
88
description = "Protects your Pants from the elements."
9-
version = "0.4.1"
9+
version = "0.4.2"
1010
edition = "2021"
1111
authors = [
1212
"John Sirois <[email protected]>",
@@ -34,3 +34,6 @@ serde = { version = "1.0", features = ["derive"] }
3434
tempfile = { workspace = true }
3535
toml = "0.7"
3636
uuid = { version = "1.3", features = ["v4"] }
37+
38+
[target.'cfg(unix)'.dependencies]
39+
shell-quote = "0.3.0"

package/src/main.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,17 +800,31 @@ fn test(
800800
);
801801
env::set_var("PANTS_PANTSRC", "False");
802802

803+
// Our `.pants.bootstrap` uses `tput` which requires TERM be set: ensure it is.
804+
env::set_var("TERM", env::var_os("TERM").unwrap_or_else(|| "dumb".into()));
805+
803806
// Max Python supported is 3.9 and only Linux x86_64 and macOS aarch64 and x86_64 wheels were
804807
// released.
805808
if matches!(
806809
*CURRENT_PLATFORM,
807810
Platform::LinuxX86_64 | Platform::MacOSAarch64 | Platform::MacOSX86_64
808811
) {
809812
integration_test!("Linting, testing and packaging the tools codebase");
813+
let tput_output = |subcommand| {
814+
let result =
815+
execute(Command::new("tput").arg(subcommand).stdout(Stdio::piped()))?.stdout;
816+
String::from_utf8(result).map_err(|e| {
817+
Code::FAILURE.with_message(format!(
818+
"Failed to decode output of tput {subcommand} as UTF-*: {e}"
819+
))
820+
})
821+
};
810822
execute(
811823
Command::new(scie_pants_scie)
812824
.args(["fmt", "lint", "check", "test", "package", "::"])
813-
.env("PEX_SCRIPT", "Does not exist!"),
825+
.env("PEX_SCRIPT", "Does not exist!")
826+
.env("EXPECTED_COLUMNS", tput_output("cols")?.trim())
827+
.env("EXPECTED_LINES", tput_output("lines")?.trim()),
814828
)?;
815829

816830
integration_test!("Checking .pants.bootstrap handling ignores bash functions");

pants.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[GLOBAL]
2-
pants_version = "2.15.0rc0"
2+
pants_version = "2.15.0rc3"
33

44
backend_packages = [
55
"pants.backend.python",

src/main.rs

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use std::env;
55
use std::ffi::{OsStr, OsString};
6+
use std::fmt::Debug;
67
use std::path::PathBuf;
78

89
use anyhow::{anyhow, bail, Context, Result};
@@ -12,11 +13,9 @@ use logging_timer::{time, timer, Level};
1213
use uuid::Uuid;
1314

1415
use crate::config::PantsConfig;
15-
use crate::pants_bootstrap::PantsBootstrap;
1616

1717
mod build_root;
1818
mod config;
19-
mod pants_bootstrap;
2019

2120
#[derive(Debug, Default)]
2221
struct Process {
@@ -86,15 +85,73 @@ fn env_version(env_var_name: &str) -> Result<Option<String>> {
8685

8786
fn find_pants_installation() -> Result<Option<PantsConfig>> {
8887
if let Ok(build_root) = BuildRoot::find(None) {
89-
if let Some(pants_bootstrap) = PantsBootstrap::load(&build_root)? {
90-
pants_bootstrap.export_env();
91-
}
9288
let pants_config = PantsConfig::parse(build_root)?;
9389
return Ok(Some(pants_config));
9490
}
9591
Ok(None)
9692
}
9793

94+
#[derive(Eq, PartialEq)]
95+
enum ScieBoot {
96+
BootstrapTools,
97+
Pants,
98+
PantsDebug,
99+
}
100+
101+
impl ScieBoot {
102+
fn env_value(&self) -> OsString {
103+
match self {
104+
ScieBoot::BootstrapTools => "bootstrap-tools",
105+
ScieBoot::Pants => "pants",
106+
ScieBoot::PantsDebug => "pants-debug",
107+
}
108+
.into()
109+
}
110+
111+
#[cfg(unix)]
112+
fn quote<T: Into<OsString> + Debug>(value: T) -> Result<String> {
113+
String::from_utf8(shell_quote::bash::escape(value))
114+
.context("Shell-quoted value could not be interpreted as UTF-8.")
115+
}
116+
117+
#[cfg(windows)]
118+
fn quote<T: Into<OsString> + Debug>(_value: T) -> Result<String> {
119+
// The shell_quote crate assumes unix and fails to compile on Windows.
120+
todo!("TODO(John Sirois): Figure out Git bash? shell quoting for Windows WTF-16 strings.")
121+
}
122+
123+
fn into_process(
124+
self,
125+
scie: String,
126+
build_root: Option<PathBuf>,
127+
env: Vec<(OsString, OsString)>,
128+
) -> Result<Process> {
129+
Ok(match build_root.map(|br| br.join(".pants.bootstrap")) {
130+
Some(pants_bootstrap) if self != Self::BootstrapTools && pants_bootstrap.is_file() => {
131+
Process {
132+
exe: "/usr/bin/env".into(),
133+
args: vec![
134+
"bash".into(),
135+
"-c".into(),
136+
format!(
137+
r#"set -eou pipefail; source {bootstrap}; exec {scie} "$0" "$@""#,
138+
bootstrap = Self::quote(pants_bootstrap)?,
139+
scie = Self::quote(scie)?
140+
)
141+
.into(),
142+
],
143+
env,
144+
}
145+
}
146+
_ => Process {
147+
exe: scie.into(),
148+
env,
149+
..Default::default()
150+
},
151+
})
152+
}
153+
}
154+
98155
#[time("debug", "scie-pants::{}")]
99156
fn get_pants_process() -> Result<Process> {
100157
let pants_installation = find_pants_installation()?;
@@ -142,23 +199,23 @@ fn get_pants_process() -> Result<Process> {
142199
info!("The required Pants version is {pants_version:?}");
143200

144201
let scie =
145-
env::var_os("SCIE").context("Failed to retrieve SCIE location from the environment.")?;
202+
env::var("SCIE").context("Failed to retrieve SCIE location from the environment.")?;
146203

147204
let pants_debug = matches!(env::var_os("PANTS_DEBUG"), Some(value) if !value.is_empty());
148205
let scie_boot = match env::var_os("PANTS_BOOTSTRAP_TOOLS") {
149-
Some(_) => "bootstrap-tools",
206+
Some(_) => ScieBoot::BootstrapTools,
150207
None => {
151208
if pants_debug {
152-
"pants-debug"
209+
ScieBoot::PantsDebug
153210
} else {
154-
"pants"
211+
ScieBoot::Pants
155212
}
156213
}
157214
};
158215

159216
let mut env = vec![
160-
("SCIE_BOOT".into(), scie_boot.into()),
161-
("PANTS_BIN_NAME".into(), scie.as_os_str().into()),
217+
("SCIE_BOOT".into(), scie_boot.env_value()),
218+
("PANTS_BIN_NAME".into(), scie.clone().into()),
162219
(
163220
"PANTS_DEBUG".into(),
164221
if pants_debug { "1" } else { "" }.into(),
@@ -168,7 +225,7 @@ fn get_pants_process() -> Result<Process> {
168225
debugpy_version.unwrap_or_default().into(),
169226
),
170227
];
171-
if let Some(build_root) = build_root {
228+
if let Some(ref build_root) = build_root {
172229
env.push((
173230
"PANTS_BUILDROOT_OVERRIDE".into(),
174231
build_root.as_os_str().to_os_string(),
@@ -192,11 +249,7 @@ fn get_pants_process() -> Result<Process> {
192249
))
193250
}
194251

195-
Ok(Process {
196-
exe: scie,
197-
env,
198-
..Default::default()
199-
})
252+
scie_boot.into_process(scie, build_root, env)
200253
}
201254

202255
fn get_pants_from_sources_process(pants_repo_location: PathBuf) -> Result<Process> {

0 commit comments

Comments
 (0)