Skip to content

Conversation

@Yohe-Am
Copy link
Contributor

@Yohe-Am Yohe-Am commented Sep 20, 2025


  • The change comes with new or modified tests
  • Hard-to-understand functions have explanatory comments
  • End-user documentation is updated to reflect the change

Summary by CodeRabbit

  • New Features

    • Test task now runs from ./src by default (configurable working directory).
    • Development environment now includes CMake.
  • Bug Fixes

    • Correct timestamp detection on Linux in the Fish shell hook.
    • Installer downloads from a more reliable host.
  • Refactor

    • More reliable Deno task execution via isolated subprocess handling.
  • Chores

    • Removed an obsolete port from configuration.
  • Tests

    • Added E2E test to verify working directory behavior across POSIX and Deno.

@coderabbitai
Copy link

coderabbitai bot commented Sep 20, 2025

📝 Walkthrough

Walkthrough

Introduces a plumbing pathway to execute Deno tasks via JSON and a subprocess, updates CLI fast-path handling and enums, exposes the tasks::exec module, adjusts task “test” to async with workingDir "./src", removes a port from .ghjk/lock.json, adds cmake to flake.nix, updates installer URL host, fixes Linux stat format, and adds an E2E workingDir test.

Changes

Cohort / File(s) Summary
Plumbing execution for Deno task
src/ghjk/cli.rs, src/ghjk/systems/tasks.rs, src/ghjk/systems/tasks/exec.rs
Adds QuickCliResult::ExecDenoTask, new Plumbing subcommand and routing; exposes tasks::exec; replaces in-process Deno execution with JSON file request/response via subprocess; adds pub async fn exec_deno_task_from_json(...).
Task workingDir support
.ghjk/lock.json, ghjk.ts, tests/tasks.ts
Removes a specific port and its references from lock; makes test task async and sets workingDir: "./src"; adds E2E test verifying POSIX and Deno cwd reflect workingDir.
Nix dev shell
flake.nix
Adds cmake to baseShell.buildInputs.
Installer script
install.sh
Switches download host to raw.githubusercontent.com; normalizes final echo/newline.
Shell hook timestamp
src/hooks/hook.fish
Uses stat -c "%Y" for Linux/wildcard cases to get mtime in seconds; Darwin unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as ghjk CLI
  participant QC as Quick CLI
  participant Tasks as tasks::exec
  participant Sub as Subprocess (plumbing exec-deno-task)
  participant FS as FS (request.json/result.json)

  User->>CLI: ghjk plumbing exec-deno-task --json <file>
  CLI->>QC: try_quick_cli(...)
  QC-->>CLI: QuickCliResult::ExecDenoTask(path)
  CLI->>Tasks: exec_deno_task_from_json(gcx, path)
  Tasks->>FS: read request.json
  Tasks->>Sub: spawn with json path
  Sub->>FS: write result.json
  Sub-->>Tasks: exit (code)
  Tasks->>FS: read result.json
  Tasks-->>CLI: Res<()> (or error)
  CLI-->>User: ExitCode::SUCCESS or error
Loading
sequenceDiagram
  autonumber
  actor Dev as Developer
  participant TS as ghjk.ts (task test)
  participant Runner as Task Runner
  participant OS as OS (POSIX)
  participant Deno as Deno Runtime

  Dev->>TS: define async function test($), workingDir: "./src"
  TS->>Runner: register task with workingDir
  Runner->>OS: set POSIX cwd to ./src
  Runner->>Deno: start runtime with cwd ./src
  Deno-->>Runner: task executes with Deno.cwd() === ./src
  Runner-->>Dev: test completes
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • michael-0acf4
  • Natoandro
  • luckasRanarison

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR includes several edits that are unrelated to making workingDir affect POSIX cwd, notably flake.nix (adding cmake to the dev shell), install.sh (changing the raw host URL), .ghjk/lock.json (removal of an unrelated port), and a hook.fish stat-format tweak; these changes are not required for the linked-issue objective and increase review scope. Either remove or split these unrelated changes into separate PRs, or provide a short justification and tests showing they are necessary for this change before merging to keep the review focused.
Description Check ⚠️ Warning The PR description indicates it fixes #103 and that tasks run in subprocesses and includes a checklist, but it does not follow the repository template: it is missing the required "WHY the change cannot be made simpler" section, migration notes under "HOW users should update their code", and leaves documentation/comments unchecked, and contains minor typos. Please expand the description to match the repository template by adding a clear WHAT and WHY, migration notes describing any breaking or behavioral changes, resolve checklist items or explain why they remain unchecked, and fix typos before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "fix: subprocess task execution" is concise, follows conventional commit style, and accurately highlights the primary change (switching task execution to subprocesses) introduced by this changeset.
Linked Issues Check ✅ Passed The changes implement subprocess-based task execution (added exec_deno_task_from_json, plumbing CLI to spawn exec-deno-task, subprocess JSON request/response handling) and include an E2E test verifying workingDir affects both POSIX cwd and Deno.cwd, so the PR fulfills the core objective of issue #103.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/posix-working-dir

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/ghjk/systems/tasks/exec.rs (2)

