2026-02-21 15:43:14 +00:00
|
|
|
package forge
|
|
|
|
|
|
|
|
|
|
import (
|
2026-04-02 07:58:17 +00:00
|
|
|
"encoding/json"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2026-03-16 19:06:09 +00:00
|
|
|
|
2026-03-26 18:00:20 +00:00
|
|
|
core "dappco.re/go/core"
|
2026-04-02 07:58:17 +00:00
|
|
|
coreio "dappco.re/go/core/io"
|
2026-02-21 15:43:14 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
// DefaultURL is the fallback Forgejo instance URL when neither flag nor
|
|
|
|
|
// environment variable is set.
|
2026-03-26 18:00:20 +00:00
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
// cfgURL, _, _ := forge.ResolveConfig("", "")
|
|
|
|
|
// _ = cfgURL == forge.DefaultURL
|
2026-02-21 15:43:14 +00:00
|
|
|
DefaultURL = "http://localhost:3000"
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-02 07:58:17 +00:00
|
|
|
const defaultConfigPath = ".config/forge/config.json"
|
|
|
|
|
|
|
|
|
|
type configFile struct {
|
|
|
|
|
URL string `json:"url"`
|
|
|
|
|
Token string `json:"token"`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 08:54:24 +00:00
|
|
|
// ConfigPath returns the default config file path used by SaveConfig and
|
|
|
|
|
// ResolveConfig.
|
|
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
//
|
|
|
|
|
// path, err := forge.ConfigPath()
|
|
|
|
|
// _ = path
|
|
|
|
|
func ConfigPath() (string, error) {
|
2026-04-02 07:58:17 +00:00
|
|
|
home, err := os.UserHomeDir()
|
|
|
|
|
if err != nil {
|
2026-04-02 08:54:24 +00:00
|
|
|
return "", core.E("ConfigPath", "forge: resolve home directory", err)
|
2026-04-02 07:58:17 +00:00
|
|
|
}
|
|
|
|
|
return filepath.Join(home, defaultConfigPath), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func readConfigFile() (url, token string, err error) {
|
2026-04-02 08:54:24 +00:00
|
|
|
path, err := ConfigPath()
|
2026-04-02 07:58:17 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return "", "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := coreio.Local.Read(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return "", "", nil
|
|
|
|
|
}
|
|
|
|
|
return "", "", core.E("ResolveConfig", "forge: read config file", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cfg configFile
|
|
|
|
|
if err := json.Unmarshal([]byte(data), &cfg); err != nil {
|
|
|
|
|
return "", "", core.E("ResolveConfig", "forge: decode config file", err)
|
|
|
|
|
}
|
|
|
|
|
return cfg.URL, cfg.Token, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveConfig persists the Forgejo URL and API token to the default config file.
|
2026-04-02 08:44:36 +00:00
|
|
|
// It creates the parent directory if it does not already exist.
|
2026-04-02 07:58:17 +00:00
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
//
|
|
|
|
|
// _ = forge.SaveConfig("https://forge.example.com", "token")
|
|
|
|
|
func SaveConfig(url, token string) error {
|
2026-04-02 08:54:24 +00:00
|
|
|
path, err := ConfigPath()
|
2026-04-02 07:58:17 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-04-02 08:44:36 +00:00
|
|
|
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
|
|
|
|
|
return core.E("SaveConfig", "forge: create config directory", err)
|
|
|
|
|
}
|
2026-04-02 07:58:17 +00:00
|
|
|
payload, err := json.MarshalIndent(configFile{URL: url, Token: token}, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return core.E("SaveConfig", "forge: encode config file", err)
|
|
|
|
|
}
|
|
|
|
|
return coreio.Local.WriteMode(path, string(payload), 0600)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 15:43:14 +00:00
|
|
|
// ResolveConfig resolves the Forgejo URL and API token from flags, environment
|
2026-04-02 07:58:17 +00:00
|
|
|
// variables, config file, and built-in defaults. Priority order:
|
|
|
|
|
// flags > env > config file > defaults.
|
2026-02-21 15:43:14 +00:00
|
|
|
//
|
|
|
|
|
// Environment variables:
|
|
|
|
|
// - FORGE_URL — base URL of the Forgejo instance
|
|
|
|
|
// - FORGE_TOKEN — API token for authentication
|
2026-03-26 18:00:20 +00:00
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
//
|
|
|
|
|
// url, token, err := forge.ResolveConfig("", "")
|
|
|
|
|
// _ = url
|
|
|
|
|
// _ = token
|
2026-02-21 15:43:14 +00:00
|
|
|
func ResolveConfig(flagURL, flagToken string) (url, token string, err error) {
|
2026-04-02 08:40:50 +00:00
|
|
|
if envURL, ok := os.LookupEnv("FORGE_URL"); ok && envURL != "" {
|
2026-04-02 07:58:17 +00:00
|
|
|
url = envURL
|
|
|
|
|
}
|
2026-04-02 08:40:50 +00:00
|
|
|
if envToken, ok := os.LookupEnv("FORGE_TOKEN"); ok && envToken != "" {
|
2026-04-02 07:58:17 +00:00
|
|
|
token = envToken
|
|
|
|
|
}
|
2026-02-21 15:43:14 +00:00
|
|
|
|
|
|
|
|
if flagURL != "" {
|
|
|
|
|
url = flagURL
|
|
|
|
|
}
|
|
|
|
|
if flagToken != "" {
|
|
|
|
|
token = flagToken
|
|
|
|
|
}
|
2026-04-02 08:46:50 +00:00
|
|
|
if url == "" || token == "" {
|
|
|
|
|
fileURL, fileToken, fileErr := readConfigFile()
|
|
|
|
|
if fileErr != nil {
|
|
|
|
|
return "", "", fileErr
|
|
|
|
|
}
|
|
|
|
|
if url == "" {
|
|
|
|
|
url = fileURL
|
|
|
|
|
}
|
|
|
|
|
if token == "" {
|
|
|
|
|
token = fileToken
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-21 15:43:14 +00:00
|
|
|
if url == "" {
|
|
|
|
|
url = DefaultURL
|
|
|
|
|
}
|
|
|
|
|
return url, token, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 07:58:17 +00:00
|
|
|
// NewFromConfig creates a new Forge client using resolved configuration.
|
|
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
//
|
|
|
|
|
// f, err := forge.NewFromConfig("", "")
|
|
|
|
|
// _ = f
|
|
|
|
|
func NewFromConfig(flagURL, flagToken string, opts ...Option) (*Forge, error) {
|
|
|
|
|
return NewForgeFromConfig(flagURL, flagToken, opts...)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 15:43:14 +00:00
|
|
|
// NewForgeFromConfig creates a new Forge client using resolved configuration.
|
2026-04-02 08:54:24 +00:00
|
|
|
// It returns an error if no API token is available from flags, environment,
|
|
|
|
|
// or the saved config file.
|
2026-03-26 18:00:20 +00:00
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
//
|
|
|
|
|
// f, err := forge.NewForgeFromConfig("", "")
|
|
|
|
|
// _ = f
|
2026-02-21 15:43:14 +00:00
|
|
|
func NewForgeFromConfig(flagURL, flagToken string, opts ...Option) (*Forge, error) {
|
|
|
|
|
url, token, err := ResolveConfig(flagURL, flagToken)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if token == "" {
|
2026-03-26 18:00:20 +00:00
|
|
|
return nil, core.E("NewForgeFromConfig", "forge: no API token configured (set FORGE_TOKEN or pass --token)", nil)
|
2026-02-21 15:43:14 +00:00
|
|
|
}
|
|
|
|
|
return NewForge(url, token, opts...), nil
|
|
|
|
|
}
|