Skip to content
This repository was archived by the owner on Sep 26, 2025. It is now read-only.

Commit 90c8eb6

Browse files
authored
feat: added cloud subcommand to develop against a cloud project (#969)
1 parent 7427252 commit 90c8eb6

File tree

13 files changed

+679
-37
lines changed

13 files changed

+679
-37
lines changed

clienv/wf_login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func (ce *CliEnv) loginGithub(ctx context.Context) (credentials.Credentials, err
166166
}
167167
}()
168168

169-
signinPage := ce.AuthURL() + "/signin/provider/github/?redirectTo=https://local.dashboard.nhost.run:8099/signin"
169+
signinPage := ce.AuthURL() + "/signin/provider/github/?redirectTo=https://local.dashboard.local.nhost.run:8099/signin"
170170
ce.Infoln("Opening browser to sign-in")
171171
if err := openBrowser(signinPage); err != nil {
172172
return credentials.Credentials{}, err

cmd/config/apply.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package config
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
9+
"github.com/nhost/be/services/mimir/model"
10+
"github.com/nhost/cli/clienv"
11+
"github.com/urfave/cli/v2"
12+
)
13+
14+
func CommandApply() *cli.Command {
15+
return &cli.Command{ //nolint:exhaustruct
16+
Name: "apply",
17+
Aliases: []string{},
18+
Usage: "Apply configuration to cloud project",
19+
Action: commandApply,
20+
Flags: []cli.Flag{
21+
&cli.StringFlag{ //nolint:exhaustruct
22+
Name: flagSubdomain,
23+
Usage: "Subdomain of the Nhost project to apply configuration to. Defaults to linked project",
24+
Required: true,
25+
EnvVars: []string{"NHOST_SUBDOMAIN"},
26+
},
27+
&cli.BoolFlag{ //nolint:exhaustruct
28+
Name: flagYes,
29+
Usage: "Skip confirmation",
30+
EnvVars: []string{"NHOST_YES"},
31+
},
32+
},
33+
}
34+
}
35+
36+
func commandApply(cCtx *cli.Context) error {
37+
ce := clienv.FromCLI(cCtx)
38+
39+
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
40+
if err != nil {
41+
return fmt.Errorf("failed to get app info: %w", err)
42+
}
43+
44+
ce.Infoln("Validating configuration...")
45+
cfg, _, err := ValidateRemote(
46+
cCtx.Context,
47+
ce,
48+
proj.GetSubdomain(),
49+
proj.GetID(),
50+
)
51+
if err != nil {
52+
return err
53+
}
54+
55+
return Apply(cCtx.Context, ce, proj.ID, cfg, cCtx.Bool(flagYes))
56+
}
57+
58+
func Apply(
59+
ctx context.Context,
60+
ce *clienv.CliEnv,
61+
appID string,
62+
cfg *model.ConfigConfig,
63+
skipConfirmation bool,
64+
) error {
65+
if !skipConfirmation {
66+
ce.PromptMessage(
67+
"We are going to overwrite the project's configuration. Do you want to proceed? [y/N] ",
68+
)
69+
resp, err := ce.PromptInput(false)
70+
if err != nil {
71+
return fmt.Errorf("failed to read input: %w", err)
72+
}
73+
if resp != "y" && resp != "Y" {
74+
return errors.New("aborting") //nolint:goerr113
75+
}
76+
}
77+
78+
cl, err := ce.GetNhostClient(ctx)
79+
if err != nil {
80+
return fmt.Errorf("failed to get nhost client: %w", err)
81+
}
82+
83+
b, err := json.Marshal(cfg)
84+
if err != nil {
85+
return fmt.Errorf("failed to marshal config: %w", err)
86+
}
87+
88+
if _, err := cl.ReplaceConfigRawJSON(
89+
ctx,
90+
appID,
91+
string(b),
92+
); err != nil {
93+
return fmt.Errorf("failed to apply config: %w", err)
94+
}
95+
96+
ce.Infoln("Configuration applied successfully!")
97+
98+
return nil
99+
}

