//go:build dist package main import ( "archive/tar" "compress/gzip" "context" "crypto/sha512" _ "embed" "encoding/hex" "fmt" "io" "io/fs" "log" "os" "os/exec" "os/signal" "path/filepath" "runtime" ) // getenv looks up an environment variable, and returns fallback if it is unset. func getenv(key, fallback string) string { if v, ok := os.LookupEnv(key); ok { return v } return fallback } // mustRun runs a command with the current process's environment and panics // on error or non-zero exit code. func mustRun(ctx context.Context, name string, arg ...string) { cmd := exec.CommandContext(ctx, name, arg...) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr if err := cmd.Run(); err != nil { panic(err) } } //go:embed comp/_hakurei var comp []byte func main() { fmt.Println() log.SetFlags(0) log.SetPrefix("# ") version := getenv("VERSION", "untagged") prefix := getenv("PREFIX", "/usr") destdir := getenv("DESTDIR", "dist") if err := os.MkdirAll(destdir, 0755); err != nil { log.Fatal(err) } s, err := os.MkdirTemp(destdir, ".dist.*") if err != nil { log.Fatal(err) } defer func() { var code int if err = os.RemoveAll(s); err != nil { code = 1 log.Println(err) } if r := recover(); r != nil { code = 1 log.Println(r) } os.Exit(code) }() ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() log.Println("Building hakurei.") mustRun(ctx, "go", "generate", "./...") mustRun( ctx, "go", "build", "-trimpath", "-v", "-o", s, "-ldflags=-s -w "+ "-buildid= -linkmode external -extldflags=-static "+ "-X hakurei.app/internal/info.buildVersion="+version+" "+ "-X hakurei.app/internal/info.hakureiPath="+prefix+"/bin/hakurei "+ "-X hakurei.app/internal/info.hsuPath="+prefix+"/bin/hsu "+ "-X main.hakureiPath="+prefix+"/bin/hakurei", "./...", ) fmt.Println() log.Println("Testing Hakurei.") mustRun( ctx, "go", "test", "-ldflags=-buildid= -linkmode external -extldflags=-static", "./...", ) fmt.Println() log.Println("Creating distribution.") const suffix = ".tar.gz" distName := "hakurei-" + version + "-" + runtime.GOARCH var f *os.File if f, err = os.OpenFile( filepath.Join(s, distName+suffix), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644, ); err != nil { panic(err) } defer func() { if f == nil { return } if err = f.Close(); err != nil { log.Println(err) } }() h := sha512.New() gw := gzip.NewWriter(io.MultiWriter(f, h)) tw := tar.NewWriter(gw) mustWriteHeader := func(name string, size int64, mode os.FileMode) { if err = tw.WriteHeader(&tar.Header{ Name: filepath.Join(distName, name), Size: size, Mode: int64(mode), Uname: "root", Gname: "root", }); err != nil { panic(err) } if mode&os.ModeDir != 0 { fmt.Printf("%s %s\n", mode, name) } else { fmt.Printf("%s %s (%d bytes)\n", mode, name, size) } } mustWriteFile := func(name string, data []byte, mode os.FileMode) { mustWriteHeader(name, int64(len(data)), mode) if mode&os.ModeDir != 0 { return } if _, err = tw.Write(data); err != nil { panic(err) } } mustWriteFromPath := func(dst, src string) { var r *os.File if r, err = os.Open(src); err != nil { panic(err) } var fi os.FileInfo if fi, err = r.Stat(); err != nil { _ = r.Close() panic(err) } mustWriteHeader(dst, fi.Size(), fi.Mode()) if _, err = io.Copy(tw, r); err != nil { _ = r.Close() panic(err) } else if err = r.Close(); err != nil { panic(err) } } mustWriteFile(".", nil, fs.ModeDir|0755) mustWriteFile("comp/", nil, os.ModeDir|0755) mustWriteFile("comp/_hakurei", comp, 0644) mustWriteFile("install.sh", []byte(`#!/bin/sh -e cd "$(dirname -- "$0")" || exit 1 install -vDm0755 "bin/hakurei" "${DESTDIR}`+prefix+`/bin/hakurei" install -vDm0755 "bin/sharefs" "${DESTDIR}`+prefix+`/bin/sharefs" install -vDm4511 "bin/hsu" "${DESTDIR}`+prefix+`/bin/hsu" if [ ! -f "${DESTDIR}/etc/hsurc" ]; then install -vDm0400 "hsurc.default" "${DESTDIR}/etc/hsurc" fi install -vDm0644 "comp/_hakurei" "${DESTDIR}`+prefix+`/share/zsh/site-functions/_hakurei" `), 0755) mustWriteFromPath("README.md", "README.md") mustWriteFile("hsurc.default", []byte("1000 0"), 0400) for _, name := range []string{ "hsu", "hakurei", "sharefs", } { mustWriteFromPath( filepath.Join("bin", name), filepath.Join(s, name), ) } if err = tw.Close(); err != nil { panic(err) } else if err = gw.Close(); err != nil { panic(err) } else if err = f.Close(); err != nil { panic(err) } f = nil if err = os.WriteFile( filepath.Join(destdir, distName+suffix+".sha512"), append(hex.AppendEncode(nil, h.Sum(nil)), " "+distName+suffix+"\n"...), 0644, ); err != nil { panic(err) } if err = os.Rename( filepath.Join(s, distName+suffix), filepath.Join(destdir, distName+suffix), ); err != nil { panic(err) } }