180-255: Subprocess execution: add timeout and include stderr on failure.

Avoid indefinite hangs and surface actionable errors. Also, fail early if working_dir doesn’t exist.

-                let status = cmd
-                    .status()
-                    .await
-                    .wrap_err("error running plumbing exec-deno-task")?;
-                if !status.success() {
-                    eyre::bail!(
-                        "exec-deno-task subprocess failed with status {:?}",
-                        status.code()
-                    );
-                }
+                if !working_dir.exists() {
+                    eyre::bail!("workingDir does not exist: {}", working_dir.display());
+                }
+                use tokio::time::{timeout, Duration};
+                let out = timeout(Duration::from_secs(300), cmd.output())
+                    .await
+                    .wrap_err("timeout running plumbing exec-deno-task")?
+                    .wrap_err("error running plumbing exec-deno-task")?;
+                if !out.status.success() {
+                    let stderr = String::from_utf8_lossy(&out.stderr);
+                    eyre::bail!(
+                        "exec-deno-task failed (code {:?}): {}",
+                        out.status.code(),
+                        stderr
+                    );
+                }

Note: add use tokio::time::{timeout, Duration}; at the top or fully qualify as shown.


333-427: Fix compile issue and tighten Deno permissions.

  • repo_root.join(...).wrap_err(...) attempts to call wrap_err on a PathBuf (non-Result) and will not compile.
  • allow_all: true is overly permissive for task execution.
-    let main_module = gcx
-        .config
-        .repo_root
-        .join("src/ghjk/systems/tasks/bindings.ts")
-        .wrap_err("repo url error")?;
+    let main_module = gcx
+        .config
+        .repo_root
+        .join("src/ghjk/systems/tasks/bindings.ts");

And consider narrowing permissions (example below keeps broad access but documents intent):

-            deno_runtime::deno_permissions::PermissionsOptions {
-                allow_env: Some(vec![]),
-                allow_import: Some(vec![]),
-                allow_read: Some(vec![]),
-                allow_net: Some(vec![]),
-                allow_ffi: Some(vec![]),
-                allow_run: Some(vec![]),
-                allow_sys: Some(vec![]),
-                allow_write: Some(vec![]),
-                allow_all: true,
-                prompt: false,
-                ..default()
-            },
+            deno_runtime::deno_permissions::PermissionsOptions {
+                // TODO: scope these to the workspace instead of allow_all
+                allow_all: true,
+                prompt: false,
+                ..default()
+            },
🧹 Nitpick comments (14)
src/hooks/hook.fish (2)

4-4: Harden stat calls against leading-dash filenames.

Add -- before $argv to avoid accidental option parsing when a file starts with -.

-            stat -c "%Y" $argv
+            stat -c "%Y" -- $argv

10-10: Ditto for the wildcard/default case.

Protect stat with -- here too.

-            stat -c "%Y" $argv
+            stat -c "%Y" -- $argv
install.sh (2)

5-7: Track the TODOs in an issue and gate move-on-success.

Consider moving the binary only after install.ts succeeds to avoid leaving a half-installed CLI.

-  if [ -w "${GHJK_INSTALL_EXE_DIR}" ]; then
-    mv "$TMP_DIR/$EXE" "$GHJK_INSTALL_EXE_DIR"
-    rm -r "$TMP_DIR"
+  if [ -w "${GHJK_INSTALL_EXE_DIR}" ]; then
+    # run installer from temp path first
+    "$TMP_DIR/$EXE" deno run -A "${GHJK_INSTALLER_URL}"
+    mv "$TMP_DIR/$EXE" "$GHJK_INSTALL_EXE_DIR"
+    rm -r "$TMP_DIR"

132-132: Pinned installer URL to tag: verify availability before running.

Optional: preflight with a HEAD request to fail fast with a clear error if the tag is missing.

+curl -fsSIL "$GHJK_INSTALLER_URL" >/dev/null || {
+  echo "Installer $GHJK_INSTALLER_URL not found." >&2
+  exit 1
+}
src/ghjk/systems/tasks.rs (1)

