Skip to content

Commit 449399d

Browse files
committed
fix: match blacksmith sync markers
1 parent d56b7e6 commit 449399d

4 files changed

Lines changed: 63 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
### Fixed
2020

21+
- Fixed the Blacksmith Testbox sync-stall guard to match current `blacksmith` CLI sync start and completion messages.
2122
- Fixed GCP leases so exact `--type` requests still use configured zone and Spot-to-on-demand fallback, aliases derive GCP class defaults, explicit brokered tags replace Worker default tags, custom networks and ingress policies get separate SSH firewall rules, and brokered pool views include instances outside the Worker's default zone.
2223
- Fixed `crabbox actions hydrate/register` so AWS Windows WSL2 leases can use Linux GitHub Actions hydration instead of being rejected as Windows targets, including root-runner and stale apt-list handling.
2324
- Fixed `scripts/openclaw-wsl2-tests.sh` so follow-up hydrate/run/cleanup commands keep the AWS Windows WSL2 target configuration and warmup failures print captured output.

internal/providers/blacksmith/backend.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ func (b *blacksmithBackend) runTestbox(ctx context.Context, leaseID string, comm
271271
if timedOut {
272272
fmt.Fprintf(
273273
b.rt.Stderr,
274-
"Blacksmith Testbox sync produced no post-sync output for %s; terminating local runner. "+
274+
"Blacksmith Testbox sync did not print a completion marker for %s; terminating local runner. "+
275275
"Rerun with CRABBOX_BLACKSMITH_SYNC_TIMEOUT_MS=0 to disable this guard.\n",
276276
blacksmithSyncTimeout(os.Getenv),
277277
)
@@ -344,18 +344,37 @@ func (b *blacksmithBackend) runCommandWithSyncGuard(ctx context.Context, args []
344344
type blacksmithSyncTracker struct {
345345
mu sync.Mutex
346346
syncingSince time.Time
347+
pending string
347348
}
348349

349350
func (t *blacksmithSyncTracker) observe(text string, now time.Time) {
350351
t.mu.Lock()
351352
defer t.mu.Unlock()
352-
if strings.Contains(text, "Syncing...") {
353+
t.pending += text
354+
if len(t.pending) > 4096 {
355+
t.pending = t.pending[len(t.pending)-4096:]
356+
}
357+
for {
358+
i := strings.IndexByte(t.pending, '\n')
359+
if i < 0 {
360+
break
361+
}
362+
t.observeLineLocked(t.pending[:i+1], now)
363+
t.pending = t.pending[i+1:]
364+
}
365+
if t.pending != "" {
366+
t.observeLineLocked(t.pending, now)
367+
}
368+
}
369+
370+
func (t *blacksmithSyncTracker) observeLineLocked(line string, now time.Time) {
371+
if blacksmithSyncStartPattern.MatchString(line) {
353372
if t.syncingSince.IsZero() {
354373
t.syncingSince = now
355374
}
356375
return
357376
}
358-
if !t.syncingSince.IsZero() && blacksmithPostSyncPattern.MatchString(text) {
377+
if !t.syncingSince.IsZero() && blacksmithSyncDonePattern.MatchString(line) {
359378
t.syncingSince = time.Time{}
360379
}
361380
}

internal/providers/blacksmith/backend_test.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ type blockingSyncRunner struct{}
3434

3535
func (blockingSyncRunner) Run(ctx context.Context, req LocalCommandRequest) (LocalCommandResult, error) {
3636
if req.Stdout != nil {
37-
_, _ = req.Stdout.Write([]byte("Syncing... waiting for remote workspace\n"))
37+
_, _ = req.Stdout.Write([]byte("Syncing from repo root: /repo\n"))
3838
}
3939
<-ctx.Done()
4040
return LocalCommandResult{ExitCode: 1}, ctx.Err()
@@ -319,11 +319,43 @@ func TestBlacksmithRunTerminatesSyncStall(t *testing.T) {
319319
if code != 124 {
320320
t.Fatalf("exit=%d want 124", code)
321321
}
322-
if !strings.Contains(stderr.String(), "Blacksmith Testbox sync produced no post-sync output") {
322+
if !strings.Contains(stderr.String(), "Blacksmith Testbox sync did not print a completion marker") {
323323
t.Fatalf("stderr=%q", stderr.String())
324324
}
325325
}
326326

327+
func TestBlacksmithSyncTrackerMatchesCurrentMarkers(t *testing.T) {
328+
start := time.Unix(100, 0)
329+
tracker := &blacksmithSyncTracker{}
330+
331+
tracker.observe("Syncing from repo root: /repo\n", start)
332+
if !tracker.syncStalled(time.Second, start.Add(2*time.Second)) {
333+
t.Fatal("sync start marker did not arm stall guard")
334+
}
335+
336+
tracker.observe("Changes synced in 2.4s\n", start.Add(500*time.Millisecond))
337+
if tracker.syncStalled(time.Second, start.Add(3*time.Second)) {
338+
t.Fatal("sync completion marker did not clear stall guard")
339+
}
340+
}
341+
342+
func TestBlacksmithSyncTrackerHandlesSplitMarkers(t *testing.T) {
343+
start := time.Unix(100, 0)
344+
tracker := &blacksmithSyncTracker{}
345+
346+
tracker.observe("Syncing from repo", start)
347+
tracker.observe(" root: /repo\n", start)
348+
if !tracker.syncStalled(time.Second, start.Add(2*time.Second)) {
349+
t.Fatal("split sync start marker did not arm stall guard")
350+
}
351+
352+
tracker.observe("Changes synced", start.Add(500*time.Millisecond))
353+
tracker.observe(" in 2.4s\n", start.Add(500*time.Millisecond))
354+
if tracker.syncStalled(time.Second, start.Add(3*time.Second)) {
355+
t.Fatal("split sync completion marker did not clear stall guard")
356+
}
357+
}
358+
327359
func TestBlacksmithBackendUsesInjectedCommandRunnerForListAndStatus(t *testing.T) {
328360
runner := &blacksmithFuncRunner{fn: func(LocalCommandRequest) (LocalCommandResult, error) {
329361
return LocalCommandResult{

internal/providers/blacksmith/helpers.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ import (
1414
const blacksmithTestboxProvider = "blacksmith-testbox"
1515

1616
var (
17-
blacksmithIDPattern = regexp.MustCompile(`\btbx_[A-Za-z0-9_-]+\b`)
18-
blacksmithPostSyncPattern = regexp.MustCompile(`(?i)\b(running|executing|command|pnpm|npm|yarn|bun)\b`)
19-
blacksmithCleanupAttempts = 36
20-
blacksmithCleanupDelay = 5 * time.Second
21-
blacksmithCleanupQuiet = 12
17+
blacksmithIDPattern = regexp.MustCompile(`\btbx_[A-Za-z0-9_-]+\b`)
18+
blacksmithSyncStartPattern = regexp.MustCompile(`(?i)^\s*Syncing(?:\.\.\.| from repo root:)`)
19+
blacksmithSyncDonePattern = regexp.MustCompile(`(?i)^\s*(Changes synced in|No changes to sync|Sync complete)\b`)
20+
blacksmithCleanupAttempts = 36
21+
blacksmithCleanupDelay = 5 * time.Second
22+
blacksmithCleanupQuiet = 12
2223
)
2324

2425
type blacksmithFlagValues struct {

0 commit comments

Comments
 (0)