Skip to content

Commit f57ccbc

Browse files
fix: scripts use project root as current working directory (#3891)
1 parent 5aa1e6d commit f57ccbc

File tree

21 files changed

+187
-62
lines changed

21 files changed

+187
-62
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ Support Motoko's enhanced orthogonal persistence by automatically setting the ca
1010

1111
`dfx start --pocketic` no longer requires `--clean`, and can persist replica state between runs.
1212

13+
### fix: Scripts always run with current directory set to the project root
14+
15+
Build scripts and other scripts now always run with the working directory
16+
set to the project root (directory containing dfx.json).
17+
18+
This applies to the following:
19+
- build scripts
20+
- extension run
21+
- tech stack value computation
22+
- packtool (vessel, mops etc)
23+
1324
# 0.23.0
1425

1526
### feat: Add canister snapshots

docs/dfx-json-schema.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@
288288
"properties": {
289289
"build": {
290290
"title": "Build Commands",
291-
"description": "Commands that are executed in order to produce this canister's Wasm module. Expected to produce the Wasm in the path specified by the 'wasm' field. No build commands are allowed if the `wasm` field is a URL.",
291+
"description": "Commands that are executed in order to produce this canister's Wasm module. Expected to produce the Wasm in the path specified by the 'wasm' field. No build commands are allowed if the `wasm` field is a URL. These commands are executed in the root of the project.",
292292
"default": [],
293293
"allOf": [
294294
{
@@ -467,7 +467,7 @@
467467
},
468468
"post_install": {
469469
"title": "Post-Install Commands",
470-
"description": "One or more commands to run post canister installation.",
470+
"description": "One or more commands to run post canister installation. These commands are executed in the root of the project.",
471471
"default": [],
472472
"allOf": [
473473
{
@@ -698,7 +698,7 @@
698698
]
699699
},
700700
"packtool": {
701-
"description": "Main command to run the packtool.",
701+
"description": "Main command to run the packtool. This command is executed in the root of the project.",
702702
"type": [
703703
"string",
704704
"null"

e2e/assets/custom_canister/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
#!/usr/bin/env bash
2+
echo "working directory of build script: '$(pwd)'"
23
echo CUSTOM_CANISTER2_BUILD_DONE

e2e/assets/metadata/tech_stack/dfx.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@
114114
"k": {
115115
"type": "motoko",
116116
"main": "main.mo"
117+
},
118+
"m": {
119+
"type": "motoko",
120+
"main": "main.mo",
121+
"tech_stack": {
122+
"other": {
123+
"command": {
124+
"cwd": "$(pwd)"
125+
}
126+
}
127+
}
117128
}
118129
}
119130
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
#!/usr/bin/env bash
2+
echo "working directory of post-install script: '$(pwd)'"
23
echo hello-script

e2e/tests-dfx/build.bash

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,21 @@ teardown() {
279279
assert_match CUSTOM_CANISTER2_BUILD_DONE
280280
}
281281

282+
@test "custom canister build script runs in project root" {
283+
install_asset custom_canister
284+
install_asset wasm/identity
285+
286+
dfx_start
287+
dfx canister create custom2
288+
289+
cd src/e2e_project_backend
290+
pwd
291+
292+
assert_command dfx build custom2
293+
assert_match CUSTOM_CANISTER2_BUILD_DONE
294+
assert_match "working directory of build script: '.*/working-dir/e2e_project'"
295+
}
296+
282297
@test "build succeeds with network parameter" {
283298
dfx_start
284299
dfx canister create --all --network local

e2e/tests-dfx/extension.bash

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,48 @@ EOF
675675
assert_eq "pamparam the param is 123"
676676
}
677677

678+
@test "extension run uses project root" {
679+
CACHE_DIR=$(dfx cache show)
680+
mkdir -p "$CACHE_DIR"/extensions/test_extension
681+
682+
cat > "$CACHE_DIR"/extensions/test_extension/test_extension << "EOF"
683+
#!/usr/bin/env bash
684+
685+
echo "the current directory is '$(pwd)'"
686+
687+
EOF
688+
689+
chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension
690+
691+
cat > "$CACHE_DIR"/extensions/test_extension/extension.json <<EOF
692+
{
693+
"name": "test_extension",
694+
"version": "0.1.0",
695+
"homepage": "https://github.com/dfinity/dfx-extensions",
696+
"authors": "DFINITY",
697+
"summary": "Test extension for e2e purposes.",
698+
"categories": [],
699+
"keywords": [],
700+
"subcommands": {
701+
"abc": {
702+
"about": "something something",
703+
"args": {
704+
}
705+
}
706+
}
707+
}
708+
EOF
709+
710+
mkdir -p project || exit
711+
cd project || exit
712+
echo "{}" >dfx.json
713+
mkdir -p subdir || exit
714+
cd subdir || exit
715+
716+
assert_command dfx test_extension abc
717+
assert_match "the current directory is '.*/working-dir/project'"
718+
}
719+
678720
@test "run with multiple values for the same parameter" {
679721
CACHE_DIR=$(dfx cache show)
680722
mkdir -p "$CACHE_DIR"/extensions/test_extension

e2e/tests-dfx/install.bash

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ teardown() {
108108
assert_match 'hello-script'
109109
}
110110

111+
@test "post-install tasks run in project root" {
112+
install_asset post_install
113+
dfx_start
114+
115+
assert_command dfx canister create --all
116+
assert_command dfx build
117+
118+
cd src/e2e_project_backend
119+
120+
assert_command dfx canister install postinstall_script
121+
assert_match 'hello-script'
122+
assert_match "working directory of post-install script: '.*/working-dir/e2e_project'"
123+
}
124+
111125
@test "post-install tasks receive environment variables" {
112126
install_asset post_install
113127
dfx_start

e2e/tests-dfx/metadata.bash

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,21 @@ ic-stable-structures"
300300
assert_command jq -r '.tech_stack.cdk."ic-cdk" | keys[]' e2e_project_backend.json
301301
assert_eq "version"
302302
}
303+
304+
# shellcheck disable=SC2154
305+
@test "tech stack value generation uses project root as working directory" {
306+
dfx_new
307+
install_asset metadata/tech_stack
308+
309+
dfx_start
310+
311+
312+
# m exposes other->command->working-directory
313+
314+
cd src/e2e_project_backend || exit
315+
assert_command dfx deploy m
316+
assert_command dfx canister metadata m dfx
317+
echo "$stdout" > m.json
318+
assert_command jq -r '.tech_stack.other.command.cwd' m.json
319+
assert_match ".*/working-dir/e2e_project$"
320+
}

e2e/tests-dfx/packtool.bash

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ teardown() {
5050
assert_eq '("php: No comment.")'
5151
}
5252

53+
@test "project calls packtool in project root" {
54+
install_asset packtool
55+
# shellcheck disable=SC1091
56+
source configure_packtool.bash
57+
58+
dfx_start
59+
60+
cd vessel || exit
61+
dfx canister create --all
62+
dfx build
63+
dfx canister install e2e_project_backend
64+
65+
assert_command dfx canister call e2e_project_backend rate '("rust")'
66+
assert_eq '("rust: So hot right now.")'
67+
68+
assert_command dfx canister call e2e_project_backend rate '("php")'
69+
assert_eq '("php: No comment.")'
70+
}
71+
5372
@test "failure to invoke the package tool reports the command line and reason" {
5473
install_asset packtool
5574
jq '.defaults.build.packtool="./no-such-command that command cannot be invoked"' dfx.json | sponge dfx.json

src/dfx-core/src/config/model/dfinity.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub struct ConfigCanistersCanister {
271271

272272
/// # Post-Install Commands
273273
/// One or more commands to run post canister installation.
274+
/// These commands are executed in the root of the project.
274275
#[serde(default)]
275276
pub post_install: SerdeVec<String>,
276277

@@ -379,6 +380,7 @@ pub enum CanisterTypeProperties {
379380
/// Commands that are executed in order to produce this canister's Wasm module.
380381
/// Expected to produce the Wasm in the path specified by the 'wasm' field.
381382
/// No build commands are allowed if the `wasm` field is a URL.
383+
/// These commands are executed in the root of the project.
382384
#[schemars(default)]
383385
build: SerdeVec<String>,
384386
},
@@ -617,6 +619,7 @@ impl Default for ConfigDefaultsBootstrap {
617619
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
618620
pub struct ConfigDefaultsBuild {
619621
/// Main command to run the packtool.
622+
/// This command is executed in the root of the project.
620623
pub packtool: Option<String>,
621624

622625
/// Arguments for packtool.

src/dfx-core/src/extension/manager/execute.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,23 @@ use super::ExtensionManager;
22
use crate::config::cache::get_bin_cache;
33
use crate::error::extension::RunExtensionError;
44
use std::ffi::OsString;
5+
use std::path::PathBuf;
56

67
impl ExtensionManager {
78
pub fn run_extension(
89
&self,
910
extension_name: OsString,
1011
mut params: Vec<OsString>,
12+
project_root: Option<PathBuf>,
1113
) -> Result<(), RunExtensionError> {
1214
let extension_name = extension_name
1315
.into_string()
1416
.map_err(RunExtensionError::InvalidExtensionName)?;
1517

1618
let mut extension_binary = self.get_extension_binary(&extension_name)?;
19+
if let Some(project_root) = project_root {
20+
extension_binary.current_dir(project_root);
21+
}
1722
let dfx_cache = get_bin_cache(self.dfx_version.to_string().as_str())?;
1823

1924
params.extend(["--dfx-cache-path".into(), dfx_cache.into_os_string()]);

src/dfx/src/commands/extension/run.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ impl From<Vec<OsString>> for RunOpts {
2424

2525
pub fn exec(env: &dyn Environment, opts: RunOpts) -> DfxResult<()> {
2626
let mgr = env.get_extension_manager();
27-
mgr.run_extension(opts.name, opts.params)?;
27+
let project_root = env
28+
.get_config()?
29+
.map(|c| c.get_project_root().to_path_buf());
30+
mgr.run_extension(opts.name, opts.params, project_root)?;
2831
Ok(())
2932
}

src/dfx/src/commands/language_service.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ pub fn exec(env: &dyn Environment, opts: LanguageServiceOpts) -> DfxResult {
4747
.get_build()
4848
.get_packtool();
4949

50-
let mut package_arguments = package_arguments::load(env.get_cache().as_ref(), packtool)?;
50+
let mut package_arguments = package_arguments::load(
51+
env.get_cache().as_ref(),
52+
packtool,
53+
config.get_project_root(),
54+
)?;
5155

5256
// Include actor alias flags
5357
let canister_names = config

src/dfx/src/lib/builders/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ pub fn execute_command(
349349
sh_cmd.args(["-c", command]);
350350
sh_cmd
351351
};
352+
cmd.current_dir(cwd);
352353

353354
if !catch_output {
354355
cmd.stdin(Stdio::inherit())

src/dfx/src/lib/builders/motoko.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ fn get_imports(cache: &dyn Cache, info: &MotokoCanisterInfo) -> DfxResult<BTreeS
4646
#[context("Failed recursive dependency detection at {}.", file.display())]
4747
fn get_imports_recursive(
4848
cache: &dyn Cache,
49+
workspace_root: &Path,
4950
file: &Path,
5051
result: &mut BTreeSet<MotokoImport>,
5152
) -> DfxResult {
@@ -56,6 +57,7 @@ fn get_imports(cache: &dyn Cache, info: &MotokoCanisterInfo) -> DfxResult<BTreeS
5657
result.insert(MotokoImport::Relative(file.to_path_buf()));
5758

5859
let mut command = cache.get_binary_command("moc")?;
60+
command.current_dir(workspace_root);
5961
let command = command.arg("--print-deps").arg(file);
6062
let output = command
6163
.output()
@@ -66,7 +68,7 @@ fn get_imports(cache: &dyn Cache, info: &MotokoCanisterInfo) -> DfxResult<BTreeS
6668
let import = MotokoImport::try_from(line).context("Failed to create MotokoImport.")?;
6769
match import {
6870
MotokoImport::Relative(path) => {
69-
get_imports_recursive(cache, path.as_path(), result)?;
71+
get_imports_recursive(cache, workspace_root, path.as_path(), result)?;
7072
}
7173
_ => {
7274
result.insert(import);
@@ -78,7 +80,12 @@ fn get_imports(cache: &dyn Cache, info: &MotokoCanisterInfo) -> DfxResult<BTreeS
7880
}
7981

8082
let mut result = BTreeSet::new();
81-
get_imports_recursive(cache, info.get_main_path(), &mut result)?;
83+
get_imports_recursive(
84+
cache,
85+
info.get_workspace_root(),
86+
info.get_main_path(),
87+
&mut result,
88+
)?;
8289

8390
Ok(result)
8491
}
@@ -154,8 +161,11 @@ impl CanisterBuilder for MotokoBuilder {
154161
config.env_file.as_deref(),
155162
)?;
156163

157-
let package_arguments =
158-
package_arguments::load(cache.as_ref(), motoko_info.get_packtool())?;
164+
let package_arguments = package_arguments::load(
165+
cache.as_ref(),
166+
motoko_info.get_packtool(),
167+
canister_info.get_workspace_root(),
168+
)?;
159169

160170
let moc_arguments = match motoko_info.get_args() {
161171
Some(args) => [
@@ -190,6 +200,7 @@ impl CanisterBuilder for MotokoBuilder {
190200
output: output_wasm_path,
191201
idl_path: idl_dir_path,
192202
idl_map: &id_map,
203+
workspace_root: canister_info.get_workspace_root(),
193204
};
194205
motoko_compile(&self.logger, cache.as_ref(), &params)?;
195206

@@ -221,6 +232,7 @@ enum BuildTarget {
221232

222233
struct MotokoParams<'a> {
223234
build_target: BuildTarget,
235+
workspace_root: &'a Path,
224236
idl_path: &'a Path,
225237
idl_map: &'a CanisterIdMap,
226238
package_arguments: &'a PackageArguments,
@@ -263,6 +275,7 @@ impl MotokoParams<'_> {
263275
#[context("Failed to compile Motoko.")]
264276
fn motoko_compile(logger: &Logger, cache: &dyn Cache, params: &MotokoParams<'_>) -> DfxResult {
265277
let mut cmd = cache.get_binary_command("moc")?;
278+
cmd.current_dir(params.workspace_root);
266279
params.to_args(&mut cmd);
267280
run_command(logger, &mut cmd, params.suppress_warning).context("Failed to run 'moc'.")?;
268281
Ok(())

src/dfx/src/lib/builders/rust.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl CanisterBuilder for RustBuilder {
6666
cargo
6767
.stdout(Stdio::inherit())
6868
.stderr(Stdio::inherit())
69+
.current_dir(canister_info.get_workspace_root())
6970
.arg("build")
7071
.arg("--target")
7172
.arg("wasm32-unknown-unknown")

0 commit comments

Comments
 (0)