💥 Add --dry-run and --yes to mutating subcommands
This commit is contained in:
1 parent
31ad273946
commit
b55029cb27
40 files changed
+574
-76
No files matched your search
+11
-1
@@ -1,9 +1,10 @@
|
||||
load("@rules_go//go:def.bzl", "go_library")
|
||||
load("@rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "cmd",
|
||||
srcs = [
|
||||
"auth.go",
|
||||
"flags.go",
|
||||
"mcp.go",
|
||||
"root.go",
|
||||
"version.go",
|
||||
@@ -22,9 +23,18 @@ go_library(
|
||||
"//pkg",
|
||||
"//pkg/auth",
|
||||
"//pkg/common",
|
||||
"//pkg/utils",
|
||||
"@com_github_eat_pray_ai_cobra_mcp//:cobra-mcp",
|
||||
"@com_github_modelcontextprotocol_go_sdk//oauthex",
|
||||
"@com_github_savioxavier_termlink//:termlink",
|
||||
"@com_github_spf13_cobra//:cobra",
|
||||
"@org_golang_x_term//:term",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "cmd_test",
|
||||
srcs = ["flags_test.go"],
|
||||
embed = [":cmd"],
|
||||
deps = ["@com_github_spf13_cobra//:cobra"],
|
||||
)
|
||||
@@ -5,6 +5,7 @@ package caption
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -65,6 +66,7 @@ func init() {
|
||||
)
|
||||
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -72,12 +74,17 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would delete caption(s): %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := caption.NewCaption(
|
||||
caption.WithIds(ids),
|
||||
caption.WithOnBehalfOf(onBehalfOf),
|
||||
caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -107,6 +107,7 @@ func init() {
|
||||
|
||||
_ = insertCmd.MarkFlagRequired("file")
|
||||
_ = insertCmd.MarkFlagRequired("videoId")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -114,7 +115,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert caption")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := caption.NewCaption(
|
||||
caption.WithFile(file),
|
||||
caption.WithAudioTrackType(audioTrackType),
|
||||
@@ -131,6 +137,6 @@ var insertCmd = &cobra.Command{
|
||||
caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
caption.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -105,6 +105,7 @@ func init() {
|
||||
updateCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("videoId")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -112,7 +113,12 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would update caption for video: %s", videoId)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := caption.NewCaption(
|
||||
caption.WithFile(file),
|
||||
caption.WithAudioTrackType(audioTrackType),
|
||||
@@ -129,6 +135,6 @@ var updateCmd = &cobra.Command{
|
||||
caption.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
caption.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Update(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Update(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package channel
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -81,6 +82,7 @@ func init() {
|
||||
updateCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("id")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -88,7 +90,12 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would update channel: %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := channel.NewChannel(
|
||||
channel.WithIds(ids),
|
||||
channel.WithCountry(country),
|
||||
@@ -98,6 +105,6 @@ var updateCmd = &cobra.Command{
|
||||
channel.WithTitle(title),
|
||||
channel.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Update(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Update(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -81,6 +81,7 @@ func init() {
|
||||
|
||||
_ = insertCmd.MarkFlagRequired("channelId")
|
||||
_ = insertCmd.MarkFlagRequired("file")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -88,7 +89,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert channel banner")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := channelBanner.NewChannelBanner(
|
||||
channelBanner.WithChannelId(channelId),
|
||||
channelBanner.WithFile(file),
|
||||
@@ -96,6 +102,6 @@ var insertCmd = &cobra.Command{
|
||||
channelBanner.WithOnBehalfOfContentOwnerChannel(onBehalfOfContentOwnerChannel),
|
||||
channelBanner.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package channelSection
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -64,6 +65,7 @@ func init() {
|
||||
)
|
||||
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -71,11 +73,18 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(
|
||||
c, "Would delete channel section(s): %s", strings.Join(ids, ", "),
|
||||
)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := channelSection.NewChannelSection(
|
||||
channelSection.WithIds(ids),
|
||||
channelSection.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package comment
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -56,6 +57,7 @@ func init() {
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, idsUsage)
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -63,8 +65,13 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would delete comment(s): %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := comment.NewComment(comment.WithIds(ids))
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -83,6 +83,7 @@ func init() {
|
||||
|
||||
_ = insertCmd.MarkFlagRequired("parentId")
|
||||
_ = insertCmd.MarkFlagRequired("textOriginal")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -90,7 +91,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert comment")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := comment.NewComment(
|
||||
comment.WithAuthorChannelId(authorChannelId),
|
||||
comment.WithChannelId(channelId),
|
||||
@@ -100,6 +106,6 @@ var insertCmd = &cobra.Command{
|
||||
comment.WithVideoId(videoId),
|
||||
comment.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package comment
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -64,6 +65,7 @@ func init() {
|
||||
markAsSpamCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = markAsSpamCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(markAsSpamCmd)
|
||||
}
|
||||
|
||||
var markAsSpamCmd = &cobra.Command{
|
||||
@@ -71,11 +73,16 @@ var markAsSpamCmd = &cobra.Command{
|
||||
Short: masShort,
|
||||
Long: masLong,
|
||||
Example: masExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would mark comment(s) as spam: %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := comment.NewComment(
|
||||
comment.WithIds(ids),
|
||||
comment.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.MarkAsSpam(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.MarkAsSpam(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package comment
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -83,6 +84,7 @@ func init() {
|
||||
|
||||
_ = setModerationStatusCmd.MarkFlagRequired("ids")
|
||||
_ = setModerationStatusCmd.MarkFlagRequired("moderationStatus")
|
||||
cmd.AddMutationFlags(setModerationStatusCmd)
|
||||
}
|
||||
|
||||
var setModerationStatusCmd = &cobra.Command{
|
||||
@@ -90,13 +92,18 @@ var setModerationStatusCmd = &cobra.Command{
|
||||
Short: smsShort,
|
||||
Long: smsLong,
|
||||
Example: smsExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would set moderation status of comment(s): %s to %s", strings.Join(ids, ", "), moderationStatus)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := comment.NewComment(
|
||||
comment.WithIds(ids),
|
||||
comment.WithModerationStatus(moderationStatus),
|
||||
comment.WithBanAuthor(banAuthor),
|
||||
comment.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.SetModerationStatus(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.SetModerationStatus(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package comment
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -80,6 +81,7 @@ func init() {
|
||||
updateCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("id")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -87,7 +89,12 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would update comment: %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := comment.NewComment(
|
||||
comment.WithIds(ids),
|
||||
comment.WithCanRate(canRate),
|
||||
@@ -95,6 +102,6 @@ var updateCmd = &cobra.Command{
|
||||
comment.WithViewerRating(viewerRating),
|
||||
comment.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Update(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Update(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -74,6 +74,7 @@ func init() {
|
||||
_ = insertCmd.MarkFlagRequired("channelId")
|
||||
_ = insertCmd.MarkFlagRequired("textOriginal")
|
||||
_ = insertCmd.MarkFlagRequired("videoId")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -81,7 +82,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert comment thread")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := commentThread.NewCommentThread(
|
||||
commentThread.WithAuthorChannelId(authorChannelId),
|
||||
commentThread.WithChannelId(channelId),
|
||||
@@ -89,6 +95,6 @@ var insertCmd = &cobra.Command{
|
||||
commentThread.WithVideoId(videoId),
|
||||
commentThread.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2026 eat-pray-ai & OpenWaygate
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/eat-pray-ai/yutu/pkg/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// ErrDryRun is returned by Confirm when --dry-run is set.
|
||||
var ErrDryRun = utils.ErrDryRun
|
||||
|
||||
// errAborted is returned by Confirm when the user declines the prompt.
|
||||
var errAborted = errors.New("aborted")
|
||||
|
||||
// AddMutationFlags registers --dry-run and --yes on the given command.
|
||||
func AddMutationFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool(
|
||||
"dry-run", false,
|
||||
"Print what would happen without calling the API",
|
||||
)
|
||||
cmd.Flags().Bool(
|
||||
"yes", false,
|
||||
"Skip the confirmation prompt",
|
||||
)
|
||||
}
|
||||
|
||||
// DryRun returns whether --dry-run was passed.
|
||||
func DryRun(cmd *cobra.Command) bool {
|
||||
v, _ := cmd.Flags().GetBool("dry-run")
|
||||
return v
|
||||
}
|
||||
|
||||
// Confirm checks dry-run and yes flags, then prompts the user for
|
||||
// confirmation if running in an interactive terminal.
|
||||
//
|
||||
// Returns ErrDryRun if --dry-run is set, nil if --yes is set or the user
|
||||
// confirms, and errAborted if the user declines.
|
||||
func Confirm(cmd *cobra.Command, format string, args ...any) error {
|
||||
isTTY := term.IsTerminal(int(os.Stdin.Fd()))
|
||||
return confirmWithTTYOverride(cmd, isTTY, format, args...)
|
||||
}
|
||||
|
||||
// confirmWithTTYOverride is an internal helper that accepts a pre-computed
|
||||
// isTTY value so tests can exercise the TTY and non-TTY paths without
|
||||
// needing a real terminal.
|
||||
func confirmWithTTYOverride(cmd *cobra.Command, isTTY bool, format string, args ...any) error {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
|
||||
// 1. --dry-run takes precedence over everything.
|
||||
if DryRun(cmd) {
|
||||
fmt.Fprintln(cmd.OutOrStdout(), msg)
|
||||
return ErrDryRun
|
||||
}
|
||||
|
||||
// 2. --yes skips the prompt entirely.
|
||||
yes, _ := cmd.Flags().GetBool("yes")
|
||||
if yes {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3. Non-interactive stdin requires --yes.
|
||||
if !isTTY {
|
||||
return errors.New("terminal required for confirmation; use --yes to skip")
|
||||
}
|
||||
|
||||
// 4. Interactive prompt.
|
||||
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s [y/N] ", msg)
|
||||
|
||||
scanner := bufio.NewScanner(cmd.InOrStdin())
|
||||
if !scanner.Scan() {
|
||||
return errAborted
|
||||
}
|
||||
answer := strings.TrimSpace(scanner.Text())
|
||||
|
||||
if answer == "y" || answer == "Y" {
|
||||
return nil
|
||||
}
|
||||
return errAborted
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright 2026 eat-pray-ai & OpenWaygate
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// newTestCmd creates a minimal cobra.Command with mutation flags registered.
|
||||
func newTestCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "test",
|
||||
Run: func(cmd *cobra.Command, args []string) {},
|
||||
}
|
||||
AddMutationFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestAddMutationFlags(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
|
||||
dr := cmd.Flags().Lookup("dry-run")
|
||||
if dr == nil {
|
||||
t.Fatal("expected --dry-run flag to be registered")
|
||||
}
|
||||
if dr.DefValue != "false" {
|
||||
t.Fatalf("expected --dry-run default to be false, got %s", dr.DefValue)
|
||||
}
|
||||
|
||||
yes := cmd.Flags().Lookup("yes")
|
||||
if yes == nil {
|
||||
t.Fatal("expected --yes flag to be registered")
|
||||
}
|
||||
if yes.DefValue != "false" {
|
||||
t.Fatalf("expected --yes default to be false, got %s", yes.DefValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDryRun_DefaultFalse(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
if DryRun(cmd) {
|
||||
t.Fatal("expected DryRun to be false by default")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDryRun_SetTrue(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
if err := cmd.Flags().Set("dry-run", "true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !DryRun(cmd) {
|
||||
t.Fatal("expected DryRun to be true after setting flag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_DryRun(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
if err := cmd.Flags().Set("dry-run", "true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
|
||||
err := Confirm(cmd, "would delete %s", "video123")
|
||||
if !errors.Is(err, ErrDryRun) {
|
||||
t.Fatalf("expected ErrDryRun, got %v", err)
|
||||
}
|
||||
|
||||
got := stdout.String()
|
||||
if !strings.Contains(got, "would delete video123") {
|
||||
t.Fatalf("expected message in stdout, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_Yes(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
if err := cmd.Flags().Set("yes", "true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := Confirm(cmd, "delete video?")
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_NonTTY(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
cmd.SetIn(strings.NewReader(""))
|
||||
|
||||
// Use the internal helper with isTTY=false to simulate non-TTY stdin.
|
||||
err := confirmWithTTYOverride(cmd, false, "delete video?")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-TTY stdin")
|
||||
}
|
||||
if !strings.Contains(
|
||||
err.Error(), "terminal required for confirmation; use --yes to skip",
|
||||
) {
|
||||
t.Fatalf("unexpected error message: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_TTY_Yes(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
cmd.SetIn(strings.NewReader("y\n"))
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.SetErr(&stderr)
|
||||
|
||||
err := confirmWithTTYOverride(cmd, true, "delete %s?", "video123")
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil, got %v", err)
|
||||
}
|
||||
|
||||
prompt := stderr.String()
|
||||
if !strings.Contains(prompt, "delete video123?") {
|
||||
t.Fatalf("expected prompt on stderr, got %q", prompt)
|
||||
}
|
||||
if !strings.Contains(prompt, "[y/N]") {
|
||||
t.Fatalf("expected [y/N] in prompt, got %q", prompt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_TTY_No(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
cmd.SetIn(strings.NewReader("n\n"))
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.SetErr(&stderr)
|
||||
|
||||
err := confirmWithTTYOverride(cmd, true, "delete video?")
|
||||
if !errors.Is(err, errAborted) {
|
||||
t.Fatalf("expected errAborted, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_TTY_Default(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
cmd.SetIn(strings.NewReader("\n"))
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.SetErr(&stderr)
|
||||
|
||||
err := confirmWithTTYOverride(cmd, true, "delete video?")
|
||||
if !errors.Is(err, errAborted) {
|
||||
t.Fatalf("expected errAborted for empty input (default N), got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfirm_DryRunTakesPrecedence(t *testing.T) {
|
||||
cmd := newTestCmd()
|
||||
if err := cmd.Flags().Set("dry-run", "true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := cmd.Flags().Set("yes", "true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
|
||||
err := Confirm(cmd, "would delete video")
|
||||
if !errors.Is(err, ErrDryRun) {
|
||||
t.Fatalf("expected ErrDryRun even with --yes, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package playlist
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -63,6 +64,7 @@ func init() {
|
||||
)
|
||||
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -70,11 +72,16 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would delete playlist(s): %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlist.NewPlaylist(
|
||||
playlist.WithIds(ids),
|
||||
playlist.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -82,6 +82,7 @@ func init() {
|
||||
_ = insertCmd.MarkFlagRequired("title")
|
||||
_ = insertCmd.MarkFlagRequired("channelId")
|
||||
_ = insertCmd.MarkFlagRequired("privacy")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -89,7 +90,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert playlist")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlist.NewPlaylist(
|
||||
playlist.WithTitle(title),
|
||||
playlist.WithDescription(description),
|
||||
@@ -99,6 +105,6 @@ var insertCmd = &cobra.Command{
|
||||
playlist.WithPrivacy(privacy),
|
||||
playlist.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
+10
-3
@@ -6,6 +6,7 @@ package playlist
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -85,6 +86,7 @@ func init() {
|
||||
updateCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("id")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -92,7 +94,12 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
confirmErr := cmd.Confirm(c, "Would update playlist: %s", strings.Join(ids, ", "))
|
||||
if confirmErr != nil {
|
||||
utils.HandleCmdError(confirmErr, c)
|
||||
return
|
||||
}
|
||||
p := playlist.NewPlaylist(
|
||||
playlist.WithIds(ids),
|
||||
playlist.WithTitle(title),
|
||||
@@ -102,7 +109,7 @@ var updateCmd = &cobra.Command{
|
||||
playlist.WithPrivacy(privacy),
|
||||
playlist.WithOutput(output),
|
||||
)
|
||||
err := p.Update(cmd.OutOrStdout())
|
||||
utils.HandleCmdError(err, cmd)
|
||||
err := p.Update(c.OutOrStdout())
|
||||
utils.HandleCmdError(err, c)
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package playlistImage
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -63,6 +64,7 @@ func init() {
|
||||
)
|
||||
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -70,11 +72,18 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(
|
||||
c, "Would delete playlist image(s): %s", strings.Join(ids, ", "),
|
||||
)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlistImage.NewPlaylistImage(
|
||||
playlistImage.WithIds(ids),
|
||||
playlistImage.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -94,6 +94,7 @@ func init() {
|
||||
|
||||
_ = insertCmd.MarkFlagRequired("file")
|
||||
_ = insertCmd.MarkFlagRequired("playlistId")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -101,7 +102,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
confirmErr := cmd.Confirm(c, "Would insert playlist image")
|
||||
if confirmErr != nil {
|
||||
utils.HandleCmdError(confirmErr, c)
|
||||
return
|
||||
}
|
||||
pi := playlistImage.NewPlaylistImage(
|
||||
playlistImage.WithFile(file),
|
||||
playlistImage.WithPlaylistId(playlistId),
|
||||
@@ -112,7 +118,7 @@ var insertCmd = &cobra.Command{
|
||||
playlistImage.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
playlistImage.WithOnBehalfOfContentOwnerChannel(onBehalfOfContentOwnerChannel),
|
||||
)
|
||||
err := pi.Insert(cmd.OutOrStdout())
|
||||
utils.HandleCmdError(err, cmd)
|
||||
err := pi.Insert(c.OutOrStdout())
|
||||
utils.HandleCmdError(err, c)
|
||||
},
|
||||
}
|
||||
@@ -87,6 +87,7 @@ func init() {
|
||||
)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("playlistId")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -94,7 +95,14 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(
|
||||
c, "Would update playlist image for playlist: %s", playlistId,
|
||||
)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlistImage.NewPlaylistImage(
|
||||
playlistImage.WithPlaylistId(playlistId),
|
||||
playlistImage.WithType(type_),
|
||||
@@ -105,6 +113,6 @@ var updateCmd = &cobra.Command{
|
||||
playlistImage.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
playlistImage.WithOnBehalfOfContentOwnerChannel(onBehalfOfContentOwnerChannel),
|
||||
)
|
||||
utils.HandleCmdError(input.Update(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Update(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package playlistItem
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -63,6 +64,7 @@ func init() {
|
||||
)
|
||||
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -70,11 +72,18 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(
|
||||
c, "Would delete playlist item(s): %s", strings.Join(ids, ", "),
|
||||
)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlistItem.NewPlaylistItem(
|
||||
playlistItem.WithIds(ids),
|
||||
playlistItem.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -92,6 +92,7 @@ func init() {
|
||||
_ = insertCmd.MarkFlagRequired("kind")
|
||||
_ = insertCmd.MarkFlagRequired("playlistId")
|
||||
_ = insertCmd.MarkFlagRequired("channelId")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -99,7 +100,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert playlist item")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlistItem.NewPlaylistItem(
|
||||
playlistItem.WithTitle(title),
|
||||
playlistItem.WithDescription(description),
|
||||
@@ -113,6 +119,6 @@ var insertCmd = &cobra.Command{
|
||||
playlistItem.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
playlistItem.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package playlistItem
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -80,6 +81,7 @@ func init() {
|
||||
updateCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("id")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -87,7 +89,14 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(
|
||||
c, "Would update playlist item: %s", strings.Join(ids, ", "),
|
||||
)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := playlistItem.NewPlaylistItem(
|
||||
playlistItem.WithIds(ids),
|
||||
playlistItem.WithTitle(title),
|
||||
@@ -96,6 +105,6 @@ var updateCmd = &cobra.Command{
|
||||
playlistItem.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
playlistItem.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Update(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Update(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package subscription
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -57,6 +58,7 @@ func init() {
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -64,10 +66,15 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would delete subscription(s): %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := subscription.NewSubscription(
|
||||
subscription.WithIds(ids),
|
||||
)
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -71,6 +71,7 @@ func init() {
|
||||
|
||||
_ = insertCmd.MarkFlagRequired("subscriberChannelId")
|
||||
_ = insertCmd.MarkFlagRequired("channelId")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -78,7 +79,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert subscription")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := subscription.NewSubscription(
|
||||
subscription.WithSubscriberChannelId(subscriberChannelId),
|
||||
subscription.WithDescription(description),
|
||||
@@ -86,6 +92,6 @@ var insertCmd = &cobra.Command{
|
||||
subscription.WithTitle(title),
|
||||
subscription.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -64,6 +64,7 @@ func init() {
|
||||
|
||||
_ = setCmd.MarkFlagRequired("file")
|
||||
_ = setCmd.MarkFlagRequired("videoId")
|
||||
cmd.AddMutationFlags(setCmd)
|
||||
}
|
||||
|
||||
var setCmd = &cobra.Command{
|
||||
@@ -71,12 +72,17 @@ var setCmd = &cobra.Command{
|
||||
Short: setShort,
|
||||
Long: setLong,
|
||||
Example: setExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would set thumbnail for video: %s", videoId)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := thumbnail.NewThumbnail(
|
||||
thumbnail.WithFile(file),
|
||||
thumbnail.WithVideoId(videoId),
|
||||
thumbnail.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Set(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Set(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
+9
-2
@@ -5,6 +5,7 @@ package video
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -57,6 +58,7 @@ func init() {
|
||||
|
||||
deleteCmd.Flags().StringSliceVarP(&ids, "ids", "i", []string{}, deleteIdsUsage)
|
||||
_ = deleteCmd.MarkFlagRequired("ids")
|
||||
cmd.AddMutationFlags(deleteCmd)
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
@@ -64,8 +66,13 @@ var deleteCmd = &cobra.Command{
|
||||
Short: deleteShort,
|
||||
Long: deleteLong,
|
||||
Example: deleteExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would delete video(s): %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := video.NewVideo(video.WithIds(ids))
|
||||
utils.HandleCmdError(input.Delete(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Delete(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
+8
-2
@@ -139,6 +139,7 @@ func init() {
|
||||
_ = insertCmd.MarkFlagRequired("file")
|
||||
_ = insertCmd.MarkFlagRequired("categoryId")
|
||||
_ = insertCmd.MarkFlagRequired("privacy")
|
||||
cmd.AddMutationFlags(insertCmd)
|
||||
}
|
||||
|
||||
var insertCmd = &cobra.Command{
|
||||
@@ -146,7 +147,12 @@ var insertCmd = &cobra.Command{
|
||||
Short: insertShort,
|
||||
Long: insertLong,
|
||||
Example: insertExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would insert video")
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := video.NewVideo(
|
||||
video.WithAutoLevels(autoLevels),
|
||||
video.WithFile(file),
|
||||
@@ -172,6 +178,6 @@ var insertCmd = &cobra.Command{
|
||||
video.WithOnBehalfOfContentOwnerChannel(onBehalfOfContentOwnerChannel),
|
||||
video.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Insert(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Insert(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
+9
-2
@@ -5,6 +5,7 @@ package video
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -67,6 +68,7 @@ func init() {
|
||||
|
||||
_ = rateCmd.MarkFlagRequired("ids")
|
||||
_ = rateCmd.MarkFlagRequired("rating")
|
||||
cmd.AddMutationFlags(rateCmd)
|
||||
}
|
||||
|
||||
var rateCmd = &cobra.Command{
|
||||
@@ -74,11 +76,16 @@ var rateCmd = &cobra.Command{
|
||||
Short: rateShort,
|
||||
Long: rateLong,
|
||||
Example: rateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would rate video(s): %s as %s", strings.Join(ids, ", "), rating)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := video.NewVideo(
|
||||
video.WithIds(ids),
|
||||
video.WithRating(rating),
|
||||
)
|
||||
utils.HandleCmdError(input.Rate(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Rate(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package video
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -82,6 +83,7 @@ func init() {
|
||||
|
||||
_ = reportAbuseCmd.MarkFlagRequired("ids")
|
||||
_ = reportAbuseCmd.MarkFlagRequired("reasonId")
|
||||
cmd.AddMutationFlags(reportAbuseCmd)
|
||||
}
|
||||
|
||||
var reportAbuseCmd = &cobra.Command{
|
||||
@@ -89,7 +91,12 @@ var reportAbuseCmd = &cobra.Command{
|
||||
Short: reportAbuseShort,
|
||||
Long: reportAbuseLong,
|
||||
Example: reportAbuseExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would report abuse on video(s): %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := video.NewVideo(
|
||||
video.WithIds(ids),
|
||||
video.WithReasonId(reasonId),
|
||||
@@ -98,6 +105,6 @@ var reportAbuseCmd = &cobra.Command{
|
||||
video.WithLanguage(language),
|
||||
video.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.ReportAbuse(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.ReportAbuse(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
+9
-2
@@ -6,6 +6,7 @@ package video
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
cobramcp "github.com/eat-pray-ai/cobra-mcp"
|
||||
"github.com/eat-pray-ai/yutu/cmd"
|
||||
@@ -106,6 +107,7 @@ func init() {
|
||||
updateCmd.Flags().StringVarP(&output, "output", "o", "", pkg.SilentUsage)
|
||||
|
||||
_ = updateCmd.MarkFlagRequired("id")
|
||||
cmd.AddMutationFlags(updateCmd)
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
@@ -113,7 +115,12 @@ var updateCmd = &cobra.Command{
|
||||
Short: updateShort,
|
||||
Long: updateLong,
|
||||
Example: updateExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would update video: %s", strings.Join(ids, ", "))
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := video.NewVideo(
|
||||
video.WithIds(ids),
|
||||
video.WithTitle(title),
|
||||
@@ -131,6 +138,6 @@ var updateCmd = &cobra.Command{
|
||||
video.WithMaxResults(1),
|
||||
video.WithOutput(output),
|
||||
)
|
||||
utils.HandleCmdError(input.Update(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Update(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -80,6 +80,7 @@ func init() {
|
||||
|
||||
_ = setCmd.MarkFlagRequired("channelId")
|
||||
_ = setCmd.MarkFlagRequired("file")
|
||||
cmd.AddMutationFlags(setCmd)
|
||||
}
|
||||
|
||||
var setCmd = &cobra.Command{
|
||||
@@ -87,7 +88,12 @@ var setCmd = &cobra.Command{
|
||||
Short: setShort,
|
||||
Long: setLong,
|
||||
Example: setExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would set watermark for channel: %s", channelId)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := watermark.NewWatermark(
|
||||
watermark.WithChannelId(channelId),
|
||||
watermark.WithFile(file),
|
||||
@@ -97,6 +103,6 @@ var setCmd = &cobra.Command{
|
||||
watermark.WithOffsetType(offsetType),
|
||||
watermark.WithOnBehalfOfContentOwner(onBehalfOfContentOwner),
|
||||
)
|
||||
utils.HandleCmdError(input.Set(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Set(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -51,6 +51,7 @@ func init() {
|
||||
|
||||
unsetCmd.Flags().StringVarP(&channelId, "channelId", "c", "", cidUsage)
|
||||
_ = unsetCmd.MarkFlagRequired("channelId")
|
||||
cmd.AddMutationFlags(unsetCmd)
|
||||
}
|
||||
|
||||
var unsetCmd = &cobra.Command{
|
||||
@@ -58,8 +59,13 @@ var unsetCmd = &cobra.Command{
|
||||
Short: unsetShort,
|
||||
Long: unsetLong,
|
||||
Example: unsetExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
err := cmd.Confirm(c, "Would unset watermark for channel: %s", channelId)
|
||||
if err != nil {
|
||||
utils.HandleCmdError(err, c)
|
||||
return
|
||||
}
|
||||
input := watermark.NewWatermark(watermark.WithChannelId(channelId))
|
||||
utils.HandleCmdError(input.Unset(cmd.OutOrStdout()), cmd)
|
||||
utils.HandleCmdError(input.Unset(c.OutOrStdout()), c)
|
||||
},
|
||||
}
|
||||
@@ -20,7 +20,6 @@ require (
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -35,7 +34,6 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/segmentio/asm v1.2.1 // indirect
|
||||
github.com/segmentio/encoding v0.5.4 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
@@ -52,10 +50,10 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/term v0.42.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
|
||||
@@ -19,8 +19,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eat-pray-ai/cobra-mcp v0.1.0 h1:szHIgFhCH0HqWSRRlyVVWzrB+3c9SMNfQCxlKA4lNLo=
|
||||
@@ -74,7 +72,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/savioxavier/termlink v1.4.3 h1:Gh6vrG7jSn21cRiYdQqFXYcdXfM+Fg14aG487JTfKpA=
|
||||
github.com/savioxavier/termlink v1.4.3/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc=
|
||||
@@ -123,7 +120,6 @@ go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpu
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
@@ -135,6 +131,8 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
|
||||
@@ -7,8 +7,8 @@ go_library(
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//cmd",
|
||||
"//cmd/agent",
|
||||
"//cmd/activity",
|
||||
"//cmd/agent",
|
||||
"//cmd/caption",
|
||||
"//cmd/channel",
|
||||
"//cmd/channelBanner",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 eat-pray-ai & OpenWaygate
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
|
||||
+8
-3
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
@@ -99,9 +100,13 @@ func ExtractHl(uri string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// ErrDryRun is returned when --dry-run is set; HandleCmdError silently ignores it.
|
||||
var ErrDryRun = errors.New("dry run")
|
||||
|
||||
func HandleCmdError(err error, cmd *cobra.Command) {
|
||||
if err != nil {
|
||||
_ = cmd.Help()
|
||||
cmd.PrintErrf("Error: %v\n", err)
|
||||
if err == nil || errors.Is(err, ErrDryRun) {
|
||||
return
|
||||
}
|
||||
_ = cmd.Help()
|
||||
cmd.PrintErrf("Error: %v\n", err)
|
||||
}
|
||||
@@ -331,6 +331,16 @@ func TestRandomStage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleCmdError_DryRun(t *testing.T) {
|
||||
c := &cobra.Command{Use: "test"}
|
||||
var stderr bytes.Buffer
|
||||
c.SetErr(&stderr)
|
||||
HandleCmdError(ErrDryRun, c)
|
||||
if stderr.Len() != 0 {
|
||||
t.Errorf("expected no error output for ErrDryRun, got %q", stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleCmdError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
Reference in new issue
Block a user