mirror of
https://github.com/starship/starship.git
synced 2025-01-08 07:28:56 +01:00
Merge branch 'master' into conditional-style
This commit is contained in:
commit
e1626bd4b3
4
.github/config-schema.json
vendored
4
.github/config-schema.json
vendored
@ -5783,6 +5783,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"require_repo": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"shell": {
|
||||
"default": [],
|
||||
"allOf": [
|
||||
|
54
Cargo.lock
generated
54
Cargo.lock
generated
@ -2808,7 +2808,7 @@ dependencies = [
|
||||
"urlencoding",
|
||||
"versions",
|
||||
"which",
|
||||
"windows 0.47.0",
|
||||
"windows 0.48.0",
|
||||
"winres",
|
||||
"yaml-rust",
|
||||
]
|
||||
@ -3447,11 +3447,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.47.0",
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3508,17 +3508,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.47.0",
|
||||
"windows_aarch64_msvc 0.47.0",
|
||||
"windows_i686_gnu 0.47.0",
|
||||
"windows_i686_msvc 0.47.0",
|
||||
"windows_x86_64_gnu 0.47.0",
|
||||
"windows_x86_64_gnullvm 0.47.0",
|
||||
"windows_x86_64_msvc 0.47.0",
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3529,9 +3529,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
@ -3553,9 +3553,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
@ -3577,9 +3577,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
@ -3601,9 +3601,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
@ -3625,9 +3625,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
@ -3637,9 +3637,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
@ -3661,9 +3661,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
@ -104,7 +104,7 @@ features = ["preserve_order", "indexmap"]
|
||||
deelevate = "0.2.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_UI_Shell",
|
||||
|
@ -4346,6 +4346,7 @@ Format strings can also contain shell specific prompt sequences, e.g.
|
||||
| ------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `command` | `''` | The command whose output should be printed. The command will be passed on stdin to the shell. |
|
||||
| `when` | `false` | Either a boolean value (`true` or `false`, without quotes) or a string shell command used as a condition to show the module. In case of a string, the module will be shown if the command returns a `0` status code. |
|
||||
| `require_repo` | `false` | If `true`, the module will only be shown in paths containing a (git) repository. This option alone is not sufficient display condition in absence of other options. |
|
||||
| `shell` | | [See below](#custom-command-shell) |
|
||||
| `description` | `'<custom module>'` | The description of the module that is shown when running `starship explain`. |
|
||||
| `detect_files` | `[]` | The files that will be searched in the working directory for a match. |
|
||||
|
@ -14,6 +14,7 @@ pub struct CustomConfig<'a> {
|
||||
pub symbol: &'a str,
|
||||
pub command: &'a str,
|
||||
pub when: Either<bool, &'a str>,
|
||||
pub require_repo: bool,
|
||||
pub shell: VecOr<&'a str>,
|
||||
pub description: &'a str,
|
||||
pub style: &'a str,
|
||||
@ -38,6 +39,7 @@ impl<'a> Default for CustomConfig<'a> {
|
||||
symbol: "",
|
||||
command: "",
|
||||
when: Either::First(false),
|
||||
require_repo: false,
|
||||
shell: VecOr::default(),
|
||||
description: "<custom config>",
|
||||
style: "green bold",
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::config::{ModuleConfig, StarshipConfig};
|
||||
use crate::configs::StarshipRootConfig;
|
||||
use crate::module::Module;
|
||||
use crate::utils::{create_command, exec_timeout, read_file, CommandOutput};
|
||||
use crate::utils::{create_command, exec_timeout, read_file, CommandOutput, PathExt};
|
||||
|
||||
use crate::modules;
|
||||
use crate::utils::{self, home_dir};
|
||||
@ -248,6 +248,16 @@ impl<'a> Context<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Begins an ancestor scan at the current directory, see [`ScanAncestors`] for available
|
||||
/// methods.
|
||||
pub fn begin_ancestor_scan(&'a self) -> ScanAncestors<'a> {
|
||||
ScanAncestors {
|
||||
path: &self.current_dir,
|
||||
files: &[],
|
||||
folders: &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// Will lazily get repo root and branch when a module requests it.
|
||||
pub fn get_repo(&self) -> Result<&Repo, Box<gix::discover::Error>> {
|
||||
self.repo
|
||||
@ -607,6 +617,48 @@ impl<'a> ScanDir<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Scans the ancestors of a given path until a directory containing one of the given files or
|
||||
/// folders is found.
|
||||
pub struct ScanAncestors<'a> {
|
||||
path: &'a Path,
|
||||
files: &'a [&'a str],
|
||||
folders: &'a [&'a str],
|
||||
}
|
||||
|
||||
impl<'a> ScanAncestors<'a> {
|
||||
#[must_use]
|
||||
pub const fn set_files(mut self, files: &'a [&'a str]) -> Self {
|
||||
self.files = files;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn set_folders(mut self, folders: &'a [&'a str]) -> Self {
|
||||
self.folders = folders;
|
||||
self
|
||||
}
|
||||
|
||||
/// Scans upwards starting from the initial path until a directory containing one of the given
|
||||
/// files or folders is found.
|
||||
///
|
||||
/// The scan does not cross device boundaries.
|
||||
pub fn scan(&self) -> Option<&'a Path> {
|
||||
let initial_device_id = self.path.device_id();
|
||||
for dir in self.path.ancestors() {
|
||||
if initial_device_id != dir.device_id() {
|
||||
break;
|
||||
}
|
||||
|
||||
if self.files.iter().any(|name| dir.join(name).is_file())
|
||||
|| self.folders.iter().any(|name| dir.join(name).is_dir())
|
||||
{
|
||||
return Some(dir);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_current_branch(repository: &Repository) -> Option<String> {
|
||||
let name = repository.head_name().ok()??;
|
||||
let shorthand = name.shorten();
|
||||
|
@ -204,6 +204,29 @@ fn has_defined_credentials(
|
||||
Some(section.contains_key("aws_access_key_id"))
|
||||
}
|
||||
|
||||
// https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-settings
|
||||
fn has_source_profile(
|
||||
context: &Context,
|
||||
aws_profile: Option<&Profile>,
|
||||
aws_config: &AwsConfigFile,
|
||||
aws_creds: &AwsCredsFile,
|
||||
) -> Option<bool> {
|
||||
let config = get_config(context, aws_config)?;
|
||||
|
||||
let config_section = get_profile_config(config, aws_profile)?;
|
||||
let source_profile = config_section
|
||||
.get("source_profile")
|
||||
.map(std::borrow::ToOwned::to_owned);
|
||||
|
||||
let has_credential_process =
|
||||
has_credential_process_or_sso(context, source_profile.as_ref(), aws_config, aws_creds)
|
||||
.unwrap_or(false);
|
||||
let has_credentials =
|
||||
has_defined_credentials(context, source_profile.as_ref(), aws_creds).unwrap_or(false);
|
||||
|
||||
Some(has_credential_process || has_credentials)
|
||||
}
|
||||
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let mut module = context.new_module("aws");
|
||||
let config: AwsConfig = AwsConfig::try_load(module.config);
|
||||
@ -216,10 +239,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
// only display if credential_process is defined or has valid credentials
|
||||
// only display in the presence of credential_process, source_profile or valid credentials
|
||||
if !config.force_display
|
||||
&& !has_credential_process_or_sso(context, aws_profile.as_ref(), &aws_config, &aws_creds)
|
||||
.unwrap_or(false)
|
||||
&& !has_source_profile(context, aws_profile.as_ref(), &aws_config, &aws_creds)
|
||||
.unwrap_or(false)
|
||||
&& !has_defined_credentials(context, aws_profile.as_ref(), &aws_creds).unwrap_or(false)
|
||||
{
|
||||
return None;
|
||||
@ -1042,4 +1067,92 @@ sso_role_name = <AWS-ROLE-NAME>
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_profile_set() -> io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let config_path = dir.path().join("config");
|
||||
let credential_path = dir.path().join("credentials");
|
||||
let mut config = File::create(&config_path)?;
|
||||
config.write_all(
|
||||
"[profile astronauts]
|
||||
source_profile = starship
|
||||
"
|
||||
.as_bytes(),
|
||||
)?;
|
||||
let mut credentials = File::create(&credential_path)?;
|
||||
credentials.write_all(
|
||||
"[starship]
|
||||
aws_access_key_id=dummy
|
||||
aws_secret_access_key=dummy
|
||||
"
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
let actual = ModuleRenderer::new("aws")
|
||||
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
|
||||
.env(
|
||||
"AWS_CREDENTIALS_FILE",
|
||||
credential_path.to_string_lossy().as_ref(),
|
||||
)
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.collect();
|
||||
let expected = Some(format!(
|
||||
"on {}",
|
||||
Color::Yellow.bold().paint("☁️ astronauts ")
|
||||
));
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_profile_not_exists() -> io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let config_path = dir.path().join("config");
|
||||
let mut config = File::create(&config_path)?;
|
||||
config.write_all(
|
||||
"[profile astronauts]
|
||||
source_profile = starship
|
||||
"
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
let actual = ModuleRenderer::new("aws")
|
||||
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.collect();
|
||||
let expected = None;
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_profile_uses_credential_process() -> io::Result<()> {
|
||||
let dir = tempfile::tempdir()?;
|
||||
let config_path = dir.path().join("config");
|
||||
let mut config = File::create(&config_path)?;
|
||||
config.write_all(
|
||||
"[profile starship]
|
||||
credential_process = /opt/bin/awscreds-retriever --username starship
|
||||
|
||||
[profile astronauts]
|
||||
source_profile = starship
|
||||
"
|
||||
.as_bytes(),
|
||||
)?;
|
||||
|
||||
let actual = ModuleRenderer::new("aws")
|
||||
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
|
||||
.env("AWS_PROFILE", "astronauts")
|
||||
.collect();
|
||||
let expected = Some(format!(
|
||||
"on {}",
|
||||
Color::Yellow.bold().paint("☁️ astronauts ")
|
||||
));
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
dir.close()
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,10 @@ pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
if config.require_repo && context.get_repo().is_err() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Note: Forward config if `Module` ends up needing `config`
|
||||
let mut module = Module::new(&format!("custom.{name}"), config.description, None);
|
||||
|
||||
@ -294,7 +298,7 @@ fn handle_shell(command: &mut Command, shell: &str, shell_args: &[&str]) -> bool
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::test::ModuleRenderer;
|
||||
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
|
||||
use nu_ansi_term::Color;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
@ -721,4 +725,40 @@ mod tests {
|
||||
let expected = None;
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_require_repo_not_in() -> io::Result<()> {
|
||||
let repo_dir = tempfile::tempdir()?;
|
||||
|
||||
let actual = ModuleRenderer::new("custom.test")
|
||||
.path(repo_dir.path())
|
||||
.config(toml::toml! {
|
||||
[custom.test]
|
||||
when = true
|
||||
require_repo = true
|
||||
format = "test"
|
||||
})
|
||||
.collect();
|
||||
let expected = None;
|
||||
assert_eq!(expected, actual);
|
||||
repo_dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_require_repo_in() -> io::Result<()> {
|
||||
let repo_dir = fixture_repo(FixtureProvider::Git)?;
|
||||
|
||||
let actual = ModuleRenderer::new("custom.test")
|
||||
.path(repo_dir.path())
|
||||
.config(toml::toml! {
|
||||
[custom.test]
|
||||
when = true
|
||||
require_repo = true
|
||||
format = "test"
|
||||
})
|
||||
.collect();
|
||||
let expected = Some("test".to_string());
|
||||
assert_eq!(expected, actual);
|
||||
repo_dir.close()
|
||||
}
|
||||
}
|
||||
|
@ -22,15 +22,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
} else {
|
||||
".fslckout"
|
||||
};
|
||||
|
||||
let is_checkout = context
|
||||
.try_begin_scan()?
|
||||
// See if we're in a check-out by scanning upwards for a directory containing the checkout_db file
|
||||
context
|
||||
.begin_ancestor_scan()
|
||||
.set_files(&[checkout_db])
|
||||
.is_match();
|
||||
|
||||
if !is_checkout {
|
||||
return None;
|
||||
}
|
||||
.scan()?;
|
||||
|
||||
let len = if config.truncation_length <= 0 {
|
||||
log::warn!(
|
||||
@ -143,6 +139,18 @@ mod tests {
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_branch_subdir() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
|
||||
let checkout_dir = tempdir.path();
|
||||
expect_fossil_branch_with_config(
|
||||
&checkout_dir.join("subdir"),
|
||||
None,
|
||||
&[Expect::BranchName("topic-branch"), Expect::NoTruncation],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fossil_branch_configured() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Fossil)?;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::io::Error;
|
||||
use std::path::Path;
|
||||
|
||||
use super::utils::truncate::truncate_text;
|
||||
@ -6,7 +6,6 @@ use super::{Context, Module, ModuleConfig};
|
||||
|
||||
use crate::configs::hg_branch::HgBranchConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
use crate::modules::utils::path::PathExt;
|
||||
use crate::utils::read_file;
|
||||
|
||||
/// Creates a module with the Hg bookmark or branch in the current directory
|
||||
@ -32,7 +31,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
config.truncation_length as usize
|
||||
};
|
||||
|
||||
let repo_root = get_hg_repo_root(context).ok()?;
|
||||
let repo_root = context.begin_ancestor_scan().set_folders(&[".hg"]).scan()?;
|
||||
let branch_name = get_hg_current_bookmark(repo_root).unwrap_or_else(|_| {
|
||||
get_hg_branch_name(repo_root).unwrap_or_else(|_| String::from("default"))
|
||||
});
|
||||
@ -73,20 +72,6 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn get_hg_repo_root<'a>(ctx: &'a Context) -> Result<&'a Path, Error> {
|
||||
let dir = ctx.current_dir.as_path();
|
||||
let dev_id = dir.device_id();
|
||||
for root_dir in dir.ancestors() {
|
||||
if dev_id != root_dir.device_id() {
|
||||
break;
|
||||
}
|
||||
if root_dir.join(".hg").is_dir() {
|
||||
return Ok(root_dir);
|
||||
}
|
||||
}
|
||||
Err(Error::new(ErrorKind::Other, "No .hg found!"))
|
||||
}
|
||||
|
||||
fn get_hg_branch_name(hg_root: &Path) -> Result<String, Error> {
|
||||
match read_file(hg_root.join(".hg").join("branch")) {
|
||||
Ok(b) => Ok(b.trim().to_string()),
|
||||
|
@ -81,24 +81,28 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the output of `pulumi version` into just the version string.
|
||||
/// Parse and sanitize the output of `pulumi version` into just the version string.
|
||||
///
|
||||
/// Normally, this just means returning it. When Pulumi is being developed, it
|
||||
/// can return results like `3.12.0-alpha.1630554544+f89e9a29.dirty`, which we
|
||||
/// don't want to see. Instead we display that as `3.12.0-alpha`.
|
||||
fn parse_version(version: &str) -> &str {
|
||||
let new_version = version.strip_prefix('v').unwrap_or(version);
|
||||
|
||||
let sanitized_version = new_version.trim_end();
|
||||
|
||||
let mut periods = 0;
|
||||
for (i, c) in version.as_bytes().iter().enumerate() {
|
||||
for (i, c) in sanitized_version.as_bytes().iter().enumerate() {
|
||||
if *c == b'.' {
|
||||
if periods == 2 {
|
||||
return &version[0..i];
|
||||
return &sanitized_version[0..i];
|
||||
} else {
|
||||
periods += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We didn't hit 3 periods, so we just return the whole string.
|
||||
version
|
||||
sanitized_version
|
||||
}
|
||||
|
||||
/// Find a file describing a Pulumi package in the current directory (or any parent directory).
|
||||
@ -212,20 +216,53 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pulumi_version_release() {
|
||||
let input = "3.12.0";
|
||||
assert_eq!(parse_version(input), input);
|
||||
let expected = "3.12.0";
|
||||
let inputs: [&str; 6] = [
|
||||
"v3.12.0\r\n",
|
||||
"v3.12.0\n",
|
||||
"v3.12.0",
|
||||
"3.12.0\r\n",
|
||||
"3.12.0\n",
|
||||
"3.12.0",
|
||||
];
|
||||
|
||||
for input in inputs.iter() {
|
||||
assert_eq!(parse_version(input), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pulumi_version_prerelease() {
|
||||
let input = "3.12.0-alpha";
|
||||
assert_eq!(parse_version(input), input);
|
||||
let expected = "3.12.0-alpha";
|
||||
let inputs: [&str; 6] = [
|
||||
"v3.12.0-alpha\r\n",
|
||||
"v3.12.0-alpha\n",
|
||||
"v3.12.0-alpha",
|
||||
"3.12.0-alpha\r\n",
|
||||
"3.12.0-alpha\n",
|
||||
"3.12.0-alpha",
|
||||
];
|
||||
|
||||
for input in inputs.iter() {
|
||||
assert_eq!(parse_version(input), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pulumi_version_dirty() {
|
||||
let input = "3.12.0-alpha.1630554544+f89e9a29.dirty";
|
||||
assert_eq!(parse_version(input), "3.12.0-alpha");
|
||||
let expected = "3.12.0-alpha";
|
||||
let inputs: [&str; 6] = [
|
||||
"v3.12.0-alpha.1630554544+f89e9a29.dirty\r\n",
|
||||
"v3.12.0-alpha.1630554544+f89e9a29.dirty\n",
|
||||
"v3.12.0-alpha.1630554544+f89e9a29.dirty",
|
||||
"3.12.0-alpha.1630554544+f89e9a29.dirty\r\n",
|
||||
"3.12.0-alpha.1630554544+f89e9a29.dirty\n",
|
||||
"3.12.0-alpha.1630554544+f89e9a29.dirty",
|
||||
];
|
||||
|
||||
for input in inputs.iter() {
|
||||
assert_eq!(parse_version(input), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -13,8 +13,6 @@ pub trait PathExt {
|
||||
/// E.g. `\\?\UNC\server\share\foo` => `\foo`
|
||||
/// E.g. `/foo/bar` => `/foo/bar`
|
||||
fn without_prefix(&self) -> &Path;
|
||||
/// Get device / volume info
|
||||
fn device_id(&self) -> Option<u64>;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
@ -82,11 +80,6 @@ impl PathExt for Path {
|
||||
let (_, path) = normalize::normalize_path(self);
|
||||
path
|
||||
}
|
||||
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
// Maybe it should use unimplemented!
|
||||
Some(42u64)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Windows path prefixes are only parsed on Windows.
|
||||
@ -107,24 +100,6 @@ impl PathExt for Path {
|
||||
fn without_prefix(&self) -> &Path {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
match self.metadata() {
|
||||
Ok(m) => Some(m.st_dev()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
match self.metadata() {
|
||||
Ok(m) => Some(m.dev()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -182,6 +182,7 @@ pub fn fixture_repo(provider: FixtureProvider) -> io::Result<TempDir> {
|
||||
".fslckout"
|
||||
};
|
||||
let path = tempfile::tempdir()?;
|
||||
fs::create_dir(path.path().join("subdir"))?;
|
||||
fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
|
34
src/utils.rs
34
src/utils.rs
@ -644,6 +644,40 @@ pub fn encode_to_hex(slice: &[u8]) -> String {
|
||||
String::from_utf8(dst).unwrap()
|
||||
}
|
||||
|
||||
pub trait PathExt {
|
||||
/// Get device / volume info
|
||||
fn device_id(&self) -> Option<u64>;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl PathExt for Path {
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
// Maybe it should use unimplemented!
|
||||
Some(42u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl PathExt for Path {
|
||||
#[cfg(target_os = "linux")]
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
match self.metadata() {
|
||||
Ok(m) => Some(m.st_dev()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "linux")))]
|
||||
fn device_id(&self) -> Option<u64> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
match self.metadata() {
|
||||
Ok(m) => Some(m.dev()),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
Loading…
Reference in New Issue
Block a user