-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tooling for Mac Distribution #324
base: main
Are you sure you want to change the base?
Conversation
|
||
By default, notarization is disabled and will output dryrun logs. To enable it you must either set the following options: | ||
```shell | ||
mac-distribution --apple-username="" --apple-password="" --signing-identity="" --bundle-id="" ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we support flags at all? Doesn't this just make it more likely that secrets get recorded in shell history files?
However this isn't desirable in CI environments where notarization must happen. Enabling dryrun will "silently" cause a failure. | ||
For convenience the `--force-notarization` flag is provided to fail in the scenario where creds are missing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most CI environments set a CI
environment variable - what do you think about automatically forcing notarization when $CI
is set?
// Dry run if no credentials are provided | ||
dryRun := g.AppleUsername == "" || g.ApplePassword == "" || g.SigningID == "" | ||
if dryRun && g.ForceNotarization { | ||
return nil, fmt.Errorf("notarization credentials not provided and force-notarization is enabled") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return nil, fmt.Errorf("notarization credentials not provided and force-notarization is enabled") | |
return nil, errors.New("notarization credentials not provided and force-notarization is enabled") |
// Retry sets the max number of attempts for a succesful notarization | ||
retry int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Retry sets the max number of attempts for a succesful notarization | |
retry int | |
maxRetries int |
A well-named variable often eliminates the need for extra comments.
zip zipper.DirZipper | ||
} | ||
|
||
// Creds is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creds is ......
cmd.Stdout = &stdout | ||
|
||
err := cmd.Run() | ||
out := strings.TrimSpace(stdout.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could save some allocations by keeping a []byte
until the last possible moment. The bytes
package and the strings
package share most of these utilities in common.
|
||
// CopyFile copies a file from src to dst. | ||
func CopyFile(src, dst string) error { | ||
w, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) // create or overwrite |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be preserving the permissions of the original file rather than using 0755?
|
||
r, err := os.OpenFile(src, os.O_RDONLY, 0) | ||
if err != nil { | ||
return trace.Wrap(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On error should we be deleting the dst file or leaving it behind?
"github.com/gravitational/trace" | ||
) | ||
|
||
type DirZipper interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need an interface here? Looks like there's only one implementation of it..
} | ||
|
||
if !opts.IncludePrefix { | ||
path, _ = strings.CutPrefix(path, root+string(os.PathSeparator)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is TrimPrefix
more appropriate here?
|
||
App Bundle (.app) | ||
```shell | ||
mac-distribution package-app tsh tsh.app/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having a separate tool for every artifact, what do you think about having a single build tool with subcommands that we can extend as we convert more stuff? E.g. something like:
$ tbuild build-mac ... --notarize
type DefaultZipper struct{} | ||
|
||
// ZipDir will zip the directory into the specified output file | ||
func (z *DefaultZipper) ZipDir(dir string, out io.Writer, opts DirZipperOpts) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Not a huge deal but I'd consider making "opts" into functional options so you can keep parameter list clean when you need to not pass any options.
return trace.Wrap(tool.NotarizeBinaries(c.Files)) | ||
} | ||
|
||
func (g *GlobalFlags) InitNotaryTool() (*notarize.Tool, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be public?
|
||
func (g *GlobalFlags) InitNotaryTool() (*notarize.Tool, error) { | ||
// Dry run if no credentials are provided | ||
dryRun := g.AppleUsername == "" || g.ApplePassword == "" || g.SigningID == "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More of a nit, but I would probably prefer if the dry run mode was an explicit flag and creds required if it's not set. Less room for mistake IMO.
log.Warn("notarization dry run enabled", "reason", "notarization credentials missing") | ||
} | ||
|
||
// Initialize notary tool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's pretty clear what it does lol
// Initialize notary tool |
|
||
// The Apple Notary Service requires a zip file for notarization. | ||
// The files will be staged in a temporary directory and zipped for submission. | ||
notaryDir, err := os.MkdirTemp("", "notarize-binaries-*") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we include something in the dir name to differentiate between different builds? Like a version number? Just for potential debugging/troubleshooting.
|
||
// RunCommand logs the command that would have been run. | ||
func (d *DryRunner) RunCommand(path string, args ...string) (string, error) { | ||
d.log.Info("dry run", "path", path, "args", args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at usage, this will potentially log all sensitive CLI arguments like Apple username and password, right? For example, when we call notarytool
. We should not do that.
"--output-format", "json", | ||
} | ||
|
||
stdout, err := t.cmdRunner.RunCommand("xcrun", args...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like mentioned above, in dry run mode this looks like it will dump all arguments, including password, into the logs.
"--apple-password", t.Creds.ApplePassword, | ||
"--output-format", "json", | ||
} | ||
stdout, err := t.cmdRunner.RunCommand("xcrun", args...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above regarding arguments logging.
|
||
// Package creates an app bundle from the provided info. | ||
func (a *AppBundlePackager) Package() error { | ||
if err := a.Info.validate(); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we validating this inside "Package()" rather than inside the NewAppBundlePackager "constructor"?
No description provided.