3-3: Narrow module visibility or re‑export the minimal surface.

If external crates don’t need exec, prefer pub(crate) and re‑export only what’s needed to avoid expanding the public API surface.

-pub mod exec;
+pub(crate) mod exec;
+pub use exec::exec_deno_task_from_json;
ghjk.ts (1)

210-216: Avoid debug noise in default task definition.

Drop or guard the console.log calls behind a debug flag to keep CI logs clean.

-  console.log($.workingDir, "custom");
-  await $`echo $PWD`;
-  console.log(Deno.cwd(), "posix");
+  if (Deno.env.get("GHJK_DEBUG")) {
+    console.log($.workingDir, "custom");
+    await $`echo $PWD`;
+    console.log(Deno.cwd(), "posix");
+  }
tests/tasks.ts (1)

200-225: Good E2E coverage; add robustness.

Use /bin/pwd -P to avoid symlink surprises and normalize both outputs before matching.

-        const posix = await $`/bin/sh -c 'pwd'`.text();
+        const posix = await $`/bin/sh -c '/bin/pwd -P'`.text();
src/ghjk/systems/tasks/exec.rs (3)

160-178: PATH merge heuristic may overreach.

k.contains("PATH") will also modify variables like LD_LIBRARY_PATH. If that’s unintended, restrict to PATH and *PATH cases explicitly.

