Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add options for package manager #3328

Merged
merged 13 commits into from
Oct 27, 2024
13 changes: 11 additions & 2 deletions cli/src/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use semver::{Version, VersionReq};

use crate::{
config::{Config, Manifest, WithPath},
VERSION,
PackageManager, VERSION,
};

/// Check whether `overflow-checks` codegen option is enabled.
Expand Down Expand Up @@ -69,12 +69,21 @@ pub fn check_anchor_version(cfg: &WithPath<Config>) -> Result<()> {
.and_then(|ver| VersionReq::parse(ver).ok())
.filter(|ver| !ver.matches(&cli_version));

let update_cmd = match &cfg.toolchain.package_manager {
Some(pkg_manager) => match pkg_manager {
PackageManager::NPM => "npm update",
PackageManager::Yarn => "yarn upgrade",
PackageManager::PNPM => "pnpm update",
},
None => "npm update",
};

if let Some(ver) = mismatched_ts_version {
eprintln!(
"WARNING: `@coral-xyz/anchor` version({ver}) and the current CLI version\
({cli_version}) don't match.\n\n\t\
This can lead to unwanted behavior. To fix, upgrade the package by running:\n\n\t\
yarn upgrade @coral-xyz/anchor@{cli_version}\n"
{update_cmd} @coral-xyz/anchor@{cli_version}\n"
);
}

Expand Down
3 changes: 2 additions & 1 deletion cli/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{get_keypair, is_hidden, keys_sync};
use crate::{get_keypair, is_hidden, keys_sync, PackageManager};
use anchor_client::Cluster;
use anchor_lang_idl::types::Idl;
use anyhow::{anyhow, bail, Context, Error, Result};
Expand Down Expand Up @@ -380,6 +380,7 @@ pub struct Config {
pub struct ToolchainConfig {
pub anchor_version: Option<String>,
pub solana_version: Option<String>,
pub package_manager: Option<PackageManager>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
84 changes: 74 additions & 10 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use anchor_lang_idl::convert::convert_idl;
use anchor_lang_idl::types::{Idl, IdlArrayLen, IdlDefinedFields, IdlType, IdlTypeDefTy};
use anyhow::{anyhow, Context, Result};
use checks::{check_anchor_version, check_deps, check_idl_build_feature, check_overflow};
use clap::{CommandFactory, Parser};
use clap::{CommandFactory, Parser, ValueEnum};
use dirs::home_dir;
use flate2::read::GzDecoder;
use flate2::read::ZlibDecoder;
Expand All @@ -22,7 +22,7 @@ use reqwest::blocking::multipart::{Form, Part};
use reqwest::blocking::Client;
use rust_template::{ProgramTemplate, TestTemplate};
use semver::{Version, VersionReq};
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value as JsonValue};
use solana_client::rpc_client::RpcClient;
use solana_program::instruction::{AccountMeta, Instruction};
Expand Down Expand Up @@ -82,6 +82,9 @@ pub enum Command {
/// Don't install JavaScript dependencies
#[clap(long)]
no_install: bool,
/// Package Manager to use
#[clap(value_enum, long, default_value = "npm")]
arihantbansal marked this conversation as resolved.
Show resolved Hide resolved
package_manager: PackageManager,
/// Don't initialize git
#[clap(long)]
no_git: bool,
Expand Down Expand Up @@ -526,6 +529,45 @@ pub enum ClusterCommand {
List,
}

/// Package manager to use for the project.
#[derive(Clone, Debug, Default, Eq, PartialEq, Parser, ValueEnum, Serialize, Deserialize)]
pub enum PackageManager {
arihantbansal marked this conversation as resolved.
Show resolved Hide resolved
/// Use npm as the package manager.
#[default]
NPM,
arihantbansal marked this conversation as resolved.
Show resolved Hide resolved
/// Use yarn as the package manager.
Yarn,
/// Use pnpm as the package manager.
PNPM,
}

impl FromStr for PackageManager {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<PackageManager> {
match s {
"npm" => Ok(PackageManager::NPM),
"yarn" => Ok(PackageManager::Yarn),
"pnpm" => Ok(PackageManager::PNPM),
_ => Err(anyhow!(
"Package manager should be one of [npm, yarn, pnpm]\n"
)),
}
}
}
arihantbansal marked this conversation as resolved.
Show resolved Hide resolved

impl std::fmt::Display for PackageManager {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let pkg_manager_str = match self {
PackageManager::NPM => "npm",
PackageManager::Yarn => "yarn",
PackageManager::PNPM => "pnpm",
};

write!(f, "{pkg_manager_str}")
}
}

fn get_keypair(path: &str) -> Result<Keypair> {
solana_sdk::signature::read_keypair_file(path)
.map_err(|_| anyhow!("Unable to read keypair file ({path})"))
Expand Down Expand Up @@ -756,6 +798,7 @@ fn process_command(opts: Opts) -> Result<()> {
javascript,
solidity,
no_install,
package_manager,
no_git,
template,
test_template,
Expand All @@ -766,6 +809,7 @@ fn process_command(opts: Opts) -> Result<()> {
javascript,
solidity,
no_install,
package_manager,
no_git,
template,
test_template,
Expand Down Expand Up @@ -952,6 +996,7 @@ fn init(
javascript: bool,
solidity: bool,
no_install: bool,
package_manager: PackageManager,
no_git: bool,
template: ProgramTemplate,
test_template: TestTemplate,
Expand Down Expand Up @@ -990,9 +1035,12 @@ fn init(
fs::create_dir_all("app")?;

let mut cfg = Config::default();
let test_script = test_template.get_test_script(javascript);
cfg.scripts
.insert("test".to_owned(), test_script.to_owned());

let package_manager_cmd = package_manager.to_string();
let test_script = test_template.get_test_script(javascript, &package_manager);
cfg.scripts.insert("test".to_owned(), test_script);

cfg.toolchain.package_manager = Some(package_manager);
arihantbansal marked this conversation as resolved.
Show resolved Hide resolved

let mut localnet = BTreeMap::new();
let program_id = rust_template::get_or_create_program_id(&rust_name);
Expand Down Expand Up @@ -1064,10 +1112,18 @@ fn init(
)?;

if !no_install {
let yarn_result = install_node_modules("yarn")?;
if !yarn_result.status.success() {
println!("Failed yarn install will attempt to npm install");
install_node_modules("npm")?;
if package_manager_cmd != PackageManager::NPM.to_string() {
let package_manager_result = install_node_modules(&package_manager_cmd)?;

if !package_manager_result.status.success() {
println!(
"Failed {} install will attempt to npm install",
package_manager_cmd
);
install_node_modules("npm")?;
}
} else {
install_node_modules(&package_manager_cmd)?;
arihantbansal marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -4124,7 +4180,12 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
rust_template::deploy_ts_script_host(&url, &module_path.display().to_string());
fs::write(deploy_ts, deploy_script_host_str)?;

std::process::Command::new("yarn")
let pkg_manager_cmd = match &cfg.toolchain.package_manager {
Some(pkg_manager) => pkg_manager.to_string(),
None => PackageManager::default().to_string(),
};

std::process::Command::new(pkg_manager_cmd)
.args([
"run",
"ts-node",
Expand Down Expand Up @@ -4812,6 +4873,7 @@ mod tests {
true,
false,
true,
PackageManager::default(),
false,
ProgramTemplate::default(),
TestTemplate::default(),
Expand All @@ -4832,6 +4894,7 @@ mod tests {
true,
false,
true,
PackageManager::default(),
false,
ProgramTemplate::default(),
TestTemplate::default(),
Expand All @@ -4852,6 +4915,7 @@ mod tests {
true,
false,
true,
PackageManager::default(),
false,
ProgramTemplate::default(),
TestTemplate::default(),
Expand Down
26 changes: 16 additions & 10 deletions cli/src/rust_template.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
config::ProgramWorkspace, create_files, override_or_create_files, solidity_template, Files,
VERSION,
PackageManager, VERSION,
};
use anyhow::Result;
use clap::{Parser, ValueEnum};
Expand Down Expand Up @@ -401,7 +401,7 @@ pub fn ts_package_json(jest: bool, license: String) -> String {
if jest {
format!(
r#"{{
"license": "{license}",
"license": "{license}",
"scripts": {{
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
Expand All @@ -423,7 +423,7 @@ pub fn ts_package_json(jest: bool, license: String) -> String {
} else {
format!(
r#"{{
"license": "{license}",
"license": "{license}",
"scripts": {{
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
Expand Down Expand Up @@ -607,30 +607,36 @@ pub enum TestTemplate {
/// Generate template for Mocha unit-test
#[default]
Mocha,
/// Generate template for Jest unit-test
/// Generate template for Jest unit-test
Jest,
/// Generate template for Rust unit-test
Rust,
}

impl TestTemplate {
pub fn get_test_script(&self, js: bool) -> &str {
pub fn get_test_script(&self, js: bool, pkg_manager: &PackageManager) -> String {
let pkg_manager_exec_cmd = match pkg_manager {
PackageManager::Yarn => "yarn run",
PackageManager::NPM => "npx",
PackageManager::PNPM => "pnpm exec",
};

match &self {
Self::Mocha => {
if js {
"yarn run mocha -t 1000000 tests/"
format!("{pkg_manager_exec_cmd} mocha -t 1000000 tests/")
} else {
"yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
format!("{pkg_manager_exec_cmd} ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts")
}
}
Self::Jest => {
if js {
"yarn run jest"
format!("{pkg_manager_exec_cmd} jest")
} else {
"yarn run jest --preset ts-jest"
format!("{pkg_manager_exec_cmd} jest --preset ts-jest")
}
}
Self::Rust => "cargo test",
Self::Rust => "cargo test".to_owned(),
}
}

Expand Down
Loading