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
31 changes: 31 additions & 0 deletions packages/opencode-router/src/pod-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,37 @@ describe("listUserSessions", () => {
// Must NOT try to look up "calm-snails-dream" on remote (it's always a new branch)
expect(script).not.toContain('ls-remote --exit-code --heads origin "calm-snails-dream"')
})

it("ensurePod git-init script skips fetch/checkout when /workspace has uncommitted changes from a prior session", async () => {
// Pod restarts re-mount the PVC which can contain uncommitted edits from the previous
// session. Running `git checkout` against a dirty workspace aborts with "local changes
// would be overwritten" and the init container crashloops. The script must detect the
// dirty state and skip the git phase, leaving the workspace exactly as the user left it.
fakePVCs = []
fakePods = []
createPodCalls = []

const { ensurePod } = await import("./pod-manager.js")
const session = { email: EMAIL, repoUrl: REPO, branch: "resilient-branch", sourceBranch: "main" }

await (ensurePod as any)(computeHash(EMAIL, REPO, "resilient-branch"), session)

expect(createPodCalls).toHaveLength(1)
const pod = (createPodCalls[0] as any).body
const script: string = pod.spec.initContainers[0].args[0]
// Dirty-workspace guard must check both tracked diffs and untracked files
expect(script).toContain("$GIT diff --quiet HEAD")
expect(script).toContain("$GIT ls-files --others --exclude-standard")
// The guard wraps fetch + both checkout branches so none of them run when dirty
const guardIdx = script.indexOf("$GIT diff --quiet HEAD")
const fetchIdx = script.indexOf("$GIT fetch --all")
const checkoutIdx = script.indexOf("$GIT checkout")
expect(guardIdx).toBeGreaterThan(-1)
expect(fetchIdx).toBeGreaterThan(guardIdx)
expect(checkoutIdx).toBeGreaterThan(guardIdx)
// Must NOT introduce an auto-stash — work is preserved by leaving the workspace untouched
expect(script).not.toContain("stash push")
})
})

// ---------------------------------------------------------------------------
Expand Down
21 changes: 16 additions & 5 deletions packages/opencode-router/src/pod-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,12 +658,23 @@ export async function ensurePod(
` git clone "${repoUrl}" /workspace`,
`fi`,
`cd /workspace`,
`$GIT fetch --all`,
`if $GIT rev-parse --verify "${branch}" >/dev/null 2>&1; then`,
` $GIT checkout "${branch}"`,
// If the workspace carries uncommitted work from a prior session, skip
// fetch + checkout entirely and start the pod against the existing
// state. The repo was initialized on an earlier pod start so the user
// can keep working; otherwise `git checkout` would abort with "local
// changes would be overwritten" and the pod crashloops in Init:0/1.
// To get back on the session branch, commit or discard the changes
// from inside the pod and restart.
`if ! $GIT diff --quiet HEAD 2>/dev/null || [ -n "$($GIT ls-files --others --exclude-standard 2>/dev/null)" ]; then`,
` echo "opencode-init: uncommitted changes detected in /workspace; skipping git fetch/checkout to preserve work"`,
`else`,
` $GIT checkout -B "${sourceBranch}" "origin/${sourceBranch}"`,
` $GIT checkout -b "${branch}"`,
` $GIT fetch --all`,
` if $GIT rev-parse --verify "${branch}" >/dev/null 2>&1; then`,
` $GIT checkout "${branch}"`,
` else`,
` $GIT checkout -B "${sourceBranch}" "origin/${sourceBranch}"`,
` $GIT checkout -b "${branch}"`,
` fi`,
`fi`,
]
: [
Expand Down
Loading