2026-03-21 21:50:24 -06:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
|
|
|
|
"time"
|
|
|
|
|
|
2026-04-26 08:16:52 -06:00
|
|
|
"forgejo.zerova.net/public/fj/internal/api"
|
|
|
|
|
"forgejo.zerova.net/public/fj/internal/config"
|
|
|
|
|
"forgejo.zerova.net/public/fj/internal/text"
|
2026-03-21 21:50:24 -06:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Wiki API response types
|
|
|
|
|
|
|
|
|
|
type wikiPageMeta struct {
|
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
|
|
|
Title string `json:"title"`
|
|
|
|
|
HTMLURL string `json:"html_url"`
|
|
|
|
|
SubURL string `json:"sub_url"`
|
2026-03-21 21:50:24 -06:00
|
|
|
LastCommit *wikiCommit `json:"last_commit"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type wikiCommit struct {
|
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
|
|
|
ID string `json:"id"`
|
|
|
|
|
Author *wikiUser `json:"author"`
|
|
|
|
|
Committer *wikiUser `json:"committer"`
|
|
|
|
|
Message string `json:"message"`
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type wikiUser struct {
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Email string `json:"email"`
|
|
|
|
|
Date string `json:"date"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type wikiPage struct {
|
|
|
|
|
Title string `json:"title"`
|
|
|
|
|
HTMLURL string `json:"html_url"`
|
|
|
|
|
SubURL string `json:"sub_url"`
|
|
|
|
|
ContentBase64 string `json:"content_base64"`
|
|
|
|
|
LastCommit *wikiCommit `json:"last_commit"`
|
|
|
|
|
// Decoded content for JSON output
|
|
|
|
|
Content string `json:"content,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type wikiCreateRequest struct {
|
|
|
|
|
Title string `json:"title"`
|
|
|
|
|
ContentBase64 string `json:"content_base64"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wikiCmd = &cobra.Command{
|
|
|
|
|
Use: "wiki",
|
|
|
|
|
Short: "Manage repository wiki pages",
|
|
|
|
|
Long: "View and manage wiki pages for a repository.",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wikiListCmd = &cobra.Command{
|
|
|
|
|
Use: "list",
|
|
|
|
|
Short: "List wiki pages",
|
|
|
|
|
Long: "List all wiki pages for a repository.",
|
|
|
|
|
Example: ` # List wiki pages for the current repo
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki list
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# List wiki pages for a specific repo
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki list -R owner/repo
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Output as JSON
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki list --json`,
|
2026-03-21 21:50:24 -06:00
|
|
|
RunE: runWikiList,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wikiViewCmd = &cobra.Command{
|
|
|
|
|
Use: "view <title>",
|
|
|
|
|
Short: "View a wiki page",
|
|
|
|
|
Long: "Display the content of a wiki page.",
|
|
|
|
|
Example: ` # View a wiki page
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki view Home
|
2026-03-21 21:50:24 -06:00
|
|
|
|
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
|
|
|
# Open in browser
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki view Home --web
|
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
|
|
|
|
2026-03-21 21:50:24 -06:00
|
|
|
# View a wiki page as JSON (includes content)
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki view Home --json
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# View a wiki page from a specific repo
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki view "Getting-Started" -R owner/repo`,
|
2026-03-21 21:50:24 -06:00
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
|
RunE: runWikiView,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wikiCreateCmd = &cobra.Command{
|
|
|
|
|
Use: "create <title>",
|
|
|
|
|
Short: "Create a wiki page",
|
|
|
|
|
Long: "Create a new wiki page in the repository.",
|
|
|
|
|
Example: ` # Create a wiki page with inline content
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki create "Getting Started" -b "# Welcome\nThis is the getting started guide."
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Create a wiki page from a file
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki create "Setup Guide" --body-file setup.md
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Create a wiki page from stdin
|
2026-04-26 08:16:52 -06:00
|
|
|
echo "# FAQ" | fj wiki create FAQ --body-file -
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Output as JSON
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki create "New Page" -b "Content here" --json`,
|
2026-03-21 21:50:24 -06:00
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
|
RunE: runWikiCreate,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wikiEditCmd = &cobra.Command{
|
|
|
|
|
Use: "edit <title>",
|
|
|
|
|
Short: "Edit a wiki page",
|
|
|
|
|
Long: "Edit an existing wiki page in the repository.",
|
|
|
|
|
Example: ` # Edit a wiki page with new content
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki edit Home -b "# Updated Home\nNew content here."
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Edit a wiki page from a file
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki edit "Setup Guide" --body-file updated-setup.md
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Edit a wiki page from stdin
|
2026-04-26 08:16:52 -06:00
|
|
|
cat new-content.md | fj wiki edit Home --body-file -
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
# Output as JSON
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki edit Home -b "Updated content" --json`,
|
2026-03-21 21:50:24 -06:00
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
|
RunE: runWikiEdit,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var wikiDeleteCmd = &cobra.Command{
|
|
|
|
|
Use: "delete <title>",
|
|
|
|
|
Short: "Delete a wiki page",
|
|
|
|
|
Long: "Delete a wiki page from the repository.",
|
|
|
|
|
Example: ` # Delete a wiki page
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki delete "Old Page"
|
2026-03-21 21:50:24 -06:00
|
|
|
|
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
|
|
|
# Delete without confirmation
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki delete "Old Page" -y
|
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
|
|
|
|
2026-03-21 21:50:24 -06:00
|
|
|
# Delete a wiki page from a specific repo
|
2026-04-26 08:16:52 -06:00
|
|
|
fj wiki delete "Outdated Guide" -R owner/repo`,
|
2026-03-21 21:50:24 -06:00
|
|
|
Args: cobra.ExactArgs(1),
|
|
|
|
|
RunE: runWikiDelete,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
rootCmd.AddCommand(wikiCmd)
|
|
|
|
|
wikiCmd.AddCommand(wikiListCmd)
|
|
|
|
|
wikiCmd.AddCommand(wikiViewCmd)
|
|
|
|
|
wikiCmd.AddCommand(wikiCreateCmd)
|
|
|
|
|
wikiCmd.AddCommand(wikiEditCmd)
|
|
|
|
|
wikiCmd.AddCommand(wikiDeleteCmd)
|
|
|
|
|
|
|
|
|
|
wikiListCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
|
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
|
|
|
addJSONFlags(wikiListCmd, "Output as JSON")
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
wikiViewCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
|
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
|
|
|
addJSONFlags(wikiViewCmd, "Output as JSON")
|
|
|
|
|
wikiViewCmd.Flags().BoolP("web", "w", false, "Open in web browser")
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
wikiCreateCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
|
|
|
|
|
wikiCreateCmd.Flags().StringP("body", "b", "", "Wiki page content")
|
|
|
|
|
wikiCreateCmd.Flags().String("body-file", "", "Read content from file (use \"-\" for stdin)")
|
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
|
|
|
addJSONFlags(wikiCreateCmd, "Output created page as JSON")
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
wikiEditCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
|
|
|
|
|
wikiEditCmd.Flags().StringP("body", "b", "", "Wiki page content")
|
|
|
|
|
wikiEditCmd.Flags().String("body-file", "", "Read content from file (use \"-\" for stdin)")
|
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
|
|
|
addJSONFlags(wikiEditCmd, "Output updated page as JSON")
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
wikiDeleteCmd.Flags().StringP("repo", "R", "", "Repository in owner/name format")
|
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
|
|
|
wikiDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt")
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newWikiClient(cmd *cobra.Command) (*api.Client, string, string, error) {
|
|
|
|
|
repo, _ := cmd.Flags().GetString("repo")
|
|
|
|
|
owner, name, err := parseRepo(repo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, "", "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfg, err := config.Load()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, "", "", err
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-23 12:39:51 -06:00
|
|
|
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
|
2026-03-21 21:50:24 -06:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, "", "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return client, owner, name, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runWikiList(cmd *cobra.Command, args []string) error {
|
|
|
|
|
client, owner, name, err := newWikiClient(cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/pages", url.PathEscape(owner), url.PathEscape(name))
|
|
|
|
|
|
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("Fetching wiki pages...")
|
2026-03-21 21:50:24 -06:00
|
|
|
var pages []wikiPageMeta
|
|
|
|
|
if err := client.GetJSON(path, &pages); err != 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
|
|
|
ios.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
return fmt.Errorf("failed to list wiki pages: %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.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
|
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
|
|
|
if wantJSON(cmd) {
|
|
|
|
|
return outputJSON(cmd, pages)
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(pages) == 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, "No wiki pages found")
|
2026-03-21 21:50:24 -06: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
|
|
|
isTTY := ios.IsStdoutTTY()
|
|
|
|
|
tp := ios.NewTablePrinter()
|
|
|
|
|
tp.AddHeader("TITLE", "LAST UPDATED")
|
2026-03-21 21:50:24 -06:00
|
|
|
for _, p := range pages {
|
|
|
|
|
updated := ""
|
|
|
|
|
if p.LastCommit != nil && p.LastCommit.Committer != nil && p.LastCommit.Committer.Date != "" {
|
|
|
|
|
if t, err := time.Parse(time.RFC3339, p.LastCommit.Committer.Date); err == 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
|
|
|
updated = text.FormatDate(t, isTTY)
|
2026-03-21 21:50:24 -06:00
|
|
|
} else {
|
|
|
|
|
updated = p.LastCommit.Committer.Date
|
|
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
tp.AddRow(p.Title, updated)
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
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
|
|
|
return tp.Render()
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runWikiView(cmd *cobra.Command, args []string) error {
|
|
|
|
|
title := args[0]
|
|
|
|
|
|
|
|
|
|
client, owner, name, err := newWikiClient(cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s",
|
|
|
|
|
url.PathEscape(owner), url.PathEscape(name), url.PathEscape(title))
|
|
|
|
|
|
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("Fetching wiki page...")
|
2026-03-21 21:50:24 -06:00
|
|
|
var page wikiPage
|
|
|
|
|
if err := client.GetJSON(path, &page); err != 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
|
|
|
ios.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
return fmt.Errorf("failed to get wiki page: %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.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
content, err := base64.StdEncoding.DecodeString(page.ContentBase64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to decode wiki page content: %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
|
|
|
if web, _ := cmd.Flags().GetBool("web"); web {
|
|
|
|
|
if page.HTMLURL != "" {
|
|
|
|
|
return ios.OpenInBrowser(page.HTMLURL)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("wiki page has no HTML URL")
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
if wantJSON(cmd) {
|
2026-03-21 21:50:24 -06:00
|
|
|
page.Content = string(content)
|
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
|
|
|
return outputJSON(cmd, page)
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
if err := ios.StartPager(); err != nil {
|
|
|
|
|
fmt.Fprintf(ios.ErrOut, "warning: failed to start pager: %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
defer ios.StopPager()
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(ios.Out, "# %s\n\n", page.Title)
|
|
|
|
|
fmt.Fprint(ios.Out, string(content))
|
2026-03-21 21:50:24 -06:00
|
|
|
// Ensure trailing newline
|
|
|
|
|
if len(content) > 0 && content[len(content)-1] != '\n' {
|
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)
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runWikiCreate(cmd *cobra.Command, args []string) error {
|
|
|
|
|
title := args[0]
|
|
|
|
|
|
|
|
|
|
client, owner, name, err := newWikiClient(cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body, err := readBody(cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if body == "" {
|
|
|
|
|
return fmt.Errorf("content is required (use --body or --body-file)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/new",
|
|
|
|
|
url.PathEscape(owner), url.PathEscape(name))
|
|
|
|
|
|
|
|
|
|
reqBody := wikiCreateRequest{
|
|
|
|
|
Title: title,
|
|
|
|
|
ContentBase64: base64.StdEncoding.EncodeToString([]byte(body)),
|
|
|
|
|
}
|
|
|
|
|
|
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("Creating wiki page...")
|
2026-03-21 21:50:24 -06:00
|
|
|
var page wikiPage
|
|
|
|
|
if _, err := client.DoJSON(http.MethodPost, path, reqBody, &page); err != 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
|
|
|
ios.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
return fmt.Errorf("failed to create wiki page: %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.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
|
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
|
|
|
if wantJSON(cmd) {
|
|
|
|
|
return outputJSON(cmd, page)
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
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 Wiki page created: %s\n", cs.SuccessIcon(), title)
|
2026-03-21 21:50:24 -06:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runWikiEdit(cmd *cobra.Command, args []string) error {
|
|
|
|
|
title := args[0]
|
|
|
|
|
|
|
|
|
|
client, owner, name, err := newWikiClient(cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body, err := readBody(cmd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if body == "" {
|
|
|
|
|
return fmt.Errorf("content is required (use --body or --body-file)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s",
|
|
|
|
|
url.PathEscape(owner), url.PathEscape(name), url.PathEscape(title))
|
|
|
|
|
|
|
|
|
|
reqBody := wikiCreateRequest{
|
|
|
|
|
Title: title,
|
|
|
|
|
ContentBase64: base64.StdEncoding.EncodeToString([]byte(body)),
|
|
|
|
|
}
|
|
|
|
|
|
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("Updating wiki page...")
|
2026-03-21 21:50:24 -06:00
|
|
|
var page wikiPage
|
|
|
|
|
if _, err := client.DoJSON(http.MethodPatch, path, reqBody, &page); err != 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
|
|
|
ios.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
return fmt.Errorf("failed to update wiki page: %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.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
|
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
|
|
|
if wantJSON(cmd) {
|
|
|
|
|
return outputJSON(cmd, page)
|
2026-03-21 21:50:24 -06:00
|
|
|
}
|
|
|
|
|
|
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 Wiki page updated: %s\n", cs.SuccessIcon(), title)
|
2026-03-21 21:50:24 -06:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func runWikiDelete(cmd *cobra.Command, args []string) error {
|
|
|
|
|
title := args[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
|
|
|
yes, _ := cmd.Flags().GetBool("yes")
|
2026-03-21 21:50:24 -06:00
|
|
|
|
|
|
|
|
client, owner, name, err := newWikiClient(cmd)
|
|
|
|
|
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
|
|
|
if !yes {
|
|
|
|
|
confirmed, err := ios.ConfirmAction(fmt.Sprintf("Delete wiki page %q?", title))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !confirmed {
|
|
|
|
|
fmt.Fprintln(ios.ErrOut, "Aborted")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 21:50:24 -06:00
|
|
|
path := fmt.Sprintf("/api/v1/repos/%s/%s/wiki/page/%s",
|
|
|
|
|
url.PathEscape(owner), url.PathEscape(name), url.PathEscape(title))
|
|
|
|
|
|
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("Deleting wiki page...")
|
2026-03-21 21:50:24 -06:00
|
|
|
if _, err := client.DoJSON(http.MethodDelete, path, nil, nil); err != 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
|
|
|
ios.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
return fmt.Errorf("failed to delete wiki page: %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.StopSpinner()
|
2026-03-21 21:50:24 -06:00
|
|
|
|
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 Wiki page deleted: %s\n", cs.SuccessIcon(), title)
|
2026-03-21 21:50:24 -06:00
|
|
|
return nil
|
|
|
|
|
}
|