feat: logins list/default, actions run delete, date filters, label update alias
- fgj logins {list,default}: complementary UI to 'fgj auth'. 'list'
shows all configured hosts (hostname, user, protocol, default flag,
match_dirs) with --json. 'default [hostname]' gets or sets which
host wins in resolution when no other signal is present.
Adds 'Default bool' field to HostConfig; GetHost consults it between
match_dirs and the codeberg.org fallback. Multiple defaults tolerated
with a stderr warning; alphabetical-first wins.
- fgj actions run delete: delete a completed workflow run via raw
DELETE (SDK v0.23.2 has no DeleteRepoActionRun). Fetches run first
and refuses to delete non-terminal states unless --force; suggests
'actions run cancel' for those. Confirmation prompt unless --yes.
- pr list / issue list gain --since and --before date filter flags.
Accepts YYYY-MM-DD, RFC 3339, YYYY-MM-DD HH:MM:SS, and relative
deltas (7d, 24h, 2w, 1m — months=30 days). Issues use server-side
filter via ListIssueOption.Since/Before; PRs fall back to client-side
(SDK lacks Since/Before on ListPullRequestsOptions).
- fgj label update added as alias for 'fgj label edit' (tea-compat).
All changes:
cmd/logins.go (new, 140 LOC)
cmd/actions_run_delete.go (new, ~85 LOC)
cmd/pr.go, cmd/issue.go (+parseDateArg helper, filter wiring)
cmd/label.go (1-line alias)
internal/config/config.go (Default field + DefaultHost method)
CHANGELOG.md
Built in parallel by three sub-agents; plus the label alias done
serially. go build / go vet / go test -race all clean.
This commit is contained in:
parent
d15deaf064
commit
2d69873f3e
7 changed files with 462 additions and 7 deletions
160
cmd/logins.go
Normal file
160
cmd/logins.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"forgejo.zerova.net/public/fgj-sid/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var loginsCmd = &cobra.Command{
|
||||
Use: "logins",
|
||||
Short: "Manage configured Forgejo/Gitea logins",
|
||||
Long: `Manage configured Forgejo/Gitea logins.
|
||||
|
||||
This is a complementary command to 'fgj auth' using the noun vocabulary
|
||||
familiar to users of tea. Use 'fgj auth login' to add a new login and
|
||||
'fgj auth logout' to remove one.`,
|
||||
}
|
||||
|
||||
var loginsListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List all configured logins",
|
||||
Long: "List all configured Forgejo/Gitea logins with their hostname, user, protocol, default flag, and match_dirs.",
|
||||
RunE: runLoginsList,
|
||||
}
|
||||
|
||||
var loginsDefaultCmd = &cobra.Command{
|
||||
Use: "default [hostname]",
|
||||
Short: "Get or set the default login",
|
||||
Long: `Get or set the default login.
|
||||
|
||||
With no argument, prints the currently-configured default hostname
|
||||
(or "no default set" if none is configured).
|
||||
|
||||
With a hostname argument, marks that login as the default and unsets
|
||||
the default flag on all other logins.`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: runLoginsDefault,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(loginsCmd)
|
||||
loginsCmd.AddCommand(loginsListCmd)
|
||||
loginsCmd.AddCommand(loginsDefaultCmd)
|
||||
|
||||
addJSONFlags(loginsListCmd, "Output logins as JSON")
|
||||
}
|
||||
|
||||
// loginEntry is the JSON representation of a configured login.
|
||||
type loginEntry struct {
|
||||
Hostname string `json:"hostname"`
|
||||
User string `json:"user"`
|
||||
GitProtocol string `json:"git_protocol"`
|
||||
Default bool `json:"default"`
|
||||
MatchDirs []string `json:"match_dirs"`
|
||||
}
|
||||
|
||||
func runLoginsList(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
hostnames := make([]string, 0, len(cfg.Hosts))
|
||||
for hostname := range cfg.Hosts {
|
||||
hostnames = append(hostnames, hostname)
|
||||
}
|
||||
|
||||
// Sort by config-file order; fall back to alphabetical when Order is
|
||||
// equal (e.g., both zero because the config was constructed in-memory
|
||||
// rather than loaded from disk).
|
||||
sort.Slice(hostnames, func(i, j int) bool {
|
||||
a, b := cfg.Hosts[hostnames[i]], cfg.Hosts[hostnames[j]]
|
||||
if a.Order != b.Order {
|
||||
return a.Order < b.Order
|
||||
}
|
||||
return hostnames[i] < hostnames[j]
|
||||
})
|
||||
|
||||
if wantJSON(cmd) {
|
||||
entries := make([]loginEntry, 0, len(hostnames))
|
||||
for _, hostname := range hostnames {
|
||||
h := cfg.Hosts[hostname]
|
||||
dirs := h.MatchDirs
|
||||
if dirs == nil {
|
||||
dirs = []string{}
|
||||
}
|
||||
entries = append(entries, loginEntry{
|
||||
Hostname: hostname,
|
||||
User: h.User,
|
||||
GitProtocol: h.GitProtocol,
|
||||
Default: h.Default,
|
||||
MatchDirs: dirs,
|
||||
})
|
||||
}
|
||||
return outputJSON(cmd, entries)
|
||||
}
|
||||
|
||||
if len(hostnames) == 0 {
|
||||
fmt.Fprintln(ios.Out, "No logins configured")
|
||||
fmt.Fprintln(ios.Out, "Run 'fgj auth login' to add a login")
|
||||
return nil
|
||||
}
|
||||
|
||||
tp := ios.NewTablePrinter()
|
||||
tp.AddHeader("HOSTNAME", "USER", "PROTOCOL", "DEFAULT", "MATCH_DIRS")
|
||||
for _, hostname := range hostnames {
|
||||
h := cfg.Hosts[hostname]
|
||||
defaultMark := ""
|
||||
if h.Default {
|
||||
defaultMark = "*"
|
||||
}
|
||||
tp.AddRow(
|
||||
hostname,
|
||||
h.User,
|
||||
h.GitProtocol,
|
||||
defaultMark,
|
||||
strings.Join(h.MatchDirs, ", "),
|
||||
)
|
||||
}
|
||||
return tp.Render()
|
||||
}
|
||||
|
||||
func runLoginsDefault(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
current := cfg.DefaultHost()
|
||||
if current == "" {
|
||||
fmt.Fprintln(ios.Out, "no default set")
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintln(ios.Out, current)
|
||||
return nil
|
||||
}
|
||||
|
||||
target := args[0]
|
||||
if _, ok := cfg.Hosts[target]; !ok {
|
||||
return fmt.Errorf("no configuration found for host %s", target)
|
||||
}
|
||||
|
||||
for hostname, h := range cfg.Hosts {
|
||||
h.Default = (hostname == target)
|
||||
cfg.Hosts[hostname] = h
|
||||
}
|
||||
|
||||
if err := cfg.Save(); err != nil {
|
||||
return fmt.Errorf("failed to save config: %w", err)
|
||||
}
|
||||
|
||||
cs := ios.ColorScheme()
|
||||
fmt.Fprintf(ios.Out, "%s Default host set to %s\n", cs.SuccessIcon(), target)
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue