cli: Add `anchor expand` command (#1160)
This commit is contained in:
parent
22c2c30ef4
commit
1749a7bd53
|
@ -11,7 +11,6 @@ incremented for features.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
### Fixes
|
||||
|
||||
* ts: Change commitment message `recent` to `processed` and `max` to `finalized` ([#1128](https://github.com/project-serum/anchor/pull/1128))
|
||||
|
@ -26,6 +25,7 @@ incremented for features.
|
|||
* lang: Handle arrays with const as length ([#968](https://github.com/project-serum/anchor/pull/968)).
|
||||
* ts: Add optional commitment argument to `fetch` and `fetchMultiple` ([#1171](https://github.com/project-serum/anchor/pull/1171)).
|
||||
* lang: Implement `AsRef<T>` for `Account<'a, T>`([#1173](https://github.com/project-serum/anchor/pull/1173))
|
||||
* cli: Add `anchor expand` command which wraps around `cargo expand` ([#1160](https://github.com/project-serum/anchor/pull/1160))
|
||||
|
||||
### Breaking
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ dependencies = [
|
|||
"anchor-syn",
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
"chrono",
|
||||
"clap 3.0.0-beta.4",
|
||||
"dirs",
|
||||
"flate2",
|
||||
|
|
|
@ -38,3 +38,4 @@ tokio = "1.0"
|
|||
pathdiff = "0.2.0"
|
||||
cargo_toml = "0.9.2"
|
||||
walkdir = "2"
|
||||
chrono = "0.4.19"
|
||||
|
|
120
cli/src/lib.rs
120
cli/src/lib.rs
|
@ -33,6 +33,7 @@ use solana_sdk::sysvar;
|
|||
use solana_sdk::transaction::Transaction;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -97,6 +98,25 @@ pub enum Command {
|
|||
)]
|
||||
cargo_args: Vec<String>,
|
||||
},
|
||||
/// Expands macros (wrapper around cargo expand)
|
||||
///
|
||||
/// Use it in a program folder to expand program
|
||||
///
|
||||
/// Use it in a workspace but outside a program
|
||||
/// folder to expand the entire workspace
|
||||
Expand {
|
||||
/// Expand only this program
|
||||
#[clap(short, long)]
|
||||
program_name: Option<String>,
|
||||
/// Arguments to pass to the underlying `cargo expand` command
|
||||
#[clap(
|
||||
required = false,
|
||||
takes_value = true,
|
||||
multiple_values = true,
|
||||
last = true
|
||||
)]
|
||||
cargo_args: Vec<String>,
|
||||
},
|
||||
/// Verifies the on-chain bytecode matches the locally compiled artifact.
|
||||
/// Run this command inside a program subdirectory, i.e., in the dir
|
||||
/// containing the program's Cargo.toml.
|
||||
|
@ -370,6 +390,10 @@ pub fn entry(opts: Opts) -> Result<()> {
|
|||
cargo_args,
|
||||
),
|
||||
Command::Deploy { program_name } => deploy(&opts.cfg_override, program_name),
|
||||
Command::Expand {
|
||||
program_name,
|
||||
cargo_args,
|
||||
} => expand(&opts.cfg_override, program_name, &cargo_args),
|
||||
Command::Upgrade {
|
||||
program_id,
|
||||
program_filepath,
|
||||
|
@ -559,6 +583,102 @@ fn new_program(name: &str) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn expand(
|
||||
cfg_override: &ConfigOverride,
|
||||
program_name: Option<String>,
|
||||
cargo_args: &[String],
|
||||
) -> Result<()> {
|
||||
// Change to the workspace member directory, if needed.
|
||||
if let Some(program_name) = program_name.as_ref() {
|
||||
cd_member(cfg_override, program_name)?;
|
||||
}
|
||||
|
||||
let workspace_cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
let cfg_parent = workspace_cfg.path().parent().expect("Invalid Anchor.toml");
|
||||
let cargo = Manifest::discover()?;
|
||||
|
||||
let expansions_path = cfg_parent.join(".anchor/expanded-macros");
|
||||
fs::create_dir_all(&expansions_path)?;
|
||||
|
||||
match cargo {
|
||||
// No Cargo.toml found, expand entire workspace
|
||||
None => expand_all(&workspace_cfg, expansions_path, cargo_args),
|
||||
// Cargo.toml is at root of workspace, expand entire workspace
|
||||
Some(cargo) if cargo.path().parent() == workspace_cfg.path().parent() => {
|
||||
expand_all(&workspace_cfg, expansions_path, cargo_args)
|
||||
}
|
||||
// Reaching this arm means Cargo.toml belongs to a single package. Expand it.
|
||||
Some(cargo) => expand_program(
|
||||
// If we found Cargo.toml, it must be in a directory so unwrap is safe
|
||||
cargo.path().parent().unwrap().to_path_buf(),
|
||||
expansions_path,
|
||||
cargo_args,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_all(
|
||||
workspace_cfg: &WithPath<Config>,
|
||||
expansions_path: PathBuf,
|
||||
cargo_args: &[String],
|
||||
) -> Result<()> {
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
for p in workspace_cfg.get_program_list()? {
|
||||
expand_program(p, expansions_path.clone(), cargo_args)?;
|
||||
}
|
||||
std::env::set_current_dir(cur_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_program(
|
||||
program_path: PathBuf,
|
||||
expansions_path: PathBuf,
|
||||
cargo_args: &[String],
|
||||
) -> Result<()> {
|
||||
let cargo = Manifest::from_path(program_path.join("Cargo.toml"))
|
||||
.map_err(|_| anyhow!("Could not find Cargo.toml for program"))?;
|
||||
|
||||
let target_dir_arg = {
|
||||
let mut target_dir_arg = OsString::from("--target-dir=");
|
||||
target_dir_arg.push(expansions_path.join("expand-target"));
|
||||
target_dir_arg
|
||||
};
|
||||
|
||||
let package_name = &cargo
|
||||
.package
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("Cargo config is missing a package"))?
|
||||
.name;
|
||||
let program_expansions_path = expansions_path.join(package_name);
|
||||
fs::create_dir_all(&program_expansions_path)?;
|
||||
|
||||
let exit = std::process::Command::new("cargo")
|
||||
.arg("expand")
|
||||
.arg(target_dir_arg)
|
||||
.arg(&format!("--package={}", package_name))
|
||||
.args(cargo_args)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
|
||||
if !exit.status.success() {
|
||||
eprintln!("'anchor expand' failed. Perhaps you have not installed 'cargo-expand'? https://github.com/dtolnay/cargo-expand#installation");
|
||||
std::process::exit(exit.status.code().unwrap_or(1));
|
||||
}
|
||||
|
||||
let version = cargo.version();
|
||||
let time = chrono::Utc::now().to_string().replace(' ', "_");
|
||||
let file_path =
|
||||
program_expansions_path.join(format!("{}-{}-{}.rs", package_name, version, time));
|
||||
fs::write(&file_path, &exit.stdout).map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
|
||||
|
||||
println!(
|
||||
"Expanded {} into file {}\n",
|
||||
package_name,
|
||||
file_path.to_string_lossy()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build(
|
||||
cfg_override: &ConfigOverride,
|
||||
|
|
|
@ -18,6 +18,7 @@ SUBCOMMANDS:
|
|||
build Builds the workspace
|
||||
cluster Cluster commands
|
||||
deploy Deploys each program in the workspace
|
||||
expand Expands the macros of a program or the workspace
|
||||
help Prints this message or the help of the given subcommand(s)
|
||||
idl Commands for interacting with interface definitions
|
||||
init Initializes a workspace
|
||||
|
@ -30,21 +31,6 @@ SUBCOMMANDS:
|
|||
Cargo.toml
|
||||
```
|
||||
|
||||
## Init
|
||||
|
||||
```
|
||||
anchor init
|
||||
```
|
||||
|
||||
Initializes a project workspace with the following structure.
|
||||
|
||||
* `Anchor.toml`: Anchor configuration file.
|
||||
* `Cargo.toml`: Rust workspace configuration file.
|
||||
* `package.json`: JavaScript dependencies file.
|
||||
* `programs/`: Directory for Solana program crates.
|
||||
* `app/`: Directory for your application frontend.
|
||||
* `tests/`: Directory for JavaScript integration tests.
|
||||
* `migrations/deploy.js`: Deploy script.
|
||||
|
||||
## Build
|
||||
|
||||
|
@ -60,6 +46,25 @@ anchor build --verifiable
|
|||
|
||||
Runs the build inside a docker image so that the output binary is deterministic (assuming a Cargo.lock file is used). This command must be run from within a single crate subdirectory within the workspace. For example, `programs/<my-program>/`.
|
||||
|
||||
## Cluster
|
||||
|
||||
### Cluster list
|
||||
|
||||
```
|
||||
anchor cluster list
|
||||
```
|
||||
|
||||
This lists cluster endpoints:
|
||||
|
||||
```
|
||||
Cluster Endpoints:
|
||||
|
||||
* Mainnet - https://solana-api.projectserum.com
|
||||
* Mainnet - https://api.mainnet-beta.solana.com
|
||||
* Devnet - https://api.devnet.solana.com
|
||||
* Testnet - https://api.testnet.solana.com
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
```
|
||||
|
@ -73,64 +78,17 @@ This is different from the `solana program deploy` command, because everytime it
|
|||
it will generate a *new* program address.
|
||||
:::
|
||||
|
||||
## Upgrade
|
||||
## Expand
|
||||
|
||||
```
|
||||
anchor upgrade <target/deploy/program.so> --program-id <program-id>
|
||||
anchor expand
|
||||
```
|
||||
|
||||
Uses Solana's upgradeable BPF loader to upgrade the on chain program code.
|
||||
If run inside a program folder, expands the macros of the program.
|
||||
|
||||
## Test
|
||||
If run in the workspace but outside a program folder, expands the macros of the workspace.
|
||||
|
||||
```
|
||||
anchor test
|
||||
```
|
||||
|
||||
Run an integration test suite against the configured cluster, deploying new versions
|
||||
of all workspace programs before running them.
|
||||
|
||||
If the configured network is a localnet, then automatically starts the localnetwork and runs
|
||||
the test.
|
||||
|
||||
::: tip Note
|
||||
Be sure to shutdown any other local validators, otherwise `anchor test` will fail to run.
|
||||
|
||||
If you'd prefer to run the program against your local validator use `anchor test --skip-local-validator`.
|
||||
:::
|
||||
|
||||
When running tests we stream program logs to `.anchor/program-logs/<address>.<program-name>.log`
|
||||
|
||||
::: tip Note
|
||||
The Anchor workflow [recommends](https://www.parity.io/paritys-checklist-for-secure-smart-contract-development/)
|
||||
to test your program using integration tests in a language other
|
||||
than Rust to make sure that bugs related to syntax misunderstandings
|
||||
are coverable with tests and not just replicated in tests.
|
||||
:::
|
||||
|
||||
## Migrate
|
||||
|
||||
```
|
||||
anchor migrate
|
||||
```
|
||||
|
||||
Runs the deploy script located at `migrations/deploy.js`, injecting a provider configured
|
||||
from the workspace's `Anchor.toml`. For example,
|
||||
|
||||
```javascript
|
||||
// File: migrations/deploys.js
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
}
|
||||
```
|
||||
|
||||
Migrations are a new feature
|
||||
and only support this simple deploy script at the moment.
|
||||
If run with the `--program-name` option, expand only the given program.
|
||||
|
||||
## Idl
|
||||
|
||||
|
@ -195,6 +153,46 @@ anchor idl set-authority -n <new-authority> -p <program-id>
|
|||
Sets a new authority on the IDL account. Both the `new-authority` and `program-id`
|
||||
must be encoded in base 58.
|
||||
|
||||
## Init
|
||||
|
||||
```
|
||||
anchor init
|
||||
```
|
||||
|
||||
Initializes a project workspace with the following structure.
|
||||
|
||||
* `Anchor.toml`: Anchor configuration file.
|
||||
* `Cargo.toml`: Rust workspace configuration file.
|
||||
* `package.json`: JavaScript dependencies file.
|
||||
* `programs/`: Directory for Solana program crates.
|
||||
* `app/`: Directory for your application frontend.
|
||||
* `tests/`: Directory for JavaScript integration tests.
|
||||
* `migrations/deploy.js`: Deploy script.
|
||||
|
||||
## Migrate
|
||||
|
||||
```
|
||||
anchor migrate
|
||||
```
|
||||
|
||||
Runs the deploy script located at `migrations/deploy.js`, injecting a provider configured
|
||||
from the workspace's `Anchor.toml`. For example,
|
||||
|
||||
```javascript
|
||||
// File: migrations/deploys.js
|
||||
|
||||
const anchor = require("@project-serum/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
}
|
||||
```
|
||||
|
||||
Migrations are a new feature
|
||||
and only support this simple deploy script at the moment.
|
||||
|
||||
## New
|
||||
|
||||
```
|
||||
|
@ -203,25 +201,41 @@ anchor new <program-name>
|
|||
|
||||
Creates a new program in the workspace's `programs/` directory initialized with boilerplate.
|
||||
|
||||
## Cluster
|
||||
|
||||
### Cluster list
|
||||
## Test
|
||||
|
||||
```
|
||||
anchor cluster list
|
||||
anchor test
|
||||
```
|
||||
|
||||
This lists cluster endpoints:
|
||||
Run an integration test suit against the configured cluster, deploying new versions
|
||||
of all workspace programs before running them.
|
||||
|
||||
If the configured network is a localnet, then automatically starts the localnetwork and runs
|
||||
the test.
|
||||
|
||||
::: tip Note
|
||||
Be sure to shutdown any other local validators, otherwise `anchor test` will fail to run.
|
||||
|
||||
If you'd prefer to run the program against your local validator use `anchor test --skip-local-validator`.
|
||||
:::
|
||||
|
||||
When running tests we stream program logs to `.anchor/program-logs/<address>.<program-name>.log`
|
||||
|
||||
::: tip Note
|
||||
The Anchor workflow [recommends](https://www.parity.io/paritys-checklist-for-secure-smart-contract-development/)
|
||||
to test your program using integration tests in a language other
|
||||
than Rust to make sure that bugs related to syntax misunderstandings
|
||||
are coverable with tests and not just replicated in tests.
|
||||
:::
|
||||
|
||||
## Upgrade
|
||||
|
||||
```
|
||||
Cluster Endpoints:
|
||||
|
||||
* Mainnet - https://solana-api.projectserum.com
|
||||
* Mainnet - https://api.mainnet-beta.solana.com
|
||||
* Devnet - https://api.devnet.solana.com
|
||||
* Testnet - https://api.testnet.solana.com
|
||||
anchor upgrade <target/deploy/program.so> --program-id <program-id>
|
||||
```
|
||||
|
||||
Uses Solana's upgradeable BPF loader to upgrade the on chain program code.
|
||||
|
||||
## Verify
|
||||
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue