Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Commit

Permalink
Merge pull request #6 from mitchellh/f-notarization-log
Browse files Browse the repository at this point in the history
Download notarization log, parse issues, error if any issues
  • Loading branch information
mitchellh authored Nov 6, 2019
2 parents 0a56f02 + a5f0eae commit 8962847
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 7 deletions.
88 changes: 82 additions & 6 deletions cmd/gon/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package main

import (
"context"
"fmt"
"os"
"sync"

"github.com/fatih/color"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-multierror"

"github.com/mitchellh/gon/internal/config"
"github.com/mitchellh/gon/notarize"
Expand Down Expand Up @@ -64,7 +66,7 @@ func (i *item) notarize(ctx context.Context, opts *processOptions) error {
}

// Start notarization
_, err := notarize.Notarize(ctx, &notarize.Options{
info, err := notarize.Notarize(ctx, &notarize.Options{
File: i.Path,
BundleId: bundleId,
Username: opts.Config.AppleId.Username,
Expand All @@ -75,18 +77,92 @@ func (i *item) notarize(ctx context.Context, opts *processOptions) error {
UploadLock: opts.UploadLock,
})

// Save our state
i.State.Notarized = err == nil
// Save the error state. We don't save the notarization result yet
// because we don't know it for sure until we download the log file.
i.State.NotarizeError = err

// After we're done we want to output information for this
// file right away.
lock.Lock()
// If we had an error, we mention immediate we have an error.
if err != nil {
lock.Lock()
color.New(color.FgRed).Fprintf(os.Stdout, " %sError notarizing\n", opts.Prefix)
lock.Unlock()
}

// If we have a log file, download it. We do this whether we have an error
// or not because the log file can contain more details about the error.
if info != nil && info.LogFileURL != "" {
opts.Logger.Info(
"downloading log file for notarization",
"request_uuid", info.RequestUUID,
"url", info.LogFileURL,
)

log, logerr := notarize.DownloadLog(info.LogFileURL)
opts.Logger.Debug("log file downloaded", "log", log, "err", logerr)
if logerr != nil {
opts.Logger.Warn(
"error downloading log file, this isn't a fatal error",
"err", err,
)

// If we already failed notarization, just return that error
if err := i.State.NotarizeError; err != nil {
return err
}

// If it appears we succeeded notification, we make a new error.
// We can't say notarization is successful without downloading this
// file because warnings will cause notarization to not work
// when loaded.
lock.Lock()
color.New(color.FgRed).Fprintf(os.Stdout,
" %sError downloading log file to verify notarization.\n",
opts.Prefix,
)
lock.Unlock()

return fmt.Errorf(
"Error downloading log file to verify notarization success: %s\n\n"+
"You can download the log file manually at: %s",
logerr, info.LogFileURL,
)
}

// If we have any issues then it is a failed notarization. Notarization
// can "succeed" with warnings, but when you attempt to use/open a file
// Gatekeeper rejects it. So we currently reject any and all issues.
if len(log.Issues) > 0 {
var err error

lock.Lock()
color.New(color.FgRed).Fprintf(os.Stdout,
" %s%d issues during notarization:\n",
opts.Prefix, len(log.Issues))
for idx, issue := range log.Issues {
color.New(color.FgRed).Fprintf(os.Stdout,
" %sIssue #%d (%s) for path %q: %s\n",
opts.Prefix, idx+1, issue.Severity, issue.Path, issue.Message)

// Append the error so we can return it
err = multierror.Append(err, fmt.Errorf(
"%s for path %q: %s",
issue.Severity, issue.Path, issue.Message,
))
}
lock.Unlock()

return err
}
}

// If we aren't notarized, then return
if err := i.State.NotarizeError; err != nil {
return err
}

// Save our state
i.State.Notarized = true
lock.Lock()
color.New(color.FgGreen).Fprintf(os.Stdout, " %sFile notarized!\n", opts.Prefix)
lock.Unlock()

Expand Down
2 changes: 1 addition & 1 deletion cmd/gon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ func realMain() int {

// If totalErr is not nil then we had one or more errors.
if totalErr != nil {
fmt.Fprintf(os.Stdout, color.RedString("❗️ Error notarizing:\n\n%s\n", totalErr))
fmt.Fprintf(os.Stdout, color.RedString("\n❗️ Error notarizing:\n\n%s\n", totalErr))
return 1
}

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ go 1.13
require (
github.com/davecgh/go-spew v1.1.1
github.com/fatih/color v1.7.0
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-retryablehttp v0.6.3
github.com/hashicorp/hcl/v2 v2.0.0
github.com/sebdah/goldie v1.0.0
github.com/stretchr/testify v1.3.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FK
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2 h1:STV8OvzphW1vlhPFxcG8d6OIilzBSKRAoWFJt+Onu10=
github.com/hashicorp/go-hclog v0.9.3-0.20191025211905-234833755cb2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.6.3 h1:tuulM+WnToeqa05z83YLmKabZxrySOmJAd4mJ+s2Nfg=
github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl/v2 v2.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8=
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
Expand Down
79 changes: 79 additions & 0 deletions notarize/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package notarize

import (
"encoding/json"
"fmt"
"io"

"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-retryablehttp"
)

// Log is the structure that is available when downloading the log file
// that the notarization service creates.
//
// This may not be complete with all fields. I only included fields that
// I saw and even then only the more useful ones.
type Log struct {
JobId string `json:"jobId"`
Status string `json:"status"`
StatusSummary string `json:"statusSummary"`
StatusCode int `json:"statusCode"`
ArchiveFilename string `json:"archiveFilename"`
UploadDate string `json:"uploadDate"`
SHA256 string `json:"sha256"`
Issues []LogIssue `json:"issues"`
TicketContents []LogTicketContent `json:"ticketContents"`
}

// LogIssue is a single issue that may have occurred during notarization.
type LogIssue struct {
Severity string `json:"severity"`
Path string `json:"path"`
Message string `json:"message"`
}

// LogTicketContent is an entry that was noted as being within the archive.
type LogTicketContent struct {
Path string `json:"path"`
DigestAlgorithm string `json:"digestAlgorithm"`
CDHash string `json:"cdhash"`
Arch string `json:"arch"`
}

// These are the log severities that may exist.
const (
LogSeverityError = "error"
LogSeverityWarning = "warning"
)

// ParseLog parses a log from the given reader, such as an HTTP response.
func ParseLog(r io.Reader) (*Log, error) {
// Protect against this since it is common with HTTP responses.
if r == nil {
return nil, fmt.Errorf("nil reader given to ParseLog")
}

var result Log
return &result, json.NewDecoder(r).Decode(&result)
}

// DownloadLog downloads a log file and parses it using a default HTTP client.
// If you want more fine-grained control over the download, download it
// using your own client and use ParseLog.
func DownloadLog(path string) (*Log, error) {
// Build our HTTP client
client := retryablehttp.NewClient()
client.HTTPClient = cleanhttp.DefaultClient()

// Get it!
resp, err := client.Get(path)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}

return ParseLog(resp.Body)
}

0 comments on commit 8962847

Please sign in to comment.