Skip to content

Commit 3d96fe0

Browse files
authored
Merge pull request #61 from intercube/feat/sync-rewrite-env-targets
Rewrite sync flow and introduce ssh command
2 parents 9228102 + 93a9665 commit 3d96fe0

35 files changed

Lines changed: 4253 additions & 387 deletions

README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,38 @@ intercube onboarding
1111

1212
The wizard can help you:
1313
- configure login defaults (`username`, `password`, `scope`, `auth_method`, `instance_url`)
14-
- optionally set sync defaults (`remote_user`, `file_syncing.from_server`, `file_syncing.path`)
15-
- verify local prerequisites such as Boundary CLI and the Intercube sync helper
14+
- optionally configure file path mappings for `intercube sync`
15+
- verify local prerequisites such as Boundary CLI and `rsync`
1616

1717
After onboarding, use:
1818

1919
```bash
20-
intercube login
20+
intercube ssh
2121
```
2222

2323
If required settings are missing (or the config file does not exist), the CLI
2424
will prompt only when needed and save values automatically:
25-
- `intercube login` prompts for required login settings
26-
- `intercube sync files ...` prompts for missing sync defaults
27-
- `intercube map` prompts to create mappings when none exist
25+
- `intercube ssh` prompts for required login settings
26+
- `intercube sync` prompts for missing file path mappings
27+
- `intercube map --interactive` prompts to create mappings when none exist
28+
29+
`intercube login` is kept as a deprecated alias and prints a warning to use `intercube ssh`.
30+
31+
### Sync
32+
33+
Use sync from a source environment host:
34+
35+
```bash
36+
intercube sync
37+
intercube sync staging.example.com
38+
intercube sync --files
39+
intercube sync --database
40+
intercube sync --dry-run
41+
```
42+
43+
Behavior:
44+
- always fetches current site inventory at runtime
45+
- interactive target selection when no argument is passed
46+
- argument auto-resolves against site ID/domain/server/user when possible
47+
- stores only file path mappings in config (`sync.files.items`)
48+
- database details are requested interactively for each run (not persisted)

cmd/auth-login.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"strings"
8+
"time"
9+
10+
"github.com/intercube/cli/util/appconfig"
11+
authutil "github.com/intercube/cli/util/auth"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var authLoginCmd = &cobra.Command{
16+
Use: "login",
17+
Short: "Sign in with Clerk in your browser",
18+
RunE: func(cmd *cobra.Command, args []string) error {
19+
if err := appconfig.ValidateClerk(); err != nil {
20+
return fmt.Errorf("%w (set via env/.env or build-time)", err)
21+
}
22+
23+
store, err := authutil.NewSessionStore("intercube-cli")
24+
if err != nil {
25+
return err
26+
}
27+
28+
var previousSession *authutil.Session
29+
storedSession, loadErr := store.Load(cmd.Context())
30+
if loadErr == nil {
31+
previousSession = storedSession
32+
} else if !errors.Is(loadErr, authutil.ErrNoSession) {
33+
return loadErr
34+
}
35+
36+
clerkClient := &authutil.ClerkClient{
37+
Issuer: appconfig.ClerkIssuer,
38+
ClientID: appconfig.ClerkClientID,
39+
Audience: appconfig.ClerkAudience,
40+
Scopes: appconfig.ClerkScopes,
41+
CallbackPort: appconfig.ParsedCallbackPort(),
42+
}
43+
44+
ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Minute)
45+
defer cancel()
46+
47+
fmt.Println("Opening browser for Clerk sign-in...")
48+
session, err := clerkClient.Login(ctx)
49+
if err != nil {
50+
return err
51+
}
52+
53+
if previousSession != nil {
54+
session.OrganizationID = strings.TrimSpace(previousSession.OrganizationID)
55+
for _, known := range previousSession.KnownOrgIDs {
56+
session.KnownOrgIDs = addKnownOrganizationID(session.KnownOrgIDs, known)
57+
}
58+
}
59+
60+
if appconfig.OrganizationID != "" {
61+
session.KnownOrgIDs = addKnownOrganizationID(session.KnownOrgIDs, appconfig.OrganizationID)
62+
}
63+
64+
session.KnownOrgIDs = addKnownOrganizationID(session.KnownOrgIDs, session.OrganizationID)
65+
66+
if err := store.Save(ctx, session); err != nil {
67+
return err
68+
}
69+
70+
fmt.Printf("Authenticated. Session expires at %s\n", session.ExpiresAt.Format(time.RFC3339))
71+
72+
if selectErr := runAuthOrgSelect(cmd, nil, true); selectErr != nil {
73+
return selectErr
74+
}
75+
76+
return nil
77+
},
78+
}
79+
80+
func init() {
81+
authCmd.AddCommand(authLoginCmd)
82+
}

cmd/auth-logout.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/intercube/cli/util/appconfig"
7+
authutil "github.com/intercube/cli/util/auth"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var authLogoutCmd = &cobra.Command{
12+
Use: "logout",
13+
Short: "Clear local API auth session",
14+
RunE: func(cmd *cobra.Command, args []string) error {
15+
store, err := authutil.NewSessionStore("intercube-cli")
16+
if err != nil {
17+
return err
18+
}
19+
20+
session, err := store.Load(cmd.Context())
21+
if err == nil && session != nil {
22+
if appconfig.ValidateClerk() == nil {
23+
clerkClient := &authutil.ClerkClient{
24+
Issuer: appconfig.ClerkIssuer,
25+
ClientID: appconfig.ClerkClientID,
26+
}
27+
_ = clerkClient.RevokeRefreshToken(cmd.Context(), session.RefreshToken)
28+
}
29+
}
30+
31+
if err := store.Clear(cmd.Context()); err != nil {
32+
return err
33+
}
34+
35+
fmt.Println("Signed out.")
36+
return nil
37+
},
38+
}
39+
40+
func init() {
41+
authCmd.AddCommand(authLogoutCmd)
42+
}

0 commit comments

Comments
 (0)