fix(cli): make app_dir() logic consistent (#10418)

* fix(cli): Make app_dir() consistent by basing it on the explicit invocation directory rather than the current working directory

* resolve app paths before everything else

* fix xcode script

* fix test

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
Sam Kearney 2024-08-11 07:44:15 -05:00 committed by GitHub
parent 0afee5ed80
commit 2d47352a07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 145 additions and 74 deletions

View File

@ -0,0 +1,6 @@
---
"tauri-cli": patch:bug
"@tauri-apps/cli": patch:bug
---
CLI commands will now consistently search for the `app_dir` (the directory containing `package.json`) from the current working directory of the command invocation.

View File

@ -801,6 +801,15 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "fluent-uri"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1562,15 +1571,27 @@ dependencies = [
[[package]]
name = "json-patch"
version = "1.4.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec9ad60d674508f3ca8f380a928cfe7b096bc729c4e2dbfe3852bc45da3ab30b"
checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
dependencies = [
"jsonptr",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "jsonptr"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627"
dependencies = [
"fluent-uri",
"serde",
"serde_json",
]
[[package]]
name = "keyboard-types"
version = "0.7.0"
@ -3033,7 +3054,7 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
[[package]]
name = "tauri"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"anyhow",
"bytes",
@ -3083,7 +3104,7 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"anyhow",
"cargo_toml",
@ -3105,7 +3126,7 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"base64 0.22.1",
"brotli",
@ -3130,7 +3151,7 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@ -3142,7 +3163,7 @@ dependencies = [
[[package]]
name = "tauri-plugin"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"anyhow",
"glob",
@ -3168,7 +3189,7 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"dpi",
"gtk",
@ -3185,7 +3206,7 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"cocoa",
"gtk",
@ -3207,7 +3228,7 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.0.0-rc.0"
version = "2.0.0-rc.2"
dependencies = [
"aes-gcm",
"brotli",

View File

@ -36,6 +36,8 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let identifier = match options.identifier {
Some(i) => i,
None => prompts::input("What's the capability identifier?", None, false, false)?.unwrap(),

View File

@ -7,7 +7,7 @@ use std::path::Path;
use clap::Parser;
use crate::{
helpers::{app_paths::tauri_dir_opt, prompts},
helpers::{app_paths::resolve_tauri_dir, prompts},
Result,
};
@ -87,7 +87,7 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
let dir = match tauri_dir_opt() {
let dir = match resolve_tauri_dir() {
Some(t) => t,
None => std::env::current_dir()?,
};

View File

@ -21,6 +21,8 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let tauri_dir = tauri_dir();
let acl_manifests_path = tauri_dir
.join("gen")

View File

@ -8,7 +8,7 @@ use clap::Parser;
use crate::{
acl::FileFormat,
helpers::{app_paths::tauri_dir_opt, prompts},
helpers::{app_paths::resolve_tauri_dir, prompts},
Result,
};
@ -69,7 +69,7 @@ pub fn command(options: Options) -> Result<()> {
let path = match options.out {
Some(o) => o.canonicalize()?,
None => {
let dir = match tauri_dir_opt() {
let dir = match resolve_tauri_dir() {
Some(t) => t,
None => std::env::current_dir()?,
};

View File

@ -7,7 +7,7 @@ use std::path::Path;
use clap::Parser;
use tauri_utils::acl::{manifest::PermissionFile, PERMISSION_SCHEMA_FILE_NAME};
use crate::{acl::FileFormat, helpers::app_paths::tauri_dir_opt, Result};
use crate::{acl::FileFormat, helpers::app_paths::resolve_tauri_dir, Result};
fn rm_permission_files(identifier: &str, dir: &Path) -> Result<()> {
for entry in std::fs::read_dir(dir)?.flatten() {
@ -126,7 +126,7 @@ pub fn command(options: Options) -> Result<()> {
rm_permission_files(&options.identifier, &permissions_dir)?;
}
if let Some(tauri_dir) = tauri_dir_opt() {
if let Some(tauri_dir) = resolve_tauri_dir() {
let capabilities_dir = tauri_dir.join("capabilities");
if capabilities_dir.exists() {
rm_permission_from_capabilities(&options.identifier, &capabilities_dir)?;

View File

@ -9,7 +9,7 @@ use regex::Regex;
use crate::{
acl,
helpers::{
app_paths::{app_dir, tauri_dir},
app_paths::{resolve_app_dir, tauri_dir},
cargo,
npm::PackageManager,
},
@ -92,6 +92,8 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let (plugin, version) = options
.plugin
.split_once('@')
@ -105,6 +107,7 @@ pub fn command(options: Options) -> Result<()> {
let mut plugins = plugins();
let metadata = plugins.remove(plugin).unwrap_or_default();
let app_dir = resolve_app_dir();
let tauri_dir = tauri_dir();
let target_str = metadata
@ -122,14 +125,12 @@ pub fn command(options: Options) -> Result<()> {
branch: options.branch.as_deref(),
rev: options.rev.as_deref(),
tag: options.tag.as_deref(),
cwd: Some(&tauri_dir),
cwd: Some(tauri_dir),
target: target_str,
})?;
if !metadata.rust_only {
if let Some(manager) = std::panic::catch_unwind(app_dir)
.map(Some)
.unwrap_or_default()
if let Some(manager) = app_dir
.map(PackageManager::from_project)
.and_then(|managers| managers.into_iter().next())
{
@ -149,7 +150,7 @@ pub fn command(options: Options) -> Result<()> {
(None, None, None, None) => npm_name,
_ => anyhow::bail!("Only one of --tag, --rev and --branch can be specified"),
};
manager.install(&[npm_spec], &tauri_dir)?;
manager.install(&[npm_spec], tauri_dir)?;
}
let _ = acl::permission::add::command(acl::permission::add::Options {
@ -193,7 +194,7 @@ pub fn command(options: Options) -> Result<()> {
log::info!("Running `cargo fmt`...");
let _ = Command::new("cargo")
.arg("fmt")
.current_dir(&tauri_dir)
.current_dir(tauri_dir)
.status();
}

View File

@ -58,6 +58,8 @@ pub struct Options {
}
pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
crate::helpers::app_paths::resolve();
let ci = options.ci;
let target = options

View File

@ -94,6 +94,8 @@ impl From<crate::build::Options> for Options {
}
pub fn command(options: Options, verbosity: u8) -> crate::Result<()> {
crate::helpers::app_paths::resolve();
let ci = options.ci;
let target = options
@ -133,6 +135,7 @@ pub fn command(options: Options, verbosity: u8) -> crate::Result<()> {
)
}
#[allow(clippy::too_many_arguments)]
pub fn bundle<A: AppSettings>(
options: &Options,
verbosity: u8,

View File

@ -88,6 +88,8 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let r = command_internal(options);
if r.is_err() {
kill_before_dev_process();

View File

@ -18,6 +18,8 @@ use tauri_utils::{
};
const TAURI_GITIGNORE: &[u8] = include_bytes!("../../tauri.gitignore");
static APP_DIR: OnceLock<PathBuf> = OnceLock::new();
static TAURI_DIR: OnceLock<PathBuf> = OnceLock::new();
pub fn walk_builder(path: &Path) -> WalkBuilder {
let mut default_gitignore = std::env::temp_dir();
@ -66,7 +68,7 @@ fn lookup<F: Fn(&PathBuf) -> bool>(dir: &Path, checker: F) -> Option<PathBuf> {
None
}
pub fn tauri_dir_opt() -> Option<PathBuf> {
pub fn resolve_tauri_dir() -> Option<PathBuf> {
let Ok(cwd) = current_dir() else {
return None;
};
@ -100,19 +102,27 @@ pub fn tauri_dir_opt() -> Option<PathBuf> {
})
}
pub fn tauri_dir() -> PathBuf {
tauri_dir_opt().unwrap_or_else(||
pub fn resolve() {
TAURI_DIR.set(resolve_tauri_dir().unwrap_or_else(||
panic!("Couldn't recognize the current folder as a Tauri project. It must contain a `{}`, `{}` or `{}` file in any subfolder.",
ConfigFormat::Json.into_file_name(),
ConfigFormat::Json5.into_file_name(),
ConfigFormat::Toml.into_file_name()
)
)
)).expect("tauri dir already resolved");
APP_DIR
.set(resolve_app_dir().unwrap_or_else(|| tauri_dir().parent().unwrap().to_path_buf()))
.expect("app dir already resolved");
}
fn get_app_dir() -> Option<PathBuf> {
let cwd = current_dir().expect("failed to read cwd");
pub fn tauri_dir() -> &'static PathBuf {
TAURI_DIR
.get()
.expect("app paths not initialized, this is a Tauri CLI bug")
}
pub fn resolve_app_dir() -> Option<PathBuf> {
let cwd = current_dir().expect("failed to read cwd");
if cwd.join("package.json").exists() {
return Some(cwd);
}
@ -128,7 +138,7 @@ fn get_app_dir() -> Option<PathBuf> {
}
pub fn app_dir() -> &'static PathBuf {
static APP_DIR: OnceLock<PathBuf> = OnceLock::new();
APP_DIR
.get_or_init(|| get_app_dir().unwrap_or_else(|| tauri_dir().parent().unwrap().to_path_buf()))
.get()
.expect("app paths not initialized, this is a Tauri CLI bug")
}

View File

@ -132,7 +132,7 @@ fn get_internal(
let mut extensions = HashMap::new();
if let Some((platform_config, config_path)) =
tauri_utils::config::parse::read_platform(target, tauri_dir)?
tauri_utils::config::parse::read_platform(target, tauri_dir.to_path_buf())?
{
merge(&mut config, &platform_config);
extensions.insert(

View File

@ -100,7 +100,10 @@ impl Source {
pub fn command(options: Options) -> Result<()> {
let input = options.input;
let out_dir = options.output.unwrap_or_else(|| tauri_dir().join("icons"));
let out_dir = options.output.unwrap_or_else(|| {
crate::helpers::app_paths::resolve();
tauri_dir().join("icons")
});
let png_icon_sizes = options.png.unwrap_or_default();
let ios_color = css_color::Srgb::from_str(&options.ios_color)
.map(|color| {

View File

@ -7,10 +7,7 @@ use clap::Parser;
use colored::{ColoredString, Colorize};
use dialoguer::{theme::ColorfulTheme, Confirm};
use serde::Deserialize;
use std::{
fmt::{self, Display, Formatter},
panic,
};
use std::fmt::{self, Display, Formatter};
mod app;
mod env_nodejs;
@ -259,17 +256,15 @@ pub struct Options {
pub fn command(options: Options) -> Result<()> {
let Options { interactive } = options;
let hook = panic::take_hook();
panic::set_hook(Box::new(|_info| {
// do nothing
}));
let app_dir = panic::catch_unwind(crate::helpers::app_paths::app_dir)
.map(Some)
.unwrap_or_default();
let tauri_dir = panic::catch_unwind(crate::helpers::app_paths::tauri_dir)
.map(Some)
.unwrap_or_default();
panic::set_hook(hook);
let app_dir = crate::helpers::app_paths::resolve_app_dir();
let tauri_dir = crate::helpers::app_paths::resolve_tauri_dir();
if tauri_dir.is_some() {
// safe to initialize
crate::helpers::app_paths::resolve();
}
let metadata = version_metadata()?;
let mut environment = Section {
@ -289,17 +284,19 @@ pub fn command(options: Options) -> Result<()> {
};
packages
.items
.extend(packages_rust::items(app_dir, tauri_dir.as_deref()));
.extend(packages_rust::items(app_dir.as_ref(), tauri_dir.as_deref()));
packages
.items
.extend(packages_nodejs::items(app_dir, &metadata));
.extend(packages_nodejs::items(app_dir.as_ref(), &metadata));
let mut app = Section {
label: "App",
interactive,
items: Vec::new(),
};
app.items.extend(app::items(app_dir, tauri_dir.as_deref()));
app
.items
.extend(app::items(app_dir.as_ref(), tauri_dir.as_deref()));
environment.display();
packages.display();

View File

@ -905,7 +905,7 @@ impl AppSettings for RustAppSettings {
}
}
let mut bins_path = tauri_dir();
let mut bins_path = tauri_dir().to_path_buf();
bins_path.push("src/bin");
if let Ok(fs_bins) = std::fs::read_dir(bins_path) {
for entry in fs_bins {
@ -977,7 +977,7 @@ impl AppSettings for RustAppSettings {
impl RustAppSettings {
pub fn new(config: &Config, manifest: Manifest, target: Option<String>) -> crate::Result<Self> {
let cargo_settings =
CargoSettings::load(&tauri_dir()).with_context(|| "failed to load cargo settings")?;
CargoSettings::load(tauri_dir()).with_context(|| "failed to load cargo settings")?;
let cargo_package_settings = match &cargo_settings.package {
Some(package_info) => package_info.clone(),
None => {
@ -1051,7 +1051,7 @@ impl RustAppSettings {
default_run: cargo_package_settings.default_run.clone(),
};
let cargo_config = CargoConfig::load(&tauri_dir())?;
let cargo_config = CargoConfig::load(tauri_dir())?;
let target_triple = target.unwrap_or_else(|| {
cargo_config
@ -1582,6 +1582,7 @@ mod tests {
#[test]
fn parse_target_dir_from_opts() {
crate::helpers::app_paths::resolve();
let current_dir = std::env::current_dir().unwrap();
let options = Options {

View File

@ -17,9 +17,9 @@ pub fn run() -> Result<()> {
let tauri_dir = tauri_dir();
let app_dir = app_dir();
let migrated = config::migrate(&tauri_dir).context("Could not migrate config")?;
manifest::migrate(&tauri_dir).context("Could not migrate manifest")?;
frontend::migrate(app_dir, &tauri_dir)?;
let migrated = config::migrate(tauri_dir).context("Could not migrate config")?;
manifest::migrate(tauri_dir).context("Could not migrate manifest")?;
frontend::migrate(app_dir, tauri_dir)?;
// Add plugins
for plugin in migrated.plugins {

View File

@ -24,7 +24,7 @@ pub fn run() -> Result<()> {
let (mut manifest, _) = read_manifest(&manifest_path)?;
migrate_manifest(&mut manifest)?;
migrate_permissions(&tauri_dir)?;
migrate_permissions(tauri_dir)?;
migrate_npm_dependencies(app_dir)?;

View File

@ -18,6 +18,8 @@ use anyhow::Context;
mod migrations;
pub fn command() -> Result<()> {
crate::helpers::app_paths::resolve();
let tauri_dir = tauri_dir();
let manifest_contents =
@ -36,7 +38,7 @@ pub fn command() -> Result<()> {
None
};
let tauri_version = crate_version(&tauri_dir, Some(&manifest), lock.as_ref(), "tauri").version;
let tauri_version = crate_version(tauri_dir, Some(&manifest), lock.as_ref(), "tauri").version;
let tauri_version = semver::Version::from_str(&tauri_version)?;
if tauri_version.major == 1 {

View File

@ -37,6 +37,8 @@ pub struct Options {
}
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let profile = if options.release {
Profile::Release
} else {

View File

@ -86,6 +86,8 @@ impl From<Options> for BuildOptions {
}
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
crate::helpers::app_paths::resolve();
delete_codegen_vars();
let mut build_options: BuildOptions = options.clone().into();

View File

@ -91,6 +91,8 @@ impl From<Options> for DevOptions {
}
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
crate::helpers::app_paths::resolve();
let result = run_command(options, noise_level);
if result.is_err() {
crate::dev::kill_before_dev_process();

View File

@ -75,12 +75,15 @@ enum Commands {
pub fn command(cli: Cli, verbosity: u8) -> Result<()> {
let noise_level = NoiseLevel::from_occurrences(verbosity as u64);
match cli.command {
Commands::Init(options) => init_command(
MobileTarget::Android,
options.ci,
false,
options.skip_targets_install,
)?,
Commands::Init(options) => {
crate::helpers::app_paths::resolve();
init_command(
MobileTarget::Android,
options.ci,
false,
options.skip_targets_install,
)?
}
Commands::Dev(options) => dev::command(options, noise_level)?,
Commands::Build(options) => build::command(options, noise_level)?,
Commands::AndroidStudioScript(options) => android_studio_script::command(options)?,

View File

@ -117,6 +117,8 @@ impl From<Options> for BuildOptions {
}
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
crate::helpers::app_paths::resolve();
let mut build_options: BuildOptions = options.clone().into();
build_options.target = Some(
Target::all()
@ -154,7 +156,7 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
};
let tauri_path = tauri_dir();
set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?;
set_current_dir(tauri_path).with_context(|| "failed to change current working directory")?;
ensure_init(
&tauri_config,
@ -248,6 +250,7 @@ fn create_export_options(
(!plist.is_empty()).then(|| plist.into())
}
#[allow(clippy::too_many_arguments)]
fn run_build(
interface: AppInterface,
options: Options,

View File

@ -117,6 +117,8 @@ impl From<Options> for DevOptions {
}
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
crate::helpers::app_paths::resolve();
let result = run_command(options, noise_level);
if result.is_err() {
crate::dev::kill_before_dev_process();
@ -166,7 +168,7 @@ fn run_command(options: Options, noise_level: NoiseLevel) -> Result<()> {
};
let tauri_path = tauri_dir();
set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?;
set_current_dir(tauri_path).with_context(|| "failed to change current working directory")?;
ensure_init(
&tauri_config,

View File

@ -86,12 +86,15 @@ enum Commands {
pub fn command(cli: Cli, verbosity: u8) -> Result<()> {
let noise_level = NoiseLevel::from_occurrences(verbosity as u64);
match cli.command {
Commands::Init(options) => init_command(
MobileTarget::Ios,
options.ci,
options.reinstall_deps,
options.skip_targets_install,
)?,
Commands::Init(options) => {
crate::helpers::app_paths::resolve();
init_command(
MobileTarget::Ios,
options.ci,
options.reinstall_deps,
options.skip_targets_install,
)?
}
Commands::Dev(options) => dev::command(options, noise_level)?,
Commands::Build(options) => build::command(options, noise_level)?,
Commands::XcodeScript(options) => xcode_script::command(options)?,

View File

@ -66,6 +66,8 @@ pub fn command(options: Options) -> Result<()> {
set_current_dir(current_dir()?.parent().unwrap().parent().unwrap()).unwrap();
}
crate::helpers::app_paths::resolve();
let profile = profile_from_configuration(&options.configuration);
let macos = macos_from_platform(&options.platform);

View File

@ -268,7 +268,7 @@ pub fn get_app(config: &TauriConfig, interface: &AppInterface) -> App {
};
let app_settings = interface.app_settings();
App::from_raw(tauri_dir(), raw)
App::from_raw(tauri_dir().to_path_buf(), raw)
.unwrap()
.with_target_dir_resolver(move |target, profile| {
let bin_path = app_settings