Skip to content

Commit 1492dbc

Browse files
raphaeltmclaude
andcommitted
fix: split snapshot archive helpers and de-flake crash report test
Extract git-bundle and HOME-tar archive helpers from session_snapshot.go into session_snapshot_archive.go to keep both files under the 800-line limit. Make the AgentCrashReportView copy test await the "Copied" state so it no longer races the async clipboard write in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 59fd385 commit 1492dbc

3 files changed

Lines changed: 234 additions & 221 deletions

File tree

packages/acp-client/tests/unit/components/AgentCrashReportView.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('AgentCrashReportView', () => {
5353
expect(copiedReport).toContain('stderr truncated: yes');
5454
expect(copiedReport).toContain('fatal: peer disconnected before response');
5555
expect(copiedReport).not.toContain('sk-secret');
56-
expect(screen.getByRole('button', { name: 'Copied' })).not.toBeNull();
56+
expect(await screen.findByRole('button', { name: 'Copied' })).not.toBeNull();
5757
});
5858

5959
it('shows copy failure feedback when clipboard is unavailable', async () => {

packages/vm-agent/internal/server/session_snapshot.go

Lines changed: 0 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -390,204 +390,6 @@ func (s *Server) doSnapshotJSON(ctx context.Context, method, workspaceID, path,
390390
}
391391
return nil
392392
}
393-
func createWIPBundle(ctx context.Context, workDir string, entryThreshold int64) (string, string, []snapshotSkippedEntry, error) {
394-
if ok, err := standaloneRepositoryPresent(workDir); err != nil || !ok {
395-
if err != nil {
396-
return "", "", nil, err
397-
}
398-
return "", "", nil, nil
399-
}
400-
if gitOperationInProgress(workDir) {
401-
return "", "", []snapshotSkippedEntry{{Path: workDir, Reason: "git operation in progress"}}, nil
402-
}
403-
base, err := runStandaloneGitCommand(ctx, workDir, nil, "rev-parse", "HEAD")
404-
if err != nil {
405-
return "", "", nil, fmt.Errorf("resolve base commit: %w", err)
406-
}
407-
status, err := runStandaloneGitCommand(ctx, workDir, nil, "status", "--porcelain")
408-
if err != nil {
409-
return base, "", nil, fmt.Errorf("git status: %w", err)
410-
}
411-
if strings.TrimSpace(status) == "" {
412-
return base, "", nil, nil
413-
}
414-
415-
indexFile, err := os.CreateTemp("", "sam-session-index-*")
416-
if err != nil {
417-
return base, "", nil, err
418-
}
419-
indexPath := indexFile.Name()
420-
_ = indexFile.Close()
421-
_ = os.Remove(indexPath)
422-
defer os.Remove(indexPath)
423-
gitEnv := []string{"GIT_INDEX_FILE=" + indexPath}
424-
if _, err := runStandaloneGitCommand(ctx, workDir, gitEnv, "read-tree", "HEAD"); err != nil {
425-
return base, "", nil, fmt.Errorf("initialize snapshot index: %w", err)
426-
}
427-
if _, err := runStandaloneGitCommand(ctx, workDir, gitEnv, "add", "-A"); err != nil {
428-
return base, "", nil, fmt.Errorf("stage snapshot index: %w", err)
429-
}
430-
skipped := skipOversizedUntracked(workDir, entryThreshold)
431-
for _, entry := range skipped {
432-
if entry.Path != "" {
433-
_, _ = runStandaloneGitCommand(ctx, workDir, gitEnv, "reset", "--", entry.Path)
434-
}
435-
}
436-
tree, err := runStandaloneGitCommand(ctx, workDir, gitEnv, "write-tree")
437-
if err != nil {
438-
return base, "", skipped, fmt.Errorf("write snapshot tree: %w", err)
439-
}
440-
commitEnv := append(gitEnv, "GIT_AUTHOR_NAME=SAM Snapshot", "GIT_AUTHOR_EMAIL=snapshot@localhost", "GIT_COMMITTER_NAME=SAM Snapshot", "GIT_COMMITTER_EMAIL=snapshot@localhost")
441-
commit, err := runStandaloneGitCommand(ctx, workDir, commitEnv, "commit-tree", tree, "-p", base, "-m", "SAM session snapshot")
442-
if err != nil {
443-
return base, "", skipped, fmt.Errorf("create snapshot commit: %w", err)
444-
}
445-
bundle, err := os.CreateTemp("", "sam-session-wip-*.bundle")
446-
if err != nil {
447-
return base, "", skipped, err
448-
}
449-
bundlePath := bundle.Name()
450-
_ = bundle.Close()
451-
snapshotRef := "refs/sam/session-snapshot/" + strings.TrimSuffix(filepath.Base(bundlePath), ".bundle")
452-
if _, err := runStandaloneGitCommand(ctx, workDir, nil, "update-ref", snapshotRef, commit); err != nil {
453-
_ = os.Remove(bundlePath)
454-
return base, "", skipped, fmt.Errorf("create snapshot ref: %w", err)
455-
}
456-
defer func() {
457-
_, _ = runStandaloneGitCommand(context.Background(), workDir, nil, "update-ref", "-d", snapshotRef)
458-
}()
459-
if _, err := runStandaloneGitCommand(ctx, workDir, nil, "bundle", "create", bundlePath, snapshotRef); err != nil {
460-
_ = os.Remove(bundlePath)
461-
return base, "", skipped, fmt.Errorf("create git bundle: %w", err)
462-
}
463-
return base, bundlePath, skipped, nil
464-
}
465-
func gitOperationInProgress(workDir string) bool {
466-
gitDir := filepath.Join(workDir, ".git")
467-
for _, marker := range []string{"MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"} {
468-
if _, err := os.Stat(filepath.Join(gitDir, marker)); err == nil {
469-
return true
470-
}
471-
}
472-
return false
473-
}
474-
475-
func skipOversizedUntracked(workDir string, threshold int64) []snapshotSkippedEntry {
476-
var skipped []snapshotSkippedEntry
477-
_ = filepath.WalkDir(workDir, func(path string, d os.DirEntry, err error) error {
478-
if err != nil || path == workDir {
479-
return nil
480-
}
481-
if d.IsDir() && d.Name() == ".git" {
482-
return filepath.SkipDir
483-
}
484-
if d.IsDir() {
485-
return nil
486-
}
487-
info, statErr := d.Info()
488-
if statErr != nil || info.Size() <= threshold {
489-
return nil
490-
}
491-
rel, _ := filepath.Rel(workDir, path)
492-
if out, gitErr := runStandaloneGitCommand(context.Background(), workDir, nil, "check-ignore", "-q", rel); gitErr == nil && strings.TrimSpace(out) == "" {
493-
return nil
494-
}
495-
skipped = append(skipped, snapshotSkippedEntry{Path: rel, Reason: "entry exceeds size threshold", SizeBytes: info.Size()})
496-
return nil
497-
})
498-
return skipped
499-
}
500-
501-
func createHomeTar(homeDirFn func() (string, error), entryThreshold, totalBudget int64) (string, []snapshotSkippedEntry, error) {
502-
home, err := homeDirFn()
503-
if err != nil {
504-
return "", nil, err
505-
}
506-
home = filepath.Clean(home)
507-
out, err := os.CreateTemp("", "sam-session-home-*.tar")
508-
if err != nil {
509-
return "", nil, err
510-
}
511-
path := out.Name()
512-
tw := tar.NewWriter(out)
513-
var written int64
514-
var skipped []snapshotSkippedEntry
515-
walkErr := filepath.WalkDir(home, func(path string, d os.DirEntry, err error) error {
516-
if err != nil || path == home {
517-
return nil
518-
}
519-
rel, _ := filepath.Rel(home, path)
520-
if shouldExcludeHomePath(rel) {
521-
if d.IsDir() {
522-
return filepath.SkipDir
523-
}
524-
return nil
525-
}
526-
info, statErr := d.Info()
527-
if statErr != nil {
528-
return nil
529-
}
530-
if info.Size() > entryThreshold {
531-
skipped = append(skipped, snapshotSkippedEntry{Path: "~/" + rel, Reason: "entry exceeds size threshold", SizeBytes: info.Size()})
532-
if d.IsDir() {
533-
return filepath.SkipDir
534-
}
535-
return nil
536-
}
537-
if !info.Mode().IsRegular() && !info.IsDir() {
538-
skipped = append(skipped, snapshotSkippedEntry{Path: "~/" + rel, Reason: "unsupported home entry type"})
539-
return nil
540-
}
541-
if !info.IsDir() && written+info.Size() > totalBudget {
542-
skipped = append(skipped, snapshotSkippedEntry{Path: "~/" + rel, Reason: "snapshot budget exhausted", SizeBytes: info.Size()})
543-
return nil
544-
}
545-
header, headerErr := tar.FileInfoHeader(info, "")
546-
if headerErr != nil {
547-
return nil
548-
}
549-
header.Name = rel
550-
if err := tw.WriteHeader(header); err != nil {
551-
return err
552-
}
553-
if info.Mode().IsRegular() {
554-
f, openErr := os.Open(path)
555-
if openErr != nil {
556-
return nil
557-
}
558-
n, copyErr := io.Copy(tw, f)
559-
_ = f.Close()
560-
written += n
561-
if copyErr != nil {
562-
return copyErr
563-
}
564-
}
565-
return nil
566-
})
567-
closeErr := tw.Close()
568-
fileCloseErr := out.Close()
569-
if walkErr != nil || closeErr != nil || fileCloseErr != nil {
570-
_ = os.Remove(path)
571-
if walkErr != nil {
572-
return "", skipped, walkErr
573-
}
574-
if closeErr != nil {
575-
return "", skipped, closeErr
576-
}
577-
return "", skipped, fileCloseErr
578-
}
579-
return path, skipped, nil
580-
}
581-
582-
func shouldExcludeHomePath(rel string) bool {
583-
first := strings.Split(filepath.ToSlash(rel), "/")[0]
584-
switch first {
585-
case ".cache", ".npm", ".cargo", ".rustup", ".local", "node_modules", ".docker":
586-
return true
587-
default:
588-
return false
589-
}
590-
}
591393

592394
func (s *Server) uploadSnapshotFile(ctx context.Context, uploadPath, filePath, token string, idleTimeout time.Duration) (int64, string, error) {
593395
target := absoluteControlPlaneURL(s.config.ControlPlaneURL, uploadPath)
@@ -788,25 +590,3 @@ func choosePositiveDurationMs(value int64, fallback time.Duration) time.Duration
788590
}
789591
return fallback
790592
}
791-
792-
func rejectSymlinkPath(root, target string) error {
793-
rel, err := filepath.Rel(root, target)
794-
if err != nil || rel == "." || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
795-
return fmt.Errorf("snapshot target is outside home")
796-
}
797-
current := root
798-
for _, part := range strings.Split(rel, string(filepath.Separator)) {
799-
current = filepath.Join(current, part)
800-
info, statErr := os.Lstat(current)
801-
if os.IsNotExist(statErr) {
802-
continue
803-
}
804-
if statErr != nil {
805-
return statErr
806-
}
807-
if info.Mode()&os.ModeSymlink != 0 {
808-
return fmt.Errorf("snapshot target traverses symlink: %s", rel)
809-
}
810-
}
811-
return nil
812-
}

0 commit comments

Comments
 (0)