Skip to content

Commit 687d959

Browse files
committed
fix(container): use explicit plan-to-git repo target
1 parent 4349caf commit 687d959

4 files changed

Lines changed: 40 additions & 107 deletions

File tree

packages/container/src/core/templates-entrypoint/plan-to-git.ts

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,21 @@ PLAN_TO_GIT_CLAUDE_HOOK="$HOOKS_DIR/plan-to-git-claude-hook"
66
CODEX_REQUIREMENTS_FILE="/etc/codex/requirements.toml"
77
CLAUDE_PLAN_TO_GIT_SETTINGS_FILE="$CLAUDE_CONFIG_DIR/settings.json"`
88

9-
const planToGitRunnerTemplate = String.raw`${renderGitHubRemoteHelpers()}
9+
const planToGitRunnerTemplate = `${renderGitHubRemoteHelpers()}
1010
1111
docker_git_plan_to_git_run() {
1212
local base_repo=""
13-
local origin_repo=""
14-
local origin_url=""
15-
local base_url=""
16-
local config_index="0"
1713
1814
if ! base_repo="$(docker_git_github_repo_from_remote upstream)"; then
1915
base_repo="$(docker_git_github_repo_from_remote origin || true)"
2016
fi
21-
origin_repo="$(docker_git_github_repo_from_remote origin || true)"
22-
origin_url="$(git remote get-url origin 2>/dev/null || true)"
2317
24-
if [[ -z "$base_repo" || -z "$origin_repo" || -z "$origin_url" ]]; then
18+
if [[ -z "$base_repo" ]]; then
2519
plan-to-git "$@"
2620
return $?
2721
fi
2822
29-
if [[ "$origin_repo" == "$base_repo" ]]; then
30-
plan-to-git "$@"
31-
return $?
32-
fi
33-
34-
base_url="https://github.com/${"${"}base_repo}.git"
35-
config_index="${"${"}GIT_CONFIG_COUNT:-0}"
36-
if ! [[ "$config_index" =~ ^[0-9]+$ ]]; then
37-
config_index="0"
38-
fi
39-
40-
env \
41-
GIT_CONFIG_COUNT="$((config_index + 1))" \
42-
"GIT_CONFIG_KEY_${"${"}config_index}=url.${"${"}base_url}.insteadOf" \
43-
"GIT_CONFIG_VALUE_${"${"}config_index}=${"${"}origin_url}" \
44-
plan-to-git "$@"
23+
PLAN_TO_GIT_REPO="$base_repo" plan-to-git "$@"
4524
}`
4625

4726
const planToGitSyncHelperInstallTemplate = String.raw`cat <<'EOF' > "$PLAN_TO_GIT_SYNC_HELPER"

packages/container/src/core/templates/dockerfile-prelude.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ RUN cargo install --git https://github.com/ProverCoderAI/rust-browser-connection
8383
RUN printf "%s\\n" "ALL ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/zz-all \
8484
&& chmod 0440 /etc/sudoers.d/zz-all`
8585

86-
const planToGitRevision = "f60fbe71131854be4c6c1d9fb79abafd2dd6949b"
86+
const planToGitRevision = "3286a253381798f679145ca95be4e309e7bd8d63"
8787

8888
// CHANGE: install plan-to-git in generated project containers.
8989
// WHY: issue #397 requires multi-agent plan capture, Claude Code hooks, temp-backed state, and explicit PR sync.
9090
// QUOTE(ТЗ): "подключение новое версии plan-to-git и настройки hooks для claude code и настройки что бы всё уходило на гитхаб автоматически"
9191
// REF: issue-397
92-
// SOURCE: https://github.com/ProverCoderAI/plan-to-git/tree/f60fbe71131854be4c6c1d9fb79abafd2dd6949b
92+
// SOURCE: https://github.com/ProverCoderAI/plan-to-git/tree/3286a253381798f679145ca95be4e309e7bd8d63
9393
// FORMAT THEOREM: image_build_success -> executable(/usr/local/bin/plan-to-git)
9494
// PURITY: SHELL
9595
// EFFECT: Docker build downloads and installs a pinned Rust CLI from GitHub.
@@ -99,6 +99,7 @@ const renderDockerfilePlanToGit = (): string =>
9999
`# Install plan-to-git for multi-agent plan capture and explicit PR sync (issue #397)
100100
RUN cargo install --git https://github.com/ProverCoderAI/plan-to-git --rev ${planToGitRevision} --locked --bins --root /usr/local \
101101
&& /usr/local/bin/plan-to-git --help >/dev/null \
102+
&& /usr/local/bin/plan-to-git --help | grep -q -- "--repo" \
102103
&& /usr/local/bin/plan-to-git hook --help | grep -q -- "claude" \
103104
&& /usr/local/bin/plan-to-git sync --help | grep -q -- "--pr <PR>"`
104105

