Skip to content

Commit e541500

Browse files
committed
Add optional post-build hook
1 parent 90bbc8b commit e541500

File tree

2 files changed

+74
-13
lines changed

2 files changed

+74
-13
lines changed

cli/src/config.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ pub struct Config {
274274
pub provider: ProviderConfig,
275275
pub programs: ProgramsConfig,
276276
pub scripts: ScriptsConfig,
277+
pub hooks: Option<HooksConfig>,
277278
pub workspace: WorkspaceConfig,
278279
// Separate entry next to test_config because
279280
// "anchor localnet" only has access to the Anchor.toml,
@@ -309,6 +310,16 @@ pub struct ProviderConfig {
309310

310311
pub type ScriptsConfig = BTreeMap<String, String>;
311312

313+
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
314+
pub struct HooksConfig {
315+
pub pre_build: Option<String>,
316+
pub post_build: Option<String>,
317+
pub pre_test: Option<String>,
318+
pub post_test: Option<String>,
319+
pub pre_deploy: Option<String>,
320+
pub post_deploy: Option<String>,
321+
}
322+
312323
pub type ProgramsConfig = BTreeMap<Cluster, BTreeMap<String, ProgramDeployment>>;
313324

314325
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
@@ -413,6 +424,7 @@ struct _Config {
413424
provider: Provider,
414425
workspace: Option<WorkspaceConfig>,
415426
scripts: Option<ScriptsConfig>,
427+
hooks: Option<HooksConfig>,
416428
test: Option<_TestValidator>,
417429
}
418430

@@ -446,6 +458,7 @@ impl ToString for Config {
446458
true => None,
447459
false => Some(self.scripts.clone()),
448460
},
461+
hooks: self.hooks.clone(),
449462
programs,
450463
workspace: (!self.workspace.members.is_empty() || !self.workspace.exclude.is_empty())
451464
.then(|| self.workspace.clone()),
@@ -471,6 +484,7 @@ impl FromStr for Config {
471484
wallet: shellexpand::tilde(&cfg.provider.wallet).parse()?,
472485
},
473486
scripts: cfg.scripts.unwrap_or_default(),
487+
hooks: cfg.hooks,
474488
test_validator: cfg.test.map(Into::into),
475489
test_config: None,
476490
programs: cfg.programs.map_or(Ok(BTreeMap::new()), deser_programs)?,

cli/src/lib.rs

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,9 @@ pub fn build(
771771
}
772772

773773
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
774+
775+
try_run_hook(&cfg, HookType::PreBuild)?;
776+
774777
let build_config = BuildConfig {
775778
verifiable,
776779
solana_version: solana_version.or_else(|| cfg.solana_version.clone()),
@@ -841,6 +844,8 @@ pub fn build(
841844

842845
set_workspace_dir_or_exit();
843846

847+
try_run_hook(&cfg, HookType::PostBuild)?;
848+
844849
Ok(())
845850
}
846851

@@ -1892,6 +1897,9 @@ fn test(
18921897
if (!is_localnet || skip_local_validator) && !skip_deploy {
18931898
deploy(cfg_override, None, None)?;
18941899
}
1900+
1901+
try_run_hook(cfg, HookType::PreTest)?;
1902+
18951903
let mut is_first_suite = true;
18961904
if cfg.scripts.get("test").is_some() {
18971905
is_first_suite = false;
@@ -1936,6 +1944,9 @@ fn test(
19361944
)?;
19371945
}
19381946
}
1947+
1948+
try_run_hook(cfg, HookType::PostTest)?;
1949+
19391950
Ok(())
19401951
})
19411952
}
@@ -2401,6 +2412,8 @@ fn deploy(
24012412
let url = cluster_url(cfg, &cfg.test_validator);
24022413
let keypair = cfg.provider.wallet.to_string();
24032414

2415+
try_run_hook(cfg, HookType::PreDeploy)?;
2416+
24042417
// Deploy the programs.
24052418
println!("Deploying workspace: {}", url);
24062419
println!("Upgrade authority: {}", keypair);
@@ -2462,6 +2475,8 @@ fn deploy(
24622475

24632476
println!("Deploy success");
24642477

2478+
try_run_hook(cfg, HookType::PostDeploy)?;
2479+
24652480
Ok(())
24662481
})
24672482
}
@@ -2811,27 +2826,59 @@ fn shell(cfg_override: &ConfigOverride) -> Result<()> {
28112826

28122827
fn run(cfg_override: &ConfigOverride, script: String) -> Result<()> {
28132828
with_workspace(cfg_override, |cfg| {
2814-
let url = cluster_url(cfg, &cfg.test_validator);
28152829
let script = cfg
28162830
.scripts
28172831
.get(&script)
28182832
.ok_or_else(|| anyhow!("Unable to find script"))?;
2819-
let exit = std::process::Command::new("bash")
2820-
.arg("-c")
2821-
.arg(&script)
2822-
.env("ANCHOR_PROVIDER_URL", url)
2823-
.env("ANCHOR_WALLET", cfg.provider.wallet.to_string())
2824-
.stdout(Stdio::inherit())
2825-
.stderr(Stdio::inherit())
2826-
.output()
2827-
.unwrap();
2828-
if !exit.status.success() {
2829-
std::process::exit(exit.status.code().unwrap_or(1));
2830-
}
2833+
run_bash_cmd(cfg, script)?;
28312834
Ok(())
28322835
})
28332836
}
28342837

2838+
#[derive(Debug, Clone, Serialize, Deserialize)]
2839+
pub enum HookType {
2840+
PreBuild,
2841+
PostBuild,
2842+
PreTest,
2843+
PostTest,
2844+
PreDeploy,
2845+
PostDeploy,
2846+
}
2847+
2848+
fn try_run_hook(cfg: &WithPath<Config>, hook_type: HookType) -> Result<()> {
2849+
if let Some(hooks) = &cfg.hooks {
2850+
let cmd = match hook_type {
2851+
HookType::PreBuild => &hooks.pre_build,
2852+
HookType::PostBuild => &hooks.post_build,
2853+
HookType::PreTest => &hooks.pre_test,
2854+
HookType::PostTest => &hooks.post_test,
2855+
HookType::PreDeploy => &hooks.pre_deploy,
2856+
HookType::PostDeploy => &hooks.post_deploy,
2857+
};
2858+
if let Some(cmd) = &cmd {
2859+
run_bash_cmd(cfg, cmd)?;
2860+
};
2861+
};
2862+
Ok(())
2863+
}
2864+
2865+
fn run_bash_cmd(cfg: &Config, cmd: &String) -> Result<()> {
2866+
let url = cluster_url(cfg, &cfg.test_validator);
2867+
let exit = std::process::Command::new("bash")
2868+
.arg("-c")
2869+
.arg(cmd)
2870+
.env("ANCHOR_PROVIDER_URL", url)
2871+
.env("ANCHOR_WALLET", cfg.provider.wallet.to_string())
2872+
.stdout(Stdio::inherit())
2873+
.stderr(Stdio::inherit())
2874+
.output()
2875+
.unwrap();
2876+
if !exit.status.success() {
2877+
std::process::exit(exit.status.code().unwrap_or(1));
2878+
}
2879+
Ok(())
2880+
}
2881+
28352882
fn login(_cfg_override: &ConfigOverride, token: String) -> Result<()> {
28362883
let dir = shellexpand::tilde("~/.config/anchor");
28372884
if !Path::new(&dir.to_string()).exists() {

0 commit comments

Comments
 (0)