package cmd import ( "fmt" "os" "github.com/packwiz/packwiz/cmdshared" "github.com/packwiz/packwiz/core" "github.com/spf13/cobra" "github.com/spf13/viper" ) // UpdateCmd represents the update command var UpdateCmd = &cobra.Command{ Use: "update [name]", Short: "Update an external file (or all external files) in the modpack", Aliases: []string{"upgrade"}, Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { // TODO: --check flag? // TODO: specify multiple files to update at once? fmt.Println("Loading modpack...") pack, err := core.LoadPack() if err != nil { fmt.Println(err) os.Exit(1) } index, err := pack.LoadIndex() if err != nil { fmt.Println(err) os.Exit(1) } var singleUpdatedName string if viper.GetBool("update.all") { filesWithUpdater := make(map[string][]*core.Mod) fmt.Println("Reading metadata files...") mods, err := index.LoadAllMods() if err != nil { fmt.Printf("Failed to update all files: %v\n", err) os.Exit(1) } for _, modData := range mods { updaterFound := false for k := range modData.Update { slice, ok := filesWithUpdater[k] if !ok { _, ok = core.Updaters[k] if !ok { continue } slice = []*core.Mod{} } updaterFound = true filesWithUpdater[k] = append(slice, modData) } if !updaterFound { fmt.Printf("A supported update system for \"%s\" cannot be found.\n", modData.Name) } } fmt.Println("Checking for updates...") updatesFound := false updatableFiles := make(map[string][]*core.Mod) updaterCachedStateMap := make(map[string][]interface{}) for k, v := range filesWithUpdater { checks, err := core.Updaters[k].CheckUpdate(v, pack) if err != nil { // TODO: do we return err code 1? fmt.Printf("Failed to check updates for %s: %s\n", k, err.Error()) continue } for i, check := range checks { if check.Error != nil { // TODO: do we return err code 1? fmt.Printf("Failed to check updates for %s: %s\n", v[i].Name, check.Error.Error()) continue } if check.UpdateAvailable { if v[i].Pin { fmt.Printf("Update skipped for pinned mod %s\n", v[i].Name) continue } if !updatesFound { fmt.Println("Updates found:") updatesFound = true } fmt.Printf("%s: %s\n", v[i].Name, check.UpdateString) updatableFiles[k] = append(updatableFiles[k], v[i]) updaterCachedStateMap[k] = append(updaterCachedStateMap[k], check.CachedState) } } } if !updatesFound { fmt.Println("All files are up to date!") return } if !cmdshared.PromptYesNo("Do you want to update? [Y/n]: ") { fmt.Println("Cancelled!") return } for k, v := range updatableFiles { err := core.Updaters[k].DoUpdate(v, updaterCachedStateMap[k]) if err != nil { // TODO: do we return err code 1? fmt.Println(err.Error()) continue } for _, modData := range v { format, hash, err := modData.Write() if err != nil { fmt.Println(err.Error()) continue } err = index.RefreshFileWithHash(modData.GetFilePath(), format, hash, true) if err != nil { fmt.Println(err.Error()) continue } } } } else { if len(args) < 1 || len(args[0]) == 0 { fmt.Println("Must specify a valid file, or use the --all flag!") os.Exit(1) } modPath, ok := index.FindMod(args[0]) if !ok { fmt.Println("Can't find this file; please ensure you have run packwiz refresh and use the name of the .pw.toml file (defaults to the project slug)") os.Exit(1) } modData, err := core.LoadMod(modPath) if err != nil { fmt.Println(err) os.Exit(1) } if modData.Pin { fmt.Println("Version is pinned; run the unpin command to allow updating") os.Exit(1) } singleUpdatedName = modData.Name updaterFound := false for k := range modData.Update { updater, ok := core.Updaters[k] if !ok { continue } updaterFound = true check, err := updater.CheckUpdate([]*core.Mod{&modData}, pack) if err != nil { fmt.Println(err) os.Exit(1) } if len(check) != 1 { fmt.Println("Invalid update check response") os.Exit(1) } if check[0].UpdateAvailable { fmt.Printf("Update available: %s\n", check[0].UpdateString) err = updater.DoUpdate([]*core.Mod{&modData}, []interface{}{check[0].CachedState}) if err != nil { fmt.Println(err) os.Exit(1) } format, hash, err := modData.Write() if err != nil { fmt.Println(err) os.Exit(1) } err = index.RefreshFileWithHash(modPath, format, hash, true) if err != nil { fmt.Println(err) os.Exit(1) } } else { fmt.Printf("\"%s\" is already up to date!\n", modData.Name) return } break } if !updaterFound { // TODO: use file name instead of Name when len(Name) == 0 in all places? fmt.Println("A supported update system for \"" + modData.Name + "\" cannot be found.") os.Exit(1) } } err = index.Write() if err != nil { fmt.Println(err) os.Exit(1) } err = pack.UpdateIndexHash() if err != nil { fmt.Println(err) os.Exit(1) } err = pack.Write() if err != nil { fmt.Println(err) os.Exit(1) } if viper.GetBool("update.all") { fmt.Println("Files updated!") } else { fmt.Printf("\"%s\" updated!\n", singleUpdatedName) } }, } func init() { rootCmd.AddCommand(UpdateCmd) UpdateCmd.Flags().BoolP("all", "a", false, "Update all external files") _ = viper.BindPFlag("update.all", UpdateCmd.Flags().Lookup("all")) }