in tools/releaser/main.go [321:456]
func makeTgz(w io.Writer, repoRoot string, ref string) (string, error) {
hashw := sha256.New()
gzw, err := gzip.NewWriterLevel(io.MultiWriter(w, hashw), gzip.BestCompression)
if err != nil {
return "", fmt.Errorf("create gzip writer: %w", err)
}
tw := tar.NewWriter(gzw)
// WORKSPACE in the resulting tarball needs to be much
// smaller than of hermetic_cc_toolchain. See #15.
// See that README why we are not adding the top-level README.md.
// These files will become top-level during processing.
substitutes := map[string]string{
"tools/releaser/data/WORKSPACE": "WORKSPACE",
"tools/releaser/data/README": "README",
}
removals := map[string]struct{}{
"tools/": struct{}{},
"tools/releaser/": struct{}{},
"tools/releaser/data/": struct{}{},
}
updates := map[string]struct{}{
"MODULE.bazel": {},
}
// Paths to be included to the release
cmd := exec.Command(
"git",
"archive",
"--format=tar",
ref,
"LICENSE",
"toolchain/*",
// files to be renamed
"tools/releaser/data/WORKSPACE",
"tools/releaser/data/README",
// files to be updated
"MODULE.bazel",
)
// the tarball produced by `git archive` has too many artifacts:
// - file metadata is different when different SHAs are used.
// - the archive contains the repo SHA as a "comment".
// Therefore, parse whatever `git archive` outputs and sanitize it.
cmd.Dir = repoRoot
cmd.Stderr = os.Stderr
stdout, err := cmd.StdoutPipe()
if err != nil {
return "", fmt.Errorf("StdoutPipe: %w", err)
}
if err := cmd.Start(); err != nil {
return "", fmt.Errorf("git archive: %w", err)
}
tr := tar.NewReader(stdout)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return "", fmt.Errorf("read archive: %w", err)
}
// pax headers contain things we want to clean in
// the first place.
if hdr.Typeflag == tar.TypeXGlobalHeader {
continue
}
name := hdr.Name
if _, ok := removals[name]; ok {
continue
}
if n, ok := substitutes[name]; ok {
name = n
}
source := io.NopCloser(tr)
size := hdr.Size
if _, ok := updates[name]; ok {
newFile, err := os.Open(path.Join(repoRoot, name))
if err != nil {
return "", err
}
source = newFile
fileInfo, err := newFile.Stat()
if err != nil {
return "", err
}
size = fileInfo.Size()
}
if err := tw.WriteHeader(&tar.Header{
Name: name,
Mode: int64(hdr.Mode & 0777),
Size: size,
ModTime: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
Format: tar.FormatGNU,
}); err != nil {
return "", err
}
_, err = io.Copy(tw, source)
_ = source.Close()
if err != nil {
return "", fmt.Errorf("writing %q to archive: %w", name, err)
}
}
if err := tw.Close(); err != nil {
return "", fmt.Errorf("close tar writer: %w", err)
}
if err := gzw.Close(); err != nil {
return "", fmt.Errorf("close gzip stream: %w", err)
}
// If at this point cmd.Wait() is called, it gets stuck
// on Windows. Since there isn't any value from the subprocess
// at this point, do the best-effort Kill followed by Wait.
_ = cmd.Process.Kill()
_ = cmd.Wait()
return fmt.Sprintf("%x", hashw.Sum(nil)), nil
}