Skip to content

Commit

Permalink
Merge branch 'master' into mraszyk/pic-impersonate
Browse files Browse the repository at this point in the history
  • Loading branch information
mraszyk authored Jan 6, 2025
2 parents fc344fd + 102066c commit 4f54f23
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 34 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/publish-manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ jobs:
run: go install mvdan.cc/sh/v3/cmd/shfmt@latest
- name: Generate
run: |
shellcheck --shell=sh public/install-dfxvm.sh --exclude SC2154,SC2034,SC3003,SC3014,SC3043
shellcheck -V
shellcheck --shell=sh public/install-dfxvm.sh --exclude SC2154,SC2034,SC3003,SC3014,SC3043,SC2317
~/go/bin/shfmt -d -p -i 4 -ci -bn -s public/install-dfxvm.sh
sed -i "s/@revision@/${GITHUB_SHA}/" public/install-dfxvm.sh
mkdir _out
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ Your principal for ICP wallets and decentralized exchanges: ueuar-wxbnk-bdcsr-dn
(run `dfx identity get-principal` to display)
```

### feat: Add pre-install tasks

Add pre-install tasks, which can be defined by the new `pre-install` key for canister objects in `dfx.json` with a command or list of commands.

## Dependencies

### Frontend canister
Expand Down
10 changes: 10 additions & 0 deletions docs/dfx-json-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,16 @@
}
]
},
"pre_install": {
"title": "Pre-Install Commands",
"description": "One or more commands to run pre canister installation. These commands are executed in the root of the project.",
"default": [],
"allOf": [
{
"$ref": "#/definitions/SerdeVec_for_String"
}
]
},
"pullable": {
"title": "Pullable",
"description": "Defines required properties so that this canister is ready for `dfx deps pull` by other projects.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
{
"version": 1,
"canisters": {
"preinstall": {
"main": "main.mo",
"pre_install": "echo hello-pre-file"
},
"preinstall_script": {
"main": "main.mo",
"pre_install": "preinstall.sh",
"dependencies": ["preinstall"]
},
"postinstall": {
"main": "main.mo",
"post_install": "echo hello-file"
"post_install": "echo hello-post-file"
},
"postinstall_script": {
"main": "main.mo",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
echo "working directory of post-install script: '$(pwd)'"
echo hello-script
echo hello-post-script
3 changes: 3 additions & 0 deletions e2e/assets/pre_post_install/preinstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
echo "working directory of pre-install script: '$(pwd)'"
echo hello-pre-script
63 changes: 44 additions & 19 deletions e2e/tests-dfx/install.bash
Original file line number Diff line number Diff line change
Expand Up @@ -90,69 +90,94 @@ teardown() {
assert_command_fail dfx canister install --all --wasm "${archive:?}/wallet/0.10.0/wallet.wasm"
}

@test "install runs post-install tasks" {
install_asset post_install
@test "install runs pre-post-install tasks" {
install_asset pre_post_install
dfx_start

assert_command dfx canister create --all
assert_command dfx build

assert_command dfx canister install preinstall
assert_match 'hello-pre-file'

assert_command dfx canister install preinstall_script
assert_match 'hello-pre-script'

echo 'return 1' >> preinstall.sh
assert_command_fail dfx canister install preinstall_script --mode upgrade
assert_match 'hello-pre-script'

assert_command dfx canister install postinstall
assert_match 'hello-file'
assert_match 'hello-post-file'

assert_command dfx canister install postinstall_script
assert_match 'hello-script'
assert_match 'hello-post-script'

echo 'return 1' >> postinstall.sh
assert_command_fail dfx canister install postinstall_script --mode upgrade
assert_match 'hello-script'
assert_match 'hello-post-script'
}

@test "post-install tasks run in project root" {
install_asset post_install
@test "pre-post-install tasks run in project root" {
install_asset pre_post_install
dfx_start

assert_command dfx canister create --all
assert_command dfx build

cd src/e2e_project_backend

assert_command dfx canister install preinstall_script
assert_match 'hello-pre-script'
assert_match "working directory of pre-install script: '.*/working-dir/e2e_project'"

assert_command dfx canister install postinstall_script
assert_match 'hello-script'
assert_match 'hello-post-script'
assert_match "working directory of post-install script: '.*/working-dir/e2e_project'"
}

@test "post-install tasks receive environment variables" {
install_asset post_install
@test "pre-post-install tasks receive environment variables" {
install_asset pre_post_install
dfx_start
echo "echo hello \$CANISTER_ID" >> preinstall.sh
echo "echo hello \$CANISTER_ID" >> postinstall.sh

assert_command dfx canister create --all
assert_command dfx build
id=$(dfx canister id postinstall_script)
id_pre=$(dfx canister id preinstall_script)
id_post=$(dfx canister id postinstall_script)

assert_command dfx canister install --all
assert_match "hello $id"
assert_match "hello $id_post"
assert_command dfx canister install preinstall_script --mode upgrade
assert_match "hello $id_pre"
assert_command dfx canister install postinstall_script --mode upgrade
assert_match "hello $id"
assert_match "hello $id_post"

assert_command dfx deploy
assert_match "hello $id"
assert_match "hello $id_post"
assert_command dfx deploy preinstall_script
assert_match "hello $id_pre"
assert_command dfx deploy postinstall_script
assert_match "hello $id"
assert_match "hello $id_post"
}

@test "post-install tasks discover dependencies" {
install_asset post_install
@test "pre-post-install tasks discover dependencies" {
install_asset pre_post_install
dfx_start
echo "echo hello \$CANISTER_ID_PREINSTALL" >> preinstall.sh
echo "echo hello \$CANISTER_ID_POSTINSTALL" >> postinstall.sh

assert_command dfx canister create --all
assert_command dfx build
id=$(dfx canister id postinstall)
id_pre=$(dfx canister id preinstall)
id_post=$(dfx canister id postinstall)

assert_command dfx canister install preinstall_script
assert_match "hello $id_pre"

assert_command dfx canister install postinstall_script
assert_match "hello $id"
assert_match "hello $id_post"
}

@test "can install gzip wasm" {
Expand Down
6 changes: 6 additions & 0 deletions src/dfx-core/src/config/model/dfinity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,12 @@ pub struct ConfigCanistersCanister {
#[serde(flatten)]
pub type_specific: CanisterTypeProperties,

/// # Pre-Install Commands
/// One or more commands to run pre canister installation.
/// These commands are executed in the root of the project.
#[serde(default)]
pub pre_install: SerdeVec<String>,

/// # Post-Install Commands
/// One or more commands to run post canister installation.
/// These commands are executed in the root of the project.
Expand Down
7 changes: 7 additions & 0 deletions src/dfx/src/lib/canister_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub struct CanisterInfo {
type_specific: CanisterTypeProperties,

dependencies: Vec<String>,
pre_install: Vec<String>,
post_install: Vec<String>,
main: Option<PathBuf>,
shrink: Option<bool>,
Expand Down Expand Up @@ -171,6 +172,7 @@ impl CanisterInfo {
_ => build_defaults.get_args(),
};

let pre_install = canister_config.pre_install.clone().into_vec();
let post_install = canister_config.post_install.clone().into_vec();
let metadata = CanisterMetadataConfig::new(&canister_config.metadata, &network_name);

Expand All @@ -190,6 +192,7 @@ impl CanisterInfo {
args,
type_specific,
dependencies,
pre_install,
post_install,
main: canister_config.main.clone(),
shrink: canister_config.shrink,
Expand Down Expand Up @@ -262,6 +265,10 @@ impl CanisterInfo {
&self.packtool
}

pub fn get_pre_install(&self) -> &[String] {
&self.pre_install
}

pub fn get_post_install(&self) -> &[String] {
&self.post_install
}
Expand Down
56 changes: 44 additions & 12 deletions src/dfx/src/lib/operations/canister/install_canister.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ pub async fn install_canister(
let log = env.get_logger();
let agent = env.get_agent();
let network = env.get_network_descriptor();
if !canister_info.get_pre_install().is_empty() {
let config = env.get_config()?;
run_customized_install_tasks(
env,
canister_info,
true,
network,
pool,
env_file.or_else(|| config.as_ref()?.get_config().output_env_file.as_deref()),
)?;
}
if !network.is_ic && named_canister::get_ui_canister_id(canister_id_store).is_none() {
named_canister::install_ui_canister(env, canister_id_store, None).await?;
}
Expand Down Expand Up @@ -282,9 +293,10 @@ The command line value will be used.",
}
if !canister_info.get_post_install().is_empty() {
let config = env.get_config()?;
run_post_install_tasks(
run_customized_install_tasks(
env,
canister_info,
false,
network,
pool,
env_file.or_else(|| config.as_ref()?.get_config().output_env_file.as_deref()),
Expand Down Expand Up @@ -435,23 +447,26 @@ fn check_stable_compatibility(
})
}

#[context("Failed to run post-install tasks")]
fn run_post_install_tasks(
#[context("Failed to run {}-install tasks", if is_pre_install { "pre" } else { "post" })]
fn run_customized_install_tasks(
env: &dyn Environment,
canister: &CanisterInfo,
is_pre_install: bool,
network: &NetworkDescriptor,
pool: Option<&CanisterPool>,
env_file: Option<&Path>,
) -> DfxResult {
let pre_or_post = if is_pre_install { "pre" } else { "post" };
let tmp;
let pool = match pool {
Some(pool) => pool,
None => {
let config = env.get_config_or_anyhow()?;
let canisters_to_load = all_project_canisters_with_ids(env, &config);

tmp = CanisterPool::load(env, false, &canisters_to_load)
.context("Error collecting canisters for post-install task")?;
tmp = CanisterPool::load(env, false, &canisters_to_load).context(format!(
"Error collecting canisters for {pre_or_post}-install task"
))?;
&tmp
}
};
Expand All @@ -460,27 +475,43 @@ fn run_post_install_tasks(
.iter()
.map(|can| can.canister_id())
.collect_vec();
for task in canister.get_post_install() {
run_post_install_task(canister, task, network, pool, &dependencies, env_file)?;
let tasks = if is_pre_install {
canister.get_pre_install()
} else {
canister.get_post_install()
};
for task in tasks {
run_customized_install_task(
canister,
task,
is_pre_install,
network,
pool,
&dependencies,
env_file,
)?;
}
Ok(())
}

#[context("Failed to run post-install task {task}")]
fn run_post_install_task(
#[context("Failed to run {}-install task {}", if is_pre_install { "pre" } else { "post" }, task)]
fn run_customized_install_task(
canister: &CanisterInfo,
task: &str,
is_pre_install: bool,
network: &NetworkDescriptor,
pool: &CanisterPool,
dependencies: &[Principal],
env_file: Option<&Path>,
) -> DfxResult {
let pre_or_post = if is_pre_install { "pre" } else { "post" };
let cwd = canister.get_workspace_root();
let words = shell_words::split(task)
.with_context(|| format!("Error interpreting post-install task `{task}`"))?;
.with_context(|| format!("Error interpreting {pre_or_post}-install task `{task}`"))?;
let canonicalized = dfx_core::fs::canonicalize(&cwd.join(&words[0]))
.or_else(|_| which::which(&words[0]))
.map_err(|_| anyhow!("Cannot find command or file {}", &words[0]))?;

let mut command = Command::new(canonicalized);
command.args(&words[1..]);
let vars =
Expand All @@ -492,13 +523,14 @@ fn run_post_install_task(
.current_dir(cwd)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());

let status = command.status()?;
if !status.success() {
match status.code() {
Some(code) => {
bail!("The post-install task `{task}` failed with exit code {code}")
bail!("The {pre_or_post}-install task `{task}` failed with exit code {code}")
}
None => bail!("The post-install task `{task}` was terminated by a signal"),
None => bail!("The {pre_or_post}-install task `{task}` was terminated by a signal"),
}
}
Ok(())
Expand Down

0 comments on commit 4f54f23

Please sign in to comment.