Skip to content

Commit

Permalink
Inherit env vars from user's shell on unix platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
willcrichton committed Sep 25, 2024
1 parent 1b2a3d2 commit ad35cf0
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 114 deletions.
59 changes: 32 additions & 27 deletions js/packages/repo-quest/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -392,33 +392,35 @@ let QuestView: React.FC<{
</button>
</div>

<div>
<select
defaultValue={""}
onChange={async e => {
if (e.target.value === "") return;
let confirmed = await dialog.confirm(
"This will irrevocably overwrite any changes you've made. Are you sure?"
);
let stage = Number.parseInt(e.target.value);
e.target.value = "";
if (confirmed)
await loader.loadAwait(commands.hardReset(stage));
}}
>
<option disabled={true} value="">
Skip to chapter...
</option>
{quest.stages
.map<[Stage, number]>((stage, i) => [stage, i])
.filter(([_stage, i]) => i > cur_stage)
.map(([stage, i]) => (
<option key={stage.label} value={i}>
Chapter {i}: {stage.name}
</option>
))}
</select>
</div>
{initialState.can_skip && (
<div>
<select
defaultValue={""}
onChange={async e => {
if (e.target.value === "") return;
let confirmed = await dialog.confirm(
"This will irrevocably overwrite any changes you've made. Are you sure?"
);
let stage = Number.parseInt(e.target.value);
e.target.value = "";
if (confirmed)
await loader.loadAwait(commands.skipToStage(stage));
}}
>
<option disabled={true} value="">
Skip to chapter...
</option>
{quest.stages
.map<[Stage, number]>((stage, i) => [stage, i])
.filter(([_stage, i]) => i > cur_stage)
.map(([stage, i]) => (
<option key={stage.label} value={i}>
Chapter {i}: {stage.name}
</option>
))}
</select>
</div>
)}
</div>
</div>
</div>
Expand Down Expand Up @@ -557,3 +559,6 @@ let App = () => {
};

ReactDOM.createRoot(document.getElementById("root")!).render(<App />);

/* @ts-ignore */
window.devDump = async () => console.log(await commands.devDump());
20 changes: 1 addition & 19 deletions rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rs/crates/repo-quest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ default-run = "repo-quest"
tauri-build = { version = "2.0.0-rc", features = ["config-toml"] }

[dependencies]
tauri = { version = "2.0.0-rc", features = ["config-toml"] }
tauri = { version = "2.0.0-rc", features = ["config-toml", "devtools"] }
tauri-plugin-dialog = "2.0.0-rc"
tauri-plugin-shell = "2.0.0-rc"
tokio = { workspace = true }
Expand Down
21 changes: 18 additions & 3 deletions rs/crates/repo-quest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::{env, path::PathBuf, sync::Arc};
use std::{collections::HashMap, env, path::PathBuf, sync::Arc};

use rq_core::{
github::{self, GithubToken},
Expand Down Expand Up @@ -131,12 +131,26 @@ async fn refresh_state(quest: State<'_, Arc<Quest>>) -> Result<(), String> {

#[tauri::command]
#[specta::specta]
async fn hard_reset(quest: State<'_, Arc<Quest>>, stage: u32) -> Result<(), String> {
async fn skip_to_stage(quest: State<'_, Arc<Quest>>, stage: u32) -> Result<(), String> {
let stage = usize::try_from(stage).unwrap();
fmt_err(quest.skip_to_stage(stage).await)?;
Ok(())
}

#[derive(Serialize, Deserialize, Type)]
struct DevDump {
env: HashMap<String, String>,
token: GithubToken,
}

#[tauri::command]
#[specta::specta]
fn dev_dump() -> DevDump {
let env = env::vars().collect::<HashMap<_, _>>();
let token = github::get_github_token();
DevDump { env, token }
}

pub fn specta_builder() -> tauri_specta::Builder {
tauri_specta::Builder::<tauri::Wry>::new()
.commands(tauri_specta::collect_commands![
Expand All @@ -148,7 +162,8 @@ pub fn specta_builder() -> tauri_specta::Builder {
file_feature_and_issue,
file_solution,
refresh_state,
hard_reset
skip_to_stage,
dev_dump
])
.events(collect_events![StateEvent])
}
4 changes: 2 additions & 2 deletions rs/crates/rq-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ tokio = { workspace = true, features = ["macros"] }
tokio-retry = "0.3.0"
toml = "0.8.15"
specta = { workspace = true, features = ["serde_json", "derive"] }
which = "6.0.3"
anyhow = { workspace = true }
tracing = { workspace = true }
flate2 = "1.0.33"
async-trait = "0.1.82"
shlex = "1.3.0"
semver = { version = "1.0.23", features = ["serde"] }
cfg-if = "1.0.0"
shlex = "1.3.0"

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
Expand Down
37 changes: 37 additions & 0 deletions rs/crates/rq-core/src/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::{collections::HashMap, ops::Deref, path::Path, process::Command, sync::LazyLock};

use cfg_if::cfg_if;

#[cfg(unix)]
fn get_user_env() -> HashMap<String, String> {
let shell = env!("SHELL");
let output = Command::new(shell)
.args(["-c", "env"])
.output()
.expect("Failed to get shell env");
let stdout = String::from_utf8(output.stdout).expect("Env vars not utf8");
stdout
.lines()
.map(|line| {
let (key, value) = line.split_once("=").expect("Failed to parse env k/v");
(key.to_string(), value.to_string())
})
.collect()
}

static ENV: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
cfg_if! {
if #[cfg(unix)] {
get_user_env()
} else {
HashMap::default()
}
}
});

pub fn command(args: &str, dir: &Path) -> Command {
let mut arg_vec = shlex::split(args).expect("Invalid command");
let mut cmd = Command::new(arg_vec.remove(0));
cmd.current_dir(dir).envs(ENV.deref()).args(arg_vec);
cmd
}
73 changes: 41 additions & 32 deletions rs/crates/rq-core/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use std::{
fs,
io::Write,
path::{Path, PathBuf},
process::{Command, Stdio},
process::Stdio,
};

use anyhow::{ensure, Context, Result};

use crate::{
command::command,
github::{GitProtocol, GithubRepo},
package::QuestPackage,
template::QuestTemplate,
Expand All @@ -30,20 +31,16 @@ pub enum MergeType {
macro_rules! git {
($self:expr, $($arg:tt)*) => {{
let arg = format!($($arg)*);
$self.git(|cmd| {
tracing::debug!("git: {arg}");
cmd.args(shlex::split(&arg).unwrap());
}).with_context(|| format!("git failed: {arg}"))
tracing::debug!("git: {arg}");
$self.git(&arg).with_context(|| format!("git failed: {arg}"))
}}
}

macro_rules! git_output {
($self:expr, $($arg:tt)*) => {{
let arg = format!($($arg)*);
$self.git_output(|cmd| {
tracing::debug!("git: {arg}");
cmd.args(shlex::split(&format!($($arg)*)).unwrap());
}).with_context(|| format!("git failed: {arg}"))
tracing::debug!("git: {arg}");
$self.git_output(&arg).with_context(|| format!("git failed: {arg}"))
}}
}

Expand All @@ -54,10 +51,18 @@ impl GitRepo {
}
}

fn git_core(&self, f: impl FnOnce(&mut Command), capture: bool) -> Result<Option<String>> {
let mut cmd = Command::new("git");
cmd.current_dir(&self.path);
f(&mut cmd);
pub fn clone(path: &Path, url: &str) -> Result<Self> {
let output = command(&format!("git clone {url}"), path.parent().unwrap()).output()?;
ensure!(
output.status.success(),
"`git clone {url}` failed, stderr:\n{}",
String::from_utf8(output.stderr)?
);
Ok(GitRepo::new(path))
}

fn git_core(&self, args: &str, capture: bool) -> Result<Option<String>> {
let mut cmd = command(&format!("git {args}"), &self.path);
cmd.stderr(Stdio::piped());
if capture {
cmd.stdout(Stdio::piped());
Expand All @@ -79,12 +84,12 @@ impl GitRepo {
Ok(stdout)
}

fn git(&self, f: impl FnOnce(&mut Command)) -> Result<()> {
self.git_core(f, false).map(|_| ())
fn git(&self, args: &str) -> Result<()> {
self.git_core(args, false).map(|_| ())
}

fn git_output(&self, f: impl FnOnce(&mut Command)) -> Result<String> {
self.git_core(f, true).map(|s| s.unwrap())
fn git_output(&self, args: &str) -> Result<String> {
self.git_core(args, true).map(|s| s.unwrap())
}

pub fn setup_upstream(&self, upstream: &GithubRepo) -> Result<()> {
Expand All @@ -95,27 +100,28 @@ impl GitRepo {
}

pub fn has_upstream(&self) -> Result<bool> {
let status = Command::new("git")
.args(["remote", "get-url", UPSTREAM])
.current_dir(&self.path)
let status = command(&format!("git remote get-url {UPSTREAM}"), &self.path)
.status()
.context("`git remote` failed")?;
Ok(status.success())
}

fn apply(&self, patch: &str) -> Result<()> {
tracing::trace!("Applying patch:\n{patch}");
let mut cmd = Command::new("git");
cmd
.args(["apply", "-"])
.current_dir(&self.path)
.stdin(Stdio::piped());
let mut child = cmd.spawn()?;
let mut child = command("git apply -", &self.path)
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
stdin.write_all(patch.as_bytes())?;
drop(stdin);
let status = child.wait()?;
ensure!(status.success(), "git apply failed");
let output = child.wait_with_output()?;
ensure!(
output.status.success(),
"git apply failed with stderr:\n{}",
String::from_utf8(output.stderr)?
);
tracing::trace!("wtf: {}", String::from_utf8(output.stderr)?);
Ok(())
}

Expand Down Expand Up @@ -209,10 +215,14 @@ impl GitRepo {
}

pub fn show_bin(&self, branch: &str, file: &str) -> Result<Vec<u8>> {
let output = Command::new("git")
.args(["show", &format!("{branch}:{file}")])
let output = command(&format!("git show {branch}:{file}"), &self.path)
.output()
.with_context(|| format!("Failed to `git show {branch}:{file}"))?;
ensure!(
output.status.success(),
"git show failed with stderr:\n{}",
String::from_utf8(output.stderr)?
);
Ok(output.stdout)
}

Expand Down Expand Up @@ -293,8 +303,7 @@ impl GitRepo {
if hooks_dir.exists() {
let post_checkout = hooks_dir.join("post-checkout");
if post_checkout.exists() {
let status = Command::new(post_checkout)
.current_dir(&self.path)
let status = command(&post_checkout.display().to_string(), &self.path)
.status()
.context("post-checkout hook failed")?;
ensure!(status.success(), "post-checkout hook failed");
Expand Down
Loading

0 comments on commit ad35cf0

Please sign in to comment.