packages/container/tests/core/git-post-push-wrapper.test.ts

Lines changed: 30 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ type WrapperHarness = {
3030
readonly ghLogPath: string
3131
}
3232

33+
const expectedPlanToGitRuns = (
34+
repoDir: string,
35+
commands: ReadonlyArray<string>,
36+
targetRepo = "org/repo"
37+
): ReadonlyArray<string> =>
38+
commands.flatMap((command) => [`${repoDir}\ttarget-repo:${targetRepo}`, `${repoDir}\t${command}`])
39+
3340
const fakeGitScript = `#!/usr/bin/env bash
3441
set -euo pipefail
3542
@@ -168,50 +175,14 @@ exit 0
168175
const fakePlanToGitScript = `#!/usr/bin/env bash
169176
set -euo pipefail
170177
171-
fake_effective_origin_url() {
172-
local origin_url="\${FAKE_GIT_ORIGIN_URL:-https://github.com/org/repo.git}"
173-
local config_index=0
174-
local key_var=""
175-
local value_var=""
176-
local key=""
177-
local value=""
178-
local base_url=""
179-
local suffix=""
180-
181-
if [[ -n "\${GIT_CONFIG_COUNT:-}" && "\${GIT_CONFIG_COUNT}" =~ ^[0-9]+$ ]]; then
182-
while [[ "$config_index" -lt "$GIT_CONFIG_COUNT" ]]; do
183-
key_var="GIT_CONFIG_KEY_\${config_index}"
184-
value_var="GIT_CONFIG_VALUE_\${config_index}"
185-
key="\${!key_var:-}"
186-
value="\${!value_var:-}"
187-
if [[ "$key" == url.*.insteadOf && -n "$value" && "$origin_url" == "$value"* ]]; then
188-
base_url="\${key#url.}"
189-
base_url="\${base_url%.insteadOf}"
190-
suffix="\${origin_url#"$value"}"
191-
printf '%s\\n' "\${base_url}\${suffix}"
192-
return 0
193-
fi
194-
config_index=$((config_index + 1))
195-
done
196-
fi
197-
198-
printf '%s\\n' "$origin_url"
199-
}
200-
201178
if [[ "\${1:-}" == "sync" && "\${2:-}" == "--help" ]]; then
202179
printf '%s\\n' "--pr <PR>"
203180
exit 0
204181
fi
205182
206183
if [[ -n "\${FAKE_PLAN_TO_GIT_LOG_PATH:-}" ]]; then
207-
if [[ -n "\${GIT_CONFIG_COUNT:-}" && "\${GIT_CONFIG_COUNT}" =~ ^[0-9]+$ ]]; then
208-
config_index=0
209-
while [[ "$config_index" -lt "$GIT_CONFIG_COUNT" ]]; do
210-
key_var="GIT_CONFIG_KEY_\${config_index}"
211-
value_var="GIT_CONFIG_VALUE_\${config_index}"
212-
printf '%s\\tgit-config:%s=%s\\n' "$PWD" "\${!key_var:-}" "\${!value_var:-}" >> "$FAKE_PLAN_TO_GIT_LOG_PATH"
213-
config_index=$((config_index + 1))
214-
done
184+
if [[ -n "\${PLAN_TO_GIT_REPO:-}" ]]; then
185+
printf '%s\\ttarget-repo:%s\\n' "$PWD" "$PLAN_TO_GIT_REPO" >> "$FAKE_PLAN_TO_GIT_LOG_PATH"
215186
fi
216187
printf '%s\\t%s\\n' "$PWD" "$*" >> "$FAKE_PLAN_TO_GIT_LOG_PATH"
217188
fi
@@ -224,10 +195,9 @@ if [[ "\${1:-}" != "import-codex" && "\${1:-}" != "import-claude" && "\${1:-}" !
224195
exit 127
225196
fi
226197
227-
if [[ "\${1:-}" == "sync" && "\${2:-}" == "--pr" && -n "\${FAKE_PLAN_TO_GIT_EXPECT_EFFECTIVE_ORIGIN_URL:-}" ]]; then
228-
effective_origin_url="$(fake_effective_origin_url)"
229-
if [[ "$effective_origin_url" != "$FAKE_PLAN_TO_GIT_EXPECT_EFFECTIVE_ORIGIN_URL" ]]; then
230-
echo "fakePlanToGit: expected effective origin $FAKE_PLAN_TO_GIT_EXPECT_EFFECTIVE_ORIGIN_URL, got $effective_origin_url" >&2
198+
if [[ "\${1:-}" == "sync" && "\${2:-}" == "--pr" && -n "\${FAKE_PLAN_TO_GIT_EXPECT_TARGET_REPO:-}" ]]; then
199+
if [[ "\${PLAN_TO_GIT_REPO:-}" != "$FAKE_PLAN_TO_GIT_EXPECT_TARGET_REPO" ]]; then
200+
echo "fakePlanToGit: expected target repo $FAKE_PLAN_TO_GIT_EXPECT_TARGET_REPO, got \${PLAN_TO_GIT_REPO:-<unset>}" >&2
231201
exit 44
232202
fi
233203
fi
@@ -442,11 +412,9 @@ describe("git post-push wrapper", () => {
442412
expect(nodeCwd).toEqual([harness.repoDir])
443413
expect(nodeRepoRoot).toEqual([harness.repoDir])
444414
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
445-
expect(planToGit).toEqual([
446-
`${harness.repoDir}\timport-codex --no-sync`,
447-
`${harness.repoDir}\timport-claude --no-sync`,
448-
`${harness.repoDir}\tsync`
449-
])
415+
expect(planToGit).toEqual(
416+
expectedPlanToGitRuns(harness.repoDir, ["import-codex --no-sync", "import-claude --no-sync", "sync"])
417+
)
450418
expect(gh).toContain(`${harness.repoDir}\tpr create --repo org/repo --base main --head issue-375 --fill`)
451419
})
452420
).pipe(Effect.provide(NodeContext.layer)))
@@ -466,11 +434,9 @@ describe("git post-push wrapper", () => {
466434
expect(nodeCwd).toEqual([harness.repoDir])
467435
expect(nodeRepoRoot).toEqual([harness.repoDir])
468436
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
469-
expect(planToGit).toEqual([
470-
`${harness.repoDir}\timport-codex --no-sync`,
471-
`${harness.repoDir}\timport-claude --no-sync`,
472-
`${harness.repoDir}\tsync`
473-
])
437+
expect(planToGit).toEqual(
438+
expectedPlanToGitRuns(harness.repoDir, ["import-codex --no-sync", "import-claude --no-sync", "sync"])
439+
)
474440
expect(gh).toContain(`${harness.repoDir}\tpr create --repo org/repo --base main --head issue-375 --fill`)
475441
expect(gitLog.some((line) => line.startsWith(`${harness.externalDir}\t-C ${harness.repoDir} push`))).toBe(true)
476442
})
@@ -559,7 +525,7 @@ describe("git post-push wrapper", () => {
559525
const gh = yield* _(readLogLines(harness.ghLogPath))
560526

561527
expect(nodeScript).toEqual([])
562-
expect(planToGit).toEqual([`${harness.repoDir}\timport-codex --no-sync`])
528+
expect(planToGit).toEqual(expectedPlanToGitRuns(harness.repoDir, ["import-codex --no-sync"]))
563529
expect(gh).toContain(`${harness.repoDir}\tpr create --repo org/repo --base main --head issue-375 --fill`)
564530
})
565531
).pipe(Effect.provide(NodeContext.layer)))
@@ -578,11 +544,9 @@ describe("git post-push wrapper", () => {
578544
const planToGit = yield* _(readLogLines(harness.planToGitLogPath))
579545

580546
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
581-
expect(planToGit).toEqual([
582-
`${harness.repoDir}\timport-codex --no-sync`,
583-
`${harness.repoDir}\timport-claude --no-sync`,
584-
`${harness.repoDir}\tsync`
585-
])
547+
expect(planToGit).toEqual(
548+
expectedPlanToGitRuns(harness.repoDir, ["import-codex --no-sync", "import-claude --no-sync", "sync"])
549+
)
586550
})
587551
).pipe(Effect.provide(NodeContext.layer)))
588552

@@ -600,11 +564,9 @@ describe("git post-push wrapper", () => {
600564
const gh = yield* _(readLogLines(harness.ghLogPath))
601565

602566
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
603-
expect(planToGit).toEqual([
604-
`${harness.repoDir}\timport-codex --no-sync`,
605-
`${harness.repoDir}\timport-claude --no-sync`,
606-
`${harness.repoDir}\tsync`
607-
])
567+
expect(planToGit).toEqual(
568+
expectedPlanToGitRuns(harness.repoDir, ["import-codex --no-sync", "import-claude --no-sync", "sync"])
569+
)
608570
expect(gh).toContain(`${harness.repoDir}\tpr list --repo org/repo --state open --head issue-375 --json url --jq .[0].url // ""`)
609571
expect(gh.some((line) => line.includes("pr create"))).toBe(false)
610572
})
@@ -631,7 +593,7 @@ describe("git post-push wrapper", () => {
631593
})
632594
).pipe(Effect.provide(NodeContext.layer)))
633595

634-
it.effect("syncs explicit PR plans against upstream when origin is a fork", () =>
596+
it.effect("syncs explicit PR plans against upstream target repo when origin is a fork", () =>
635597
withHarness((harness) =>
636598
Effect.gen(function*(_) {
637599
yield* _(
@@ -641,25 +603,19 @@ describe("git post-push wrapper", () => {
641603
FAKE_GH_OPEN_PR_URL: "https://github.com/org/repo/pull/375",
642604
FAKE_GIT_ORIGIN_URL: "https://github.com/me/repo.git",
643605
FAKE_GIT_UPSTREAM_URL: "https://github.com/org/repo.git",
644-
FAKE_PLAN_TO_GIT_EXPECT_EFFECTIVE_ORIGIN_URL: "https://github.com/org/repo.git"
606+
FAKE_PLAN_TO_GIT_EXPECT_TARGET_REPO: "org/repo"
645607
}
646608
})
647609
)
648610

649611
const nodeScript = yield* _(readLogLines(harness.nodeScriptLogPath))
650612
const planToGit = yield* _(readLogLines(harness.planToGitLogPath))
651613
const gh = yield* _(readLogLines(harness.ghLogPath))
652-
const scopedRepoConfig = `${harness.repoDir}\tgit-config:url.https://github.com/org/repo.git.insteadOf=https://github.com/me/repo.git`
653614

654615
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
655-
expect(planToGit).toEqual([
656-
scopedRepoConfig,
657-
`${harness.repoDir}\timport-codex --no-sync`,
658-
scopedRepoConfig,
659-
`${harness.repoDir}\timport-claude --no-sync`,
660-
scopedRepoConfig,
661-
`${harness.repoDir}\tsync --pr 375`
662-
])
616+
expect(planToGit).toEqual(
617+
expectedPlanToGitRuns(harness.repoDir, ["import-codex --no-sync", "import-claude --no-sync", "sync --pr 375"])
618+
)
663619
expect(gh).toContain(`${harness.repoDir}\tpr list --repo org/repo --state open --head me:issue-375 --json url --jq .[0].url // ""`)
664620
expect(gh.some((line) => line.includes("pr create"))).toBe(false)
665621
})

packages/container/tests/core/templates.test.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,9 @@ describe("renderDockerfile", () => {
208208
"rtk --version",
209209
"rtk gain >/dev/null 2>&1 || true",
210210
"# Install plan-to-git for multi-agent plan capture and explicit PR sync (issue #397)",
211-
"cargo install --git https://github.com/ProverCoderAI/plan-to-git --rev f60fbe71131854be4c6c1d9fb79abafd2dd6949b --locked --bins --root /usr/local",
211+
"cargo install --git https://github.com/ProverCoderAI/plan-to-git --rev 3286a253381798f679145ca95be4e309e7bd8d63 --locked --bins --root /usr/local",
212212
"/usr/local/bin/plan-to-git --help >/dev/null",
213+
'/usr/local/bin/plan-to-git --help | grep -q -- "--repo"',
213214
'/usr/local/bin/plan-to-git hook --help | grep -q -- "claude"',
214215
'/usr/local/bin/plan-to-git sync --help | grep -q -- "--pr <PR>"',
215216
'ARG DOCKER_GIT_SESSION_SYNC_PACKAGE="@prover-coder-ai/docker-git-session-sync@latest"',
@@ -527,12 +528,8 @@ describe("renderEntrypointGitHooks", () => {
527528
expect(hooks).toContain("[post-push-pr] Error: failed to list open PRs")
528529
expect(hooks).toContain("DOCKER_GIT_SKIP_PLAN_TO_GIT")
529530
expect(hooks).toContain("docker_git_plan_to_git_run")
530-
expect(hooks).toContain('origin_repo="$(docker_git_github_repo_from_remote origin || true)"')
531-
expect(hooks).toContain('[[ "$origin_repo" == "$base_repo" ]]')
532-
expect(hooks).toContain("base_url=\"https://github.com/${base_repo}.git\"")
533-
expect(hooks).toContain("GIT_CONFIG_COUNT=\"$((config_index + 1))\"")
534-
expect(hooks).toContain('"GIT_CONFIG_KEY_${config_index}=url.${base_url}.insteadOf"')
535-
expect(hooks).toContain('"GIT_CONFIG_VALUE_${config_index}=${origin_url}"')
531+
expect(hooks).toContain('base_repo="$(docker_git_github_repo_from_remote origin || true)"')
532+
expect(hooks).toContain('PLAN_TO_GIT_REPO="$base_repo" plan-to-git "$@"')
536533
expect(hooks).toContain("docker_git_plan_to_git_run import-codex --no-sync")
537534
expect(hooks).toContain("docker_git_plan_to_git_run import-claude --no-sync")
538535
expect(hooks).toContain("docker_git_plan_to_git_explicit_pr_supported")

0 commit comments

Comments
 (0)