Skip to content

Parallel execution for clone, pull, and checkout#2

Closed
ClankerGuru wants to merge 2 commits intomainfrom
parallel-tasks
Closed

Parallel execution for clone, pull, and checkout#2
ClankerGuru wants to merge 2 commits intomainfrom
parallel-tasks

Conversation

@ClankerGuru
Copy link
Copy Markdown
Owner

@ClankerGuru ClankerGuru commented Apr 6, 2026

Summary

  • Lifecycle tasks (wrkx-clone, wrkx-pull, wrkx-checkout) now run all repos in parallel using a 4-thread pool
  • Per-repo tasks (wrkx-clone-gort, etc.) still work individually for single-repo operations
  • Uses ProcessBuilder for thread-safe git execution

Test plan

  • Build passes locally (all tests green)
  • New tests for ParallelCloneTask, ParallelPullTask, ParallelCheckoutTask
  • Architecture tests pass (naming, annotations)

Summary by CodeRabbit

  • New Features

    • Introduced parallel checkout, clone, and pull operations for improved performance when managing multi-repository workspaces
  • Chores

    • Added automated release workflow to Maven Central for streamlined artifact publishing
  • Tests

    • Added comprehensive test coverage for parallel repository operations

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

This PR introduces three new parallel Gradle tasks (clone, pull, checkout) to replace sequential per-repo lifecycle task aggregation, updates task registration to support these parallel implementations, and adds a GitHub Actions workflow for automated Maven Central release publishing with version validation.

Changes

Cohort / File(s) Summary
Release Automation
.github/workflows/release.yml
New GitHub Actions workflow triggered on published releases; validates that gradle.properties version matches release tag (with optional v prefix), executes Gradle build, and publishes artifacts to Maven Central with GPG signing credentials.
Task Registration Refactoring
src/main/kotlin/zone/clanker/gradle/wrkx/Wrkx.kt
Updated lifecycle task registration to pass extension and repoDir to registerLifecycleTasks(), which now instantiates parallel task classes directly instead of aggregating per-repo tasks; removed intermediate per-repo tracking lists.
Parallel Clone Task
src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCloneTask.kt
New task clones all registered repositories concurrently using a fixed thread pool; skips existing directories, validates exit codes, and automatically checks out configured baseBranch (creating if necessary).
Parallel Checkout Task
src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTask.kt
New task checks out branches across repositories in parallel; validates repository existence, detects dirty working directories and fails fast, attempts branch creation when workingBranch override does not exist.
Parallel Pull Task
src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTask.kt
New task pulls from remote origins in parallel using fixed thread pool; validates repo directory and remote presence, executes git fetch origin followed by git merge --ff-only, aggregates results with per-repo status logging.
Parallel Task Tests
src/test/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTaskTest.kt, ParallelCloneTaskTest.kt, ParallelPullTaskTest.kt
Comprehensive test suites using Kotest; verify parallel execution with real git repositories, handle edge cases (missing directories, dirty working trees, non-existent branches), validate error conditions and empty repository containers.

Sequence Diagram(s)

sequenceDiagram
    participant Gradle as Gradle<br/>(Client)
    participant Task as Parallel Task<br/>(e.g., Clone/Pull)
    participant ThreadPool as Thread Pool<br/>(4 threads)
    participant Git as Git<br/>(Command Execution)
    participant Repos as Repositories<br/>(Filesystem)

    Gradle->>Task: Execute (cloneAll/pullAll/checkoutAll)
    Task->>Task: Load repos from container
    Task->>ThreadPool: Create fixed-size executor (4 threads)
    
    loop For each repository
        Task->>ThreadPool: Submit callable
    end
    
    par Parallel Execution
        ThreadPool->>Git: Repo 1: git clone/pull/checkout
        Git->>Repos: Access repo filesystem
        Repos-->>Git: Output/status
        ThreadPool->>Git: Repo 2: git clone/pull/checkout
        Git->>Repos: Access repo filesystem
        Repos-->>Git: Output/status
        ThreadPool->>Git: Repo N: git clone/pull/checkout
        Git->>Repos: Access repo filesystem
        Repos-->>Git: Output/status
    end
    
    Task->>Task: Await all futures
    Task->>Task: Aggregate results (success/failure)
    Task->>Gradle: Log summary + throw if any failed
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

