Skip to content

Commit 6be640d

Browse files
authored
Merge pull request #87 from nebari-dev/single-server-model
refactor: replace multi-server model with single-server model
2 parents 6569385 + 60e2d41 commit 6be640d

39 files changed

+866
-5721
lines changed

cmd/nebi/client.go

Lines changed: 35 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,37 @@ import (
99
"time"
1010

1111
"github.com/nebari-dev/nebi/internal/cliclient"
12-
"github.com/nebari-dev/nebi/internal/localstore"
12+
"github.com/nebari-dev/nebi/internal/store"
1313
)
1414

1515
// ErrWsNotFound is returned when a workspace name is not found on the server.
1616
var ErrWsNotFound = errors.New("workspace not found on server")
1717

18-
// resolveServerFlag returns the server argument, falling back to the default server from config.
19-
func resolveServerFlag(serverArg string) (string, error) {
20-
if serverArg != "" {
21-
return serverArg, nil
22-
}
23-
cfg, err := localstore.LoadConfig()
18+
// getAuthenticatedClient loads credentials and returns an authenticated API client.
19+
func getAuthenticatedClient() (*cliclient.Client, error) {
20+
s, err := store.New()
2421
if err != nil {
25-
return "", fmt.Errorf("loading config: %w", err)
26-
}
27-
if cfg.DefaultServer == "" {
28-
return "", fmt.Errorf("no server specified; use -s <server> or set a default with 'nebi server set-default <name>'")
22+
return nil, err
2923
}
30-
return cfg.DefaultServer, nil
31-
}
24+
defer s.Close()
3225

33-
// getAuthenticatedClient resolves a server name or URL, loads credentials, and returns an authenticated API client.
34-
func getAuthenticatedClient(serverArg string) (*cliclient.Client, error) {
35-
serverURL, err := resolveServerURL(serverArg)
26+
serverURL, err := s.LoadServerURL()
3627
if err != nil {
37-
return nil, err
28+
return nil, fmt.Errorf("loading server URL: %w", err)
29+
}
30+
if serverURL == "" {
31+
return nil, fmt.Errorf("no server configured; run 'nebi login <server-url>' first")
3832
}
3933

40-
creds, err := localstore.LoadCredentials()
34+
creds, err := s.LoadCredentials()
4135
if err != nil {
4236
return nil, fmt.Errorf("loading credentials: %w", err)
4337
}
44-
45-
cred, ok := creds.Servers[serverURL]
46-
if !ok {
47-
return nil, fmt.Errorf("not logged in to %s; run 'nebi login %s' first", serverURL, serverArg)
38+
if creds.Token == "" {
39+
return nil, fmt.Errorf("not logged in; run 'nebi login <server-url>' first")
4840
}
4941

50-
return cliclient.New(serverURL, cred.Token), nil
42+
return cliclient.New(serverURL, creds.Token), nil
5143
}
5244

5345
// findWsByName searches for a workspace by name on the server.
@@ -66,16 +58,6 @@ func findWsByName(client *cliclient.Client, ctx context.Context, name string) (*
6658
return nil, fmt.Errorf("%w: %q", ErrWsNotFound, name)
6759
}
6860

69-
// findGlobalWorkspaceByName looks up a global workspace by name in the local index.
70-
func findGlobalWorkspaceByName(idx *localstore.Index, name string) *localstore.Workspace {
71-
for _, ws := range idx.Workspaces {
72-
if ws.Name == name && ws.Global {
73-
return ws
74-
}
75-
}
76-
return nil
77-
}
78-
7961
// validateWorkspaceName checks that a workspace name doesn't contain path separators or colons,
8062
// which would make it ambiguous with paths or server refs.
8163
func validateWorkspaceName(name string) error {
@@ -91,74 +73,64 @@ func validateWorkspaceName(name string) error {
9173
return nil
9274
}
9375

94-
// lookupOrigin returns the origin for the current working directory and the given server.
95-
// Returns nil (no error) if no origin is set.
96-
func lookupOrigin(server string) (*localstore.Origin, error) {
76+
// lookupOrigin returns the origin fields for the current working directory workspace.
77+
// Returns nil (no error) if no workspace is tracked or no origin is set.
78+
func lookupOrigin() (*store.LocalWorkspace, error) {
9779
cwd, err := os.Getwd()
9880
if err != nil {
9981
return nil, fmt.Errorf("getting working directory: %w", err)
10082
}
10183

102-
store, err := localstore.NewStore()
84+
s, err := store.New()
10385
if err != nil {
10486
return nil, err
10587
}
88+
defer s.Close()
10689

107-
idx, err := store.LoadIndex()
90+
ws, err := s.FindWorkspaceByPath(cwd)
10891
if err != nil {
10992
return nil, err
11093
}
111-
112-
ws, exists := idx.Workspaces[cwd]
113-
if !exists || ws.Origins == nil {
94+
if ws == nil || ws.OriginName == "" {
11495
return nil, nil
11596
}
11697

117-
return ws.Origins[server], nil
98+
return ws, nil
11899
}
119100

120101
// saveOrigin records a push/pull origin for the current working directory.
121-
func saveOrigin(server, name, tag, action, tomlContent, lockContent string) error {
102+
func saveOrigin(name, tag, action, tomlContent, lockContent string) error {
122103
cwd, err := os.Getwd()
123104
if err != nil {
124105
return fmt.Errorf("getting working directory: %w", err)
125106
}
126107

127-
store, err := localstore.NewStore()
108+
s, err := store.New()
128109
if err != nil {
129110
return err
130111
}
112+
defer s.Close()
131113

132-
idx, err := store.LoadIndex()
114+
ws, err := s.FindWorkspaceByPath(cwd)
133115
if err != nil {
134116
return err
135117
}
136-
137-
ws, exists := idx.Workspaces[cwd]
138-
if !exists {
139-
// Not a tracked workspace — nothing to save
118+
if ws == nil {
140119
return nil
141120
}
142121

143-
if ws.Origins == nil {
144-
ws.Origins = make(map[string]*localstore.Origin)
145-
}
146-
147-
tomlHash, err := localstore.TomlContentHash(tomlContent)
122+
tomlHash, err := store.TomlContentHash(tomlContent)
148123
if err != nil {
149124
return fmt.Errorf("hashing pixi.toml: %w", err)
150125
}
151126

152-
ws.Origins[server] = &localstore.Origin{
153-
Name: name,
154-
Tag: tag,
155-
Action: action,
156-
TomlHash: tomlHash,
157-
LockHash: localstore.ContentHash(lockContent),
158-
Timestamp: time.Now().UTC().Format("2006-01-02T15:04:05Z"),
159-
}
127+
ws.OriginName = name
128+
ws.OriginTag = tag
129+
ws.OriginAction = action
130+
ws.OriginTomlHash = tomlHash
131+
ws.OriginLockHash = store.ContentHash(lockContent)
160132

161-
return store.SaveIndex(idx)
133+
return s.SaveWorkspace(ws)
162134
}
163135

164136
// parseWsRef parses a reference in the format workspace:tag.

cmd/nebi/diff.go

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import (
99

1010
"github.com/nebari-dev/nebi/internal/cliclient"
1111
"github.com/nebari-dev/nebi/internal/diff"
12-
"github.com/nebari-dev/nebi/internal/localstore"
12+
"github.com/nebari-dev/nebi/internal/store"
1313
"github.com/spf13/cobra"
1414
)
1515

1616
var diffLock bool
17-
var diffServer string
1817

1918
var diffCmd = &cobra.Command{
2019
Use: "diff <ref-a> [ref-b] [--lock]",
@@ -26,26 +25,25 @@ Each reference can be:
2625
- A server ref (contains a colon): myworkspace:v1
2726
2827
If no refs are given, compares the current directory against the last
29-
pushed/pulled origin on the target server.
28+
pushed/pulled origin.
3029
3130
If only one ref is given, it is compared against the current directory.
3231
3332
Examples:
34-
nebi diff # local vs origin on server
33+
nebi diff # local vs origin
3534
nebi diff ./other-project # other dir vs cwd
3635
nebi diff ./project-a ./project-b # two local dirs
3736
nebi diff data-science # global workspace vs cwd
38-
nebi diff myworkspace:v1 -s work # server version vs cwd
39-
nebi diff myworkspace:v1 myworkspace:v2 -s work # two server versions
40-
nebi diff myworkspace:v1 ./local-dir -s work # server vs local dir
37+
nebi diff myworkspace:v1 # server version vs cwd
38+
nebi diff myworkspace:v1 myworkspace:v2 # two server versions
39+
nebi diff myworkspace:v1 ./local-dir # server vs local dir
4140
4241
Use --lock to also compare pixi.lock files.`,
4342
Args: cobra.RangeArgs(0, 2),
4443
RunE: runDiff,
4544
}
4645

4746
func init() {
48-
diffCmd.Flags().StringVarP(&diffServer, "server", "s", "", "Server name or URL (uses default if not set)")
4947
diffCmd.Flags().BoolVar(&diffLock, "lock", false, "Also diff pixi.lock files")
5048
}
5149

@@ -62,18 +60,14 @@ func runDiff(cmd *cobra.Command, args []string) error {
6260
switch len(args) {
6361
case 0:
6462
// No args — diff origin vs local (origin is baseline, local shows changes)
65-
server, err := resolveServerFlag(diffServer)
66-
if err != nil {
67-
return err
68-
}
69-
origin, err := lookupOrigin(server)
63+
origin, err := lookupOrigin()
7064
if err != nil {
7165
return err
7266
}
7367
if origin == nil {
7468
return fmt.Errorf("no origin set; use 'nebi diff <ref>' or push/pull first")
7569
}
76-
refA = origin.Name + ":" + origin.Tag
70+
refA = origin.OriginName + ":" + origin.OriginTag
7771
refB = "."
7872
case 1:
7973
refA = "."
@@ -150,15 +144,14 @@ func resolveSource(ref, defaultLabel string) (*diffSource, error) {
150144
return resolveLocalSource(ref, defaultLabel)
151145
}
152146

153-
// 2. Global workspace name (check index before assuming server ref)
147+
// 2. Global workspace name (check store before assuming server ref)
154148
if !strings.Contains(ref, ":") {
155-
store, err := localstore.NewStore()
149+
s, err := store.New()
156150
if err == nil {
157-
idx, err := store.LoadIndex()
158-
if err == nil {
159-
if ws := findGlobalWorkspaceByName(idx, ref); ws != nil {
160-
return resolveLocalSource(ws.Path, ref)
161-
}
151+
defer s.Close()
152+
ws, err := s.FindGlobalWorkspaceByName(ref)
153+
if err == nil && ws != nil {
154+
return resolveLocalSource(ws.Path, ref)
162155
}
163156
}
164157
}
@@ -201,12 +194,7 @@ func resolveLocalSource(dir, defaultLabel string) (*diffSource, error) {
201194
func resolveServerSource(ref string) (*diffSource, error) {
202195
wsName, tag := parseWsRef(ref)
203196

204-
server, err := resolveServerFlag(diffServer)
205-
if err != nil {
206-
return nil, err
207-
}
208-
209-
client, err := getAuthenticatedClient(server)
197+
client, err := getAuthenticatedClient()
210198
if err != nil {
211199
return nil, err
212200
}
@@ -273,7 +261,7 @@ func resolveVersionNumber(client *cliclient.Client, ctx context.Context, wsID, w
273261
return latest.VersionNumber, nil
274262
}
275263

276-
// isPath returns true if ref looks like a filesystem path (contains a path separator or is "." or "..").
264+
// isPath returns true if ref looks like a filesystem path.
277265
func isPath(ref string) bool {
278266
return ref == "." || ref == ".." || strings.Contains(ref, "/") || strings.Contains(ref, string(filepath.Separator))
279267
}

0 commit comments

Comments
 (0)