package cli import ( "encoding/json" "fmt" "path/filepath" "strings" "time" "github.com/openclaw/gitcrawl/internal/syncer") const syncProgressSchemaV1 = "gitcrawl.sync-progress.v1" type syncProgressState string const ( syncProgressRunning syncProgressState = "running" syncProgressSucceeded syncProgressState = "succeeded" syncProgressFailed syncProgressState = "failed") const ( syncProgressConnecting = syncer.SyncProgressConnecting syncProgressThreads = syncer.SyncProgressThreads syncProgressFinalizing = syncer.SyncProgressFinalizing) type syncProgressSnapshot struct { Schema string `json:"schema"` Repository string `json:"repository"` State syncProgressState `json:"state"` Stage syncer.SyncProgressStage `json:"stage"` IssuesReceived int `json:"issues_received"` PullRequestsReceived int `json:"pull_requests_received"` CommentsReceived int `json:"comments_received"` ObservedAt string `json:"observed_at"`} type syncProgressWriter struct { path string repository string now func() time.Time latest syncer.SyncProgress} func newSyncProgressWriter( path string, repository string,) (*syncProgressWriter, error) { path = strings.TrimSpace(path) if path == "" { return nil, nil } if !filepath.IsAbs(path) { return nil, fmt.Errorf("sync progress path must be absolute") } if strings.TrimSpace(repository) == "" { return nil, fmt.Errorf("sync progress repository is required") } return &syncProgressWriter{ path: path, repository: repository, now: func() time.Time { return time.Now().UTC() }, }, nil} func (w *syncProgressWriter) report(progress syncer.SyncProgress) error { if w == nil { return nil } w.latest = progress return w.write(syncProgressRunning)} func (w *syncProgressWriter) finish(state syncProgressState) error { if w == nil { return nil } return w.write(state)} func (w *syncProgressWriter) write(state syncProgressState) error { snapshot := syncProgressSnapshot{ Schema: syncProgressSchemaV1, Repository: w.repository, State: state, Stage: w.latest.Stage, IssuesReceived: w.latest.IssuesReceived, PullRequestsReceived: w.latest.PullRequestsReceived, CommentsReceived: w.latest.CommentsReceived, ObservedAt: w.now().Format(time.RFC3339Nano), } data, err := json.Marshal(snapshot) if err != nil { return fmt.Errorf("encode sync progress: %w", err) } data = append(data, '\n') if err := writeCaptureAtomically(w.path, data); err != nil { return fmt.Errorf("write sync progress: %w", err) } return nil}
package cli
import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/openclaw/gitcrawl/internal/syncer"
)
const syncProgressSchemaV1 = "gitcrawl.sync-progress.v1"
type syncProgressState string
const (
syncProgressRunning syncProgressState = "running"
syncProgressSucceeded syncProgressState = "succeeded"
syncProgressFailed syncProgressState = "failed"
)
const (
syncProgressConnecting = syncer.SyncProgressConnecting
syncProgressThreads = syncer.SyncProgressThreads
syncProgressFinalizing = syncer.SyncProgressFinalizing
)
type syncProgressSnapshot struct {
Schema string `json:"schema"`
Repository string `json:"repository"`
State syncProgressState `json:"state"`
Stage syncer.SyncProgressStage `json:"stage"`
IssuesReceived int `json:"issues_received"`
PullRequestsReceived int `json:"pull_requests_received"`
CommentsReceived int `json:"comments_received"`
ObservedAt string `json:"observed_at"`
}
type syncProgressWriter struct {
path string
repository string
now func() time.Time
latest syncer.SyncProgress
}
func newSyncProgressWriter(
path string,
repository string,
) (*syncProgressWriter, error) {
path = strings.TrimSpace(path)
if path == "" {
return nil, nil
}
if !filepath.IsAbs(path) {
return nil, fmt.Errorf("sync progress path must be absolute")
}
if strings.TrimSpace(repository) == "" {
return nil, fmt.Errorf("sync progress repository is required")
}
return &syncProgressWriter{
path: path,
repository: repository,
now: func() time.Time { return time.Now().UTC() },
}, nil
}
func (w *syncProgressWriter) report(progress syncer.SyncProgress) error {
if w == nil {
return nil
}
w.latest = progress
return w.write(syncProgressRunning)
}
func (w *syncProgressWriter) finish(state syncProgressState) error {
if w == nil {
return nil
}
return w.write(state)
}
func (w *syncProgressWriter) write(state syncProgressState) error {
snapshot := syncProgressSnapshot{
Schema: syncProgressSchemaV1,
Repository: w.repository,
State: state,
Stage: w.latest.Stage,
IssuesReceived: w.latest.IssuesReceived,
PullRequestsReceived: w.latest.PullRequestsReceived,
CommentsReceived: w.latest.CommentsReceived,
ObservedAt: w.now().Format(time.RFC3339Nano),
}
data, err := json.Marshal(snapshot)
if err != nil {
return fmt.Errorf("encode sync progress: %w", err)
}
data = append(data, '\n')
if err := writeCaptureAtomically(w.path, data); err != nil {
return fmt.Errorf("write sync progress: %w", err)
}
return nil
}