cli: Fix Cargo.lock in workspace subdirectories when publishing (#593)
This commit is contained in:
parent
80e4030e45
commit
bd68e28f15
|
@ -15,6 +15,10 @@ incremented for features.
|
|||
|
||||
* cli: Programs embedded into genesis during tests will produce program logs.
|
||||
|
||||
### Fixes
|
||||
|
||||
* cli: Allows Cargo.lock to exist in workspace subdirectories when publishing ([#593](https://github.com/project-serum/anchor/pull/593)).
|
||||
|
||||
## [0.13.0] - 2021-08-08
|
||||
|
||||
### Features
|
||||
|
|
|
@ -159,6 +159,7 @@ dependencies = [
|
|||
"tar",
|
||||
"tokio 1.4.0",
|
||||
"toml",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -36,3 +36,4 @@ reqwest = { version = "0.11.4", features = ["multipart", "blocking"] }
|
|||
tokio = "1.0"
|
||||
pathdiff = "0.2.0"
|
||||
cargo_toml = "0.9.2"
|
||||
walkdir = "2"
|
|
@ -9,6 +9,7 @@ use std::collections::BTreeMap;
|
|||
use std::convert::TryFrom;
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::ops::Deref;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
@ -48,6 +49,68 @@ impl<T> std::convert::AsRef<T> for WithPath<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Manifest(cargo_toml::Manifest);
|
||||
|
||||
impl Manifest {
|
||||
pub fn from_path(p: impl AsRef<Path>) -> Result<Self> {
|
||||
cargo_toml::Manifest::from_path(p)
|
||||
.map(Manifest)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn lib_name(&self) -> Result<String> {
|
||||
if self.lib.is_some() && self.lib.as_ref().unwrap().name.is_some() {
|
||||
Ok(self
|
||||
.lib
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string())
|
||||
} else {
|
||||
Ok(self
|
||||
.package
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("package section not provided"))?
|
||||
.name
|
||||
.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// Climbs each parent directory until we find a Cargo.toml.
|
||||
pub fn discover() -> Result<Option<WithPath<Manifest>>> {
|
||||
let _cwd = std::env::current_dir()?;
|
||||
let mut cwd_opt = Some(_cwd.as_path());
|
||||
|
||||
while let Some(cwd) = cwd_opt {
|
||||
for f in fs::read_dir(cwd)? {
|
||||
let p = f?.path();
|
||||
if let Some(filename) = p.file_name() {
|
||||
if filename.to_str() == Some("Cargo.toml") {
|
||||
let m = WithPath::new(Manifest::from_path(&p)?, p);
|
||||
return Ok(Some(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found. Go up a directory level.
|
||||
cwd_opt = cwd.parent();
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Manifest {
|
||||
type Target = cargo_toml::Manifest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl WithPath<Config> {
|
||||
pub fn get_program_list(&self) -> Result<Vec<PathBuf>> {
|
||||
// Canonicalize the workspace filepaths to compare with relative paths.
|
||||
|
@ -82,7 +145,7 @@ impl WithPath<Config> {
|
|||
let mut r = vec![];
|
||||
for path in self.get_program_list()? {
|
||||
let idl = anchor_syn::idl::file::parse(path.join("src/lib.rs"))?;
|
||||
let lib_name = extract_lib_name(&path.join("Cargo.toml"))?;
|
||||
let lib_name = Manifest::from_path(&path.join("Cargo.toml"))?.lib_name()?;
|
||||
r.push(Program {
|
||||
lib_name,
|
||||
path,
|
||||
|
@ -97,16 +160,53 @@ impl WithPath<Config> {
|
|||
.workspace
|
||||
.members
|
||||
.iter()
|
||||
.map(|m| PathBuf::from(m).canonicalize().unwrap())
|
||||
.map(|m| {
|
||||
self.path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(m)
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
let exclude = self
|
||||
.workspace
|
||||
.exclude
|
||||
.iter()
|
||||
.map(|m| PathBuf::from(m).canonicalize().unwrap())
|
||||
.map(|m| {
|
||||
self.path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(m)
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
Ok((members, exclude))
|
||||
}
|
||||
|
||||
pub fn get_program(&self, name: &str) -> Result<Option<WithPath<Program>>> {
|
||||
for program in self.read_all_programs()? {
|
||||
let cargo_toml = program.path.join("Cargo.toml");
|
||||
if !cargo_toml.exists() {
|
||||
return Err(anyhow!(
|
||||
"Did not find Cargo.toml at the path: {}",
|
||||
program.path.display()
|
||||
));
|
||||
}
|
||||
let p_lib_name = Manifest::from_path(&cargo_toml)?.lib_name()?;
|
||||
if name == p_lib_name {
|
||||
let path = self
|
||||
.path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.canonicalize()?
|
||||
.join(&program.path);
|
||||
return Ok(Some(WithPath::new(program, path)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for WithPath<T> {
|
||||
|
@ -174,71 +274,51 @@ impl Config {
|
|||
format!("projectserum/build:v{}", ver)
|
||||
}
|
||||
|
||||
pub fn discover(
|
||||
cfg_override: &ConfigOverride,
|
||||
) -> Result<Option<(WithPath<Config>, Option<PathBuf>)>> {
|
||||
pub fn discover(cfg_override: &ConfigOverride) -> Result<Option<WithPath<Config>>> {
|
||||
Config::_discover().map(|opt| {
|
||||
opt.map(|(mut cfg, cargo_toml)| {
|
||||
opt.map(|mut cfg| {
|
||||
if let Some(cluster) = cfg_override.cluster.clone() {
|
||||
cfg.provider.cluster = cluster;
|
||||
}
|
||||
|
||||
if let Some(wallet) = cfg_override.wallet.clone() {
|
||||
cfg.provider.wallet = wallet;
|
||||
}
|
||||
(cfg, cargo_toml)
|
||||
cfg
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Searches all parent directories for an Anchor.toml file.
|
||||
fn _discover() -> Result<Option<(WithPath<Config>, Option<PathBuf>)>> {
|
||||
// Set to true if we ever see a Cargo.toml file when traversing the
|
||||
// parent directories.
|
||||
let mut cargo_toml = None;
|
||||
|
||||
// Climbs each parent directory until we find an Anchor.toml.
|
||||
fn _discover() -> Result<Option<WithPath<Config>>> {
|
||||
let _cwd = std::env::current_dir()?;
|
||||
let mut cwd_opt = Some(_cwd.as_path());
|
||||
|
||||
while let Some(cwd) = cwd_opt {
|
||||
let files = fs::read_dir(cwd)?;
|
||||
// Cargo.toml file for this directory level.
|
||||
let mut cargo_toml_level = None;
|
||||
let mut anchor_toml = None;
|
||||
for f in files {
|
||||
for f in fs::read_dir(cwd)? {
|
||||
let p = f?.path();
|
||||
if let Some(filename) = p.file_name() {
|
||||
if filename.to_str() == Some("Cargo.toml") {
|
||||
cargo_toml_level = Some(p);
|
||||
} else if filename.to_str() == Some("Anchor.toml") {
|
||||
let mut cfg_file = File::open(&p)?;
|
||||
let mut cfg_contents = String::new();
|
||||
cfg_file.read_to_string(&mut cfg_contents)?;
|
||||
let cfg = cfg_contents.parse()?;
|
||||
anchor_toml = Some((cfg, p));
|
||||
if filename.to_str() == Some("Anchor.toml") {
|
||||
let cfg = Config::from_path(&p)?;
|
||||
return Ok(Some(WithPath::new(cfg, p)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Cargo.toml if it's for a single package, i.e., not the
|
||||
// root workspace Cargo.toml.
|
||||
if cargo_toml.is_none() && cargo_toml_level.is_some() {
|
||||
let toml = cargo_toml::Manifest::from_path(cargo_toml_level.as_ref().unwrap())?;
|
||||
if toml.workspace.is_none() {
|
||||
cargo_toml = cargo_toml_level;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((cfg, parent)) = anchor_toml {
|
||||
return Ok(Some((WithPath::new(cfg, parent), cargo_toml)));
|
||||
}
|
||||
|
||||
cwd_opt = cwd.parent();
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn from_path(p: impl AsRef<Path>) -> Result<Self> {
|
||||
let mut cfg_file = File::open(&p)?;
|
||||
let mut cfg_contents = String::new();
|
||||
cfg_file.read_to_string(&mut cfg_contents)?;
|
||||
let cfg = cfg_contents.parse()?;
|
||||
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
pub fn wallet_kp(&self) -> Result<Keypair> {
|
||||
solana_sdk::signature::read_keypair_file(&self.provider.wallet.to_string())
|
||||
.map_err(|_| anyhow!("Unable to read keypair file"))
|
||||
|
@ -382,21 +462,10 @@ pub struct GenesisEntry {
|
|||
pub program: String,
|
||||
}
|
||||
|
||||
pub fn extract_lib_name(cargo_toml: impl AsRef<Path>) -> Result<String> {
|
||||
let cargo_toml = cargo_toml::Manifest::from_path(cargo_toml)?;
|
||||
if cargo_toml.lib.is_some() && cargo_toml.lib.as_ref().unwrap().name.is_some() {
|
||||
Ok(cargo_toml.lib.unwrap().name.unwrap())
|
||||
} else {
|
||||
Ok(cargo_toml
|
||||
.package
|
||||
.ok_or_else(|| anyhow!("Package section not provided"))?
|
||||
.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
pub lib_name: String,
|
||||
// Canonicalized path to the program directory.
|
||||
pub path: PathBuf,
|
||||
pub idl: Option<Idl>,
|
||||
}
|
||||
|
@ -463,7 +532,6 @@ pub struct ProgramWorkspace {
|
|||
pub struct AnchorPackage {
|
||||
pub name: String,
|
||||
pub address: String,
|
||||
pub path: String,
|
||||
pub idl: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -479,19 +547,9 @@ impl AnchorPackage {
|
|||
.ok_or_else(|| anyhow!("Program not provided in Anchor.toml"))?
|
||||
.get(&name)
|
||||
.ok_or_else(|| anyhow!("Program not provided in Anchor.toml"))?;
|
||||
let path = program_details
|
||||
.path
|
||||
.clone()
|
||||
// TODO: use a default path if one isn't provided?
|
||||
.ok_or_else(|| anyhow!("Path to program binary not provided"))?;
|
||||
let idl = program_details.idl.clone();
|
||||
let address = program_details.address.to_string();
|
||||
Ok(Self {
|
||||
name,
|
||||
path,
|
||||
address,
|
||||
idl,
|
||||
})
|
||||
Ok(Self { name, address, idl })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
283
cli/src/lib.rs
283
cli/src/lib.rs
|
@ -1,4 +1,6 @@
|
|||
use crate::config::{AnchorPackage, Config, ConfigOverride, Program, ProgramWorkspace, WithPath};
|
||||
use crate::config::{
|
||||
AnchorPackage, Config, ConfigOverride, Manifest, Program, ProgramWorkspace, WithPath,
|
||||
};
|
||||
use anchor_client::Cluster;
|
||||
use anchor_lang::idl::{IdlAccount, IdlInstruction};
|
||||
use anchor_lang::{AccountDeserialize, AnchorDeserialize, AnchorSerialize};
|
||||
|
@ -297,11 +299,8 @@ pub fn entry(opts: Opts) -> Result<()> {
|
|||
}
|
||||
|
||||
fn init(cfg_override: &ConfigOverride, name: String, typescript: bool) -> Result<()> {
|
||||
let cfg = Config::discover(cfg_override)?;
|
||||
|
||||
if cfg.is_some() {
|
||||
println!("Anchor workspace already initialized");
|
||||
}
|
||||
let _ =
|
||||
Config::discover(cfg_override)?.ok_or_else(|| anyhow!("Workspace already initialized"))?;
|
||||
|
||||
fs::create_dir(name.clone())?;
|
||||
std::env::set_current_dir(&name)?;
|
||||
|
@ -364,7 +363,7 @@ fn init(cfg_override: &ConfigOverride, name: String, typescript: bool) -> Result
|
|||
|
||||
// Creates a new program crate in the `programs/<name>` directory.
|
||||
fn new(cfg_override: &ConfigOverride, name: String) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
match cfg.path().parent() {
|
||||
None => {
|
||||
println!("Unable to make new program");
|
||||
|
@ -401,41 +400,14 @@ pub fn build(
|
|||
stdout: Option<File>, // Used for the package registry server.
|
||||
stderr: Option<File>, // Used for the package registry server.
|
||||
) -> Result<()> {
|
||||
// Change directories to the given `program_name`, if given.
|
||||
let (cfg, _cargo) = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
|
||||
let mut did_find_program = false;
|
||||
// Change to the workspace member directory, if needed.
|
||||
if let Some(program_name) = program_name.as_ref() {
|
||||
for program in cfg.read_all_programs()? {
|
||||
let cargo_toml = program.path.join("Cargo.toml");
|
||||
if !cargo_toml.exists() {
|
||||
return Err(anyhow!(
|
||||
"Did not find Cargo.toml at the path: {}",
|
||||
program.path.display()
|
||||
));
|
||||
}
|
||||
let p_lib_name = config::extract_lib_name(&cargo_toml)?;
|
||||
if program_name.as_str() == p_lib_name {
|
||||
let program_path = cfg
|
||||
.path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.canonicalize()?
|
||||
.join(program.path);
|
||||
std::env::set_current_dir(&program_path)?;
|
||||
did_find_program = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !did_find_program && program_name.is_some() {
|
||||
return Err(anyhow!(
|
||||
"{} is not part of the workspace",
|
||||
program_name.as_ref().unwrap()
|
||||
));
|
||||
cd_member(cfg_override, program_name)?;
|
||||
}
|
||||
|
||||
let (cfg, cargo) = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let cargo = Manifest::discover()?;
|
||||
|
||||
let idl_out = match idl {
|
||||
Some(idl) => Some(PathBuf::from(idl)),
|
||||
None => {
|
||||
|
@ -454,17 +426,37 @@ pub fn build(
|
|||
};
|
||||
|
||||
match cargo {
|
||||
None => build_all(&cfg, cfg.path(), idl_out, verifiable, solana_version)?,
|
||||
Some(ct) => build_cwd(
|
||||
// No Cargo.toml so build the entire workspace.
|
||||
None => build_all(
|
||||
&cfg,
|
||||
ct,
|
||||
cfg.path(),
|
||||
idl_out,
|
||||
verifiable,
|
||||
solana_version,
|
||||
stdout,
|
||||
stderr,
|
||||
)?,
|
||||
};
|
||||
// If the Cargo.toml is at the root, build the entire workspace.
|
||||
Some(cargo) if cargo.path().parent() == cfg.path().parent() => build_all(
|
||||
&cfg,
|
||||
cfg.path(),
|
||||
idl_out,
|
||||
verifiable,
|
||||
solana_version,
|
||||
stdout,
|
||||
stderr,
|
||||
)?,
|
||||
// Cargo.toml represents a single package. Build it.
|
||||
Some(cargo) => build_cwd(
|
||||
&cfg,
|
||||
cargo.path().to_path_buf(),
|
||||
idl_out,
|
||||
verifiable,
|
||||
solana_version,
|
||||
stdout,
|
||||
stderr,
|
||||
)?,
|
||||
}
|
||||
|
||||
set_workspace_dir_or_exit();
|
||||
|
||||
|
@ -477,6 +469,8 @@ fn build_all(
|
|||
idl_out: Option<PathBuf>,
|
||||
verifiable: bool,
|
||||
solana_version: Option<String>,
|
||||
stdout: Option<File>, // Used for the package registry server.
|
||||
stderr: Option<File>, // Used for the package registry server.
|
||||
) -> Result<()> {
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
let r = match cfg_path.parent() {
|
||||
|
@ -489,8 +483,8 @@ fn build_all(
|
|||
idl_out.clone(),
|
||||
verifiable,
|
||||
solana_version.clone(),
|
||||
None,
|
||||
None,
|
||||
stdout.as_ref().map(|f| f.try_clone()).transpose()?,
|
||||
stderr.as_ref().map(|f| f.try_clone()).transpose()?,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -531,7 +525,7 @@ fn build_cwd_verifiable(
|
|||
) -> Result<()> {
|
||||
// Create output dirs.
|
||||
let workspace_dir = cfg.path().parent().unwrap().canonicalize()?;
|
||||
fs::create_dir_all(workspace_dir.join("target/deploy"))?;
|
||||
fs::create_dir_all(workspace_dir.join("target/verifiable"))?;
|
||||
fs::create_dir_all(workspace_dir.join("target/idl"))?;
|
||||
|
||||
let container_name = "anchor-program";
|
||||
|
@ -595,7 +589,7 @@ fn docker_build(
|
|||
stdout: Option<File>,
|
||||
stderr: Option<File>,
|
||||
) -> Result<()> {
|
||||
let binary_name = config::extract_lib_name(&cargo_toml)?;
|
||||
let binary_name = Manifest::from_path(&cargo_toml)?.lib_name()?;
|
||||
|
||||
// Docker vars.
|
||||
let image_name = cfg.docker();
|
||||
|
@ -718,7 +712,7 @@ fn docker_build(
|
|||
.parent()
|
||||
.unwrap()
|
||||
.canonicalize()?
|
||||
.join(format!("target/deploy/{}.so", binary_name))
|
||||
.join(format!("target/verifiable/{}.so", binary_name))
|
||||
.display()
|
||||
.to_string();
|
||||
|
||||
|
@ -774,45 +768,14 @@ fn verify(
|
|||
program_name: Option<String>,
|
||||
solana_version: Option<String>,
|
||||
) -> Result<()> {
|
||||
// Change directories to the given `program_name`, if given.
|
||||
let (cfg, _cargo) = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let mut did_find_program = false;
|
||||
// Change to the workspace member directory, if needed.
|
||||
if let Some(program_name) = program_name.as_ref() {
|
||||
for program in cfg.read_all_programs()? {
|
||||
let cargo_toml = program.path.join("Cargo.toml");
|
||||
if !cargo_toml.exists() {
|
||||
return Err(anyhow!(
|
||||
"Did not find Cargo.toml at the path: {}",
|
||||
program.path.display()
|
||||
));
|
||||
}
|
||||
let p_lib_name = config::extract_lib_name(&cargo_toml)?;
|
||||
if program_name.as_str() == p_lib_name {
|
||||
let program_path = cfg
|
||||
.path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.canonicalize()?
|
||||
.join(program.path);
|
||||
std::env::set_current_dir(&program_path)?;
|
||||
did_find_program = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !did_find_program && program_name.is_some() {
|
||||
return Err(anyhow!(
|
||||
"{} is not part of the workspace",
|
||||
program_name.as_ref().unwrap()
|
||||
));
|
||||
cd_member(cfg_override, program_name)?;
|
||||
}
|
||||
|
||||
// Proceed with the command.
|
||||
let (cfg, cargo) = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let cargo = cargo.ok_or_else(|| {
|
||||
anyhow!("Must be inside program subdirectory if no program name is given.")
|
||||
})?;
|
||||
let program_dir = cargo.parent().unwrap();
|
||||
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let cargo = Manifest::discover()?.ok_or_else(|| anyhow!("Cargo.toml not found"))?;
|
||||
|
||||
// Build the program we want to verify.
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
|
@ -831,23 +794,12 @@ fn verify(
|
|||
std::env::set_current_dir(&cur_dir)?;
|
||||
|
||||
// Verify binary.
|
||||
let binary_name = {
|
||||
let cargo_toml = cargo_toml::Manifest::from_path(&cargo)?;
|
||||
match cargo_toml.lib {
|
||||
None => {
|
||||
cargo_toml
|
||||
.package
|
||||
.ok_or_else(|| anyhow!("Package section not provided"))?
|
||||
.name
|
||||
}
|
||||
Some(lib) => lib.name.ok_or_else(|| anyhow!("Name not provided"))?,
|
||||
}
|
||||
};
|
||||
let binary_name = cargo.lib_name()?;
|
||||
let bin_path = cfg
|
||||
.path()
|
||||
.parent()
|
||||
.ok_or_else(|| anyhow!("Unable to find workspace root"))?
|
||||
.join("target/deploy/")
|
||||
.join("target/verifiable/")
|
||||
.join(format!("{}.so", binary_name));
|
||||
|
||||
let bin_ver = verify_bin(program_id, &bin_path, cfg.provider.cluster.url())?;
|
||||
|
@ -859,7 +811,6 @@ fn verify(
|
|||
// Verify IDL (only if it's not a buffer account).
|
||||
if let Some(local_idl) = extract_idl("src/lib.rs")? {
|
||||
if bin_ver.state != BinVerificationState::Buffer {
|
||||
std::env::set_current_dir(program_dir)?;
|
||||
let deployed_idl = fetch_idl(cfg_override, program_id)?;
|
||||
if local_idl != deployed_idl {
|
||||
println!("Error: IDLs don't match");
|
||||
|
@ -873,6 +824,27 @@ fn verify(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn cd_member(cfg_override: &ConfigOverride, program_name: &str) -> Result<()> {
|
||||
// Change directories to the given `program_name`, if given.
|
||||
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
|
||||
for program in cfg.read_all_programs()? {
|
||||
let cargo_toml = program.path.join("Cargo.toml");
|
||||
if !cargo_toml.exists() {
|
||||
return Err(anyhow!(
|
||||
"Did not find Cargo.toml at the path: {}",
|
||||
program.path.display()
|
||||
));
|
||||
}
|
||||
let p_lib_name = Manifest::from_path(&cargo_toml)?.lib_name()?;
|
||||
if program_name == p_lib_name {
|
||||
std::env::set_current_dir(&program.path)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
return Err(anyhow!("{} is not part of the workspace", program_name,));
|
||||
}
|
||||
|
||||
pub fn verify_bin(program_id: Pubkey, bin_path: &Path, cluster: &str) -> Result<BinVerification> {
|
||||
let client = RpcClient::new(cluster.to_string());
|
||||
|
||||
|
@ -954,9 +926,7 @@ pub enum BinVerificationState {
|
|||
|
||||
// Fetches an IDL for the given program_id.
|
||||
fn fetch_idl(cfg_override: &ConfigOverride, idl_addr: Pubkey) -> Result<Idl> {
|
||||
let cfg = Config::discover(cfg_override)?
|
||||
.expect("Inside a workspace")
|
||||
.0;
|
||||
let cfg = Config::discover(cfg_override)?.expect("Inside a workspace");
|
||||
let client = RpcClient::new(cfg.provider.cluster.url().to_string());
|
||||
|
||||
let mut account = client
|
||||
|
@ -1017,7 +987,7 @@ fn idl(cfg_override: &ConfigOverride, subcmd: IdlCommand) -> Result<()> {
|
|||
}
|
||||
|
||||
fn idl_init(cfg_override: &ConfigOverride, program_id: Pubkey, idl_filepath: String) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let keypair = cfg.provider.wallet.to_string();
|
||||
|
||||
let bytes = std::fs::read(idl_filepath)?;
|
||||
|
@ -1035,7 +1005,7 @@ fn idl_write_buffer(
|
|||
program_id: Pubkey,
|
||||
idl_filepath: String,
|
||||
) -> Result<Pubkey> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let keypair = cfg.provider.wallet.to_string();
|
||||
|
||||
let bytes = std::fs::read(idl_filepath)?;
|
||||
|
@ -1051,7 +1021,7 @@ fn idl_write_buffer(
|
|||
}
|
||||
|
||||
fn idl_set_buffer(cfg_override: &ConfigOverride, program_id: Pubkey, buffer: Pubkey) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let keypair = solana_sdk::signature::read_keypair_file(&cfg.provider.wallet.to_string())
|
||||
.map_err(|_| anyhow!("Unable to read keypair file"))?;
|
||||
let client = RpcClient::new(cfg.provider.cluster.url().to_string());
|
||||
|
@ -1105,7 +1075,7 @@ fn idl_upgrade(
|
|||
}
|
||||
|
||||
fn idl_authority(cfg_override: &ConfigOverride, program_id: Pubkey) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let client = RpcClient::new(cfg.provider.cluster.url().to_string());
|
||||
let idl_address = {
|
||||
let account = client
|
||||
|
@ -1135,7 +1105,7 @@ fn idl_set_authority(
|
|||
address: Option<Pubkey>,
|
||||
new_authority: Pubkey,
|
||||
) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
// Misc.
|
||||
let idl_address = match address {
|
||||
None => IdlAccount::address(&program_id),
|
||||
|
@ -1306,7 +1276,7 @@ fn test(
|
|||
skip_build: bool,
|
||||
extra_args: Vec<String>,
|
||||
) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
// Build if needed.
|
||||
if !skip_build {
|
||||
build(cfg_override, None, false, None, None, None, None)?;
|
||||
|
@ -1534,7 +1504,7 @@ fn _deploy(
|
|||
cfg_override: &ConfigOverride,
|
||||
program_str: Option<String>,
|
||||
) -> Result<Vec<(Pubkey, Program)>> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let url = cfg.provider.cluster.url().to_string();
|
||||
let keypair = cfg.provider.wallet.to_string();
|
||||
|
||||
|
@ -1615,7 +1585,7 @@ fn upgrade(
|
|||
let path: PathBuf = program_filepath.parse().unwrap();
|
||||
let program_filepath = path.canonicalize()?.display().to_string();
|
||||
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let exit = std::process::Command::new("solana")
|
||||
.arg("program")
|
||||
.arg("deploy")
|
||||
|
@ -1655,7 +1625,7 @@ fn launch(
|
|||
)?;
|
||||
let programs = _deploy(cfg_override, program_name)?;
|
||||
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let keypair = cfg.provider.wallet.to_string();
|
||||
|
||||
// Add metadata to all IDLs.
|
||||
|
@ -1680,10 +1650,7 @@ fn launch(
|
|||
// The Solana CLI doesn't redeploy a program if this file exists.
|
||||
// So remove it to make all commands explicit.
|
||||
fn clear_program_keys(cfg_override: &ConfigOverride) -> Result<()> {
|
||||
let config = Config::discover(cfg_override)
|
||||
.unwrap_or_default()
|
||||
.unwrap()
|
||||
.0;
|
||||
let config = Config::discover(cfg_override).unwrap_or_default().unwrap();
|
||||
|
||||
for program in config.read_all_programs()? {
|
||||
let anchor_keypair_path = program.anchor_keypair_path();
|
||||
|
@ -1827,7 +1794,7 @@ fn serialize_idl_ix(ix_inner: anchor_lang::idl::IdlInstruction) -> Result<Vec<u8
|
|||
}
|
||||
|
||||
fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
println!("Running migration deploy script");
|
||||
|
||||
let url = cfg.provider.cluster.url().to_string();
|
||||
|
@ -1874,10 +1841,7 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
|
|||
}
|
||||
|
||||
fn set_workspace_dir_or_exit() {
|
||||
let d = match Config::discover(&ConfigOverride {
|
||||
cluster: None,
|
||||
wallet: None,
|
||||
}) {
|
||||
let d = match Config::discover(&ConfigOverride::default()) {
|
||||
Err(_) => {
|
||||
println!("Not in anchor workspace.");
|
||||
std::process::exit(1);
|
||||
|
@ -1889,7 +1853,7 @@ fn set_workspace_dir_or_exit() {
|
|||
println!("Not in anchor workspace.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
Some((cfg, _inside_cargo)) => {
|
||||
Some(cfg) => {
|
||||
match cfg.path().parent() {
|
||||
None => {
|
||||
println!("Unable to make new program");
|
||||
|
@ -1938,7 +1902,7 @@ fn cluster(_cmd: ClusterCommand) -> Result<()> {
|
|||
}
|
||||
|
||||
fn shell(cfg_override: &ConfigOverride) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let programs = {
|
||||
// Create idl map from all workspace programs.
|
||||
let mut idls: HashMap<String, Idl> = cfg
|
||||
|
@ -2005,7 +1969,7 @@ fn shell(cfg_override: &ConfigOverride) -> Result<()> {
|
|||
}
|
||||
|
||||
fn run(cfg_override: &ConfigOverride, script: String) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg, _cargo| {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
let script = cfg
|
||||
.scripts
|
||||
.get(&script)
|
||||
|
@ -2040,9 +2004,21 @@ fn login(_cfg_override: &ConfigOverride, token: String) -> Result<()> {
|
|||
|
||||
fn publish(cfg_override: &ConfigOverride, program_name: String) -> Result<()> {
|
||||
// Discover the various workspace configs.
|
||||
let (cfg, _cargo_path) = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
|
||||
if !Path::new("Cargo.lock").exists() {
|
||||
let program = cfg
|
||||
.get_program(&program_name)?
|
||||
.ok_or_else(|| anyhow!("Workspace member not found"))?;
|
||||
|
||||
let program_cargo_lock = pathdiff::diff_paths(
|
||||
program.path().join("Cargo.lock"),
|
||||
cfg.path().parent().unwrap(),
|
||||
)
|
||||
.ok_or_else(|| anyhow!("Unable to diff Cargo.lock path"))?;
|
||||
let cargo_lock = Path::new("Cargo.lock");
|
||||
|
||||
// There must be a Cargo.lock
|
||||
if !program_cargo_lock.exists() && !cargo_lock.exists() {
|
||||
return Err(anyhow!("Cargo.lock must exist for a verifiable build"));
|
||||
}
|
||||
|
||||
|
@ -2081,29 +2057,59 @@ fn publish(cfg_override: &ConfigOverride, program_name: String) -> Result<()> {
|
|||
let mut tar = tar::Builder::new(enc);
|
||||
|
||||
// Files that will always be included if they exist.
|
||||
println!("PACKING: Anchor.toml");
|
||||
tar.append_path("Anchor.toml")?;
|
||||
tar.append_path("Cargo.lock")?;
|
||||
if cargo_lock.exists() {
|
||||
println!("PACKING: Cargo.lock");
|
||||
tar.append_path(cargo_lock)?;
|
||||
}
|
||||
if Path::new("Cargo.toml").exists() {
|
||||
println!("PACKING: Cargo.toml");
|
||||
tar.append_path("Cargo.toml")?;
|
||||
}
|
||||
if Path::new("LICENSE").exists() {
|
||||
println!("PACKING: LICENSE");
|
||||
tar.append_path("LICENSE")?;
|
||||
}
|
||||
if Path::new("README.md").exists() {
|
||||
println!("PACKING: README.md");
|
||||
tar.append_path("README.md")?;
|
||||
}
|
||||
|
||||
// All workspace programs.
|
||||
for path in cfg.get_program_list()? {
|
||||
let mut relative_path = pathdiff::diff_paths(path, cfg.path().parent().unwrap())
|
||||
.ok_or_else(|| anyhow!("Unable to diff paths"))?;
|
||||
let mut dirs = walkdir::WalkDir::new(&path)
|
||||
.into_iter()
|
||||
.filter_entry(|e| !is_hidden(e));
|
||||
|
||||
// HACK for workspaces wtih single programs. Change this.
|
||||
if relative_path.display().to_string() == *"" {
|
||||
relative_path = "src".into();
|
||||
// Skip the parent dir.
|
||||
let _ = dirs.next().unwrap()?;
|
||||
|
||||
for entry in dirs {
|
||||
let e = entry.map_err(|e| anyhow!("{:?}", e))?;
|
||||
|
||||
let e = pathdiff::diff_paths(e.path(), cfg.path().parent().unwrap())
|
||||
.ok_or_else(|| anyhow!("Unable to diff paths"))?;
|
||||
|
||||
let path_str = e.display().to_string();
|
||||
|
||||
// Skip target dir.
|
||||
if !path_str.contains("target/") && !path_str.contains("/target") {
|
||||
// Only add the file if it's not empty.
|
||||
let metadata = std::fs::File::open(&e)?.metadata()?;
|
||||
if metadata.len() > 0 {
|
||||
println!("PACKING: {}", e.display().to_string());
|
||||
if e.is_dir() {
|
||||
tar.append_dir_all(&e, &e)?;
|
||||
} else {
|
||||
tar.append_path(&e)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tar.append_dir_all(relative_path.clone(), relative_path)?;
|
||||
}
|
||||
|
||||
// Tar pack complete.
|
||||
tar.into_inner()?;
|
||||
|
||||
// Upload the tarball to the server.
|
||||
|
@ -2158,22 +2164,27 @@ fn registry_api_token(_cfg_override: &ConfigOverride) -> Result<String> {
|
|||
//
|
||||
// The closure passed into this function must never change the working directory
|
||||
// to be outside the workspace. Doing so will have undefined behavior.
|
||||
fn with_workspace<R>(
|
||||
cfg_override: &ConfigOverride,
|
||||
f: impl FnOnce(&WithPath<Config>, Option<PathBuf>) -> R,
|
||||
) -> R {
|
||||
fn with_workspace<R>(cfg_override: &ConfigOverride, f: impl FnOnce(&WithPath<Config>) -> R) -> R {
|
||||
set_workspace_dir_or_exit();
|
||||
|
||||
clear_program_keys(cfg_override).unwrap();
|
||||
|
||||
let (cfg, cargo_toml) = Config::discover(cfg_override)
|
||||
let cfg = Config::discover(cfg_override)
|
||||
.expect("Previously set the workspace dir")
|
||||
.expect("Anchor.toml must always exist");
|
||||
|
||||
let r = f(&cfg, cargo_toml);
|
||||
let r = f(&cfg);
|
||||
|
||||
set_workspace_dir_or_exit();
|
||||
clear_program_keys(cfg_override).unwrap();
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
fn is_hidden(entry: &walkdir::DirEntry) -> bool {
|
||||
entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|s| s == "." || s.starts_with('.') || s == "target")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ cluster = "mainnet"
|
|||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.mainnet]
|
||||
multisig = { address = "A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u", path = "./target/deploy/multisig.so", idl = "./target/idl/multisig.json" }
|
||||
multisig = "A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u"
|
||||
```
|
||||
|
||||
Here there are four sections.
|
||||
|
@ -53,8 +53,8 @@ Here there are four sections.
|
|||
standard Anchor workflow, this can be ommitted. For programs not written in Anchor
|
||||
but still want to publish, this should be added.
|
||||
3. `[provider]` - configures the wallet and cluster settings. Here, `mainnet` is used because the registry only supports `mainnet` binary verification at the moment.
|
||||
3. `[programs.mainnet]` - configures each program in the workpace. Here the
|
||||
`address` of the program to verify and the `path` to it's binary build artifact. For Anchor programs with an **IDL**, an `idl = "<path>"` field should also be provided.
|
||||
3. `[programs.mainnet]` - configures each program in the workpace, providing
|
||||
the `address` of the program to verify.
|
||||
|
||||
::: tip
|
||||
When defining program in `[programs.mainnet]`, make sure the name provided
|
||||
|
|
Loading…
Reference in New Issue