// Command discourse-go is a thin CLI over the client library: enough to // drive and verify a Discourse instance by hand (categories, topics, // posts, raw passthrough). Credentials arrive via environment only // (DISCOURSE_URL / DISCOURSE_API_KEY / DISCOURSE_API_USERNAME, see // env.example) — never as flags, so they never land in shell history or // process listings. package main import ( "context" "encoding/json" "errors" "fmt" "os" "os/signal" "syscall" "git.knownelement.com/ukrrs/mopac-discourse-go" ) const usage = `discourse-go — thin Discourse client CLI (config via env) Usage: discourse-go whoami discourse-go categories list discourse-go categories create NAME [-color HEX] [-text-color HEX] [-perm GROUP=LEVEL]... discourse-go topics list [-category ID] [-slug SLUG] [-latest] discourse-go topics show ID discourse-go topics create -title TITLE -category ID [-file PATH | -raw TEXT] discourse-go posts create -topic ID [-file PATH | -raw TEXT] discourse-go posts update ID [-file PATH | -raw TEXT] [-reason TEXT] discourse-go raw METHOD PATH [-data JSON] Environment (0600 env file, sourced before the call): DISCOURSE_URL instance root, e.g. https://community.turnsys.com DISCOURSE_API_KEY API key (never a flag, never logged) DISCOURSE_API_USERNAME the user the key acts as (default: system) Perm levels: 1 = reply/see, 2 = create posts, 3 = full. raw PATH is everything after the instance root, e.g. /groups.json. Exit codes: 0 ok · 1 usage/config · 2 API/transport error (message says which class: forbidden/unauthorized/not-found/rate-limited/server/ unreachable/malformed).` func main() { os.Exit(run(os.Args[1:])) } func run(args []string) int { if len(args) == 0 { fmt.Fprint(os.Stderr, usage) return 1 } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() client, err := discourse.NewFromEnv() if err != nil { fmt.Fprintf(os.Stderr, "discourse-go: %v\n", err) return 1 } var out any switch args[0] { case "whoami": if len(args) != 1 { return usageErr() } out, err = client.CurrentUser(ctx) case "categories": out, err = runCategories(ctx, client, args[1:]) case "topics": out, err = runTopics(ctx, client, args[1:]) case "posts": out, err = runPosts(ctx, client, args[1:]) case "raw": out, err = runRaw(ctx, client, args[1:]) case "help", "-h", "--help": fmt.Print(usage) return 0 default: fmt.Fprintf(os.Stderr, "discourse-go: unknown command %q\n\n%s", args[0], usage) return 1 } if err != nil { fmt.Fprintf(os.Stderr, "discourse-go: %v\n", err) if errors.Is(err, discourse.ErrInvalidRequest) { return 1 } return 2 } if out == nil { return 0 } enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") if err := enc.Encode(out); err != nil { fmt.Fprintf(os.Stderr, "discourse-go: encode output: %v\n", err) return 1 } return 0 } func usageErr() int { fmt.Fprint(os.Stderr, usage) return 1 }