Skip to content
This repository was archived by the owner on Sep 26, 2025. It is now read-only.
Merged
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
2 changes: 1 addition & 1 deletion clienv/wf_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (ce *CliEnv) loginGithub(ctx context.Context) (credentials.Credentials, err
}
}()

signinPage := ce.AuthURL() + "/signin/provider/github/?redirectTo=https://local.dashboard.nhost.run:8099/signin"
signinPage := ce.AuthURL() + "/signin/provider/github/?redirectTo=https://local.dashboard.local.nhost.run:8099/signin"
ce.Infoln("Opening browser to sign-in")
if err := openBrowser(signinPage); err != nil {
return credentials.Credentials{}, err
Expand Down
99 changes: 99 additions & 0 deletions cmd/config/apply.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package config

import (
"context"
"encoding/json"
"errors"
"fmt"

"github.com/nhost/be/services/mimir/model"
"github.com/nhost/cli/clienv"
"github.com/urfave/cli/v2"
)

func CommandApply() *cli.Command {
return &cli.Command{ //nolint:exhaustruct
Name: "apply",
Aliases: []string{},
Usage: "Apply configuration to cloud project",
Action: commandApply,
Flags: []cli.Flag{
&cli.StringFlag{ //nolint:exhaustruct
Name: flagSubdomain,
Usage: "Subdomain of the Nhost project to apply configuration to. Defaults to linked project",
Required: true,
EnvVars: []string{"NHOST_SUBDOMAIN"},
},
&cli.BoolFlag{ //nolint:exhaustruct
Name: flagYes,
Usage: "Skip confirmation",
EnvVars: []string{"NHOST_YES"},
},
},
}
}

func commandApply(cCtx *cli.Context) error {
ce := clienv.FromCLI(cCtx)

proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
if err != nil {
return fmt.Errorf("failed to get app info: %w", err)
}

ce.Infoln("Validating configuration...")
cfg, _, err := ValidateRemote(
cCtx.Context,
ce,
proj.GetSubdomain(),
proj.GetID(),
)
if err != nil {
return err
}

return Apply(cCtx.Context, ce, proj.ID, cfg, cCtx.Bool(flagYes))
}

func Apply(
ctx context.Context,
ce *clienv.CliEnv,
appID string,
cfg *model.ConfigConfig,
skipConfirmation bool,
) error {
if !skipConfirmation {
ce.PromptMessage(
"We are going to overwrite the project's configuration. Do you want to proceed? [y/N] ",
)
resp, err := ce.PromptInput(false)
if err != nil {
return fmt.Errorf("failed to read input: %w", err)
}
if resp != "y" && resp != "Y" {
return errors.New("aborting") //nolint:goerr113
}
}

cl, err := ce.GetNhostClient(ctx)
if err != nil {
return fmt.Errorf("failed to get nhost client: %w", err)
}

b, err := json.Marshal(cfg)
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}

if _, err := cl.ReplaceConfigRawJSON(
ctx,
appID,
string(b),
); err != nil {
return fmt.Errorf("failed to apply config: %w", err)
}

ce.Infoln("Configuration applied successfully!")

return nil
}
1 change: 1 addition & 0 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func Command() *cli.Command {
Subcommands: []*cli.Command{
CommandDefault(),
CommandExample(),
CommandApply(),
CommandPull(),
CommandShow(),
CommandValidate(),
Expand Down
41 changes: 22 additions & 19 deletions cmd/config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,18 @@ func commandValidate(cCtx *cli.Context) error {

subdomain := cCtx.String(flagSubdomain)
if subdomain != "" && subdomain != "local" {
return ValidateRemote(
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
if err != nil {
return fmt.Errorf("failed to get app info: %w", err)
}

_, _, err = ValidateRemote(
cCtx.Context,
ce,
cCtx.String(flagSubdomain),
proj.GetSubdomain(),
proj.GetID(),
)
return err
}

var secrets model.Secrets
Expand Down Expand Up @@ -134,50 +141,46 @@ func ValidateRemote(
ctx context.Context,
ce *clienv.CliEnv,
subdomain string,
) error {
appID string,
) (*model.ConfigConfig, *model.ConfigConfig, error) {
cfg := &model.ConfigConfig{} //nolint:exhaustruct
if err := clienv.UnmarshalFile(ce.Path.NhostToml(), cfg, toml.Unmarshal); err != nil {
return fmt.Errorf("failed to parse config: %w", err)
return nil, nil, fmt.Errorf("failed to parse config: %w", err)
}

schema, err := schema.New()
if err != nil {
return fmt.Errorf("failed to create schema: %w", err)
}

proj, err := ce.GetAppInfo(ctx, subdomain)
if err != nil {
return fmt.Errorf("failed to get app info: %w", err)
return nil, nil, fmt.Errorf("failed to create schema: %w", err)
}

ce.Infoln("Getting secrets...")
cl, err := ce.GetNhostClient(ctx)
if err != nil {
return fmt.Errorf("failed to get nhost client: %w", err)
return nil, nil, fmt.Errorf("failed to get nhost client: %w", err)
}
secretsResp, err := cl.GetSecrets(
ctx,
proj.ID,
appID,
)
if err != nil {
return fmt.Errorf("failed to get secrets: %w", err)
return nil, nil, fmt.Errorf("failed to get secrets: %w", err)
}

if clienv.PathExists(ce.Path.Overlay(proj.GetSubdomain())) {
if clienv.PathExists(ce.Path.Overlay(subdomain)) {
var err error
cfg, err = ApplyJSONPatches(*cfg, ce.Path.Overlay(proj.GetSubdomain()))
cfg, err = ApplyJSONPatches(*cfg, ce.Path.Overlay(subdomain))
if err != nil {
return fmt.Errorf("failed to apply json patches: %w", err)
return nil, nil, fmt.Errorf("failed to apply json patches: %w", err)
}
}

secrets := respToSecrets(secretsResp.GetAppSecrets(), false)
_, err = appconfig.SecretsResolver[model.ConfigConfig](cfg, secrets, schema.Fill)
cfgSecrets, err := appconfig.SecretsResolver[model.ConfigConfig](cfg, secrets, schema.Fill)
if err != nil {
return fmt.Errorf("failed to validate config: %w", err)
return nil, nil, fmt.Errorf("failed to validate config: %w", err)
}

ce.Infoln("Config is valid!")

return nil
return cfg, cfgSecrets, nil
}
Loading
Loading