feat: pr approve/reject, repo migrate/template, secret stdin fix, docs
- fgj pr approve / pr reject: thin shortcuts over 'pr review --approve'
and '--request-changes'. Reject requires a body.
- fgj repo migrate: wrap SDK MigrateRepo. Supports git, github, gitlab,
gitea, gogs services; mirror mode with --mirror-interval; selective
import (wiki/labels/milestones/issues/PRs/releases/LFS); auth via
--auth-token or --auth-username/--auth-password. Defaults owner to
the authenticated user.
- fgj repo create-from-template: wrap SDK CreateRepoFromTemplate with
fine-grained --with-{content,topics,labels,webhooks,git-hooks,avatar}
flags. Template is owner/name; new repo defaults to the current user.
- Rework 'fgj actions secret create' input. New cmd/secret_input.go
resolves values from --body, --body-file (path or '-'), hidden TTY
prompt via term.ReadPassword, or piped stdin. Trims trailing
whitespace, rejects empty values. Replaces fmt.Scanln which broke on
spaces/newlines and echoed input.
- CHANGELOG: v0.4.0 Unreleased section documenting all additions,
changes, and development items.
- README: updated feature list with new commands.
This commit is contained in:
parent
424fb63a8b
commit
4eeef2ceca
7 changed files with 551 additions and 23 deletions
68
cmd/secret_input.go
Normal file
68
cmd/secret_input.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// readSecretValue resolves the value for a secret/token flag from, in order:
|
||||
// 1. --body (inline; visible in shell history)
|
||||
// 2. --body-file (file path, or "-" for stdin)
|
||||
// 3. interactive TTY prompt (hidden)
|
||||
// 4. piped stdin
|
||||
//
|
||||
// Trailing whitespace (including the final newline common in heredocs and
|
||||
// `echo ... | fgj ...`) is trimmed. An empty resolved value is rejected so we
|
||||
// never silently write an empty secret.
|
||||
func readSecretValue(cmd *cobra.Command, label string) (string, error) {
|
||||
if v, _ := cmd.Flags().GetString("body"); v != "" {
|
||||
return strings.TrimRight(v, "\r\n"), nil
|
||||
}
|
||||
|
||||
if path, _ := cmd.Flags().GetString("body-file"); path != "" {
|
||||
var raw []byte
|
||||
var err error
|
||||
if path == "-" {
|
||||
raw, err = io.ReadAll(ios.In)
|
||||
} else {
|
||||
raw, err = os.ReadFile(path)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read secret from %q: %w", path, err)
|
||||
}
|
||||
value := strings.TrimRight(string(raw), "\r\n")
|
||||
if value == "" {
|
||||
return "", fmt.Errorf("secret value from %q is empty", path)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
if ios.IsStdinTTY() {
|
||||
fmt.Fprintf(ios.ErrOut, "Value for %s: ", label)
|
||||
pw, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Fprintln(ios.ErrOut)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read secret: %w", err)
|
||||
}
|
||||
value := strings.TrimRight(string(pw), "\r\n")
|
||||
if value == "" {
|
||||
return "", fmt.Errorf("secret value is empty")
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(ios.In)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read secret from stdin: %w", err)
|
||||
}
|
||||
value := strings.TrimRight(string(raw), "\r\n")
|
||||
if value == "" {
|
||||
return "", fmt.Errorf("secret value from stdin is empty")
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue