go-hep.org/x/hep@v0.38.1/ci/mk-release.go (about)

     1  // Copyright ©2019 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build ignore
     6  
     7  package main
     8  
     9  import (
    10  	"bufio"
    11  	"bytes"
    12  	"flag"
    13  	"fmt"
    14  	"log"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"strings"
    19  
    20  	"golang.org/x/sync/errgroup"
    21  )
    22  
    23  func main() {
    24  	log.SetPrefix("")
    25  	log.SetFlags(0)
    26  
    27  	var (
    28  		module  = flag.String("module", "go-hep.org/x/hep", "module name to publish")
    29  		version = flag.String("version", "latest", "module version to publish")
    30  		repo    = flag.String("repo", "git@codeberg.org:go-hep/hep", "VCS URL of repository")
    31  	)
    32  
    33  	flag.Parse()
    34  
    35  	publish(*module, *version, *repo)
    36  }
    37  
    38  func publish(module, version, repo string) {
    39  	doLatest := version == "latest"
    40  	log.Printf("publishing module=%q, version=%q", module, version)
    41  	modver := module + "@" + version
    42  
    43  	tmp, err := os.MkdirTemp("", "go-hep-release-")
    44  	if err != nil {
    45  		log.Fatalf("could not create tmpdir: %+v", err)
    46  	}
    47  	defer os.RemoveAll(tmp)
    48  
    49  	os.Setenv("GO111MODULE", "on")
    50  
    51  	log.Printf("## creating modpub module...")
    52  	cmd := exec.Command("go", "mod", "init", "modpub")
    53  	cmd.Dir = tmp
    54  	cmd.Stderr = log.Writer()
    55  	cmd.Stdout = log.Writer()
    56  	err = cmd.Run()
    57  	if err != nil {
    58  		log.Fatalf("could not initialize modpub module: %+v", err)
    59  	}
    60  
    61  	log.Printf("## get %q...", modver)
    62  	cmd = exec.Command("go", "get", "-v", modver)
    63  	cmd.Dir = tmp
    64  	cmd.Stderr = log.Writer()
    65  	cmd.Stdout = log.Writer()
    66  	err = cmd.Run()
    67  	if err != nil {
    68  		log.Fatalf("could not get %q module: %+v", modver, err)
    69  	}
    70  
    71  	log.Printf("## generating main package...")
    72  	const tmpl = `package main
    73  
    74  import (
    75  	_ "%s"
    76  )
    77  
    78  func main() {}
    79  `
    80  
    81  	err = os.WriteFile(filepath.Join(tmp, "main.go"), []byte(fmt.Sprintf(tmpl, module)), 0644)
    82  	if err != nil {
    83  		log.Fatalf("could not generate main: %+v", err)
    84  	}
    85  
    86  	log.Printf("## mod download %q...", modver)
    87  	cmd = exec.Command("go", "mod", "download")
    88  	cmd.Dir = tmp
    89  	cmd.Stderr = log.Writer()
    90  	cmd.Stdout = log.Writer()
    91  	err = cmd.Run()
    92  	if err != nil {
    93  		log.Fatalf("could not mod-tidy %q module: %+v", modver, err)
    94  	}
    95  
    96  	log.Printf("## mod tidy %q...", modver)
    97  	cmd = exec.Command("go", "mod", "tidy")
    98  	cmd.Dir = tmp
    99  	cmd.Stderr = log.Writer()
   100  	cmd.Stdout = log.Writer()
   101  	err = cmd.Run()
   102  	if err != nil {
   103  		log.Fatalf("could not mod-tidy %q module: %+v", modver, err)
   104  	}
   105  
   106  	log.Printf("## go build...")
   107  	cmd = exec.Command("go", "build", "-v")
   108  	cmd.Dir = tmp
   109  	cmd.Stderr = log.Writer()
   110  	cmd.Stdout = log.Writer()
   111  	err = cmd.Run()
   112  	if err != nil {
   113  		log.Fatalf("could not get %q module: %+v", modver, err)
   114  	}
   115  
   116  	version, err = extractVersion(filepath.Join(tmp, "go.mod"), module)
   117  	if err != nil {
   118  		log.Fatalf("could not extract version from modpub module file: %+v", err)
   119  	}
   120  
   121  	buildCmds(module, version, repo)
   122  	if doLatest {
   123  		setLatest(version)
   124  	}
   125  }
   126  
   127  func extractVersion(fname, modname string) (string, error) {
   128  	f, err := os.Open(fname)
   129  	if err != nil {
   130  		return "", fmt.Errorf("could not open module file %q: %w", fname, err)
   131  	}
   132  	defer f.Close()
   133  
   134  	sc := bufio.NewScanner(f)
   135  	for sc.Scan() {
   136  		line := sc.Text()
   137  		if !strings.Contains(line, modname+" ") {
   138  			continue
   139  		}
   140  
   141  		_, after, ok := strings.Cut(line, modname)
   142  		if ok {
   143  			return strings.TrimSpace(after), nil
   144  		}
   145  	}
   146  
   147  	return "", fmt.Errorf("could not find module %q in modpub %q", modname, fname)
   148  }
   149  
   150  func buildCmds(modname, version, repo string) {
   151  	top, err := os.MkdirTemp("", "go-hep-release-")
   152  	if err != nil {
   153  		log.Fatalf("could not create tmp dir: %+v", err)
   154  	}
   155  	defer os.RemoveAll(top)
   156  
   157  	src := filepath.Join(top, "hep")
   158  	cmd := exec.Command(
   159  		"git", "clone",
   160  		"-b", version, "--depth", "1",
   161  		repo,
   162  		src,
   163  	)
   164  	cmd.Dir = top
   165  	cmd.Stderr = log.Writer()
   166  	cmd.Stdout = log.Writer()
   167  	err = cmd.Run()
   168  	if err != nil {
   169  		log.Fatalf("could not clone %q: %+v", repo, err)
   170  	}
   171  
   172  	tmp, err := os.MkdirTemp("", "go-hep-release-cmds-")
   173  	if err != nil {
   174  		log.Fatalf("could not create tmp dir for build cmds: %+v", err)
   175  	}
   176  	defer os.RemoveAll(tmp)
   177  
   178  	allpkgs, err := pkgList(src, modname, OSArch{"linux", "amd64"})
   179  	if err != nil {
   180  		log.Fatalf("could not build package list of module %q: %+v", modname, err)
   181  	}
   182  
   183  	for _, osarch := range []struct {
   184  		os, arch string
   185  	}{
   186  		{"linux", "amd64"},
   187  		{"linux", "386"},
   188  		{"linux", "arm64"},
   189  		{"windows", "amd64"},
   190  		{"windows", "386"},
   191  		{"darwin", "amd64"},
   192  		{"freebsd", "amd64"},
   193  	} {
   194  		var (
   195  			grp errgroup.Group
   196  			ctx = osarch
   197  		)
   198  		grp.SetLimit(4)
   199  		log.Printf("--> GOOS=%s, GOARCH=%s", ctx.os, ctx.arch)
   200  		cmds := make([]string, 0, len(allpkgs))
   201  		for _, pkg := range allpkgs {
   202  			if !strings.Contains(pkg, "/cmd") {
   203  				continue
   204  			}
   205  			if strings.Contains(pkg, "/internal") {
   206  				continue
   207  			}
   208  			if _, ok := excludeList[ctx][pkg]; ok {
   209  				continue
   210  			}
   211  			cmds = append(cmds, pkg)
   212  		}
   213  
   214  		tags := "-tags=netgo"
   215  		log.Printf("--> found %d commands", len(cmds))
   216  		for i := range cmds {
   217  			cmd := cmds[i]
   218  			grp.Go(func() error {
   219  				name := fmt.Sprintf("%s-%s_%s.exe", filepath.Base(cmd), ctx.os, ctx.arch)
   220  				exe := filepath.Join(tmp, name)
   221  				bld := exec.Command(
   222  					"go", "build",
   223  					"-trimpath",
   224  					"-buildvcs=true",
   225  					"-o", exe,
   226  					tags,
   227  					strings.Replace(cmd, modname, ".", 1),
   228  				)
   229  				bld.Dir = src
   230  				bld.Env = append([]string{}, os.Environ()...)
   231  				bld.Env = append(bld.Env, fmt.Sprintf("GOOS=%s", ctx.os), fmt.Sprintf("GOARCH=%s", ctx.arch))
   232  				if _, ok := needCgo[filepath.Base(cmd)]; !ok {
   233  					bld.Env = append(bld.Env, "CGO_ENABLED=0")
   234  				}
   235  				out, err := bld.CombinedOutput()
   236  				if err != nil {
   237  					log.Printf("could not compile %s: %+v\noutput:\n%s", name, err, out)
   238  					return err
   239  				}
   240  				return nil
   241  			})
   242  		}
   243  
   244  		err = grp.Wait()
   245  		if err != nil {
   246  			log.Fatalf("could not build commands for %s/%s: %+v", ctx.os, ctx.arch, err)
   247  		}
   248  	}
   249  
   250  	upload(tmp, version)
   251  }
   252  
   253  type OSArch struct {
   254  	os, arch string
   255  }
   256  
   257  func pkgList(dir, module string, ctx OSArch) ([]string, error) {
   258  	env := append([]string{}, os.Environ()...)
   259  	env = append(env, fmt.Sprintf("GOOS=%s", ctx.os), fmt.Sprintf("GOARCH=%s", ctx.arch))
   260  
   261  	cmd := exec.Command("go", "mod", "tidy")
   262  	cmd.Dir = dir
   263  	cmd.Env = env
   264  	cmd.Stdout = log.Writer()
   265  	cmd.Stderr = log.Writer()
   266  	err := cmd.Run()
   267  	if err != nil {
   268  		return nil, fmt.Errorf("could not initialize list: %w", err)
   269  	}
   270  
   271  	out := new(bytes.Buffer)
   272  	cmd = exec.Command("go", "list", "./...")
   273  	cmd.Dir = dir
   274  	cmd.Stdout = out
   275  	cmd.Stderr = os.Stderr
   276  	cmd.Stdin = os.Stdin
   277  	cmd.Env = env
   278  
   279  	err = cmd.Run()
   280  	if err != nil {
   281  		return nil, fmt.Errorf("could not get package list (%s-%s): %w", ctx.os, ctx.arch, err)
   282  	}
   283  
   284  	var pkgs []string
   285  	scan := bufio.NewScanner(out)
   286  	for scan.Scan() {
   287  		pkg := scan.Text()
   288  		if strings.Contains(pkg, "vendor") {
   289  			continue
   290  		}
   291  		if !strings.HasPrefix(pkg, module) {
   292  			continue
   293  		}
   294  		pkgs = append(pkgs, pkg)
   295  	}
   296  
   297  	return pkgs, nil
   298  }
   299  
   300  func upload(dir string, version string) {
   301  	cmd := exec.Command("scp", "-r", dir, "root@clrwebgohep.in2p3.fr:/srv/go-hep.org/dist/"+version)
   302  	cmd.Stdout = os.Stdout
   303  	cmd.Stderr = os.Stderr
   304  	err := cmd.Run()
   305  	if err != nil {
   306  		log.Fatalf("could not upload binaries to server: %+v", err)
   307  	}
   308  }
   309  
   310  func setLatest(version string) {
   311  	cmd := exec.Command("ssh", "root@clrwebgohep.in2p3.fr",
   312  		"--",
   313  		fmt.Sprintf("/bin/sh -c 'cd /srv/go-hep.org/dist && /bin/rm ./latest && ln -s %s latest'", version),
   314  	)
   315  	cmd.Stdout = os.Stdout
   316  	cmd.Stderr = os.Stderr
   317  	err := cmd.Run()
   318  	if err != nil {
   319  		log.Fatalf("could not set latest to %q: %+v", version, err)
   320  	}
   321  }
   322  
   323  var excludeList = map[OSArch]map[string]struct{}{
   324  	{"linux", "386"}:     {},
   325  	{"linux", "arm64"}:   {},
   326  	{"darwin", "amd64"}:  {},
   327  	{"freebsd", "amd64"}: {},
   328  	{"windows", "amd64"}: {},
   329  	{"windows", "386"}:   {},
   330  }
   331  
   332  var needCgo = map[string]struct{}{}