- ninja login forces account selection (interactive when >1); creds now carry ActiveAccountID/Slug. New `ninja account` group. - ninja plugin list / delete / delete-version split public vs active-account @private sections; `publish --private` is sticky in plugin.mod. - GetPluginRequest gains active_account_id so @private resolution works alongside the public (scope, name) path. - publish auto-commits a dirty plugin.mod (path-scoped, leaves other staged paths alone) so the bump→publish loop never trips the dirty check. --allow-dirty is replaced with --strict (default now ships dirty trees via stash-create). - bump auto-commits its plugin.mod write with `bump to X.Y.Z`; --no-commit opts out. - Design doc updated to match the new defaults.
89 lines
2.2 KiB
Go
89 lines
2.2 KiB
Go
package creds
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
type Credentials struct {
|
|
DefaultHost string `json:"default_host"`
|
|
Hosts map[string]HostCreds `json:"hosts"`
|
|
}
|
|
|
|
type HostCreds struct {
|
|
Token string `json:"token"`
|
|
User string `json:"user,omitempty"`
|
|
DefaultScope string `json:"default_scope,omitempty"`
|
|
// ActiveAccountID is the orchestrator-side UUID of the account that
|
|
// account-scoped commands (notably `ninja plugins publish --private`)
|
|
// operate against. Set during `ninja login` (forced selection when the
|
|
// user belongs to more than one account) and changeable via
|
|
// `ninja account set`.
|
|
ActiveAccountID string `json:"active_account_id,omitempty"`
|
|
// ActiveAccountSlug mirrors ActiveAccountID in human-readable form for
|
|
// display in CLI output. The orchestrator-side slug is authoritative;
|
|
// the CLI refreshes it whenever it talks to the server.
|
|
ActiveAccountSlug string `json:"active_account_slug,omitempty"`
|
|
}
|
|
|
|
func filePath() (string, error) {
|
|
dir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(dir, "ninja", "credentials.json"), nil
|
|
}
|
|
|
|
func Load() (*Credentials, error) {
|
|
p, err := filePath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err := os.ReadFile(p)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return &Credentials{Hosts: map[string]HostCreds{}}, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c := &Credentials{Hosts: map[string]HostCreds{}}
|
|
if err := json.Unmarshal(b, c); err != nil {
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (c *Credentials) Save() error {
|
|
p, err := filePath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(p), 0o700); err != nil {
|
|
return err
|
|
}
|
|
b, err := json.MarshalIndent(c, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(p, b, 0o600)
|
|
}
|
|
|
|
func (c *Credentials) Resolve(host string) (string, HostCreds, error) {
|
|
if host == "" {
|
|
host = c.DefaultHost
|
|
}
|
|
if host == "" {
|
|
host = "https://my.blockninjacms.com"
|
|
}
|
|
if t := os.Getenv("NINJA_TOKEN"); t != "" {
|
|
return host, HostCreds{Token: t}, nil
|
|
}
|
|
hc, ok := c.Hosts[host]
|
|
if !ok || hc.Token == "" {
|
|
return host, HostCreds{}, errors.New("not logged in; run `ninja login`")
|
|
}
|
|
return host, hc, nil
|
|
}
|