The diff introduces substantial new functionality across multiple parallel task implementations with non-trivial git operations, branch management logic, and parallel coordination; refactors existing task registration; adds release automation; and provides comprehensive test coverage. The heterogeneous mix of refactoring, new features, and tests across 10+ files with varying complexity patterns requires careful review of control flow, error handling, and integration with Gradle's task execution model.

Possibly Related PRs

  • PR #1: Adds identical .github/workflows/release.yml GitHub Actions workflow for Maven Central release publishing.

Poem

🐰 Parallel paths through git's vast land,
Clone, pull, checkout—all tasks planned!
Four threads hop swift, no repo's slow,
Maven Central waits to see them flow!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the PR: implementing parallel execution for the three core lifecycle tasks (clone, pull, checkout).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch parallel-tasks

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
Copy Markdown

@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: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 7-8: Change the workflow job permissions so the repository token
is read-only: replace the "contents: write" permission under the top-level
permissions block with "contents: read" (and do the same for the other
permissions occurrence that currently grants write). Update the "permissions"
entries so only "contents: read" is granted to avoid exposing a writable repo
credential to Gradle steps that only need to read source and publish externally.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTask.kt`:
- Around line 110-116: The checkout branch creation currently assumes the
configured baseBranch exists locally; update the logic in
ParallelCheckoutTask.kt (the workingBranch handling block where baseBranch,
targetBranch and runGitCommand are used) to fall back to the remote ref if the
local branch is absent: first attempt to create with "git checkout -b
<targetBranch> <baseBranch>", and if that fails because <baseBranch> is not a
local branch, retry creating the branch from "origin/<baseBranch>" (or
explicitly check for the local branch with "git show-ref --verify
refs/heads/<baseBranch>" and use "origin/<baseBranch>" when absent), ensuring
runGitCommand is invoked with the chosen ref and error handling/log message is
updated accordingly.
- Around line 95-99: In checkoutRepo, you're reconstructing the repo path with
File(repoDir, repo.directoryName) instead of using the
WorkspaceRepository.clonePath property, so custom clone locations are ignored;
update checkoutRepo to use repo.clonePath (or its computed value) when building
the File and checking existence and in any returned messages so wrkx-checkout
operates on the actual clone location defined by WorkspaceRepository.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCloneTask.kt`:
- Around line 90-97: The cloneRepo function currently recomputes the target path
from repoDir and repo.directoryName which can diverge from the configured clone
location; change cloneRepo to use the WorkspaceRepository's resolved clonePath
property (e.g. repo.clonePath.get()/asFile) as the target File, ensure its
parent directories are created (target.parentFile?.mkdirs()), and perform the
exists check/return and subsequent clone logic against that target so it matches
includeEnabled() and other lifecycle tasks.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTask.kt`:
- Around line 102-128: The current logic fetches origin/$base but then merges
into whatever HEAD is checked out (mergeProcess), which can fast-forward the
wrong branch; fix by ensuring the local base branch is checked out before the
merge (use repo.baseBranch.get().value as the branch name), i.e. run a git
checkout <base> and verify it succeeds (like you do for fetch/merge) before
invoking the ProcessBuilder that creates mergeProcess, then proceed to merge
origin/$base into that checked-out branch and check its exit code/output
similarly.
- Around line 91-99: The pullRepo function currently builds the checkout
directory from repoDir and repo.directoryName which can be out of sync with
WorkspaceRepository.clonePath; update pullRepo to use repo.clonePath as the
source of truth when locating the repo directory and when calling hasRemote, so
replace the File(repoDir, repo.directoryName) usage with File(repo.clonePath)
(or equivalent accessors) and adjust the existence check and subsequent
hasRemote(dir) call to operate on that clonePath-derived File; ensure all
references inside pullRepo (e.g., the check message, hasRemote invocation, and
any other path uses) consistently use WorkspaceRepository.clonePath.

In `@src/test/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTaskTest.kt`:
- Around line 94-111: The test commits in pushDir rely on global git identity;
make the test hermetic by configuring a repo-local identity in pushDir before
the commit: run git config user.name and git config user.email (targeting
pushDir, e.g., via ProcessBuilder with "-C" and pushDir.absolutePath) prior to
calling git commit in the sequence shown in ParallelPullTaskTest.kt so the
commit no longer depends on machine-global git config.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48f88fec-9a97-4a81-b24f-d6f6493bdbd4

📥 Commits

Reviewing files that changed from the base of the PR and between d7b4d9a and 3f8f87d.

📒 Files selected for processing (8)
  • .github/workflows/release.yml
  • src/main/kotlin/zone/clanker/gradle/wrkx/Wrkx.kt
  • src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTask.kt
  • src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCloneTask.kt
  • src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTask.kt
  • src/test/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTaskTest.kt
  • src/test/kotlin/zone/clanker/gradle/wrkx/task/ParallelCloneTaskTest.kt
  • src/test/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTaskTest.kt

Comment on lines +7 to +8
permissions:
contents: write
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reduce the workflow token to read-only.

actions/checkout persists the job token in git config by default, so contents: write gives every Gradle step a writable repo credential even though this workflow only reads source and publishes externally.

Suggested change
 permissions:
-  contents: write
+  contents: read

Also applies to: 14-14

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 7 - 8, Change the workflow job
permissions so the repository token is read-only: replace the "contents: write"
permission under the top-level permissions block with "contents: read" (and do
the same for the other permissions occurrence that currently grants write).
Update the "permissions" entries so only "contents: read" is granted to avoid
exposing a writable repo credential to Gradle steps that only need to read
source and publish externally.

Comment on lines +95 to +99
private fun checkoutRepo(repo: WorkspaceRepository): String {
val dir = File(repoDir, repo.directoryName)
if (!dir.exists()) {
return "SKIP -- not cloned at ${dir.absolutePath}"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use clonePath here as well.

Like the other new lifecycle tasks, Line 96 rebuilds the repo path instead of using WorkspaceRepository.clonePath. Any custom clone location will make wrkx-checkout operate on the wrong directory.

Suggested change
-            val dir = File(repoDir, repo.directoryName)
+            val dir = repo.clonePath.get().asFile
📝 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
private fun checkoutRepo(repo: WorkspaceRepository): String {
val dir = File(repoDir, repo.directoryName)
if (!dir.exists()) {
return "SKIP -- not cloned at ${dir.absolutePath}"
}
private fun checkoutRepo(repo: WorkspaceRepository): String {
val dir = repo.clonePath.get().asFile
if (!dir.exists()) {
return "SKIP -- not cloned at ${dir.absolutePath}"
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTask.kt` around
lines 95 - 99, In checkoutRepo, you're reconstructing the repo path with
File(repoDir, repo.directoryName) instead of using the
WorkspaceRepository.clonePath property, so custom clone locations are ignored;
update checkoutRepo to use repo.clonePath (or its computed value) when building
the File and checking existence and in any returned messages so wrkx-checkout
operates on the actual clone location defined by WorkspaceRepository.

