Skip to content

Commit 6225d66

Browse files
committed
cmd/release: create release archive after make.bash and before all.bash
Binary releases need to build Go and include binaries such as bin/go, bin/gofmt, and others. Previously, this was accomplished by running all.bash script for some GOOS/GOARCH pairs, and make.bash for others where it wasn't viable to run tests as part of the release process. This change makes the release process more consistent by always packaging the release archive file after running make.bash. We still run all.bash in situations where it was previously run, but we do so after the release file has already been created. This avoids the risk of any changes to GOROOT that may occur as part of all.bash (including changing file permissions to be read-only) being included in the final release file. Add a step to check that files in the buildlet's $WORKDIR/go and $WORKDIR/go/bin directories have expected permissions before creating the release file. Fixes golang/go#33537 Updates golang/go#30316 Change-Id: I7d40716dba656a8aca711377f2995df4880166c5 Reviewed-on: https://go-review.googlesource.com/c/build/+/189537 Reviewed-by: Andrew Bonventre <[email protected]>
1 parent 3eb1372 commit 6225d66

File tree

3 files changed

+158
-29
lines changed

3 files changed

+158
-29
lines changed

buildlet/buildletclient.go

+10
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,15 @@ func (de DirEntry) Name() string {
704704
return f[1]
705705
}
706706

