2025-12-08 09:49:07 +01:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
"syscall"
|
|
|
|
|
|
2026-04-26 08:16:52 -06:00
|
|
|
"forgejo.zerova.net/public/fj/internal/api"
|
|
|
|
|
"forgejo.zerova.net/public/fj/internal/config"
|
2026-01-18 11:45:46 +01:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
"github.com/spf13/viper"
|
2025-12-08 09:49:07 +01:00
|
|
|
"golang.org/x/term"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var authCmd = &cobra.Command{
|
|
|
|
|
Use: "auth",
|
2026-04-26 08:16:52 -06:00
|
|
|
Short: "Authenticate fj with a Forgejo instance",
|
2025-12-08 09:49:07 +01:00
|
|
|
Long: "Manage authentication state for Forgejo instances.",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var authLoginCmd = &cobra.Command{
|
|
|
|
|
Use: "login",
|
|
|
|
|
Short: "Authenticate with a Forgejo instance",
|
|
|
|
|
Long: "Authenticate with a Forgejo instance using a personal access token.",
|
|
|
|
|
RunE: runAuthLogin,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var authStatusCmd = &cobra.Command{
|
|
|
|
|
Use: "status",
|
|
|
|
|
Short: "View authentication status",
|
|
|
|
|
Long: "Display the authentication status for configured Forgejo instances.",
|
|
|
|
|
RunE: runAuthStatus,
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 11:45:46 +01:00
|
|
|
var authLogoutCmd = &cobra.Command{
|
|
|
|
|
Use: "logout",
|
|
|
|
|
Short: "Remove authentication for a Forgejo instance",
|
|
|
|
|
Long: "Remove authentication for a configured Forgejo instance.",
|
|
|
|
|
RunE: runAuthLogout,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var authTokenCmd = &cobra.Command{
|
|
|
|
|
Use: "token",
|
|
|
|
|
Short: "Print the stored authentication token",
|
|
|
|
|
Long: "Print the stored authentication token for a configured Forgejo instance.",
|
|
|
|
|
RunE: runAuthToken,
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 09:49:07 +01:00
|
|
|
func init() {
|
|
|
|
|
rootCmd.AddCommand(authCmd)
|
|
|
|
|
authCmd.AddCommand(authLoginCmd)
|
|
|
|
|
authCmd.AddCommand(authStatusCmd)
|
2026-01-18 11:45:46 +01:00
|
|
|
authCmd.AddCommand(authLogoutCmd)
|
|
|
|
|
authCmd.AddCommand(authTokenCmd)
|
2025-12-08 09:49:07 +01:00
|
|
|
|
fix(cmd): correctness + audit hardening across cmd/ + internal/
Addresses audit findings from a tri-partite review (codex + 2 Claude agents).
Multiple distinct fixes here because they touched overlapping files; happy
to split via interactive rebase if a reviewer prefers.
## Correctness bugs (HIGH)
* `--config` is now actually honored. cmd/root.initConfig fed Viper but
every command that mattered loaded config via `internal/config.Load()`
which always read the default path. Added `config.SetExplicitConfigPath`
consulted by `GetConfigPath`; `--config other.yaml auth login` now writes
to other.yaml.
- internal/config/config.go, cmd/root.go
* `--json` now works on `fj run …`, `fj workflow …`, and `fj wiki view`.
cmd/aliases.go registered `--json` as a Bool but the handlers call
`wantJSON()` which does `GetString("json")` and silently ignores the
type-error return. cmd/wiki.go did the inverse (`GetBool("json")` against
a string-registered flag). Both now use `addJSONFlags`/`wantJSON`/
`outputJSON` consistently.
- cmd/aliases.go, cmd/wiki.go
* `fj api` no longer lets endpoints escape the /api/v1 base via
path-traversal. `fj api '/../admin/users'` previously normalized to
`/admin/users` because `http.NewRequest` resolves `..` segments —
silently sending authenticated traffic to non-API routes. Endpoint is
now parsed, `..` segments are rejected, and JoinPath is used.
- cmd/api.go
## Design rework (BREAKING — gets rid of the `--json=fields` quirk)
* `--json` flag rebuilt from a string-with-NoOptDefVal=" " sentinel into a
plain Bool. `--json-fields` keeps comma-separated projection. The two
are mutually exclusive (`MarkFlagsMutuallyExclusive`). `--jq` composes
with either or neither. The previous design produced a `--json string[=" "]`
in --help and required `--json=fields` (with literal "=") because
`--json fields` was parsed as the bare flag plus a positional. Gone.
- cmd/json.go: addJSONFlags / wantJSON / outputJSON
- cmd/api.go: example block reflects the new shape
Migration: `--json=fields` → `--json-fields fields`. Bare `--json` still
means "everything as JSON".
* `fj api` now uses `internal/api.SharedHTTPClient` (30 s timeout, pooled)
instead of constructing a zero-value `&http.Client{}` with no timeout.
A hung Forgejo no longer pins the CLI indefinitely. Response body is
also bounded by `io.LimitReader` at 64 MB to prevent OOM-on-self.
- internal/api/client.go (export SharedHTTPClient), cmd/api.go
* `--hostname` declared as a persistent flag on rootCmd is now the only
declaration. cmd/auth.go re-declared `--hostname` on three subcommands,
shadowing the persistent flag — meaning `fj --hostname=X auth login`
and `fj auth login --hostname=X` went through different code paths
(viper read vs. local flag read). Local declarations removed.
- cmd/auth.go
## Hardening (MEDIUM/LOW)
* `--token` on `auth login` now emits a stderr warning when used, since
it puts the PAT on argv (visible in `ps auxe`/shell history). Flag not
removed — too disruptive — but discoverable now.
- cmd/auth.go
* Error handling no longer regex-matches "401"/"403" against rendered
error strings (would have triggered "auth login" hint for an error
that just mentioned issue #403). Now relies on typed `*api.APIError`.
Hints moved to a separate `Hint` field on `CLIError`, so JSON-error
consumers get clean structure and the human renderer still appends
"\nHint: …".
- cmd/errors.go
* `migrateConfigDir` now opens dst with `O_TRUNC` instead of just
`O_CREATE|O_WRONLY`. Previously a partially-pre-existing dst file
would have legacy contents overwrite a prefix and leave stale tail
bytes — silent YAML/token corruption.
- cmd/root.go (extracted into copyOneConfigFile with proper close handling)
* Config dir created with mode 0700 instead of 0755. `initConfig` warns
on stderr if the resolved config file is world/group readable
(`mode & 0o077 != 0`); doesn't fail-close.
- cmd/root.go
* Network errors (`no such host`, `connection refused`, `i/o timeout`)
now return a structured `CLIError` with code `ErrNetworkError` and a
hint, instead of a fmt.Errorf chain.
- cmd/errors.go
Verified: `go build ./...` and `go test ./...` clean. Live integration
tested against forgejo.zerova.net.
Out of scope, deferred to follow-up commits:
- Pagination unification across `repo list`/`pr list`/`issue list` (only
`release list` walks pages today; others silently truncate).
- `fj api --paginate` to follow pages like `gh api --paginate`.
- De-duplicating cmd/aliases.go ↔ cmd/actions.go subtrees.
2026-05-02 15:41:48 -06:00
|
|
|
// --hostname is a persistent flag on rootCmd (cmd/root.go). Don't
|
|
|
|
|
// re-declare it on auth subcommands — local flags shadow the persistent
|
|
|
|
|
// one, so `fj --hostname=X auth login` and `fj auth login --hostname=X`
|
|
|
|
|
// went through different code paths (viper vs. local).
|
|
|
|
|
authLoginCmd.Flags().StringP("token", "t", "", "Personal access token (DEPRECATED: visible in `ps auxe`; pipe via stdin instead)")
|
2025-12-08 09:49:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runAuthLogin(cmd *cobra.Command, args []string) error {
|
|
|
|
|
hostname, _ := cmd.Flags().GetString("hostname")
|
|
|
|
|
token, _ := cmd.Flags().GetString("token")
|
|
|
|
|
|
fix(cmd): correctness + audit hardening across cmd/ + internal/
Addresses audit findings from a tri-partite review (codex + 2 Claude agents).
Multiple distinct fixes here because they touched overlapping files; happy
to split via interactive rebase if a reviewer prefers.
## Correctness bugs (HIGH)
* `--config` is now actually honored. cmd/root.initConfig fed Viper but
every command that mattered loaded config via `internal/config.Load()`
which always read the default path. Added `config.SetExplicitConfigPath`
consulted by `GetConfigPath`; `--config other.yaml auth login` now writes
to other.yaml.
- internal/config/config.go, cmd/root.go
* `--json` now works on `fj run …`, `fj workflow …`, and `fj wiki view`.
cmd/aliases.go registered `--json` as a Bool but the handlers call
`wantJSON()` which does `GetString("json")` and silently ignores the
type-error return. cmd/wiki.go did the inverse (`GetBool("json")` against
a string-registered flag). Both now use `addJSONFlags`/`wantJSON`/
`outputJSON` consistently.
- cmd/aliases.go, cmd/wiki.go
* `fj api` no longer lets endpoints escape the /api/v1 base via
path-traversal. `fj api '/../admin/users'` previously normalized to
`/admin/users` because `http.NewRequest` resolves `..` segments —
silently sending authenticated traffic to non-API routes. Endpoint is
now parsed, `..` segments are rejected, and JoinPath is used.
- cmd/api.go
## Design rework (BREAKING — gets rid of the `--json=fields` quirk)
* `--json` flag rebuilt from a string-with-NoOptDefVal=" " sentinel into a
plain Bool. `--json-fields` keeps comma-separated projection. The two
are mutually exclusive (`MarkFlagsMutuallyExclusive`). `--jq` composes
with either or neither. The previous design produced a `--json string[=" "]`
in --help and required `--json=fields` (with literal "=") because
`--json fields` was parsed as the bare flag plus a positional. Gone.
- cmd/json.go: addJSONFlags / wantJSON / outputJSON
- cmd/api.go: example block reflects the new shape
Migration: `--json=fields` → `--json-fields fields`. Bare `--json` still
means "everything as JSON".
* `fj api` now uses `internal/api.SharedHTTPClient` (30 s timeout, pooled)
instead of constructing a zero-value `&http.Client{}` with no timeout.
A hung Forgejo no longer pins the CLI indefinitely. Response body is
also bounded by `io.LimitReader` at 64 MB to prevent OOM-on-self.
- internal/api/client.go (export SharedHTTPClient), cmd/api.go
* `--hostname` declared as a persistent flag on rootCmd is now the only
declaration. cmd/auth.go re-declared `--hostname` on three subcommands,
shadowing the persistent flag — meaning `fj --hostname=X auth login`
and `fj auth login --hostname=X` went through different code paths
(viper read vs. local flag read). Local declarations removed.
- cmd/auth.go
## Hardening (MEDIUM/LOW)
* `--token` on `auth login` now emits a stderr warning when used, since
it puts the PAT on argv (visible in `ps auxe`/shell history). Flag not
removed — too disruptive — but discoverable now.
- cmd/auth.go
* Error handling no longer regex-matches "401"/"403" against rendered
error strings (would have triggered "auth login" hint for an error
that just mentioned issue #403). Now relies on typed `*api.APIError`.
Hints moved to a separate `Hint` field on `CLIError`, so JSON-error
consumers get clean structure and the human renderer still appends
"\nHint: …".
- cmd/errors.go
* `migrateConfigDir` now opens dst with `O_TRUNC` instead of just
`O_CREATE|O_WRONLY`. Previously a partially-pre-existing dst file
would have legacy contents overwrite a prefix and leave stale tail
bytes — silent YAML/token corruption.
- cmd/root.go (extracted into copyOneConfigFile with proper close handling)
* Config dir created with mode 0700 instead of 0755. `initConfig` warns
on stderr if the resolved config file is world/group readable
(`mode & 0o077 != 0`); doesn't fail-close.
- cmd/root.go
* Network errors (`no such host`, `connection refused`, `i/o timeout`)
now return a structured `CLIError` with code `ErrNetworkError` and a
hint, instead of a fmt.Errorf chain.
- cmd/errors.go
Verified: `go build ./...` and `go test ./...` clean. Live integration
tested against forgejo.zerova.net.
Out of scope, deferred to follow-up commits:
- Pagination unification across `repo list`/`pr list`/`issue list` (only
`release list` walks pages today; others silently truncate).
- `fj api --paginate` to follow pages like `gh api --paginate`.
- De-duplicating cmd/aliases.go ↔ cmd/actions.go subtrees.
2026-05-02 15:41:48 -06:00
|
|
|
// Tokens passed via --token end up on the process command line and
|
|
|
|
|
// therefore in `ps auxe` and shell history. Warn loudly so users notice.
|
|
|
|
|
// (Don't refuse the flag — too disruptive for scripts that already use it.)
|
|
|
|
|
if cmd.Flags().Changed("token") {
|
|
|
|
|
fmt.Fprintln(ios.ErrOut, "warning: --token puts the token on the command line (visible in `ps auxe` and shell history)")
|
|
|
|
|
fmt.Fprintln(ios.ErrOut, " prefer omitting --token and pasting at the prompt, or piping via stdin.")
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-08 09:49:07 +01:00
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
|
|
|
|
|
|
if hostname == "" {
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
fmt.Fprint(ios.ErrOut, "Forgejo instance hostname (default: codeberg.org): ")
|
2025-12-08 09:49:07 +01:00
|
|
|
input, _ := reader.ReadString('\n')
|
|
|
|
|
hostname = strings.TrimSpace(input)
|
|
|
|
|
if hostname == "" {
|
|
|
|
|
hostname = "codeberg.org"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if token == "" {
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
fmt.Fprint(ios.ErrOut, "Personal access token: ")
|
2025-12-08 09:49:07 +01:00
|
|
|
tokenBytes, err := term.ReadPassword(int(syscall.Stdin))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to read token: %w", err)
|
|
|
|
|
}
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
fmt.Fprintln(ios.ErrOut)
|
2025-12-08 09:49:07 +01:00
|
|
|
token = strings.TrimSpace(string(tokenBytes))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if token == "" {
|
|
|
|
|
return fmt.Errorf("token is required")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client, err := api.NewClient(hostname, token)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create client: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
ios.StartSpinner("Authenticating...")
|
2025-12-08 09:49:07 +01:00
|
|
|
user, _, err := client.GetMyUserInfo()
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
ios.StopSpinner()
|
2025-12-08 09:49:07 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("authentication failed: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg.SetHost(hostname, config.HostConfig{
|
2026-01-18 11:45:46 +01:00
|
|
|
Hostname: hostname,
|
|
|
|
|
Token: token,
|
|
|
|
|
User: user.UserName,
|
2025-12-08 09:49:07 +01:00
|
|
|
GitProtocol: "https",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err := cfg.Save(); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
cs := ios.ColorScheme()
|
|
|
|
|
fmt.Fprintf(ios.Out, "%s Authenticated as %s on %s\n", cs.SuccessIcon(), user.UserName, hostname)
|
2025-12-08 09:49:07 +01:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runAuthStatus(cmd *cobra.Command, args []string) error {
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(cfg.Hosts) == 0 {
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
fmt.Fprintln(ios.Out, "Not authenticated with any Forgejo instances")
|
2026-04-26 08:16:52 -06:00
|
|
|
fmt.Fprintln(ios.Out, "Run 'fj auth login' to authenticate")
|
2025-12-08 09:49:07 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
fmt.Fprintln(ios.Out, "Authenticated instances:")
|
2025-12-08 09:49:07 +01:00
|
|
|
for hostname, host := range cfg.Hosts {
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
cs := ios.ColorScheme()
|
|
|
|
|
fmt.Fprintf(ios.Out, " %s %s (user: %s)\n", cs.SuccessIcon(), hostname, host.User)
|
2025-12-08 09:49:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2026-01-18 11:45:46 +01:00
|
|
|
|
|
|
|
|
func runAuthLogout(cmd *cobra.Command, args []string) error {
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hostname, _ := cmd.Flags().GetString("hostname")
|
|
|
|
|
resolved, err := resolveAuthHostname(cfg, hostname)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete(cfg.Hosts, resolved)
|
|
|
|
|
if err := cfg.Save(); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to save config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
cs := ios.ColorScheme()
|
|
|
|
|
fmt.Fprintf(ios.Out, "%s Logged out from %s\n", cs.SuccessIcon(), resolved)
|
2026-01-18 11:45:46 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runAuthToken(cmd *cobra.Command, args []string) error {
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to load config: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hostname, _ := cmd.Flags().GetString("hostname")
|
|
|
|
|
resolved, err := resolveAuthHostname(cfg, hostname)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
feat: v0.3.0d — add PR checks, iostreams, aliases, and broad enhancements
Add PR checks command, iostreams/text packages for colored table output,
top-level run/workflow aliases matching gh CLI structure. Enhance actions,
issues, PRs, releases, repos, labels, milestones, and wiki commands with
improved flags, JSON output, and error handling.
2026-03-23 11:42:44 -06:00
|
|
|
fmt.Fprintln(ios.Out, cfg.Hosts[resolved].Token)
|
2026-01-18 11:45:46 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func resolveAuthHostname(cfg *config.Config, hostname string) (string, error) {
|
|
|
|
|
if hostname == "" {
|
|
|
|
|
hostname = viper.GetString("hostname")
|
|
|
|
|
}
|
|
|
|
|
if hostname == "" {
|
2026-04-26 08:23:48 -06:00
|
|
|
hostname = config.EnvWithFallback("FJ_HOST", "FGJ_HOST")
|
2026-01-18 11:45:46 +01:00
|
|
|
}
|
|
|
|
|
if hostname == "" {
|
|
|
|
|
hostname = getDetectedHost()
|
|
|
|
|
}
|
|
|
|
|
if hostname == "" && len(cfg.Hosts) == 1 {
|
|
|
|
|
for host := range cfg.Hosts {
|
|
|
|
|
hostname = host
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if hostname == "" {
|
|
|
|
|
hostname = "codeberg.org"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := cfg.Hosts[hostname]; !ok {
|
|
|
|
|
return "", fmt.Errorf("no configuration found for host %s", hostname)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hostname, nil
|
|
|
|
|
}
|