Skip to content

Commit 66d5ca3

Browse files
committed
feat: Add Log cmd and tests
1 parent a448a73 commit 66d5ca3

12 files changed

+240
-318
lines changed

go.mod

-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ go 1.13
55
require (
66
github.com/davecgh/go-spew v1.1.1
77
github.com/fatih/color v1.7.0
8-
github.com/hashicorp/go-cleanhttp v0.5.1
98
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2
109
github.com/hashicorp/go-multierror v1.0.0
11-
github.com/hashicorp/go-retryablehttp v0.6.3
1210
github.com/hashicorp/hcl/v2 v2.0.0
1311
github.com/sebdah/goldie v1.0.0
1412
github.com/stretchr/testify v1.3.0

go.sum

-5
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,10 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
1515
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
1616
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
1717
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
18-
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
19-
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
20-
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
2118
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2 h1:STV8OvzphW1vlhPFxcG8d6OIilzBSKRAoWFJt+Onu10=
2219
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
2320
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
2421
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
25-
github.com/hashicorp/go-retryablehttp v0.6.3 h1:tuulM+WnToeqa05z83YLmKabZxrySOmJAd4mJ+s2Nfg=
26-
github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
2722
github.com/hashicorp/hcl/v2 v2.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8=
2823
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
2924
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=

notarize/info.go

+4-41
Original file line numberDiff line numberDiff line change
@@ -31,41 +31,16 @@ type Info struct {
3131
Name string `plist:"name"`
3232

3333
// Status the status of the notarization.
34-
//
35-
// StatusMessage is a human-friendly message associated with a status.
36-
Status string `plist:"status"`
37-
StatusMessage string `plist:"message"`
38-
}
39-
40-
// infoResult is the structure of the plist emitted directly from
41-
// --notarization-info
42-
type infoResult struct {
43-
// RequestUUID is the UUID provided by Apple after submitting the
44-
// notarization request. This can be used to look up notarization information
45-
// using the Apple tooling.
46-
RequestUUID string `plist:"id"`
47-
48-
// Date is the date and time of submission
49-
Date string `plist:"createdDate"`
34+
Status string `plist:"status"`
5035

51-
// Name is th file uploaded for submission.
52-
Name string `plist:"name"`
53-
54-
// Status the status of the notarization.
55-
//
5636
// StatusMessage is a human-friendly message associated with a status.
57-
Status string `plist:"status"`
5837
StatusMessage string `plist:"message"`
59-
60-
// Errors is the list of errors that occurred while uploading
61-
Errors Errors `plist:"product-errors"`
6238
}
6339

6440
// info requests the information about a notarization and returns
6541
// the updated information.
6642
func info(ctx context.Context, uuid string, opts *Options) (*Info, error) {
6743
logger := opts.Logger
68-
var info Info
6944
if logger == nil {
7045
logger = hclog.NewNullLogger()
7146
}
@@ -125,30 +100,18 @@ func info(ctx context.Context, uuid string, opts *Options) (*Info, error) {
125100

126101
// If we have any output, try to decode that since even in the case of
127102
// an error it will output some information.
128-
var result infoResult
103+
var result Info
129104
if out.Len() > 0 {
130105
if _, perr := plist.Unmarshal(out.Bytes(), &result); perr != nil {
131106
return nil, fmt.Errorf("failed to decode notarization submission output: %w", perr)
132107
}
133108
}
134109

135-
// If there are errors in the result, then show that error
136-
if len(result.Errors) > 0 {
137-
return nil, result.Errors
138-
}
139-
140110
// Now we check the error for actually running the process
141111
if err != nil {
142112
return nil, fmt.Errorf("error checking on notarization status:\n\n%s", combined.String())
143113
}
144114

145-
info = Info{RequestUUID: result.RequestUUID,
146-
Date: result.Date,
147-
Name: result.Name,
148-
Status: result.Status,
149-
StatusMessage: result.StatusMessage,
150-
}
151-
152-
logger.Info("notarization info", "uuid", uuid, "info", info)
153-
return &info, nil
115+
logger.Info("notarization info", "uuid", uuid, "info", result)
116+
return &result, nil
154117
}

notarize/info_test.go

+54-33
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,75 @@ import (
1111
)
1212

1313
func init() {
14-
childCommands["info-success"] = testCmdInfoSuccess
14+
childCommands["info-accepted"] = testCmdInfoAcceptedSubmission
15+
childCommands["info-invalid"] = testCmdInfoInvalidSubmission
1516
}
1617

17-
func TestInfo_success(t *testing.T) {
18+
func TestInfo_accepted(t *testing.T) {
1819
info, err := info(context.Background(), "foo", &Options{
1920
Logger: hclog.L(),
20-
BaseCmd: childCmd(t, "info-success"),
21+
BaseCmd: childCmd(t, "info-accepted"),
2122
})
2223

2324
require := require.New(t)
2425
require.NoError(err)
25-
require.Equal(info.RequestUUID, "edc8e846-d6ce-444d-9eef-499aa444da1c")
26-
require.Equal(info.Status, "success")
27-
require.Equal(info.StatusMessage, "Package Approved")
26+
require.Equal(info.RequestUUID, "32684f68-d63e-49ba-9234-25eeec84b369")
27+
require.Equal(info.Status, "Accepted")
28+
require.Equal(info.StatusMessage, "Successfully received submission info")
2829
}
2930

30-
// testCmdInfoSuccess mimicks a successful submission.
31-
func testCmdInfoSuccess() int {
31+
func TestInfo_invalid(t *testing.T) {
32+
info, err := info(context.Background(), "foo", &Options{
33+
Logger: hclog.L(),
34+
BaseCmd: childCmd(t, "info-invalid"),
35+
})
36+
37+
require := require.New(t)
38+
require.NoError(err)
39+
require.Equal(info.RequestUUID, "cfd69166-8e2f-1397-8636-ec06f98e3597")
40+
require.Equal(info.Status, "Invalid")
41+
}
42+
43+
// testCmdInfoAcceptedSubmission mimicks an accepted submission.
44+
func testCmdInfoAcceptedSubmission() int {
45+
fmt.Println(strings.TrimSpace(`
46+
<?xml version="1.0" encoding="UTF-8"?>
47+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
48+
<plist version="1.0">
49+
<dict>
50+
<key>createdDate</key>
51+
<string>2023-08-01T08:22:19.939Z</string>
52+
<key>id</key>
53+
<string>32684f68-d63e-49ba-9234-25eeec84b369</string>
54+
<key>message</key>
55+
<string>Successfully received submission info</string>
56+
<key>name</key>
57+
<string>binary.zip</string>
58+
<key>status</key>
59+
<string>Accepted</string>
60+
</dict>
61+
</plist>
62+
`))
63+
return 0
64+
}
65+
66+
// testCmdInfoInvalidSubmission mimicks an invalid submission.
67+
func testCmdInfoInvalidSubmission() int {
3268
fmt.Println(strings.TrimSpace(`
3369
<?xml version="1.0" encoding="UTF-8"?>
3470
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3571
<plist version="1.0">
3672
<dict>
37-
<key>notarization-info</key>
38-
<dict>
39-
<key>Date</key>
40-
<date>2019-11-02T02:17:12Z</date>
41-
<key>Hash</key>
42-
<string>644d0af906ae26c87037cd6e9073382d5b0461b39e7f23c7bb69a35debacedd4</string>
43-
<key>LogFileURL</key>
44-
<string>https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma123/v4/29/f2/81/29f28128-e2be-158a-f421-1e19692dd935/developer_log.json?accessKey=1572864491_3132212434837665280_4XLMw7lZxMfKdHhgnlPkueVue9woI2MjQ6VEc8R0cxJrL9GGcTQSiE0C9Cu5o6o%2B3JtYGSqGWdvc3mJHbS0NBRZkHT%2BbwbdMGPT8poYk7TTkfHUIcW5aBz0aFO7RB6mSWVuZWOFT0dZ4VS%2Bep2LUP2KTDtDwiGQbTULu9VgZ1oY%3D</string>
45-
<key>RequestUUID</key>
46-
<string>edc8e846-d6ce-444d-9eef-499aa444da1c</string>
47-
<key>Status</key>
48-
<string>success</string>
49-
<key>Status Code</key>
50-
<integer>0</integer>
51-
<key>Status Message</key>
52-
<string>Package Approved</string>
53-
</dict>
54-
<key>os-version</key>
55-
<string>10.15.1</string>
56-
<key>success-message</key>
57-
<string>No errors getting notarization info.</string>
58-
<key>tool-path</key>
59-
<string>/Applications/Xcode.app/Contents/SharedFrameworks/ContentDeliveryServices.framework/Versions/A/Frameworks/AppStoreService.framework</string>
60-
<key>tool-version</key>
61-
<string>4.00.1181</string>
73+
<key>createdDate</key>
74+
<string>2023-08-01T08:12:11.193Z</string>
75+
<key>id</key>
76+
<string>cfd69166-8e2f-1397-8636-ec06f98e3597</string>
77+
<key>message</key>
78+
<string>Successfully received submission info</string>
79+
<key>name</key>
80+
<string>binary.zip</string>
81+
<key>status</key>
82+
<string>Invalid</string>
6283
</dict>
6384
</plist>
6485
`))

notarize/log.go

+75-35
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
package notarize
22

33
import (
4+
"bytes"
5+
"context"
46
"encoding/json"
57
"fmt"
68
"io"
9+
"os/exec"
10+
"path/filepath"
711

8-
"github.com/hashicorp/go-cleanhttp"
912
"github.com/hashicorp/go-hclog"
10-
"github.com/hashicorp/go-retryablehttp"
1113
)
1214

13-
// Log is the structure that is available when downloading the log file
14-
// that the notarization service creates.
15-
//
16-
// This may not be complete with all fields. I only included fields that
17-
// I saw and even then only the more useful ones.
15+
// Log Retrieves notarization log for a single completed submission
1816
type Log struct {
1917
JobId string `json:"jobId"`
2018
Status string `json:"status"`
@@ -42,40 +40,82 @@ type LogTicketContent struct {
4240
Arch string `json:"arch"`
4341
}
4442

45-
// These are the log severities that may exist.
46-
const (
47-
LogSeverityError = "error"
48-
LogSeverityWarning = "warning"
49-
)
43+
// log requests the information about a notarization and returns
44+
// the updated information.
45+
func log(ctx context.Context, uuid string, opts *Options) (*Log, error) {
46+
logger := opts.Logger
47+
if logger == nil {
48+
logger = hclog.NewNullLogger()
49+
}
50+
51+
// Build our command
52+
var cmd exec.Cmd
53+
if opts.BaseCmd != nil {
54+
cmd = *opts.BaseCmd
55+
}
56+
57+
// We only set the path if it isn't set. This lets the options set the
58+
// path to the codesigning binary that we use.
59+
if cmd.Path == "" {
60+
path, err := exec.LookPath("xcrun")
61+
if err != nil {
62+
return nil, err
63+
}
64+
cmd.Path = path
65+
}
66+
67+
cmd.Args = []string{
68+
filepath.Base(cmd.Path),
69+
"notarytool",
70+
"log",
71+
uuid,
72+
"--apple-id", opts.DeveloperId,
73+
"--password", opts.Password,
74+
}
5075

51-
// ParseLog parses a log from the given reader, such as an HTTP response.
52-
func ParseLog(r io.Reader) (*Log, error) {
53-
// Protect against this since it is common with HTTP responses.
54-
if r == nil {
55-
return nil, fmt.Errorf("nil reader given to ParseLog")
76+
if opts.Provider != "" {
77+
cmd.Args = append(cmd.Args,
78+
"--team-id", opts.Provider,
79+
)
5680
}
5781

82+
// We store all output in out for logging and in case there is an error
83+
var out, combined bytes.Buffer
84+
cmd.Stdout = io.MultiWriter(&out, &combined)
85+
cmd.Stderr = &combined
86+
87+
// Log what we're going to execute
88+
logger.Info("requesting notarization info",
89+
"uuid", uuid,
90+
"command_path", cmd.Path,
91+
"command_args", cmd.Args,
92+
)
93+
94+
// Execute
95+
err := cmd.Run()
96+
97+
// Log the result
98+
logger.Info("notarization log command finished",
99+
"output", out.String(),
100+
"err", err,
101+
)
102+
103+
// If we have any output, try to decode that since even in the case of
104+
// an error it will output some information.
58105
var result Log
59-
return &result, json.NewDecoder(r).Decode(&result)
60-
}
106+
// return &result, json.NewDecoder().Decode(&result)
107+
if out.Len() > 0 {
108+
if derr := json.Unmarshal(out.Bytes(), &result); derr != nil {
109+
return nil, fmt.Errorf("failed to decode notarization submission output: %w", derr)
61110

62-
// DownloadLog downloads a log file and parses it using a default HTTP client.
63-
// If you want more fine-grained control over the download, download it
64-
// using your own client and use ParseLog.
65-
func DownloadLog(path string) (*Log, error) {
66-
// Build our HTTP client
67-
client := retryablehttp.NewClient()
68-
client.HTTPClient = cleanhttp.DefaultClient()
69-
client.Logger = hclog.NewNullLogger()
70-
71-
// Get it!
72-
resp, err := client.Get(path)
73-
if err != nil {
74-
return nil, err
111+
}
75112
}
76-
if resp.Body != nil {
77-
defer resp.Body.Close()
113+
114+
// Now we check the error for actually running the process
115+
if err != nil {
116+
return nil, fmt.Errorf("error checking on notarization status:\n\n%s", combined.String())
78117
}
79118

80-
return ParseLog(resp.Body)
119+
logger.Info("notarization log", "uuid", uuid, "info", result)
120+
return &result, nil
81121
}

0 commit comments

Comments
 (0)