707+
// Perm returns the permission bits in string form, e.g., "drw-rw-rw".
708+
func (de DirEntry) Perm() string {
709+
i := strings.IndexByte(de.line, '\t')
710+
if i == -1 {
711+
return ""
712+
}
713+
return de.line[:i]
714+
}
715+
707716
func (de DirEntry) Digest() string {
708717
f := strings.Split(de.line, "\t")
709718
if len(f) < 5 {
@@ -730,6 +739,7 @@ type ListDirOpts struct {
730739

731740
// ListDir lists the contents of a directory.
732741
// The fn callback is run for each entry.
742+
// The directory dir itself is not included.
733743
func (c *Client) ListDir(dir string, opts ListDirOpts, fn func(DirEntry)) error {
734744
param := url.Values{
735745
"dir": {dir},

cmd/release/release.go

+147-28
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func (b *Build) make() error {
254254
return err
255255
}
256256

257-
// Push source to buildlet
257+
// Push source to buildlet.
258258
b.logf("Pushing source to buildlet.")
259259
const (
260260
goDir = "go"
@@ -331,8 +331,12 @@ func (b *Build) make() error {
331331
if err := b.checkTopLevelDirs(client); err != nil {
332332
return fmt.Errorf("verifying no unwanted top-level directories: %v", err)
333333
}
334+
if err := b.checkPerm(client); err != nil {
335+
return fmt.Errorf("verifying file permissions: %v", err)
336+
}
334337

335-
return b.fetchTarball(client)
338+
finalFilename := *version + "." + b.String() + ".tar.gz"
339+
return b.fetchTarball(client, finalFilename)
336340
}
337341

338342
// Set up build environment.
@@ -353,24 +357,17 @@ func (b *Build) make() error {
353357
env = append(env, fmt.Sprintf("CGO_LDFLAGS=-march=armv%d", b.Goarm))
354358
}
355359

356-
// Execute build
357-
b.logf("Building.")
360+
// Execute build (make.bash only first).
361+
b.logf("Building (make.bash only).")
358362
out := new(bytes.Buffer)
359-
script := bc.AllScript()
360-
scriptArgs := bc.AllScriptArgs()
361-
if *skipTests || b.MakeOnly {
362-
script = bc.MakeScript()
363-
scriptArgs = bc.MakeScriptArgs()
364-
}
365-
all := filepath.Join(goDir, script)
366363
var execOut io.Writer = out
367364
if *watch && *target != "" {
368365
execOut = io.MultiWriter(out, os.Stdout)
369366
}
370-
remoteErr, err := client.Exec(all, buildlet.ExecOpts{
367+
remoteErr, err := client.Exec(filepath.Join(goDir, bc.MakeScript()), buildlet.ExecOpts{
371368
Output: execOut,
372369
ExtraEnv: env,
373-
Args: scriptArgs,
370+
Args: bc.MakeScriptArgs(),
374371
})
375372
if err != nil {
376373
return err
@@ -498,18 +495,43 @@ func (b *Build) make() error {
498495

499496
cleanFiles := []string{"releaselet.go", goPath, go14, "tmp", "gocache"}
500497

498+
// So far, we've run make.bash. We want to create the release archive next.
499+
// Since the release archive hasn't been tested yet, place it in a temporary
500+
// location. After all.bash runs successfully (or gets explicitly skipped),
501+
// we'll move the release archive to its final location.
502+
type releaseFile struct {
503+
Untested string // Temporary location of the file before the release has been tested.
504+
Final string // Final location where to move the file after the release has been tested.
505+
}
506+
var releases []releaseFile
507+
tempFile := func(ext string) string {
508+
tempDir, err := ioutil.TempDir("", "")
509+
if err != nil {
510+
log.Fatal(err)
511+
}
512+
return filepath.Join(tempDir, *version+"."+b.String()+ext+".untested")
513+
}
514+
501515
switch b.OS {
502516
case "darwin":
503-
filename := *version + "." + b.String() + ".pkg"
504-
if err := b.fetchFile(client, filename, "pkg"); err != nil {
517+
untested := tempFile(".pkg")
518+
if err := b.fetchFile(client, untested, "pkg"); err != nil {
505519
return err
506520
}
521+
releases = append(releases, releaseFile{
522+
Untested: untested,
523+
Final: *version + "." + b.String() + ".pkg",
524+
})
507525
cleanFiles = append(cleanFiles, "pkg")
508526
case "windows":
509-
filename := *version + "." + b.String() + ".msi"
510-
if err := b.fetchFile(client, filename, "msi"); err != nil {
527+
untested := tempFile(".msi")
528+
if err := b.fetchFile(client, untested, "msi"); err != nil {
511529
return err
512530
}
531+
releases = append(releases, releaseFile{
532+
Untested: untested,
533+
Final: *version + "." + b.String() + ".msi",
534+
})
513535
cleanFiles = append(cleanFiles, "msi")
514536
}
515537

@@ -525,10 +547,70 @@ func (b *Build) make() error {
525547
return fmt.Errorf("verifying no unwanted top-level directories: %v", err)
526548
}
527549

528-
if b.OS == "windows" {
529-
return b.fetchZip(client)
550+
if err := b.checkPerm(client); err != nil {
551+
return fmt.Errorf("verifying file permissions: %v", err)
552+
}
553+
554+
switch b.OS {
555+
default:
556+
untested := tempFile(".tar.gz")
557+
if err := b.fetchTarball(client, untested); err != nil {
558+
return fmt.Errorf("fetching and writing tarball: %v", err)
559+
}
560+
releases = append(releases, releaseFile{
561+
Untested: untested,
562+
Final: *version + "." + b.String() + ".tar.gz",
563+
})
564+
case "windows":
565+
untested := tempFile(".zip")
566+
if err := b.fetchZip(client, untested); err != nil {
567+
return fmt.Errorf("fetching and writing zip: %v", err)
568+
}
569+
releases = append(releases, releaseFile{
570+
Untested: untested,
571+
Final: *version + "." + b.String() + ".zip",
572+
})
530573
}
531-
return b.fetchTarball(client)
574+
575+
// Execute build (all.bash) if running tests.
576+
if *skipTests || b.MakeOnly {
577+
b.logf("Skipping all.bash tests.")
578+
} else {
579+
if u := bc.GoBootstrapURL(buildEnv); u != "" {
580+
b.logf("Installing go1.4 (second time, for all.bash).")
581+
if err := client.PutTarFromURL(u, go14); err != nil {
582+
return err
583+
}
584+
}
585+
586+
b.logf("Building (all.bash to ensure tests pass).")
587+
out := new(bytes.Buffer)
588+
var execOut io.Writer = out
589+
if *watch && *target != "" {
590+
execOut = io.MultiWriter(out, os.Stdout)
591+
}
592+
remoteErr, err := client.Exec(filepath.Join(goDir, bc.AllScript()), buildlet.ExecOpts{
593+
Output: execOut,
594+
ExtraEnv: env,
595+
Args: bc.AllScriptArgs(),
596+
})
597+
if err != nil {
598+
return err
599+
}
600+
if remoteErr != nil {
601+
return fmt.Errorf("Build failed: %v\nOutput:\n%v", remoteErr, out)
602+
}
603+
}
604+
605+
// If we get this far, the all.bash tests have passed (or been skipped).
606+
// Move untested release files to their final locations.
607+
for _, r := range releases {
608+
b.logf("Moving %q to %q.", r.Untested, r.Final)
609+
if err := os.Rename(r.Untested, r.Final); err != nil {
610+
return err
611+
}
612+
}
613+
return nil
532614
}
533615

534616
// checkTopLevelDirs checks that all files under client's "."
@@ -549,17 +631,56 @@ func (b *Build) checkTopLevelDirs(client *buildlet.Client) error {
549631
return badFileErr
550632
}
551633

552-
func (b *Build) fetchTarball(client *buildlet.Client) error {
634+
// checkPerm checks that files in client's $WORKDIR/go directory
635+
// have expected permissions.
636+
func (b *Build) checkPerm(client *buildlet.Client) error {
637+
var badPermErr error // non-nil once an unexpected perm is found
638+
checkPerm := func(ent buildlet.DirEntry, allowed ...string) {
639+
for _, p := range allowed {
640+
if ent.Perm() == p {
641+
return
642+
}
643+
}
644+
b.logf("unexpected file %q perm: %q", ent.Name(), ent.Perm())
645+
if badPermErr == nil {
646+
badPermErr = fmt.Errorf("unexpected file %q perm %q found", ent.Name(), ent.Perm())
647+
}
648+
}
649+
if err := client.ListDir("go", buildlet.ListDirOpts{Recursive: true}, func(ent buildlet.DirEntry) {
650+
switch b.OS {
651+
default:
652+
checkPerm(ent, "drwxr-xr-x", "-rw-r--r--", "-rwxr-xr-x")
653+
case "windows":
654+
checkPerm(ent, "drwxrwxrwx", "-rw-rw-rw-")
655+
}
656+
}); err != nil {
657+
return err
658+
}
659+
if !b.Source {
660+
if err := client.ListDir("go/bin", buildlet.ListDirOpts{}, func(ent buildlet.DirEntry) {
661+
switch b.OS {
662+
default:
663+
checkPerm(ent, "-rwxr-xr-x")
664+
case "windows":
665+
checkPerm(ent, "-rw-rw-rw-")
666+
}
667+
}); err != nil {
668+
return err
669+
}
670+
}
671+
return badPermErr
672+
}
673+
674+
func (b *Build) fetchTarball(client *buildlet.Client, dest string) error {
553675
b.logf("Downloading tarball.")
554676
tgz, err := client.GetTar(context.Background(), ".")
555677
if err != nil {
556678
return err
557679
}
558-
filename := *version + "." + b.String() + ".tar.gz"
559-
return b.writeFile(filename, tgz)
680+
return b.writeFile(dest, tgz)
560681
}
561682

562-
func (b *Build) fetchZip(client *buildlet.Client) error {
683+
func (b *Build) fetchZip(client *buildlet.Client, dest string) error {
563684
b.logf("Downloading tarball and re-compressing as zip.")
564685

565686
tgz, err := client.GetTar(context.Background(), ".")
@@ -568,8 +689,7 @@ func (b *Build) fetchZip(client *buildlet.Client) error {
568689
}
569690
defer tgz.Close()
570691

571-
filename := *version + "." + b.String() + ".zip"
572-
f, err := os.Create(filename)
692+
f, err := os.Create(dest)
573693
if err != nil {
574694
return err
575695
}
@@ -580,8 +700,7 @@ func (b *Build) fetchZip(client *buildlet.Client) error {
580700
if err := f.Close(); err != nil {
581701
return err
582702
}
583-
584-
b.logf("Wrote %q.", filename)
703+
b.logf("Wrote %q.", dest)
585704
return nil
586705
}
587706

cmd/releasebot/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ func (w *Work) buildRelease(target string) {
689689
if !failed {
690690
break
691691
}
692-
w.log.Printf("release %s: %s\n%s", target, err, out)
692+
w.log.Printf("release %s:\nerror from cmd/release binary = %v\noutput from cmd/release binary:\n%s", target, err, out)
693693
if failures++; failures >= 3 {
694694
w.log.Printf("release %s: too many failures\n", target)
695695
for _, out := range outs {

0 commit comments

Comments
 (0)