func makeTgz()

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
}