Skip to content
Open
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ web/dist/*
web/coverage/
web/*.tsbuildinfo
docs/production-checklist.md

# Gas Town (added by gt)
.runtime/
.claude/
.logs/
__pycache__/
5 changes: 5 additions & 0 deletions cmd/wl/cmd_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ func runConfigSet(cmd *cobra.Command, stdout, _ io.Writer, key, value string) er
return hintWrap(err)
}

// Validate mode-backend compatibility at config time.
if key == "mode" && value == federation.ModeWildWest && cfg.ResolveBackend() == federation.BackendRemote {
return fmt.Errorf("wild-west mode requires local backend; switch to local first: wl config set backend local")
}

switch key {
case "mode":
cfg.Mode = value
Expand Down
2 changes: 1 addition & 1 deletion cmd/wl/cmd_join.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func runJoinRemote(stdout, _ io.Writer, upstream, handle, displayName, email, fo

// 2. Register rig via RemoteDB.Exec on a registration branch.
fmt.Fprintf(stdout, " Registering rig via API...\n")
db := backend.NewRemoteDB(token, upstreamOrg, upstreamDB, forkOrg, upstreamDB, federation.ModePR)
db := backend.NewRemoteDB(token, upstreamOrg, upstreamDB, forkOrg, upstreamDB)
branch := fmt.Sprintf("wl/register/%s", handle)
regSQL := commons.BuildRegistrationSQL(handle, forkOrg, displayName, email, "dev")
if err := db.Exec(branch, "", false, regSQL); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/wl/cmd_merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func runMerge(cmd *cobra.Command, stdout, _ io.Writer, branch string, noPush, ke
}

if !noPush {
if err := commons.PushWithSync(cfg.LocalDir, stdout); err != nil {
if err := commons.PushAllRemotes(cfg.LocalDir, stdout); err != nil {
fmt.Fprintf(stdout, "\n %s %s\n", style.Warning.Render(style.IconWarn),
"Push failed — merge saved locally. Run 'wl sync' to retry.")
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/wl/cmd_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func runServe(cmd *cobra.Command, stdout, stderr io.Writer) error {
if err := requireDolt(); err != nil {
return err
}
localDB := backend.NewLocalDB(cfg.LocalDir, cfg.ResolveMode())
localDB := backend.NewLocalDB(cfg.LocalDir, syncFnForMode(cfg.ResolveMode()))
db = localDB

sp := style.StartSpinner(stderr, "Syncing with upstream...")
Expand All @@ -144,7 +144,7 @@ func runServe(cmd *cobra.Command, stdout, stderr io.Writer) error {
if err != nil {
return fmt.Errorf("parsing upstream: %w", err)
}
remoteDB := backend.NewRemoteDB(token, upOrg, upDB, cfg.ForkOrg, cfg.ForkDB, cfg.ResolveMode())
remoteDB := backend.NewRemoteDB(token, upOrg, upDB, cfg.ForkOrg, cfg.ForkDB)
db = remoteDB

sp := style.StartSpinner(stderr, "Syncing fork with upstream...")
Expand Down Expand Up @@ -286,7 +286,7 @@ func runServeHosted(cmd *cobra.Command, stdout, _ io.Writer) error {
apiServer := api.NewHostedWorkspace(hosted.NewClientFunc(), hosted.NewWorkspaceFunc())

// Public read-only RemoteDB against hop/wl-commons (no token needed).
publicDB := backend.NewRemoteDB("", "hop", "wl-commons", "hop", "wl-commons", "")
publicDB := backend.NewRemoteDB("", "hop", "wl-commons", "hop", "wl-commons")

// Scoreboard cache.
scoreboardCache := api.NewScoreboardCache(publicDB, 5*time.Minute)
Expand Down
4 changes: 2 additions & 2 deletions cmd/wl/cmd_tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func runTUI(cmd *cobra.Command, _, stderr io.Writer) error {
if err := requireDolt(); err != nil {
return err
}
localDB := backend.NewLocalDB(cfg.LocalDir, cfg.ResolveMode())
localDB := backend.NewLocalDB(cfg.LocalDir, syncFnForMode(cfg.ResolveMode()))
db = localDB

// Sync before launching the TUI.
Expand All @@ -61,7 +61,7 @@ func runTUI(cmd *cobra.Command, _, stderr io.Writer) error {
if err != nil {
return fmt.Errorf("parsing upstream: %w", err)
}
remoteDB := backend.NewRemoteDB(commons.DoltHubToken(), upOrg, upDB, cfg.ForkOrg, cfg.ForkDB, cfg.ResolveMode())
remoteDB := backend.NewRemoteDB(commons.DoltHubToken(), upOrg, upDB, cfg.ForkOrg, cfg.ForkDB)
db = remoteDB

sp := style.StartSpinner(stderr, "Syncing fork with upstream...")
Expand Down
14 changes: 11 additions & 3 deletions cmd/wl/store_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@ import (
// openDB creates a commons.DB for the given local database directory.
// Package-level variable to allow test overrides.
var openDB = func(localDir string) commons.DB {
return backend.NewLocalDB(localDir, "")
return backend.NewLocalDB(localDir, nil)
}

// syncFnForMode returns the appropriate sync function for the given mode.
func syncFnForMode(mode string) func(string) error {
if mode == federation.ModePR {
return backend.PRSync
}
return backend.WildWestSync
}

// openDBFromConfig creates a commons.DB using the resolved backend from config.
// Package-level variable to allow test overrides.
var openDBFromConfig = func(cfg *federation.Config) (commons.DB, error) {
if cfg.ResolveBackend() == federation.BackendLocal {
return backend.NewLocalDB(cfg.LocalDir, cfg.ResolveMode()), nil
return backend.NewLocalDB(cfg.LocalDir, syncFnForMode(cfg.ResolveMode())), nil
}
if cfg.IsGitHub() {
return nil, fmt.Errorf("GitHub backend requires local dolt\n\n Install: https://docs.dolthub.com/introduction/installation\n Then: wl join --github %s --local-db", cfg.Upstream)
Expand All @@ -31,5 +39,5 @@ var openDBFromConfig = func(cfg *federation.Config) (commons.DB, error) {
if err != nil {
return nil, err
}
return backend.NewRemoteDB(token, upOrg, upDB, cfg.ForkOrg, cfg.ForkDB, cfg.ResolveMode()), nil
return backend.NewRemoteDB(token, upOrg, upDB, cfg.ForkOrg, cfg.ForkDB), nil
}
2 changes: 1 addition & 1 deletion cmd/wl/store_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (noopDB) PushMain(io.Writer) error { return nil }
func (noopDB) Sync() error { return nil }
func (noopDB) MergeBranch(string) error { return nil }
func (noopDB) DeleteRemoteBranch(string) error { return nil }
func (noopDB) PushWithSync(io.Writer) error { return nil }
func (noopDB) PushAllRemotes(io.Writer) error { return nil }
func (noopDB) CanWildWest() error { return nil }

// withFakeSDK overrides newSDKClient and resolveWantedArg for test isolation.
Expand Down
2 changes: 1 addition & 1 deletion internal/api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ func (f *fakeDB) PushMain(_ io.Writer) error { return nil }
func (f *fakeDB) Sync() error { return nil }
func (f *fakeDB) MergeBranch(_ string) error { return nil }
func (f *fakeDB) DeleteRemoteBranch(_ string) error { return nil }
func (f *fakeDB) PushWithSync(_ io.Writer) error { return nil }
func (f *fakeDB) PushAllRemotes(_ io.Writer) error { return nil }
func (f *fakeDB) CanWildWest() error { return nil }

func (f *fakeDB) resolve(id, ref string) *fakeItem {
Expand Down
4 changes: 2 additions & 2 deletions internal/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ type DB interface {
// DeleteRemoteBranch removes a branch on the origin remote. No-op for remote.
DeleteRemoteBranch(branch string) error

// PushWithSync pushes to both upstream and origin with sync retry. No-op for remote.
PushWithSync(stdout io.Writer) error
// PushAllRemotes pushes to both upstream and origin with sync retry. No-op for remote.
PushAllRemotes(stdout io.Writer) error

// CanWildWest returns nil if the backend supports wild-west mode (direct
// upstream writes). Returns an error with a user-facing message if not.
Expand Down
47 changes: 29 additions & 18 deletions internal/backend/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ import (

// LocalDB implements DB using the local dolt CLI.
type LocalDB struct {
dir string
mode string // "pr" or "wild-west"
dir string
syncFn func(dir string) error // injected sync strategy
}

// NewLocalDB creates a DB backed by a local dolt database directory.
// mode determines Sync behavior: "pr" resets to upstream, otherwise pulls.
func NewLocalDB(dir, mode string) *LocalDB {
return &LocalDB{dir: dir, mode: mode}
// syncFn determines Sync behavior; use PRSync or WildWestSync.
// If syncFn is nil, defaults to WildWestSync.
func NewLocalDB(dir string, syncFn func(string) error) *LocalDB {
if syncFn == nil {
syncFn = WildWestSync
}
return &LocalDB{dir: dir, syncFn: syncFn}
}

// Dir returns the local database directory path.
Expand Down Expand Up @@ -88,26 +92,33 @@ func (l *LocalDB) PushMain(stdout io.Writer) error {
return commons.PushOriginMain(l.dir, stdout)
}

// PushWithSync pushes to both upstream and origin with sync retry.
func (l *LocalDB) PushWithSync(stdout io.Writer) error {
return commons.PushWithSync(l.dir, stdout)
// PushAllRemotes pushes to both upstream and origin with sync retry.
func (l *LocalDB) PushAllRemotes(stdout io.Writer) error {
return commons.PushAllRemotes(l.dir, stdout)
}

// CanWildWest returns nil — local databases support wild-west mode.
func (l *LocalDB) CanWildWest() error { return nil }

// Sync pulls latest from upstream. In PR mode, resets main to upstream
// and fetches origin branches so PR mutations are visible via AS OF.
// Sync pulls latest from upstream using the injected sync strategy.
func (l *LocalDB) Sync() error {
if l.mode == "pr" {
if err := commons.ResetMainToUpstream(l.dir); err != nil {
return err
}
_ = commons.FetchRemote(l.dir, "origin")
_ = commons.TrackOriginBranches(l.dir, "wl/")
return nil
return l.syncFn(l.dir)
}

// PRSync resets main to upstream and fetches origin branches so PR
// mutations are visible via AS OF.
func PRSync(dir string) error {
if err := commons.ResetMainToUpstream(dir); err != nil {
return err
}
return commons.PullUpstream(l.dir)
_ = commons.FetchRemote(dir, "origin")
_ = commons.TrackOriginBranches(dir, "wl/")
return nil
}

// WildWestSync pulls latest changes from the upstream remote.
func WildWestSync(dir string) error {
return commons.PullUpstream(dir)
}

// MergeBranch merges a branch into main.
Expand Down
11 changes: 4 additions & 7 deletions internal/backend/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,30 @@ type RemoteDB struct {
readDB string // upstream db name
writeOwner string // fork org
writeDB string // fork db name
mode string // "pr" or "wild-west"
client *http.Client
}

// NewRemoteDB creates a DB backed by the DoltHub REST API.
func NewRemoteDB(token, readOwner, readDB, writeOwner, writeDB, mode string) *RemoteDB {
func NewRemoteDB(token, readOwner, readDB, writeOwner, writeDB string) *RemoteDB {
return &RemoteDB{
token: token,
readOwner: readOwner,
readDB: readDB,
writeOwner: writeOwner,
writeDB: writeDB,
mode: mode,
client: &http.Client{Timeout: 60 * time.Second},
}
}

// NewRemoteDBWithClient creates a DB backed by the DoltHub REST API using a
// pre-configured HTTP client. The client's transport is responsible for auth
// (e.g. Nango proxy), so no token is stored.
func NewRemoteDBWithClient(client *http.Client, readOwner, readDB, writeOwner, writeDB, mode string) *RemoteDB {
func NewRemoteDBWithClient(client *http.Client, readOwner, readDB, writeOwner, writeDB string) *RemoteDB {
return &RemoteDB{
readOwner: readOwner,
readDB: readDB,
writeOwner: writeOwner,
writeDB: writeDB,
mode: mode,
client: client,
}
}
Expand Down Expand Up @@ -191,8 +188,8 @@ func (r *RemoteDB) PushBranch(_ string, _ io.Writer) error { return nil }
// PushMain is a no-op for remote.
func (r *RemoteDB) PushMain(_ io.Writer) error { return nil }

// PushWithSync is a no-op for remote — the write API auto-pushes.
func (r *RemoteDB) PushWithSync(_ io.Writer) error { return nil }
// PushAllRemotes is a no-op for remote — the write API auto-pushes.
func (r *RemoteDB) PushAllRemotes(_ io.Writer) error { return nil }

// CanWildWest returns an error — the DoltHub REST API cannot push from a fork
// to the upstream, so wild-west mode is not supported.
Expand Down
Loading