package main import ( "bufio" "context" "errors" "flag" "fmt" "io" "log" "os" "sort" "strings" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/updatewords" ) var authorizeInput io.Reader = os.Stdin // runReleasesAuthorize is `carlos releases authorize [--app ] ` // (also ` `, as the spec writes it): show a build's manifest // and artifact digests, and on "yes" sign a promotion record for them // (settings spec ยง4.1). Never automatic: nothing else backfills a record. func runReleasesAuthorize(args []string) { fs := flag.NewFlagSet("releases authorize", flag.ExitOnError) app := appFlag(fs, "") acctRaw, acctIDRaw, acctOf := accountFlags(fs) yes := fs.Bool("yes", false, updatewords.Text("settings.authorize.flag-yes")) consoleFlag := consoleOption(fs, updatewords.Text("settings.authorize.flag-console")) fs.Usage = flagUsage(fs, "") fs.Parse(args) usage := updatewords.Text("settings.authorize.usage") pos := fs.Args() if len(pos) == 2 && *app == "" { *app, pos = pos[0], pos[1:] } if len(pos) != 1 { log.Fatal(usage) } version := pos[0] show := func(digest string, arts map[string]release.Artifact) bool { fmt.Fprintf(stdout, updatewords.Text("settings.authorize.intro")+"\n", *app, version) fmt.Fprintf(stdout, updatewords.Text("settings.authorize.manifest-row")+"\n", digest) keys := make([]string, 0, len(arts)) for k := range arts { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Fprintf(stdout, updatewords.Text("settings.authorize.artifact-row")+"\n", k, arts[k].SHA256) } if *yes { return true } fmt.Fprint(stdout, updatewords.Text("settings.authorize.prompt")) line, _ := bufio.NewReader(authorizeInput).ReadString('\n') if strings.TrimSpace(line) != "yes" { fmt.Fprintln(stdout, updatewords.Text("settings.authorize.aborted")) return false } return true } done := func(already bool) { if already { fmt.Fprintf(stdout, updatewords.Text("settings.authorize.already")+"\n", *app, version) return } fmt.Fprintf(stdout, updatewords.Text("settings.authorize.done")+"\n", *app, version) } ctx := context.Background() sess, err := resolveMode(*consoleFlag) if err != nil { log.Fatal(err) } if sess != nil { printUsing(stdout, sess) acct := resolvedAccount(sess, acctRaw, acctIDRaw) *app = resolvedApp(sess, acct, *app, usage) plan, err := sess.authorizePlan(ctx, acct, *app, version) if err != nil { log.Fatal(err) } if !show(plan.ManifestDigest, plan.Artifacts) { return } already, err := sess.authorizeSign(ctx, acct, *app, version, plan.ManifestDigest) if err != nil { log.Fatal(err) } done(already) return } if *app == "" { log.Fatal(usage) } s, acct := openStore(), acctOf() plan, err := release.PlanAuthorize(ctx, s, acct, *app, version) if err != nil { log.Fatal(err) } if !show(plan.ManifestDigest, plan.Artifacts) { return } already, err := releaseSigner().Authorize(ctx, s, plan, promoteActor()) if errors.Is(err, release.ErrAuthorizeRootOnly) { log.Fatal(updatewords.Text("settings.authorize.root-only")) } if errors.Is(err, release.ErrAuthorizeChanged) { log.Fatalf(updatewords.Text("settings.authorize.changed"), version) } if err != nil { log.Fatal(err) } done(already) }