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:
sid 2026-04-19 22:14:43 -06:00
parent 424fb63a8b
commit 4eeef2ceca
7 changed files with 551 additions and 23 deletions

View file

@ -251,12 +251,26 @@ var actionsSecretListCmd = &cobra.Command{
var actionsSecretCreateCmd = &cobra.Command{
Use: "create <name>",
Short: "Create or update a repository secret",
Long: "Create or update a secret for Forgejo Actions. The secret value will be read from stdin.",
Example: ` # Create a secret (will prompt for value)
Long: `Create or update a secret for Forgejo Actions.
The secret value is read from the first available source:
1. --body <value>
2. --body-file <path> (use "-" for stdin)
3. interactive prompt (hidden input) if stdin is a TTY
4. stdin (when piped)
Trailing newlines are trimmed. Empty values are rejected.`,
Example: ` # Interactive (hidden prompt)
fgj actions secret create DEPLOY_TOKEN
# Create a secret for a specific repo
fgj actions secret create API_KEY -R owner/repo`,
# Pipe a value
op read op://vault/github/token | fgj actions secret create GH_TOKEN --body-file -
# Read from a file
fgj actions secret create TLS_KEY --body-file ./server.key
# Inline (visible in shell history use sparingly)
fgj actions secret create DEBUG_FLAG --body "on"`,
Args: cobra.ExactArgs(1),
RunE: runActionsSecretCreate,
}
@ -397,6 +411,8 @@ func init() {
// Add flags for secret commands
addRepoFlags(actionsSecretListCmd)
addRepoFlags(actionsSecretCreateCmd)
actionsSecretCreateCmd.Flags().String("body", "", "Secret value (visible in shell history)")
actionsSecretCreateCmd.Flags().String("body-file", "", "Read secret value from file, or '-' for stdin")
addRepoFlags(actionsSecretDeleteCmd)
// Add flags for variable commands
@ -1254,12 +1270,9 @@ func runActionsSecretCreate(cmd *cobra.Command, args []string) error {
secretName := args[0]
// Read secret value from stdin
fmt.Fprint(ios.ErrOut, "Enter secret value: ")
var secretValue string
_, err = fmt.Scanln(&secretValue)
secretValue, err := readSecretValue(cmd, secretName)
if err != nil {
return fmt.Errorf("failed to read secret value: %w", err)
return err
}
opt := gitea.CreateSecretOption{
@ -1267,12 +1280,12 @@ func runActionsSecretCreate(cmd *cobra.Command, args []string) error {
Data: secretValue,
}
_, err = client.CreateRepoActionSecret(owner, name, opt)
if err != nil {
if _, err := client.CreateRepoActionSecret(owner, name, opt); err != nil {
return fmt.Errorf("failed to create secret: %w", err)
}
fmt.Fprintf(ios.Out, "Secret '%s' created successfully\n", secretName)
cs := ios.ColorScheme()
fmt.Fprintf(ios.Out, "%s Secret %q created\n", cs.SuccessIcon(), secretName)
return nil
}