Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions packages/container/src/core/templates-entrypoint/github-remotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const githubRemoteHelpersTemplate = String.raw`docker_git_github_repo_from_remote_url() {
local remote_url="$1"
local repo_path=""
local owner=""
local repo=""

case "$remote_url" in
https://github.com/*)
repo_path="${"${"}remote_url#https://github.com/}"
;;
https://*@github.com/*)
repo_path="${"${"}remote_url#https://*@github.com/}"
;;
ssh://git@github.com/*)
repo_path="${"${"}remote_url#ssh://git@github.com/}"
;;
git@github.com:*)
repo_path="${"${"}remote_url#git@github.com:}"
;;
*)
return 1
;;
esac

repo_path="${"${"}repo_path%%\?*}"
repo_path="${"${"}repo_path%%#*}"
repo_path="${"${"}repo_path%/}"
repo_path="${"${"}repo_path%.git}"
owner="${"${"}repo_path%%/*}"
repo="${"${"}repo_path#*/}"
repo="${"${"}repo%%/*}"
repo="${"${"}repo%.git}"

if [[ -z "$owner" || -z "$repo" || "$owner" == "$repo_path" ]]; then
return 1
fi

printf "%s/%s\n" "$owner" "$repo"
}

docker_git_github_repo_from_remote() {
local remote="$1"
local remote_url=""

remote_url="$(git remote get-url "$remote" 2>/dev/null || true)"
if [[ -z "$remote_url" ]]; then
return 1
fi

docker_git_github_repo_from_remote_url "$remote_url"
}`

export const renderGitHubRemoteHelpers = (): string => githubRemoteHelpersTemplate
40 changes: 32 additions & 8 deletions packages/container/src/core/templates-entrypoint/plan-to-git.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { renderGitHubRemoteHelpers } from "./github-remotes.js"

const planToGitHookPathsTemplate = `PLAN_TO_GIT_SYNC_HELPER="$HOOKS_DIR/plan-to-git-sync"
PLAN_TO_GIT_CODEX_HOOK="$HOOKS_DIR/plan-to-git-codex-hook"
PLAN_TO_GIT_CLAUDE_HOOK="$HOOKS_DIR/plan-to-git-claude-hook"
CODEX_REQUIREMENTS_FILE="/etc/codex/requirements.toml"
CLAUDE_PLAN_TO_GIT_SETTINGS_FILE="$CLAUDE_CONFIG_DIR/settings.json"`

const planToGitRunnerTemplate = `${renderGitHubRemoteHelpers()}

docker_git_plan_to_git_run() {
local base_repo=""

if ! base_repo="$(docker_git_github_repo_from_remote upstream)"; then
base_repo="$(docker_git_github_repo_from_remote origin || true)"
fi

if [[ -z "$base_repo" ]]; then
plan-to-git "$@"
return $?
fi

PLAN_TO_GIT_REPO="$base_repo" plan-to-git "$@"
}`

const planToGitSyncHelperInstallTemplate = String.raw`cat <<'EOF' > "$PLAN_TO_GIT_SYNC_HELPER"
#!/usr/bin/env bash
set -euo pipefail
Expand All @@ -19,8 +38,10 @@ fi

export PLAN_TO_GIT_STATE_DIR="${"${"}PLAN_TO_GIT_STATE_DIR:-/tmp/plan-to-git}"

${planToGitRunnerTemplate}

docker_git_plan_to_git_explicit_pr_supported() {
plan-to-git sync --help 2>/dev/null | grep -q -- "--pr <PR>"
docker_git_plan_to_git_run sync --help 2>/dev/null | grep -q -- "--pr <PR>"
}

docker_git_plan_to_git_resolve_pr_number() {
Expand Down Expand Up @@ -61,12 +82,12 @@ docker_git_plan_to_git_sync() {

if [[ -n "$pr_number" ]] && docker_git_plan_to_git_explicit_pr_supported; then
echo "[plan-to-git] Syncing queued agent plans to PR #$pr_number"
plan-to-git sync --pr "$pr_number"
docker_git_plan_to_git_run sync --pr "$pr_number"
return 0
fi

echo "[plan-to-git] Syncing queued agent plans via current branch discovery"
plan-to-git sync
docker_git_plan_to_git_run sync
}

docker_git_plan_to_git_sync
Expand All @@ -83,14 +104,15 @@ if [ "\${DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" != "1" ]; then
echo "[plan-to-git] Error: plan-to-git not found" >&2
exit 1
fi
plan-to-git import-codex --no-sync
plan-to-git import-claude --no-sync
${planToGitRunnerTemplate}
docker_git_plan_to_git_run import-codex --no-sync
docker_git_plan_to_git_run import-claude --no-sync
PLAN_TO_GIT_SYNC_HELPER="\${DOCKER_GIT_PLAN_TO_GIT_SYNC_HELPER:-/opt/docker-git/hooks/plan-to-git-sync}"
if [[ -x "$PLAN_TO_GIT_SYNC_HELPER" ]]; then
"$PLAN_TO_GIT_SYNC_HELPER"
else
echo "[plan-to-git] Sync helper not found; falling back to current branch discovery" >&2
plan-to-git sync
docker_git_plan_to_git_run sync
fi
fi`

Expand All @@ -108,7 +130,8 @@ if ! command -v plan-to-git >/dev/null 2>&1; then
fi

export PLAN_TO_GIT_STATE_DIR="${"${"}PLAN_TO_GIT_STATE_DIR:-/tmp/plan-to-git}"
plan-to-git hook --source codex
${planToGitRunnerTemplate}
docker_git_plan_to_git_run hook --source codex
PLAN_TO_GIT_SYNC_HELPER="${"${"}DOCKER_GIT_PLAN_TO_GIT_SYNC_HELPER:-/opt/docker-git/hooks/plan-to-git-sync}"
"$PLAN_TO_GIT_SYNC_HELPER" >&2 || true
EOF
Expand All @@ -128,7 +151,8 @@ if ! command -v plan-to-git >/dev/null 2>&1; then
fi

export PLAN_TO_GIT_STATE_DIR="${"${"}PLAN_TO_GIT_STATE_DIR:-/tmp/plan-to-git}"
plan-to-git hook --source claude
${planToGitRunnerTemplate}
docker_git_plan_to_git_run hook --source claude
PLAN_TO_GIT_SYNC_HELPER="${"${"}DOCKER_GIT_PLAN_TO_GIT_SYNC_HELPER:-/opt/docker-git/hooks/plan-to-git-sync}"
"$PLAN_TO_GIT_SYNC_HELPER" >&2 || true
EOF
Expand Down
68 changes: 7 additions & 61 deletions packages/container/src/core/templates-entrypoint/post-push-pr.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,10 @@
const postPushPrEnsureTemplate = String
.raw`# CHANGE: ensure an open GitHub PR exists for the pushed branch before PR-bound post-push tools run.
import { renderGitHubRemoteHelpers } from "./github-remotes.js"

const postPushPrEnsureTemplate =
`# CHANGE: ensure an open GitHub PR exists for the pushed branch before PR-bound post-push tools run.
# WHY: issue #375 requires every successful git push to leave the branch with an open PR; plan sync and session backup both target PR discussion.
# REF: issue-375
docker_git_github_repo_from_remote_url() {
local remote_url="$1"
local repo_path=""
local owner=""
local repo=""

case "$remote_url" in
https://github.com/*)
repo_path="${"${"}remote_url#https://github.com/}"
;;
https://github.com/*)
repo_path="${"${"}remote_url#https://github.com/}"
;;
https://*@github.com/*)
repo_path="${"${"}remote_url#https://*@github.com/}"
;;
https://*@github.com/*)
repo_path="${"${"}remote_url#https://*@github.com/}"
;;
ssh://git@github.com/*)
repo_path="${"${"}remote_url#ssh://git@github.com/}"
;;
git@github.com:*)
repo_path="${"${"}remote_url#git@github.com:}"
;;
*)
return 1
;;
esac

repo_path="${"${"}repo_path%%\?*}"
repo_path="${"${"}repo_path%%#*}"
repo_path="${"${"}repo_path%/}"
repo_path="${"${"}repo_path%.git}"
owner="${"${"}repo_path%%/*}"
repo="${"${"}repo_path#*/}"
repo="${"${"}repo%%/*}"
repo="${"${"}repo%.git}"

if [[ -z "$owner" || -z "$repo" || "$owner" == "$repo_path" ]]; then
return 1
fi

printf "%s/%s\n" "$owner" "$repo"
}

docker_git_github_repo_from_remote() {
local remote="$1"
local remote_url=""

remote_url="$(git remote get-url "$remote" 2>/dev/null || true)"
if [[ -z "$remote_url" ]]; then
return 1
fi

docker_git_github_repo_from_remote_url "$remote_url"
}
${renderGitHubRemoteHelpers()}

docker_git_ensure_open_pr() {
local branch=""
Expand Down Expand Up @@ -100,8 +46,8 @@ docker_git_ensure_open_pr() {
if [[ "$head_repo" == "$base_repo" ]]; then
head_arg="$branch"
else
head_owner="${"${"}head_repo%%/*}"
head_arg="${"${"}head_owner}:${"${"}branch}"
head_owner="\${head_repo%%/*}"
head_arg="\${head_owner}:\${branch}"
fi

if ! pr_url="$(gh pr list --repo "$base_repo" --state open --head "$head_arg" --json url --jq '.[0].url // ""' 2>/dev/null)"; then
Expand Down
5 changes: 3 additions & 2 deletions packages/container/src/core/templates/dockerfile-prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ RUN cargo install --git https://github.com/ProverCoderAI/rust-browser-connection
RUN printf "%s\\n" "ALL ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/zz-all \
&& chmod 0440 /etc/sudoers.d/zz-all`

const planToGitRevision = "f60fbe71131854be4c6c1d9fb79abafd2dd6949b"
const planToGitRevision = "4e58e315d3a06db3f9e75682455be315cd29d7c8"

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

Expand Down
Loading
Loading