Comment on lines +110 to +116
if (workingBranch != null) {
val baseBranch = repo.baseBranch.get().value
val createResult = runGitCommand(dir, "git", "checkout", "-b", targetBranch, baseBranch)
check(createResult.exitCode == 0) {
"Failed to create branch '$targetBranch' from '$baseBranch': ${createResult.output}"
}
return "Created and checked out '$targetBranch' from '$baseBranch'"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Create the working branch from the remote base branch when needed.

Line 112 uses git checkout -b <target> <baseBranch>, which only works when <baseBranch> already exists locally. Fresh clones often only have origin/<baseBranch>, so this path can fail even though the configured base branch exists upstream.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCheckoutTask.kt` around
lines 110 - 116, The checkout branch creation currently assumes the configured
baseBranch exists locally; update the logic in ParallelCheckoutTask.kt (the
workingBranch handling block where baseBranch, targetBranch and runGitCommand
are used) to fall back to the remote ref if the local branch is absent: first
attempt to create with "git checkout -b <targetBranch> <baseBranch>", and if
that fails because <baseBranch> is not a local branch, retry creating the branch
from "origin/<baseBranch>" (or explicitly check for the local branch with "git
show-ref --verify refs/heads/<baseBranch>" and use "origin/<baseBranch>" when
absent), ensuring runGitCommand is invoked with the chosen ref and error
handling/log message is updated accordingly.

Comment on lines +90 to +97
private fun cloneRepo(repo: WorkspaceRepository): String {
val target = File(repoDir, repo.directoryName)
if (target.exists()) {
return "SKIP -- directory already exists at ${target.absolutePath}"
}

val url = repo.path.get().value
target.parentFile?.mkdirs()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clone into the configured clonePath, not a recomputed path.

The plugin already resolves and stores each repo's clone location on WorkspaceRepository.clonePath. Rebuilding it from repoDir here can diverge from the path later used by includeEnabled() and the other lifecycle tasks.

Suggested change
-            val target = File(repoDir, repo.directoryName)
+            val target = repo.clonePath.get().asFile
📝 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
private fun cloneRepo(repo: WorkspaceRepository): String {
val target = File(repoDir, repo.directoryName)
if (target.exists()) {
return "SKIP -- directory already exists at ${target.absolutePath}"
}
val url = repo.path.get().value
target.parentFile?.mkdirs()
private fun cloneRepo(repo: WorkspaceRepository): String {
val target = repo.clonePath.get().asFile
if (target.exists()) {
return "SKIP -- directory already exists at ${target.absolutePath}"
}
val url = repo.path.get().value
target.parentFile?.mkdirs()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelCloneTask.kt` around
lines 90 - 97, The cloneRepo function currently recomputes the target path from
repoDir and repo.directoryName which can diverge from the configured clone
location; change cloneRepo to use the WorkspaceRepository's resolved clonePath
property (e.g. repo.clonePath.get()/asFile) as the target File, ensure its
parent directories are created (target.parentFile?.mkdirs()), and perform the
exists check/return and subsequent clone logic against that target so it matches
includeEnabled() and other lifecycle tasks.

Comment on lines +91 to +99
private fun pullRepo(repo: WorkspaceRepository): String {
val dir = File(repoDir, repo.directoryName)
check(dir.exists()) {
"Cannot pull '${repo.repoName}' -- directory not found at ${dir.absolutePath}. " +
"Run './gradlew ${Wrkx.TASK_CLONE}-${repo.sanitizedBuildName}' to clone it first."
}

if (!hasRemote(dir)) {
return "SKIP -- no remote configured"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use clonePath as the repo source of truth.

Line 92 reconstructs the checkout location from repoDir and directoryName, but WorkspaceRepository already carries clonePath, and src/main/kotlin/zone/clanker/gradle/wrkx/Wrkx.kt Lines 352-358 initializes it. If that property is ever overridden, wrkx-pull will run in the wrong directory.

Suggested change
-            val dir = File(repoDir, repo.directoryName)
+            val dir = repo.clonePath.get().asFile
📝 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
private fun pullRepo(repo: WorkspaceRepository): String {
val dir = File(repoDir, repo.directoryName)
check(dir.exists()) {
"Cannot pull '${repo.repoName}' -- directory not found at ${dir.absolutePath}. " +
"Run './gradlew ${Wrkx.TASK_CLONE}-${repo.sanitizedBuildName}' to clone it first."
}
if (!hasRemote(dir)) {
return "SKIP -- no remote configured"
private fun pullRepo(repo: WorkspaceRepository): String {
val dir = repo.clonePath.get().asFile
check(dir.exists()) {
"Cannot pull '${repo.repoName}' -- directory not found at ${dir.absolutePath}. " +
"Run './gradlew ${Wrkx.TASK_CLONE}-${repo.sanitizedBuildName}' to clone it first."
}
if (!hasRemote(dir)) {
return "SKIP -- no remote configured"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTask.kt` around
lines 91 - 99, The pullRepo function currently builds the checkout directory
from repoDir and repo.directoryName which can be out of sync with
WorkspaceRepository.clonePath; update pullRepo to use repo.clonePath as the
source of truth when locating the repo directory and when calling hasRemote, so
replace the File(repoDir, repo.directoryName) usage with File(repo.clonePath)
(or equivalent accessors) and adjust the existence check and subsequent
hasRemote(dir) call to operate on that clonePath-derived File; ensure all
references inside pullRepo (e.g., the check message, hasRemote invocation, and
any other path uses) consistently use WorkspaceRepository.clonePath.

Comment on lines +102 to +128
val base = repo.baseBranch.get().value

val fetchProcess =
ProcessBuilder("git", "fetch", "origin", base)
.directory(dir)
.redirectErrorStream(true)
.start()
val fetchOutput =
fetchProcess.inputStream
.bufferedReader()
.readText()
.trim()
val fetchExit = fetchProcess.waitFor()
check(fetchExit == 0) { "git fetch failed: $fetchOutput" }

val mergeProcess =
ProcessBuilder("git", "merge", "--ff-only", "origin/$base")
.directory(dir)
.redirectErrorStream(true)
.start()
val mergeOutput =
mergeProcess.inputStream
.bufferedReader()
.readText()
.trim()
val mergeExit = mergeProcess.waitFor()
check(mergeExit == 0) { "git merge --ff-only failed: $mergeOutput" }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

wrkx-pull is updating whatever branch is currently checked out.

Line 118 merges origin/$base into HEAD, not into the local $base branch. If a repo is on feature/*, this either fast-forwards that feature branch or fails while leaving the base branch stale, which breaks the task contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTask.kt` around
lines 102 - 128, The current logic fetches origin/$base but then merges into
whatever HEAD is checked out (mergeProcess), which can fast-forward the wrong
branch; fix by ensuring the local base branch is checked out before the merge
(use repo.baseBranch.get().value as the branch name), i.e. run a git checkout
<base> and verify it succeeds (like you do for fetch/merge) before invoking the
ProcessBuilder that creates mergeProcess, then proceed to merge origin/$base
into that checked-out branch and check its exit code/output similarly.

Comment on lines +94 to +111
val pushDir = File(baseDir, "push-work")
ProcessBuilder("git", "clone", bareRepo1.absolutePath, pushDir.absolutePath)
.redirectErrorStream(true)
.start()
.waitFor()
File(pushDir, "new-file.txt").writeText("parallel pull content")
ProcessBuilder("git", "-C", pushDir.absolutePath, "add", ".")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "commit", "-m", "Add new file")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "push")
.redirectErrorStream(true)
.start()
.waitFor()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make the test repo configure its own git identity.

The commit in this scenario depends on global user.name / user.email. This is one of the reasons the new workflow now mutates runner-wide git config. Set the identity on pushDir before git commit so the test stays hermetic on clean machines.

Suggested change
                 ProcessBuilder("git", "-C", pushDir.absolutePath, "add", ".")
                     .redirectErrorStream(true)
                     .start()
                     .waitFor()
+                ProcessBuilder("git", "-C", pushDir.absolutePath, "config", "user.name", "CI")
+                    .redirectErrorStream(true)
+                    .start()
+                    .waitFor()
+                ProcessBuilder("git", "-C", pushDir.absolutePath, "config", "user.email", "ci@clanker.zone")
+                    .redirectErrorStream(true)
+                    .start()
+                    .waitFor()
                 ProcessBuilder("git", "-C", pushDir.absolutePath, "commit", "-m", "Add new file")
                     .redirectErrorStream(true)
                     .start()
                     .waitFor()
📝 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
val pushDir = File(baseDir, "push-work")
ProcessBuilder("git", "clone", bareRepo1.absolutePath, pushDir.absolutePath)
.redirectErrorStream(true)
.start()
.waitFor()
File(pushDir, "new-file.txt").writeText("parallel pull content")
ProcessBuilder("git", "-C", pushDir.absolutePath, "add", ".")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "commit", "-m", "Add new file")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "push")
.redirectErrorStream(true)
.start()
.waitFor()
val pushDir = File(baseDir, "push-work")
ProcessBuilder("git", "clone", bareRepo1.absolutePath, pushDir.absolutePath)
.redirectErrorStream(true)
.start()
.waitFor()
File(pushDir, "new-file.txt").writeText("parallel pull content")
ProcessBuilder("git", "-C", pushDir.absolutePath, "add", ".")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "config", "user.name", "CI")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "config", "user.email", "ci@clanker.zone")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "commit", "-m", "Add new file")
.redirectErrorStream(true)
.start()
.waitFor()
ProcessBuilder("git", "-C", pushDir.absolutePath, "push")
.redirectErrorStream(true)
.start()
.waitFor()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/kotlin/zone/clanker/gradle/wrkx/task/ParallelPullTaskTest.kt` around
lines 94 - 111, The test commits in pushDir rely on global git identity; make
the test hermetic by configuring a repo-local identity in pushDir before the
commit: run git config user.name and git config user.email (targeting pushDir,
e.g., via ProcessBuilder with "-C" and pushDir.absolutePath) prior to calling
git commit in the sequence shown in ParallelPullTaskTest.kt so the commit no
longer depends on machine-global git config.

@ClankerGuru ClankerGuru closed this Apr 6, 2026
@ClankerGuru ClankerGuru deleted the parallel-tasks branch April 6, 2026 15:56
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.

1 participant