carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "bufio"
5 "context"
6 "errors"
7 "flag"
8 "fmt"
9 "io"
10 "log"
11 "os"
12 "sort"
13 "strings"
14
15 "github.com/carlosframework/platform/internal/release"
16 "github.com/carlosframework/platform/internal/updatewords"
17)
18
19var authorizeInput io.Reader = os.Stdin
20
21// runReleasesAuthorize is `carlos releases authorize [--app <a>] <version>`
22// (also `<app> <version>`, as the spec writes it): show a build's manifest
23// and artifact digests, and on "yes" sign a promotion record for them
24// (settings spec §4.1). Never automatic: nothing else backfills a record.
25func runReleasesAuthorize(args []string) {
26 fs := flag.NewFlagSet("releases authorize", flag.ExitOnError)
27 app := appFlag(fs, "")
28 acctRaw, acctIDRaw, acctOf := accountFlags(fs)
29 yes := fs.Bool("yes", false, updatewords.Text("settings.authorize.flag-yes"))
30 consoleFlag := consoleOption(fs, updatewords.Text("settings.authorize.flag-console"))
31 fs.Usage = flagUsage(fs, "")
32 fs.Parse(args)
33 usage := updatewords.Text("settings.authorize.usage")
34 pos := fs.Args()
35 if len(pos) == 2 && *app == "" {
36 *app, pos = pos[0], pos[1:]
37 }
38 if len(pos) != 1 {
39 log.Fatal(usage)
40 }
41 version := pos[0]
42 show := func(digest string, arts map[string]release.Artifact) bool {
43 fmt.Fprintf(stdout, updatewords.Text("settings.authorize.intro")+"\n", *app, version)
44 fmt.Fprintf(stdout, updatewords.Text("settings.authorize.manifest-row")+"\n", digest)
45 keys := make([]string, 0, len(arts))
46 for k := range arts {
47 keys = append(keys, k)
48 }
49 sort.Strings(keys)
50 for _, k := range keys {
51 fmt.Fprintf(stdout, updatewords.Text("settings.authorize.artifact-row")+"\n", k, arts[k].SHA256)
52 }
53 if *yes {
54 return true
55 }
56 fmt.Fprint(stdout, updatewords.Text("settings.authorize.prompt"))
57 line, _ := bufio.NewReader(authorizeInput).ReadString('\n')
58 if strings.TrimSpace(line) != "yes" {
59 fmt.Fprintln(stdout, updatewords.Text("settings.authorize.aborted"))
60 return false
61 }
62 return true
63 }
64 done := func(already bool) {
65 if already {
66 fmt.Fprintf(stdout, updatewords.Text("settings.authorize.already")+"\n", *app, version)
67 return
68 }
69 fmt.Fprintf(stdout, updatewords.Text("settings.authorize.done")+"\n", *app, version)
70 }
71
72 ctx := context.Background()
73 sess, err := resolveMode(*consoleFlag)
74 if err != nil {
75 log.Fatal(err)
76 }
77 if sess != nil {
78 printUsing(stdout, sess)
79 acct := resolvedAccount(sess, acctRaw, acctIDRaw)
80 *app = resolvedApp(sess, acct, *app, usage)
81 plan, err := sess.authorizePlan(ctx, acct, *app, version)
82 if err != nil {
83 log.Fatal(err)
84 }
85 if !show(plan.ManifestDigest, plan.Artifacts) {
86 return
87 }
88 already, err := sess.authorizeSign(ctx, acct, *app, version, plan.ManifestDigest)
89 if err != nil {
90 log.Fatal(err)
91 }
92 done(already)
93 return
94 }
95 if *app == "" {
96 log.Fatal(usage)
97 }
98 s, acct := openStore(), acctOf()
99 plan, err := release.PlanAuthorize(ctx, s, acct, *app, version)
100 if err != nil {
101 log.Fatal(err)
102 }
103 if !show(plan.ManifestDigest, plan.Artifacts) {
104 return
105 }
106 already, err := releaseSigner().Authorize(ctx, s, plan, promoteActor())
107 if errors.Is(err, release.ErrAuthorizeRootOnly) {
108 log.Fatal(updatewords.Text("settings.authorize.root-only"))
109 }
110 if errors.Is(err, release.ErrAuthorizeChanged) {
111 log.Fatalf(updatewords.Text("settings.authorize.changed"), version)
112 }
113 if err != nil {
114 log.Fatal(err)
115 }
116 done(already)
117}
118