intake/cmd/actionExecute.go

159 lines
4.2 KiB
Go

package cmd
import (
"fmt"
"log"
"slices"
"time"
"github.com/Jaculabilis/intake/core"
"github.com/spf13/cobra"
)
var actionExecuteCmd = &cobra.Command{
Use: "execute",
Aliases: []string{"exec"},
Short: "Run a source action for an item",
Long: fmt.Sprintf(`Execute a source action for an item.
The item must declare support for the action by having the action's name
in its "action" field. Use --force to execute the action anyway.
The "fetch" action is special and does not execute for any specific item.
Use "intake source fetch" to run the fetch action.
In a dry run, the item will be printed in the chosen format and not updated.
%s`, makeFormatHelpText()),
Run: func(cmd *cobra.Command, args []string) {
actionExecute(
stringArg(cmd, "source"),
stringArg(cmd, "action"),
stringArg(cmd, "item"),
stringArg(cmd, "format"),
stringArg(cmd, "timeout"),
boolArg(cmd, "dry-run"),
boolArg(cmd, "diff"),
boolArg(cmd, "force"),
)
},
}
func init() {
actionCmd.AddCommand(actionExecuteCmd)
actionExecuteCmd.PersistentFlags().StringP("source", "s", "", "Source of the item")
actionExecuteCmd.MarkFlagRequired("source")
actionExecuteCmd.PersistentFlags().StringP("item", "i", "", "Item to run action on")
actionExecuteCmd.MarkFlagRequired("item")
actionExecuteCmd.PersistentFlags().StringP("action", "a", "", "Action to run")
actionExecuteCmd.MarkFlagRequired("action")
actionExecuteCmd.Flags().StringP("format", "f", "headlines", "Feed format for returned items")
actionExecuteCmd.Flags().StringP("timeout", "t", core.DefaultTimeout.String(),
fmt.Sprintf("Timeout duration (default: %s)", core.DefaultTimeout.String()))
actionExecuteCmd.Flags().Bool("dry-run", false, "Instead of updating the item, print it")
actionExecuteCmd.Flags().Bool("diff", false, "Show which fields of the item changed")
actionExecuteCmd.Flags().Bool("force", false, "Execute the action even if the item does not support it")
}
func actionExecute(
source string,
action string,
itemId string,
format string,
timeout string,
dryRun bool,
diff bool,
force bool,
) {
formatter := formatAs(format)
if source == "" {
log.Fatal("error: --source is empty")
}
if action == "" {
log.Fatal("error: --action is empty")
}
if itemId == "" {
log.Fatal("error: --item is empty")
}
duration, err := time.ParseDuration(timeout)
if err != nil {
log.Fatalf("error: invalid duration: %v", err)
}
db := openAndMigrateDb()
state, envs, argv, postProcess, err := core.GetSourceActionInputs(db, source, action)
if err != nil {
log.Fatalf("error: failed to load data for %s: %v", source, err)
}
item, err := core.GetItem(db, source, itemId)
if err != nil {
log.Fatalf("error: failed to get item: %v", err)
}
if item.Action[action] == nil {
if force {
log.Printf("warning: force-executing %s on %s/%s", action, source, itemId)
} else {
log.Fatalf("error: %s/%s does not support %s", source, itemId, action)
}
}
newItem, newState, errItem, err := core.ExecuteItemAction(item, argv, envs, state, duration, postProcess)
if err != nil {
core.AddErrorItem(db, errItem)
log.Fatalf("error executing %s: %v", action, err)
}
if diff {
if item.Title != newItem.Title {
log.Printf("title: %s => %s", item.Title, newItem.Title)
}
if item.Author != newItem.Author {
log.Printf("author: %s => %s", item.Author, newItem.Author)
}
if item.Body != newItem.Body {
log.Printf("body: %s => %s", item.Body, newItem.Body)
}
if item.Link != newItem.Link {
log.Printf("link: %s => %s", item.Link, newItem.Link)
}
if item.Time != newItem.Time {
log.Printf("time: %d => %d", item.Time, newItem.Time)
}
if core.ItemsAreEqual(item, newItem) {
log.Printf("no changes\n")
}
if !slices.Equal(state, newState) {
log.Printf("state changed (%d => %d bytes)", len(state), len(newState))
}
}
if dryRun {
fmt.Println(formatter(newItem))
return
}
if err = db.Transact(func(tx core.DB) error {
if _err := core.UpdateItems(tx, []core.Item{newItem}); err != nil {
return fmt.Errorf("failed to update item: %v", _err)
}
if _err := core.SetState(tx, source, newState); err != nil {
return fmt.Errorf("failed to set state for %s: %v", source, _err)
}
return nil
}); err != nil {
log.Fatalf("error: %v", err)
}
}