304 lines
12 KiB
Go
304 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"scout/pkg/client"
|
|
"scout/pkg/scoutpkg"
|
|
"scout/pkg/updater"
|
|
)
|
|
|
|
type targetFlags struct {
|
|
platform *string
|
|
architecture *string
|
|
channel *string
|
|
}
|
|
|
|
type managerFlags struct {
|
|
server *string
|
|
packageName *string
|
|
rootDir *string
|
|
currentVersion *string
|
|
currentPath *string
|
|
entrypoint *string
|
|
platform *string
|
|
architecture *string
|
|
channel *string
|
|
healthTimeout *time.Duration
|
|
keepReleases *int
|
|
}
|
|
|
|
func checkCommand(args []string) {
|
|
flags := flag.NewFlagSet("check", flag.ExitOnError)
|
|
server := flags.String("server", "http://localhost:8080", "\u670d\u52a1\u7aef\u5730\u5740")
|
|
name := flags.String("package", "", "\u5305\u540d")
|
|
current := flags.String("current", "", "\u5f53\u524d\u7248\u672c")
|
|
target := bindTargetFlags(flags)
|
|
flags.Parse(args)
|
|
requirePackage(*name)
|
|
api := newClient(*server, "")
|
|
latest, available, err := api.CheckUpdateFor(context.Background(), *name, *current, target.options(*current))
|
|
if err != nil {
|
|
fatal("\u68c0\u67e5\u66f4\u65b0\u5931\u8d25: %v", err)
|
|
}
|
|
if available {
|
|
fmt.Printf("\u53d1\u73b0\u66f4\u65b0\uff1a%s %s -> %s\uff08%s/%s\uff09\n", latest.Name, *current, latest.Version, latest.Platform, latest.Architecture)
|
|
return
|
|
}
|
|
fmt.Printf("\u5f53\u524d\u5df2\u662f\u6700\u65b0\u7248\u672c\uff1a%s\n", latest.Version)
|
|
}
|
|
|
|
func downloadCommand(args []string) {
|
|
flags := flag.NewFlagSet("download", flag.ExitOnError)
|
|
server := flags.String("server", "http://localhost:8080", "\u670d\u52a1\u7aef\u5730\u5740")
|
|
name := flags.String("package", "", "\u5305\u540d")
|
|
version := flags.String("version", "", "\u7248\u672c\uff1b\u4e3a\u7a7a\u65f6\u4e0b\u8f7d\u6700\u65b0\u7248\u672c")
|
|
current := flags.String("current", "", "\u5f53\u524d\u7248\u672c")
|
|
output := flags.String("output", "", "\u4fdd\u5b58\u8def\u5f84")
|
|
target := bindTargetFlags(flags)
|
|
flags.Parse(args)
|
|
requirePackage(*name)
|
|
if *output == "" {
|
|
resolvedVersion := *version
|
|
if resolvedVersion == "" {
|
|
resolvedVersion = "latest"
|
|
}
|
|
*output = filepath.Join(".", fmt.Sprintf("%s-%s.pkg", *name, resolvedVersion))
|
|
}
|
|
api := newClient(*server, "")
|
|
item, err := api.DownloadFor(context.Background(), *name, *version, *output, target.options(*current))
|
|
if err != nil {
|
|
fatal("\u4e0b\u8f7d\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Printf("\u4e0b\u8f7d\u5b8c\u6210\uff1a%s\n\u7248\u672c\uff1a%s\n\u76ee\u6807\uff1a%s/%s\nSHA-256\uff1a%s\n", *output, item.Version, item.Platform, item.Architecture, item.SHA256)
|
|
}
|
|
|
|
func uploadCommand(args []string) {
|
|
flags := flag.NewFlagSet("upload", flag.ExitOnError)
|
|
server := flags.String("server", "http://localhost:8080", "\u670d\u52a1\u7aef\u5730\u5740")
|
|
token := flags.String("token", "", "\u5f00\u53d1\u8005\u4ee4\u724c")
|
|
name := flags.String("package", "", "\u5305\u540d\uff1bScout \u5305\u53ef\u7701\u7565")
|
|
version := flags.String("version", "", "\u7248\u672c\uff1bScout \u5305\u53ef\u7701\u7565")
|
|
filePath := flags.String("file", "", "\u5305\u6587\u4ef6\u8def\u5f84")
|
|
description := flags.String("description", "", "\u5305\u63cf\u8ff0")
|
|
packageType := flags.String("package-type", "full", "\u5305\u7c7b\u578b\uff1afull \u6216 delta")
|
|
channel := flags.String("channel", "stable", "\u53d1\u5e03\u901a\u9053\uff1astable\u3001beta \u6216 dev")
|
|
status := flags.String("status", "published", "\u53d1\u5e03\u72b6\u6001")
|
|
upgradeFrom := flags.String("upgrade-from", "", "\u5dee\u5206\u5305\u6765\u6e90\u7248\u672c\uff0c\u591a\u4e2a\u7528\u9017\u53f7\u5206\u9694")
|
|
target := bindTargetFlags(flags)
|
|
flags.Parse(args)
|
|
if *filePath == "" {
|
|
fatal("\u5fc5\u987b\u6307\u5b9a --file")
|
|
}
|
|
isScout := strings.EqualFold(filepath.Ext(*filePath), ".scout")
|
|
if !isScout && (*name == "" || *version == "" || *target.platform == "" || *target.architecture == "") {
|
|
fatal("\u666e\u901a\u5305\u5fc5\u987b\u6307\u5b9a --package\u3001--version\u3001--platform \u548c --architecture")
|
|
}
|
|
packageTypeValue := strings.TrimSpace(*packageType)
|
|
channelValue := strings.TrimSpace(*channel)
|
|
statusValue := strings.TrimSpace(*status)
|
|
if !isScout {
|
|
if packageTypeValue == "" {
|
|
packageTypeValue = "full"
|
|
}
|
|
if channelValue == "" {
|
|
channelValue = "stable"
|
|
}
|
|
if statusValue == "" {
|
|
statusValue = "published"
|
|
}
|
|
}
|
|
file, err := os.Open(*filePath)
|
|
if err != nil {
|
|
fatal("\u6253\u5f00\u5305\u6587\u4ef6\u5931\u8d25: %v", err)
|
|
}
|
|
defer file.Close()
|
|
api := newClient(*server, *token)
|
|
item, err := api.Upload(context.Background(), client.UploadRequest{
|
|
Name: *name,
|
|
Version: *version,
|
|
Description: *description,
|
|
Platform: *target.platform,
|
|
Architecture: *target.architecture,
|
|
PackageType: packageTypeValue,
|
|
Channel: channelValue,
|
|
Status: statusValue,
|
|
UpgradeFrom: splitPackages(*upgradeFrom),
|
|
FileName: filepath.Base(*filePath),
|
|
File: file,
|
|
})
|
|
if err != nil {
|
|
fatal("\u4e0a\u4f20\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Printf("\u4e0a\u4f20\u6210\u529f\uff1a%s %s\n\u901a\u9053\uff1a%s\n\u76ee\u6807\u6570\uff1a%d\nSHA-256\uff1a%s\n", item.Name, item.Version, item.Channel, len(item.Targets), item.SHA256)
|
|
}
|
|
|
|
func watchCommand(args []string) {
|
|
flags := flag.NewFlagSet("watch", flag.ExitOnError)
|
|
server := flags.String("server", "http://localhost:8080", "\u670d\u52a1\u7aef\u5730\u5740")
|
|
packages := flags.String("packages", "", "\u8981\u76d1\u542c\u7684\u5305\u540d\uff0c\u591a\u4e2a\u5305\u7528\u9017\u53f7\u5206\u9694")
|
|
versions := flags.String("versions", "", "\u5f53\u524d\u7248\u672c\uff0c\u683c\u5f0f\u4e3a package:version\uff0c\u591a\u4e2a\u7528\u9017\u53f7\u5206\u9694")
|
|
target := bindTargetFlags(flags)
|
|
flags.Parse(args)
|
|
api := newClient(*server, "")
|
|
updates, err := api.WatchUpdatesFor(context.Background(), client.WatchRequest{
|
|
Packages: splitPackages(*packages),
|
|
Versions: parseVersions(*versions),
|
|
Platform: *target.platform,
|
|
Architecture: *target.architecture,
|
|
Channel: *target.channel,
|
|
})
|
|
if err != nil {
|
|
fatal("\u8fde\u63a5\u66f4\u65b0\u901a\u77e5\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Println("\u5df2\u8fde\u63a5\u66f4\u65b0\u901a\u77e5\uff0c\u7b49\u5f85\u65b0\u5305\u2026\u2026")
|
|
for update := range updates {
|
|
fmt.Printf("\u6536\u5230\u66f4\u65b0\uff1a%s %s\uff08%s/%s\uff09\n", update.Package.Name, update.Package.Version, update.Package.Platform, update.Package.Architecture)
|
|
}
|
|
}
|
|
|
|
func prepareCommand(args []string) {
|
|
flags := flag.NewFlagSet("prepare", flag.ExitOnError)
|
|
managerInput := bindManagerFlags(flags)
|
|
version := flags.String("version", "", "\u8981\u51c6\u5907\u7684\u7248\u672c\uff1b\u4e3a\u7a7a\u65f6\u4f7f\u7528\u6700\u65b0\u7248\u672c")
|
|
flags.Parse(args)
|
|
manager := newUpdateManager(managerInput)
|
|
prepared, err := manager.Prepare(context.Background(), *version)
|
|
if err != nil {
|
|
fatal("\u51c6\u5907\u66f4\u65b0\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Printf("\u66f4\u65b0\u5df2\u51c6\u5907\uff1a%s\n\u76ee\u5f55\uff1a%s\n\u5c1a\u672a\u5207\u6362\u5f53\u524d\u7a0b\u5e8f\uff0c\u53ef\u7531\u7528\u6237\u51b3\u5b9a\u4f55\u65f6\u91cd\u542f\u3002\n", prepared.Version, prepared.Path)
|
|
}
|
|
|
|
func statusCommand(args []string) {
|
|
flags := flag.NewFlagSet("status", flag.ExitOnError)
|
|
managerInput := bindManagerFlags(flags)
|
|
flags.Parse(args)
|
|
status, err := newUpdateManager(managerInput).Status()
|
|
if err != nil {
|
|
fatal("\u8bfb\u53d6\u66f4\u65b0\u72b6\u6001\u5931\u8d25: %v", err)
|
|
}
|
|
data, _ := json.MarshalIndent(status, "", " ")
|
|
fmt.Println(string(data))
|
|
}
|
|
|
|
func activateCommand(args []string) {
|
|
flags := flag.NewFlagSet("activate", flag.ExitOnError)
|
|
managerInput := bindManagerFlags(flags)
|
|
restartNow := flags.Bool("restart-now", false, "\u6fc0\u6d3b\u540e\u4f7f\u7528\u7ea6\u5b9a\u9000\u51fa\u7801\u901a\u77e5\u5bbf\u4e3b\u7acb\u5373\u91cd\u542f")
|
|
flags.Parse(args)
|
|
activated, err := newUpdateManager(managerInput).Activate()
|
|
if err != nil {
|
|
fatal("\u6fc0\u6d3b\u66f4\u65b0\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Printf("\u5df2\u6fc0\u6d3b\u7248\u672c\uff1a%s\n", activated.Version)
|
|
if *restartNow {
|
|
os.Exit(updater.ExitRestartNow)
|
|
}
|
|
}
|
|
|
|
func healthyCommand(args []string) {
|
|
flags := flag.NewFlagSet("healthy", flag.ExitOnError)
|
|
managerInput := bindManagerFlags(flags)
|
|
version := flags.String("version", "", "\u8981\u786e\u8ba4\u7684\u5f53\u524d\u7248\u672c")
|
|
flags.Parse(args)
|
|
if err := newUpdateManager(managerInput).ConfirmHealthy(*version); err != nil {
|
|
fatal("\u5065\u5eb7\u786e\u8ba4\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Println("\u65b0\u7248\u672c\u5df2\u901a\u8fc7\u5065\u5eb7\u786e\u8ba4\uff0c\u65e7\u7248\u672c\u5df2\u6309\u4fdd\u7559\u7b56\u7565\u6e05\u7406\u3002")
|
|
}
|
|
|
|
func rollbackCommand(args []string) {
|
|
flags := flag.NewFlagSet("rollback", flag.ExitOnError)
|
|
managerInput := bindManagerFlags(flags)
|
|
reason := flags.String("reason", "\u7528\u6237\u624b\u52a8\u56de\u6eda", "\u56de\u6eda\u539f\u56e0")
|
|
flags.Parse(args)
|
|
rolledBack, err := newUpdateManager(managerInput).Rollback(fmt.Errorf("%s", *reason))
|
|
if err != nil {
|
|
fatal("\u56de\u6eda\u5931\u8d25: %v", err)
|
|
}
|
|
fmt.Printf("\u5df2\u56de\u6eda\u5230\u7248\u672c\uff1a%s\n", rolledBack.Version)
|
|
}
|
|
|
|
func bindTargetFlags(flags *flag.FlagSet) targetFlags {
|
|
defaultPlatform, defaultArchitecture := scoutpkg.CurrentTarget()
|
|
return targetFlags{
|
|
platform: flags.String("platform", defaultPlatform, "\u8fd0\u884c\u5e73\u53f0"),
|
|
architecture: flags.String("architecture", defaultArchitecture, "\u8fd0\u884c\u67b6\u6784"),
|
|
channel: flags.String("channel", "stable", "\u53d1\u5e03\u901a\u9053"),
|
|
}
|
|
}
|
|
|
|
func (target targetFlags) options(currentVersion string) client.QueryOptions {
|
|
return client.QueryOptions{
|
|
Platform: *target.platform,
|
|
Architecture: *target.architecture,
|
|
Channel: *target.channel,
|
|
CurrentVersion: currentVersion,
|
|
}
|
|
}
|
|
|
|
func bindManagerFlags(flags *flag.FlagSet) managerFlags {
|
|
defaultPlatform, defaultArchitecture := scoutpkg.CurrentTarget()
|
|
return managerFlags{
|
|
server: flags.String("server", "http://localhost:8080", "\u670d\u52a1\u7aef\u5730\u5740"),
|
|
packageName: flags.String("package", "", "\u5305\u540d"),
|
|
rootDir: flags.String("root", ".scout-update", "\u66f4\u65b0\u5de5\u4f5c\u76ee\u5f55"),
|
|
currentVersion: flags.String("current", "", "\u5f53\u524d\u7248\u672c"),
|
|
currentPath: flags.String("current-path", "", "\u5f53\u524d\u7a0b\u5e8f\u76ee\u5f55"),
|
|
entrypoint: flags.String("entrypoint", "", "\u7a0b\u5e8f\u5165\u53e3\u76f8\u5bf9\u8def\u5f84"),
|
|
platform: flags.String("platform", defaultPlatform, "\u8fd0\u884c\u5e73\u53f0"),
|
|
architecture: flags.String("architecture", defaultArchitecture, "\u8fd0\u884c\u67b6\u6784"),
|
|
channel: flags.String("channel", "stable", "\u53d1\u5e03\u901a\u9053"),
|
|
healthTimeout: flags.Duration("health-timeout", 2*time.Minute, "\u5065\u5eb7\u786e\u8ba4\u8d85\u65f6\u65f6\u95f4"),
|
|
keepReleases: flags.Int("keep", 2, "\u5065\u5eb7\u786e\u8ba4\u540e\u4fdd\u7559\u7684\u7248\u672c\u6570"),
|
|
}
|
|
}
|
|
|
|
func newUpdateManager(input managerFlags) *updater.Manager {
|
|
requirePackage(*input.packageName)
|
|
api := newClient(*input.server, "")
|
|
manager, err := updater.New(updater.Config{
|
|
Client: api,
|
|
PackageName: *input.packageName,
|
|
CurrentVersion: *input.currentVersion,
|
|
CurrentPath: *input.currentPath,
|
|
RootDir: *input.rootDir,
|
|
Platform: *input.platform,
|
|
Architecture: *input.architecture,
|
|
Channel: *input.channel,
|
|
Entrypoint: *input.entrypoint,
|
|
HealthTimeout: *input.healthTimeout,
|
|
KeepReleases: *input.keepReleases,
|
|
})
|
|
if err != nil {
|
|
fatal("\u521b\u5efa\u66f4\u65b0\u7ba1\u7406\u5668\u5931\u8d25: %v", err)
|
|
}
|
|
return manager
|
|
}
|
|
|
|
func parseVersions(value string) map[string]string {
|
|
result := make(map[string]string)
|
|
for _, entry := range splitPackages(value) {
|
|
parts := strings.SplitN(entry, ":", 2)
|
|
if len(parts) == 2 && strings.TrimSpace(parts[0]) != "" {
|
|
result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func printUsage() {
|
|
fmt.Println("Scout \u8f7b\u91cf\u5316\u5305\u7ba1\u7406\u5ba2\u6237\u7aef")
|
|
fmt.Println("\u7528\u6cd5\uff1ascout-client <check|download|upload|watch|prepare|status|activate|healthy|rollback> [\u53c2\u6570]")
|
|
fmt.Println("check\u3001download\u3001watch \u7528\u4e8e\u66f4\u65b0\u67e5\u8be2\uff0cupload \u7528\u4e8e\u5f00\u53d1\u8005\u4e0a\u4f20\u3002")
|
|
fmt.Println("prepare \u53ea\u51c6\u5907\u65b0\u7248\u672c\uff0cactivate \u7531\u7528\u6237\u51b3\u5b9a\u91cd\u542f\u65f6\u673a\uff0chealthy \u786e\u8ba4\u6210\u529f\u540e\u624d\u6e05\u7406\u65e7\u7248\u672c\u3002")
|
|
}
|