Extract session subpackage with pure guest-session helpers
Moves the stateless parts of the guest-session subsystem into internal/daemon/session: - consts (BackendSSH, attach/transport kinds, StateRoot, LogTailLineDefault) - StateSnapshot plus ParseState / InspectStateFromDir / ApplyStateSnapshot / StateChanged - 10 on-guest path helpers (StateDir, StdoutLogPath, StdinPipePath, …) - 3 bash script generators (Script, InspectScript, SignalScript) - small utilities (ShellQuote, ExitCode, CloneStringMap, TailFileContent, ProcessAlive + syscallKill test seam, FormatStepError) - launch helpers (DefaultName, DefaultCWD, FailLaunch, NormalizeRequiredCommands, CWDPreflightScript, CommandPreflightScript, AttachInputCommand, AttachTailCommand, EnvLines) Callers inside the daemon package import the new package under the alias "sess" to avoid colliding with the local `session model.GuestSession` variables threaded through the orchestrator code. guest_sessions.go shrinks from 616 → 156 LOC; session_stream.go, session_attach.go, session_lifecycle.go, workspace.go, and guest_sessions_test.go rewire to the exported names. The orchestrator methods (StartGuestSession, BeginGuestSessionAttach, SendToGuestSession, GuestSessionLogs, refresh/inspect, sessionRegistry, guestSessionController) stay on *Daemon. Full Manager-style extraction would need prerequisite phases (operation protocol, workdisk helpers), mirroring Phase 4a's trade-off. All tests green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c13c8b11af
commit
37e02b1576
8 changed files with 612 additions and 566 deletions
|
|
@ -14,6 +14,7 @@ import (
|
|||
"time"
|
||||
|
||||
"banger/internal/api"
|
||||
sess "banger/internal/daemon/session"
|
||||
"banger/internal/model"
|
||||
"banger/internal/system"
|
||||
)
|
||||
|
|
@ -68,8 +69,8 @@ func (d *Daemon) ExportVMWorkspace(ctx context.Context, params api.WorkspaceExpo
|
|||
// past diffRef) and any additional uncommitted changes on top.
|
||||
patchScript := fmt.Sprintf(
|
||||
"set -euo pipefail\ncd %s\ngit add -A\ngit diff --cached %s --binary\n",
|
||||
guestShellQuote(guestPath),
|
||||
guestShellQuote(diffRef),
|
||||
sess.ShellQuote(guestPath),
|
||||
sess.ShellQuote(diffRef),
|
||||
)
|
||||
patch, err := client.RunScriptOutput(ctx, patchScript)
|
||||
if err != nil {
|
||||
|
|
@ -79,8 +80,8 @@ func (d *Daemon) ExportVMWorkspace(ctx context.Context, params api.WorkspaceExpo
|
|||
// Enumerate changed paths (index already staged; this is a cheap read).
|
||||
namesScript := fmt.Sprintf(
|
||||
"set -euo pipefail\ncd %s\ngit diff --cached %s --name-only\n",
|
||||
guestShellQuote(guestPath),
|
||||
guestShellQuote(diffRef),
|
||||
sess.ShellQuote(guestPath),
|
||||
sess.ShellQuote(diffRef),
|
||||
)
|
||||
namesOut, _ := client.RunScriptOutput(ctx, namesScript)
|
||||
var changed []string
|
||||
|
|
@ -153,9 +154,9 @@ func (d *Daemon) prepareVMWorkspaceLocked(ctx context.Context, vm model.VMRecord
|
|||
}
|
||||
if readOnly {
|
||||
var chmodLog bytes.Buffer
|
||||
chmodScript := fmt.Sprintf("set -euo pipefail\nchmod -R a-w %s\n", guestShellQuote(guestPath))
|
||||
chmodScript := fmt.Sprintf("set -euo pipefail\nchmod -R a-w %s\n", sess.ShellQuote(guestPath))
|
||||
if err := client.RunScript(ctx, chmodScript, &chmodLog); err != nil {
|
||||
return model.WorkspacePrepareResult{}, formatGuestSessionStepError("set workspace readonly", err, chmodLog.String())
|
||||
return model.WorkspacePrepareResult{}, sess.FormatStepError("set workspace readonly", err, chmodLog.String())
|
||||
}
|
||||
}
|
||||
return model.WorkspacePrepareResult{
|
||||
|
|
@ -246,13 +247,13 @@ func importWorkspaceRepoToGuest(ctx context.Context, client guestSSHClient, spec
|
|||
switch mode {
|
||||
case model.WorkspacePrepareModeFullCopy:
|
||||
var copyLog bytes.Buffer
|
||||
command := fmt.Sprintf("rm -rf %s && mkdir -p %s && tar -o -C %s --strip-components=1 -xf -", guestShellQuote(guestPath), guestShellQuote(guestPath), guestShellQuote(guestPath))
|
||||
command := fmt.Sprintf("rm -rf %s && mkdir -p %s && tar -o -C %s --strip-components=1 -xf -", sess.ShellQuote(guestPath), sess.ShellQuote(guestPath), sess.ShellQuote(guestPath))
|
||||
if err := client.StreamTar(ctx, spec.RepoRoot, command, ©Log); err != nil {
|
||||
return formatGuestSessionStepError("copy full workspace", err, copyLog.String())
|
||||
return sess.FormatStepError("copy full workspace", err, copyLog.String())
|
||||
}
|
||||
var finalizeLog bytes.Buffer
|
||||
if err := client.RunScript(ctx, workspaceFinalizeScript(spec, guestPath, mode), &finalizeLog); err != nil {
|
||||
return formatGuestSessionStepError("finalize full workspace", err, finalizeLog.String())
|
||||
return sess.FormatStepError("finalize full workspace", err, finalizeLog.String())
|
||||
}
|
||||
return nil
|
||||
case model.WorkspacePrepareModeMetadataOnly, model.WorkspacePrepareModeShallowOverlay:
|
||||
|
|
@ -262,21 +263,21 @@ func importWorkspaceRepoToGuest(ctx context.Context, client guestSSHClient, spec
|
|||
}
|
||||
defer cleanup()
|
||||
var copyLog bytes.Buffer
|
||||
command := fmt.Sprintf("rm -rf %s && mkdir -p %s && tar -o -C %s --strip-components=1 -xf -", guestShellQuote(guestPath), guestShellQuote(guestPath), guestShellQuote(guestPath))
|
||||
command := fmt.Sprintf("rm -rf %s && mkdir -p %s && tar -o -C %s --strip-components=1 -xf -", sess.ShellQuote(guestPath), sess.ShellQuote(guestPath), sess.ShellQuote(guestPath))
|
||||
if err := client.StreamTar(ctx, repoCopyDir, command, ©Log); err != nil {
|
||||
return formatGuestSessionStepError("copy guest git metadata", err, copyLog.String())
|
||||
return sess.FormatStepError("copy guest git metadata", err, copyLog.String())
|
||||
}
|
||||
var scriptLog bytes.Buffer
|
||||
if err := client.RunScript(ctx, workspaceFinalizeScript(spec, guestPath, mode), &scriptLog); err != nil {
|
||||
return formatGuestSessionStepError("prepare guest checkout", err, scriptLog.String())
|
||||
return sess.FormatStepError("prepare guest checkout", err, scriptLog.String())
|
||||
}
|
||||
if mode == model.WorkspacePrepareModeMetadataOnly {
|
||||
return nil
|
||||
}
|
||||
var overlayLog bytes.Buffer
|
||||
command = fmt.Sprintf("tar -o -C %s --strip-components=1 -xf -", guestShellQuote(guestPath))
|
||||
command = fmt.Sprintf("tar -o -C %s --strip-components=1 -xf -", sess.ShellQuote(guestPath))
|
||||
if err := client.StreamTarEntries(ctx, spec.RepoRoot, spec.OverlayPaths, command, &overlayLog); err != nil {
|
||||
return formatGuestSessionStepError("overlay workspace working tree", err, overlayLog.String())
|
||||
return sess.FormatStepError("overlay workspace working tree", err, overlayLog.String())
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
|
|
@ -287,22 +288,22 @@ func importWorkspaceRepoToGuest(ctx context.Context, client guestSSHClient, spec
|
|||
func workspaceFinalizeScript(spec workspaceRepoSpec, guestPath string, mode model.WorkspacePrepareMode) string {
|
||||
var script strings.Builder
|
||||
script.WriteString("set -euo pipefail\n")
|
||||
fmt.Fprintf(&script, "DIR=%s\n", guestShellQuote(guestPath))
|
||||
fmt.Fprintf(&script, "DIR=%s\n", sess.ShellQuote(guestPath))
|
||||
script.WriteString("git config --global --add safe.directory \"$DIR\"\n")
|
||||
if mode != model.WorkspacePrepareModeFullCopy {
|
||||
script.WriteString("find \"$DIR\" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +\n")
|
||||
}
|
||||
switch {
|
||||
case strings.TrimSpace(spec.BranchName) != "":
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" checkout -B %s %s\n", guestShellQuote(spec.BranchName), guestShellQuote(spec.BaseCommit))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" checkout -B %s %s\n", sess.ShellQuote(spec.BranchName), sess.ShellQuote(spec.BaseCommit))
|
||||
case strings.TrimSpace(spec.CurrentBranch) != "":
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" checkout -B %s %s\n", guestShellQuote(spec.CurrentBranch), guestShellQuote(spec.HeadCommit))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" checkout -B %s %s\n", sess.ShellQuote(spec.CurrentBranch), sess.ShellQuote(spec.HeadCommit))
|
||||
default:
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" checkout --detach %s\n", guestShellQuote(spec.HeadCommit))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" checkout --detach %s\n", sess.ShellQuote(spec.HeadCommit))
|
||||
}
|
||||
if strings.TrimSpace(spec.GitUserName) != "" && strings.TrimSpace(spec.GitUserEmail) != "" {
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" config user.name %s\n", guestShellQuote(spec.GitUserName))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" config user.email %s\n", guestShellQuote(spec.GitUserEmail))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" config user.name %s\n", sess.ShellQuote(spec.GitUserName))
|
||||
fmt.Fprintf(&script, "git -C \"$DIR\" config user.email %s\n", sess.ShellQuote(spec.GitUserEmail))
|
||||
}
|
||||
return script.String()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue