Skip to content

Commit

Permalink
feat: tman install local_path support (#644)
Browse files Browse the repository at this point in the history
* feat: tman install local_path support

* feat: tman install local_path support

* feat: tman install local_path support

* fix: refine codes

* fix: refine codes
  • Loading branch information
halajohn authored Feb 4, 2025
1 parent b503cdc commit 807a5cb
Show file tree
Hide file tree
Showing 20 changed files with 225 additions and 47 deletions.
200 changes: 179 additions & 21 deletions core/src/ten_manager/src/cmd/cmd_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use console::Emoji;
use indicatif::HumanDuration;
use inquire::Confirm;

use semver::VersionReq;
use ten_rust::pkg_info::{
constants::{BUILD_GN_FILENAME, MANIFEST_JSON_FILENAME},
pkg_basic_info::PkgBasicInfo,
Expand Down Expand Up @@ -84,6 +85,10 @@ pub struct InstallCommand {
pub support: PkgSupport,
pub local_install_mode: LocalInstallMode,
pub standalone: bool,

/// When the user only inputs a single path parameter, if a `manifest.json`
/// exists under that path, it indicates installation from a local path.
pub local_path: Option<String>,
}

pub fn create_sub_cmd(args_cfg: &crate::cmd_line::ArgsCfg) -> Command {
Expand All @@ -92,10 +97,7 @@ pub fn create_sub_cmd(args_cfg: &crate::cmd_line::ArgsCfg) -> Command {
.arg(
Arg::new("PACKAGE_TYPE")
.help("The type of the package")
.value_parser(args_cfg.pkg_type.possible_values.clone())
.required(false)
// If PACKAGE_TYPE is provided, PACKAGE_NAME must be too.
.requires("PACKAGE_NAME"),
.required(false),
)
.arg(
Arg::new("PACKAGE_NAME")
Expand Down Expand Up @@ -135,8 +137,8 @@ pub fn create_sub_cmd(args_cfg: &crate::cmd_line::ArgsCfg) -> Command {

pub fn parse_sub_cmd(sub_cmd_args: &ArgMatches) -> Result<InstallCommand> {
let mut cmd = InstallCommand {
package_type: sub_cmd_args.get_one::<String>("PACKAGE_TYPE").cloned(),
package_name: sub_cmd_args.get_one::<String>("PACKAGE_NAME").cloned(),
package_type: None,
package_name: None,
support: PkgSupport {
os: sub_cmd_args
.get_one::<String>("OS")
Expand All @@ -147,9 +149,64 @@ pub fn parse_sub_cmd(sub_cmd_args: &ArgMatches) -> Result<InstallCommand> {
},
local_install_mode: LocalInstallMode::Invalid,
standalone: false,
local_path: None,
};

let _ = cmd.support.set_defaults();
// Retrieve the first positional parameter (in the `PACKAGE_TYPE`
// parameter).
if let Some(first_arg) = sub_cmd_args.get_one::<String>("PACKAGE_TYPE") {
let first_arg_str = first_arg.as_str();

// Determine whether it is an absolute path or a relative path starting
// with `./` or `../`.
let is_path = {
let path = std::path::Path::new(first_arg_str);
path.is_absolute()
|| first_arg_str.starts_with("./")
|| first_arg_str.starts_with("../")
};

if is_path {
// If it is a path, treat it as `local_path` mode, and
// `package_type` and `package_name` will no longer be needed
// afterward.
cmd.local_path = Some(first_arg.clone());

if sub_cmd_args.get_one::<String>("PACKAGE_NAME").is_some() {
return Err(anyhow::anyhow!(
"PACKAGE_NAME is not required when a local path is specified."
));
}
} else {
// Otherwise, consider it as `package_type`, and `package_name` must
// be provided as well.

// Check if PACKAGE_TYPE is an allowed value.
let allowed_package_types: &[&str] =
&["system", "protocol", "addon_loader", "extension"];
if !allowed_package_types
.contains(&first_arg_str.to_lowercase().as_str())
{
return Err(anyhow::anyhow!(
"Invalid PACKAGE_TYPE: {}. Allowed values are: {}",
first_arg_str,
allowed_package_types.join(", ")
));
}

cmd.package_type = Some(first_arg.clone());

if let Some(second_arg) =
sub_cmd_args.get_one::<String>("PACKAGE_NAME")
{
cmd.package_name = Some(second_arg.clone());
} else {
return Err(anyhow::anyhow!(
"PACKAGE_NAME is required when PACKAGE_TYPE is specified."
));
}
}
}

if let Some(mode_str) = sub_cmd_args.get_one::<String>("LOCAL_INSTALL_MODE")
{
Expand Down Expand Up @@ -363,11 +420,88 @@ pub async fn execute_cmd(
&command_data.support,
);

if let Some(package_type_str) = command_data.package_type.as_ref() {
// tman install <package_type> <package_name>
//
// The `cwd` must be the base directory of a TEN app.
if command_data.local_path.is_some() {
// tman install <local_path>

// Parse the `manifest.json` inside the local_path.
let local_path_str = command_data.local_path.clone().unwrap();
let local_path = Path::new(&local_path_str);
let local_path = local_path.canonicalize().with_context(|| {
format!(
"Failed to find the specified local path {}",
local_path_str
)
})?;

let local_manifest_dir = if local_path.is_dir() {
local_path.clone()
} else {
return Err(anyhow!(
"The specified local path is not a folder.: {}",
local_path_str
));
};

let manifest_json_path =
local_manifest_dir.join(MANIFEST_JSON_FILENAME);
if !manifest_json_path.exists() {
return Err(anyhow!(
"No manifest.json found in the specified local path: {}",
local_path_str
));
}

// Read the `manifest.json` of the local package to obtain its `type`,
// `name`, and `version`.
let local_pkg_info =
get_pkg_info_from_path(&local_manifest_dir, false)?;

installing_pkg_type =
Some(local_pkg_info.basic_info.type_and_name.pkg_type);
installing_pkg_name =
Some(local_pkg_info.basic_info.type_and_name.name.clone());
let installing_pkg_version_req =
VersionReq::parse(&local_pkg_info.basic_info.version.to_string())?;

dep_relationship_from_cmd_line = Some(DependencyRelationship {
type_and_name: PkgTypeAndName {
pkg_type: app_pkg_to_work_with
.basic_info
.type_and_name
.pkg_type,
name: app_pkg_to_work_with
.basic_info
.type_and_name
.name
.clone(),
},
version: app_pkg_to_work_with.basic_info.version.clone(),
dependency: PkgDependency {
type_and_name: PkgTypeAndName {
pkg_type: installing_pkg_type.unwrap(),
name: installing_pkg_name.clone().unwrap(),
},
version_req: installing_pkg_version_req,
path: Some(local_path_str.clone()),
base_dir: Some("".to_string()),
},
});

// The `local_pkg_info` needs to be used as a candidate.
let mut local_pkg_clone = local_pkg_info.clone();

local_pkg_clone.is_local_dependency = true;
local_pkg_clone.local_dependency_path = Some(local_path_str.clone());
local_pkg_clone.local_dependency_base_dir = Some("".to_string());

all_candidates
.entry((&local_pkg_clone).into())
.or_default()
.insert((&local_pkg_clone).into(), local_pkg_clone.clone());

initial_pkgs_to_find_candidates.push(local_pkg_clone);
} else if let Some(package_type_str) = command_data.package_type.as_ref() {
// tman install <package_type> <package_name>
let installing_pkg_type_: PkgType = package_type_str.parse()?;

let (installing_pkg_name_, installing_pkg_version_req_) =
Expand Down Expand Up @@ -514,16 +648,40 @@ pub async fn execute_cmd(
.await?;

// Write the installing package info to manifest.json.
if installing_pkg_type.is_some() && installing_pkg_name.is_some() {
let mut origin_cwd_pkg =
get_pkg_info_from_path(&original_cwd, true)?;

write_installing_pkg_into_manifest_file(
&mut origin_cwd_pkg,
&solver_results,
&installing_pkg_type.unwrap(),
&installing_pkg_name.unwrap(),
)?;
//
// If it's in `local_path` mode, update `manifest.json` by writing `{
// "path": "<local_path>" }`. Otherwise, write `type`, `name`, and
// `version`.
if command_data.local_path.is_some() {
// tman install <local_path>

if installing_pkg_type.is_some() && installing_pkg_name.is_some() {
let mut origin_cwd_pkg =
get_pkg_info_from_path(&original_cwd, true)?;

write_installing_pkg_into_manifest_file(
&mut origin_cwd_pkg,
&solver_results,
&installing_pkg_type.unwrap(),
&installing_pkg_name.unwrap(),
Some(command_data.local_path.clone().unwrap()),
)?;
}
} else {
// tman install <package_type> <package_name>

if installing_pkg_type.is_some() && installing_pkg_name.is_some() {
let mut origin_cwd_pkg =
get_pkg_info_from_path(&original_cwd, true)?;

write_installing_pkg_into_manifest_file(
&mut origin_cwd_pkg,
&solver_results,
&installing_pkg_type.unwrap(),
&installing_pkg_name.unwrap(),
None,
)?;
}
}

// Change back to the original folder.
Expand Down
31 changes: 27 additions & 4 deletions core/src/ten_manager/src/install/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ pub async fn install_pkg_info(
fn update_package_manifest(
base_pkg_info: &mut PkgInfo,
added_dependency: &PkgInfo,
// If `Some(...)` is passed in, it indicates `local_path` mode.
local_path_if_any: Option<String>,
) -> Result<()> {
if let Some(ref mut dependencies) =
base_pkg_info.manifest.as_mut().unwrap().dependencies
Expand Down Expand Up @@ -306,13 +308,32 @@ fn update_package_manifest(
// If the added dependency does not exist in the `manifest.json`, add
// it.
if !is_present {
dependencies.push(added_dependency.into());
// If `local_path_if_any` has a value, write it as `{ path:
// "<local_path>" }`.
if let Some(local_path) = local_path_if_any {
dependencies.push(ManifestDependency::LocalDependency {
path: local_path,
base_dir: "".to_string(),
});
} else {
dependencies.push(added_dependency.into());
}
}
} else {
// If the `manifest.json` does not have a `dependencies` field, add the
// dependency directly.
base_pkg_info.manifest.clone().unwrap().dependencies =
Some(vec![added_dependency.into()]);
let mut new_deps = vec![];

if let Some(local_path) = local_path_if_any {
new_deps.push(ManifestDependency::LocalDependency {
path: local_path,
base_dir: "".to_string(),
});
} else {
new_deps.push(added_dependency.into());
}

base_pkg_info.manifest.as_mut().unwrap().dependencies = Some(new_deps);
}

let manifest_path: PathBuf =
Expand Down Expand Up @@ -355,6 +376,8 @@ pub fn write_installing_pkg_into_manifest_file(
solver_results: &[PkgInfo],
pkg_type: &PkgType,
pkg_name: &String,
// If `Some(...)` is passed in, it indicates `local_path` mode.
local_path_if_any: Option<String>,
) -> Result<()> {
let suitable_pkgs = filter_solver_results_by_type_and_name(
solver_results,
Expand All @@ -379,7 +402,7 @@ pub fn write_installing_pkg_into_manifest_file(
));
}

update_package_manifest(pkg_info, suitable_pkgs[0])?;
update_package_manifest(pkg_info, suitable_pkgs[0], local_path_if_any)?;

Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1000,10 +1000,7 @@ class test_app : public ten::app_t {
R"({
"_ten": {
"uri": "msgpack://127.0.0.1:8001/",
"log_level": 2,
"telemetry": {
"enabled": true
}
"log_level": 2
}
})"
// clang-format on
Expand Down Expand Up @@ -1032,7 +1029,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest,
TEST(OuterThreadTest,
OneHundredAndTwentyEightThreadsAttemptToSuspend1) { // NOLINT
// Start app.
auto *app_thread =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, SixteenThreadsAttemptToSuspend1) { // NOLINT
TEST(OuterThreadTest, SixteenThreadsAttemptToSuspend1) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, TwoThreadsAttemptToSuspend1) { // NOLINT
TEST(OuterThreadTest, TwoThreadsAttemptToSuspend1) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, TwoThreadsAttemptToSuspend2) { // NOLINT
TEST(OuterThreadTest, TwoThreadsAttemptToSuspend2) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, TwoThreadsAttemptToSuspend3) { // NOLINT
TEST(OuterThreadTest, TwoThreadsAttemptToSuspend3) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, TwoThreadsAttemptToSuspend4) { // NOLINT
TEST(OuterThreadTest, TwoThreadsAttemptToSuspend4) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, TwoThreadsAttemptToSuspend5) { // NOLINT
TEST(OuterThreadTest, TwoThreadsAttemptToSuspend5) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ TEN_CPP_REGISTER_ADDON_AS_EXTENSION(

} // namespace

TEST(ExtensionTest, TwoThreadsAttemptToSuspend6) { // NOLINT
TEST(OuterThreadTest, TwoThreadsAttemptToSuspend6) { // NOLINT
// Start app.
auto *app_thread =
ten_thread_create("app thread", test_app_thread_main, nullptr);
Expand Down
Loading

0 comments on commit 807a5cb

Please sign in to comment.