-            if k.contains("PATH") {
+            if k == "PATH" || k.ends_with("PATH") {

214-227: Request/result file I/O: write atomically.

Use a temp + rename to avoid partial reads by the child process if there’s a race.

-                let req_bytes = serde_json::to_vec_pretty(&req).expect_or_log("json error");
-                tokio::fs::write(&request_file_path, req_bytes)
+                let req_bytes = serde_json::to_vec_pretty(&req).expect_or_log("json error");
+                let tmp = request_file_path.with_extension("json.tmp");
+                tokio::fs::write(&tmp, req_bytes)
                     .await
                     .wrap_err("error writing exec-deno-task request json")?;
+                tokio::fs::rename(&tmp, &request_file_path).await.ok();

256-273: Result parsing: tolerate empty data and surface task key on error.

Optional: include def.key in the error for quicker triage.

-                if let Some(error) = resp.get("error") {
+                if let Some(error) = resp.get("error") {
                     eyre::bail!(
-                        "task execution failed: {}",
+                        "task '{}' failed: {}",
+                        def.key,
                         serde_json::to_string_pretty(error)
                             .unwrap_or_else(|_| format!("{:?}", error))
                     );
                 }
src/ghjk/cli.rs (4)

234-246: If you switch the CLI arg to PathBuf, simplify the call site.

Use &json directly instead of std::path::Path::new(&json).

-                    std::path::Path::new(&json),
+                    &json,

319-320: Avoid unreachable! here; return a safe failure code.

Defensive path prevents a panic if control flow regresses in future changes.

-            QuickCliResult::ExecDenoTask(_) => unreachable!("handled earlier"),
+            QuickCliResult::ExecDenoTask(_) => {
+                tracing::error!("ExecDenoTask reached in exit(); returning failure");
+                ExitCode::FAILURE
+            }

360-364: Propagate PathBuf directly from Quick CLI.

Once the CLI arg type is PathBuf, drop the extra conversion.

-            PlumbingCommands::ExecDenoTask { json } => {
-                return Ok(QuickCliResult::ExecDenoTask(std::path::PathBuf::from(json)));
-            }
+            PlumbingCommands::ExecDenoTask { json } => {
+                return Ok(QuickCliResult::ExecDenoTask(json));
+            }

440-449: Use PathBuf for the JSON file argument.

Prevents repeated conversions and gives better type hints in clap help.

-    ExecDenoTask {
-        /// Path to the JSON request file
-        #[arg(value_name = "JSON_FILE", value_hint = clap::ValueHint::FilePath)]
-        json: String,
-    },
+    ExecDenoTask {
+        /// Path to the JSON request file
+        #[arg(value_name = "JSON_FILE", value_hint = clap::ValueHint::FilePath)]
+        json: std::path::PathBuf,
+    },
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a9731e and db2d2c3.

⛔ Files ignored due to path filters (1)
  • flake.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • .ghjk/lock.json (1 hunks)
  • flake.nix (1 hunks)
  • ghjk.ts (1 hunks)
  • install.sh (3 hunks)
  • src/ghjk/cli.rs (7 hunks)
  • src/ghjk/systems/tasks.rs (1 hunks)
  • src/ghjk/systems/tasks/exec.rs (4 hunks)
  • src/hooks/hook.fish (1 hunks)
  • tests/tasks.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-09T06:07:47.919Z
Learnt from: Yohe-Am
PR: metatypedev/ghjk#130
File: ghjk.ts:39-45
Timestamp: 2025-08-09T06:07:47.919Z
Learning: In the ghjk project, Rust is provided through Nix flake for local development environments. The flake.nix pins Rust version 1.85.0 with rust-src extension. For CI workflows, Rust is installed using the dsherret/rust-toolchain-filev1 action which reads the rust-toolchain.toml file. Rust is required as a build dependency even for loading the ghjk.ts TypeScript configuration file. The ports.rust in allowedBuildDeps is only for build-time dependencies, not for runtime installation in environments.

Applied to files:

  • flake.nix
  • ghjk.ts
📚 Learning: 2025-08-09T06:06:50.518Z
Learnt from: Yohe-Am
PR: metatypedev/ghjk#130
File: ports/lade_ghrel.port.ts:22-22
Timestamp: 2025-08-09T06:06:50.518Z
Learning: In ghjk port manifests (files matching pattern `ports/*.port.ts` or `ports/*.ts`), the `version` field in the manifest object represents the version of the port module itself, not the version of the tool/software that the port installs. The actual tool version is typically passed via `args.installVersion` parameter in methods like `downloadUrls()`.

Applied to files:

  • .ghjk/lock.json
🧬 Code graph analysis (5)
src/ghjk/systems/tasks.rs (1)
src/ghjk/ext/callbacks.rs (1)
  • exec (75-90)
tests/tasks.ts (1)
src/deno_utils/mod.ts (1)
  • $ (105-172)
src/ghjk/cli.rs (1)
src/ghjk/systems/tasks/exec.rs (1)
  • exec_deno_task_from_json (334-427)
src/ghjk/systems/tasks/exec.rs (3)
src/ghjk/systems/envs/posix.rs (2)
  • std (216-218)
  • std (235-237)
src/ghjk/ext.rs (1)
  • new (35-37)
src/sys_deno/types.ts (1)
  • GhjkCtx (151-156)
ghjk.ts (2)
src/denort/lib.rs (1)
  • test (161-318)
src/deno_utils/mod.ts (1)
  • $ (105-172)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test-e2e (ubuntu-22.04, linux/x86_64)
  • GitHub Check: test-e2e (macos-14, darwin/aarch64)
  • GitHub Check: test-e2e (macos-13, darwin/x86_64)
  • GitHub Check: test-e2e (ubuntu-22.04-arm, linux/aarch64)
  • GitHub Check: test-pre-commit
🔇 Additional comments (7)
flake.nix (1)

35-35: Adding cmake to the dev shell looks good.

No issues; aligns with local builds requiring CMake.

install.sh (1)

205-207: Nice UX polish.

Clear, actionable guidance after install.

.ghjk/lock.json (1)

580-580: workingDir on test task: LGTM.

Matches the subprocess cwd behavior required by #103.

src/ghjk/systems/tasks/exec.rs (1)

316-332: JSON casing consistency: LGTM.

#[serde(rename_all = "camelCase")] matches the produced request fields (workingDir, envVars).

src/ghjk/cli.rs (3)

179-179: Comment update is accurate.

Plumbing command handled earlier via QuickCommands; comment aligns with code.


293-294: Adding QuickCliResult::ExecDenoTask is appropriate.

Variant cleanly models the fast‑path handoff.


432-438: Hidden “Plumbing” group looks good.

Keeps the internal surface out of public help.

Comment on lines +86 to +91
// Handle plumbing fast-path before any dynamic CLI/system init
if let QuickCliResult::ExecDenoTask(json_path) = &quick_res {
let res = crate::systems::tasks::exec::exec_deno_task_from_json(&gcx, json_path).await;
deno_cx.terminate().await?;
return res.map(|_| ExitCode::SUCCESS);
}
Copy link

@coderabbitai coderabbitai bot Sep 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Fix panic when ghjkdir is None by handling ExecDenoTask before the ghjkdir unwrap.

If quick_res is ExecDenoTask and config.ghjkdir is None, Line 41 returns quick_res.exit(None) which hits unreachable! (Line 320) and panics. Move the ghjkdir guard below the fast‑path branch.

Apply:

-    let Some(ghjkdir_path) = config.ghjkdir.clone() else {
-        return Ok(quick_res.exit(None));
-    };
+    // (moved below to avoid panics when ExecDenoTask is requested and no ghjkdir is present)
+    // NOTE: this guard can only run after the ExecDenoTask fast-path is handled.

And insert after the fast‑path (right after Line 91):

+    let Some(ghjkdir_path) = gcx.config.ghjkdir.clone() else {
+        return Ok(quick_res.exit(None));
+    };

Also verify whether skipping write_lockfile_or_log() in the fast‑path is intentional (dynamic path writes it).

Also applies to: 41-44


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== repo root: $(pwd) ==="
echo
echo "=== Searching for ExecDenoTask and QuickCliResult ==="
rg -n --hidden --no-ignore-vcs "ExecDenoTask" || true
rg -n --hidden --no-ignore-vcs "QuickCliResult" || true
echo
echo "=== Searching for ghjkdir and quick_res.exit ==="
rg -n --hidden --no-ignore-vcs "ghjkdir" || true
rg -n --hidden --no-ignore-vcs "quick_res.exit" || true
echo
echo "=== Searching for write_lockfile_or_log and exec_deno_task_from_json ==="
rg -n --hidden --no-ignore-vcs "write_lockfile_or_log" || true
rg -n --hidden --no-ignore-vcs "exec_deno_task_from_json" || true
echo
echo "=== Locate unreachable! occurrences ==="
rg -n --hidden --no-ignore-vcs "unreachable!" || true
echo
echo "=== Dump src/ghjk/cli.rs (first 400 lines) with numbers ==="
nl -ba src/ghjk/cli.rs | sed -n '1,400p' || true
echo
echo "=== Dump any QuickCliResult impl/definition matches ==="
rg -n --hidden --no-ignore-vcs "enum QuickCliResult" -S || true
rg -n --hidden --no-ignore-vcs "impl QuickCliResult" -S || true
echo
echo "=== Done ==="

Length of output: 6768


Fix panic: handle ExecDenoTask fast-path before ghjkdir unwrap

Move the ghjkdir guard below the ExecDenoTask fast‑path so quick_res.exit(None) is not called for ExecDenoTask (it hits an unreachable! arm and panics).

Apply:

-    let Some(ghjkdir_path) = config.ghjkdir.clone() else {
-        return Ok(quick_res.exit(None));
-    };
+    // (moved below to avoid panics when ExecDenoTask is requested and no ghjkdir is present)
+    // NOTE: this guard can only run after the ExecDenoTask fast-path is handled.

Insert after the ExecDenoTask fast‑path branch:

+    let Some(ghjkdir_path) = gcx.config.ghjkdir.clone() else {
+        return Ok(quick_res.exit(None));
+    };

Also verify whether skipping systems.write_lockfile_or_log() in the ExecDenoTask fast‑path is intentional.
[src/ghjk/cli.rs: ~41 (remove) → after ~91 (insert)]

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Handle plumbing fast-path before any dynamic CLI/system init
if let QuickCliResult::ExecDenoTask(json_path) = &quick_res {
let res = crate::systems::tasks::exec::exec_deno_task_from_json(&gcx, json_path).await;
deno_cx.terminate().await?;
return res.map(|_| ExitCode::SUCCESS);
}
// Handle plumbing fast-path before any dynamic CLI/system init
if let QuickCliResult::ExecDenoTask(json_path) = &quick_res {
let res = crate::systems::tasks::exec::exec_deno_task_from_json(&gcx, json_path).await;
deno_cx.terminate().await?;
return res.map(|_| ExitCode::SUCCESS);
}
let Some(ghjkdir_path) = gcx.config.ghjkdir.clone() else {
return Ok(quick_res.exit(None));
};
🤖 Prompt for AI Agents
In src/ghjk/cli.rs around lines 86 to 91, the ghjkdir guard/unwrapping currently
runs before the ExecDenoTask fast-path which causes quick_res.exit(None) to be
invoked for ExecDenoTask and hit an unreachable! arm leading to a panic; move
the ghjkdir guard and any code that calls quick_res.exit(None) to after the
ExecDenoTask branch (i.e., insert the ghjkdir check immediately after the
ExecDenoTask fast-path return) so ExecDenoTask returns early without executing
the ghjkdir-dependent code, and while making this change, confirm whether the
ExecDenoTask fast-path should also call systems.write_lockfile_or_log() — either
add that call into the fast-path if required or explicitly document/leave it out
if skipping is intentional.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic catch.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Yohe-Am Thank you! I'm glad I could help identify that potential panic scenario. The ordering issue between the ghjkdir guard and the ExecDenoTask fast-path could definitely cause problems when someone tries to use the plumbing command without a proper ghjk setup.

                /|   /|  
               ( :v:  )
                |(_)|
               /     \
              /  ___  \
             (  /   \  )
              \_\___/_/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

workingDir should affect POSIX cwd of task

1 participant