package cmd import ( "fmt" "code.gitea.io/sdk/gitea" "forgejo.zerova.net/public/fgj-sid/internal/api" "forgejo.zerova.net/public/fgj-sid/internal/config" "github.com/spf13/cobra" ) var orgCmd = &cobra.Command{ Use: "org", Aliases: []string{"organization", "organizations"}, Short: "Manage organizations", Long: "List, create, and delete organizations on the current host.", } var orgListCmd = &cobra.Command{ Use: "list", Aliases: []string{"ls"}, Short: "List organizations", Long: "List organizations the authenticated user is a member of.", Example: ` # List your organizations fgj org list # Output as JSON fgj org list --json`, RunE: runOrgList, } var orgCreateCmd = &cobra.Command{ Use: "create ", Short: "Create an organization", Long: "Create a new organization. You become the initial owner.", Example: ` # Create an organization fgj org create my-org # Create with description and visibility fgj org create my-org --description "Internal tooling" --visibility private`, Args: cobra.ExactArgs(1), RunE: runOrgCreate, } var orgDeleteCmd = &cobra.Command{ Use: "delete ", Aliases: []string{"rm"}, Short: "Delete an organization", Long: "Delete an organization. This is irreversible and removes all the organization's repositories.", Args: cobra.ExactArgs(1), RunE: runOrgDelete, } func init() { rootCmd.AddCommand(orgCmd) orgCmd.AddCommand(orgListCmd) orgCmd.AddCommand(orgCreateCmd) orgCmd.AddCommand(orgDeleteCmd) orgListCmd.Flags().IntP("limit", "L", 50, "Maximum number of organizations to list") addJSONFlags(orgListCmd, "Output as JSON") orgCreateCmd.Flags().String("description", "", "Organization description") orgCreateCmd.Flags().String("full-name", "", "Full display name") orgCreateCmd.Flags().String("website", "", "Website URL") orgCreateCmd.Flags().String("location", "", "Location") orgCreateCmd.Flags().String("visibility", "public", "Visibility: public, limited, or private") orgDeleteCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt") } func runOrgList(cmd *cobra.Command, args []string) error { client, err := loadClient() if err != nil { return err } limit, _ := cmd.Flags().GetInt("limit") if limit <= 0 { limit = 50 } orgs, _, err := client.ListMyOrgs(gitea.ListOrgsOptions{ ListOptions: gitea.ListOptions{PageSize: limit}, }) if err != nil { return fmt.Errorf("failed to list organizations: %w", err) } if wantJSON(cmd) { return outputJSON(cmd, orgs) } if len(orgs) == 0 { fmt.Fprintln(ios.Out, "No organizations found.") return nil } tp := ios.NewTablePrinter() tp.AddHeader("NAME", "FULL NAME", "VISIBILITY", "DESCRIPTION") for _, o := range orgs { tp.AddRow(o.UserName, o.FullName, string(o.Visibility), o.Description) } return tp.Render() } func runOrgCreate(cmd *cobra.Command, args []string) error { client, err := loadClient() if err != nil { return err } name := args[0] desc, _ := cmd.Flags().GetString("description") fullName, _ := cmd.Flags().GetString("full-name") website, _ := cmd.Flags().GetString("website") location, _ := cmd.Flags().GetString("location") visStr, _ := cmd.Flags().GetString("visibility") var vis gitea.VisibleType switch visStr { case "public", "": vis = gitea.VisibleTypePublic case "limited": vis = gitea.VisibleTypeLimited case "private": vis = gitea.VisibleTypePrivate default: return fmt.Errorf("invalid visibility %q (must be public, limited, or private)", visStr) } org, _, err := client.CreateOrg(gitea.CreateOrgOption{ Name: name, FullName: fullName, Description: desc, Website: website, Location: location, Visibility: vis, }) if err != nil { return fmt.Errorf("failed to create organization %q: %w", name, err) } cs := ios.ColorScheme() fmt.Fprintf(ios.Out, "%s Created organization %q\n", cs.SuccessIcon(), org.UserName) return nil } func runOrgDelete(cmd *cobra.Command, args []string) error { client, err := loadClient() if err != nil { return err } name := args[0] skipConfirm, _ := cmd.Flags().GetBool("yes") if !skipConfirm && ios.IsStdinTTY() { answer, err := promptLine(fmt.Sprintf("Delete organization %q? This is irreversible and deletes all repositories. [y/N]: ", name)) if err != nil { return err } if answer != "y" && answer != "Y" && answer != "yes" { fmt.Fprintln(ios.ErrOut, "Cancelled.") return nil } } if _, err := client.DeleteOrg(name); err != nil { return fmt.Errorf("failed to delete organization %q: %w", name, err) } cs := ios.ColorScheme() fmt.Fprintf(ios.Out, "%s Deleted organization %q\n", cs.SuccessIcon(), name) return nil } // loadClient constructs an api.Client from config without requiring a repo context. // Use this for commands that operate on the host itself (orgs, notifications, user). func loadClient() (*api.Client, error) { cfg, err := config.Load() if err != nil { return nil, err } return api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd()) }