cmd/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ func Command() *cli.Command {
1212
Subcommands: []*cli.Command{
1313
CommandDefault(),
1414
CommandExample(),
15+
CommandApply(),
1516
CommandPull(),
1617
CommandShow(),
1718
CommandValidate(),

cmd/config/validate.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ func commandValidate(cCtx *cli.Context) error {
3838

3939
subdomain := cCtx.String(flagSubdomain)
4040
if subdomain != "" && subdomain != "local" {
41-
return ValidateRemote(
41+
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
42+
if err != nil {
43+
return fmt.Errorf("failed to get app info: %w", err)
44+
}
45+
46+
_, _, err = ValidateRemote(
4247
cCtx.Context,
4348
ce,
44-
cCtx.String(flagSubdomain),
49+
proj.GetSubdomain(),
50+
proj.GetID(),
4551
)
52+
return err
4653
}
4754

4855
var secrets model.Secrets
@@ -134,50 +141,46 @@ func ValidateRemote(
134141
ctx context.Context,
135142
ce *clienv.CliEnv,
136143
subdomain string,
137-
) error {
144+
appID string,
145+
) (*model.ConfigConfig, *model.ConfigConfig, error) {
138146
cfg := &model.ConfigConfig{} //nolint:exhaustruct
139147
if err := clienv.UnmarshalFile(ce.Path.NhostToml(), cfg, toml.Unmarshal); err != nil {
140-
return fmt.Errorf("failed to parse config: %w", err)
148+
return nil, nil, fmt.Errorf("failed to parse config: %w", err)
141149
}
142150

143151
schema, err := schema.New()
144152
if err != nil {
145-
return fmt.Errorf("failed to create schema: %w", err)
146-
}
147-
148-
proj, err := ce.GetAppInfo(ctx, subdomain)
149-
if err != nil {
150-
return fmt.Errorf("failed to get app info: %w", err)
153+
return nil, nil, fmt.Errorf("failed to create schema: %w", err)
151154
}
152155

153156
ce.Infoln("Getting secrets...")
154157
cl, err := ce.GetNhostClient(ctx)
155158
if err != nil {
156-
return fmt.Errorf("failed to get nhost client: %w", err)
159+
return nil, nil, fmt.Errorf("failed to get nhost client: %w", err)
157160
}
158161
secretsResp, err := cl.GetSecrets(
159162
ctx,
160-
proj.ID,
163+
appID,
161164
)
162165
if err != nil {
163-
return fmt.Errorf("failed to get secrets: %w", err)
166+
return nil, nil, fmt.Errorf("failed to get secrets: %w", err)
164167
}
165168

166-
if clienv.PathExists(ce.Path.Overlay(proj.GetSubdomain())) {
169+
if clienv.PathExists(ce.Path.Overlay(subdomain)) {
167170
var err error
168-
cfg, err = ApplyJSONPatches(*cfg, ce.Path.Overlay(proj.GetSubdomain()))
171+
cfg, err = ApplyJSONPatches(*cfg, ce.Path.Overlay(subdomain))
169172
if err != nil {
170-
return fmt.Errorf("failed to apply json patches: %w", err)
173+
return nil, nil, fmt.Errorf("failed to apply json patches: %w", err)
171174
}
172175
}
173176

174177
secrets := respToSecrets(secretsResp.GetAppSecrets(), false)
175-
_, err = appconfig.SecretsResolver[model.ConfigConfig](cfg, secrets, schema.Fill)
178+
cfgSecrets, err := appconfig.SecretsResolver[model.ConfigConfig](cfg, secrets, schema.Fill)
176179
if err != nil {
177-
return fmt.Errorf("failed to validate config: %w", err)
180+
return nil, nil, fmt.Errorf("failed to validate config: %w", err)
178181
}
179182

180183
ce.Infoln("Config is valid!")
181184

182-
return nil
185+
return cfg, cfgSecrets, nil
183186
}

0 commit comments

Comments
 (0)