github.com/adevinta/lava@v0.7.2/internal/gitserver/gittest/gittest.go (about)

     1  // Copyright 2023 Adevinta
     2  
     3  // Package gittest provides utilities for Git testing.
     4  package gittest
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/fs"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  )
    17  
    18  // CloneTemp clones the specified local repository into a temporary
    19  // directory. It returns the temporary directory.
    20  func CloneTemp(path string) (tmpPath string, err error) {
    21  	tmpPath, err = os.MkdirTemp("", "")
    22  	if err != nil {
    23  		return "", fmt.Errorf("make temp dir: %w", err)
    24  	}
    25  	defer func() {
    26  		if err != nil {
    27  			if rmErr := os.RemoveAll(tmpPath); rmErr != nil {
    28  				err = errors.Join(err, fmt.Errorf("remove temp dir %s: %w", path, rmErr))
    29  			}
    30  		}
    31  	}()
    32  
    33  	buf := &bytes.Buffer{}
    34  	cmd := exec.Command("git", "clone", path, tmpPath)
    35  	cmd.Stderr = buf
    36  	if err = cmd.Run(); err != nil {
    37  		return "", fmt.Errorf("git clone %v: %w: %#q", path, err, buf)
    38  	}
    39  
    40  	return tmpPath, nil
    41  }
    42  
    43  // ExtractTemp extracts the provided tar archive into a temporary
    44  // directory. It returns the temporary directory. Test repositories
    45  // are distributed as tar files.
    46  func ExtractTemp(tarfile string) (tmpPath string, err error) {
    47  	tmpPath, err = os.MkdirTemp("", "")
    48  	if err != nil {
    49  		return "", fmt.Errorf("make temp dir: %w", err)
    50  	}
    51  	defer func() {
    52  		if err != nil {
    53  			if rmErr := os.RemoveAll(tmpPath); rmErr != nil {
    54  				err = errors.Join(err, fmt.Errorf("remove temp dir %s: %w", tmpPath, rmErr))
    55  			}
    56  		}
    57  	}()
    58  
    59  	if err = untar(tarfile, tmpPath); err != nil {
    60  		return "", fmt.Errorf("untar test repository: %w", err)
    61  	}
    62  
    63  	return tmpPath, nil
    64  }
    65  
    66  // untar extracts the specified tar file into path.
    67  func untar(tarfile string, path string) error {
    68  	f, err := os.Open(tarfile)
    69  	if err != nil {
    70  		return fmt.Errorf("open tar file: %w", err)
    71  	}
    72  	defer f.Close()
    73  
    74  	tr := tar.NewReader(f)
    75  	for {
    76  		hdr, err := tr.Next()
    77  		if err == io.EOF {
    78  			// End of archive.
    79  			break
    80  		}
    81  		if err != nil {
    82  			return fmt.Errorf("next entry: %w", err)
    83  		}
    84  
    85  		target := filepath.Join(path, hdr.Name)
    86  
    87  		switch hdr.Typeflag {
    88  		case tar.TypeDir:
    89  			if err := os.MkdirAll(target, 0755); err != nil {
    90  				return fmt.Errorf("create dir: %w", err)
    91  			}
    92  		case tar.TypeReg:
    93  			if err = writeFile(target, fs.FileMode(hdr.Mode), tr); err != nil {
    94  				return fmt.Errorf("create file: %w", err)
    95  			}
    96  		default:
    97  			return fmt.Errorf("unexpected type flag: %v", hdr.Typeflag)
    98  		}
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  // writeFile writes the data from the reader to the named file until
   105  // EOF is reached, creating the file if necessary. If the file does
   106  // not exist, writeFile creates it with permissions perm (before
   107  // umask); otherwise writeFile truncates it before writing, without
   108  // changing permissions.
   109  func writeFile(name string, mode fs.FileMode, r io.Reader) error {
   110  	f, err := os.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, mode)
   111  	if err != nil {
   112  		return fmt.Errorf("open destination file: %w", err)
   113  	}
   114  	defer f.Close()
   115  
   116  	if _, err = io.Copy(f, r); err != nil {
   117  		return fmt.Errorf("write file contents: %w", err)
   118  	}
   119  	return nil
   120  }