Git Repository
Stackyard
Stackyard scannt lokale Entwicklungsordner und bündelt Projekte, Git-Status, TODOs, Toolchains, Docker-Compose-Setups, Datei- und Launcher-Funktionen sowie Operations-Informationen in einer lokalen WebUI.
HTTPS
https://zanvex.de/git/stackyard.gitinternal/archive/archive.go
Zum Verzeichnispackage archive
import (
"archive/tar"
"archive/zip"
"compress/gzip"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
)
type Format string
const (
FormatZIP Format = "zip"
FormatTarGZ Format = "tar.gz"
)
type Options struct {
IgnoreDirs []string
}
func Write(w io.Writer, projectDir string, format Format, options Options) error {
root, err := filepath.EvalSymlinks(projectDir)
if err != nil {
return fmt.Errorf("resolve project directory: %w", err)
}
ignore := make(map[string]struct{}, len(options.IgnoreDirs)+1)
for _, name := range options.IgnoreDirs {
ignore[name] = struct{}{}
}
ignore[".git"] = struct{}{}
switch format {
case FormatZIP:
zw := zip.NewWriter(w)
err := walk(root, ignore, func(path, name string, info fs.FileInfo) error {
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = filepath.ToSlash(name)
header.Method = zip.Deflate
if info.IsDir() {
header.Name += "/"
}
entry, err := zw.CreateHeader(header)
if err != nil || info.IsDir() {
return err
}
return copyFile(entry, path)
})
if closeErr := zw.Close(); err == nil {
err = closeErr
}
return err
case FormatTarGZ:
gz := gzip.NewWriter(w)
tw := tar.NewWriter(gz)
err := walk(root, ignore, func(path, name string, info fs.FileInfo) error {
header, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}
header.Name = filepath.ToSlash(name)
if err := tw.WriteHeader(header); err != nil || info.IsDir() {
return err
}
return copyFile(tw, path)
})
if closeErr := tw.Close(); err == nil {
err = closeErr
}
if closeErr := gz.Close(); err == nil {
err = closeErr
}
return err
default:
return fmt.Errorf("unsupported archive format %q", format)
}
}
func walk(root string, ignore map[string]struct{}, add func(path, name string, info fs.FileInfo) error) error {
base := filepath.Base(root)
return filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if path == root {
return nil
}
if entry.Type()&fs.ModeSymlink != 0 {
if entry.IsDir() {
return filepath.SkipDir
}
return nil
}
if entry.IsDir() {
if _, skip := ignore[entry.Name()]; skip || strings.HasPrefix(entry.Name(), ".cache") {
return filepath.SkipDir
}
}
info, err := entry.Info()
if err != nil {
return err
}
rel, err := filepath.Rel(root, path)
if err != nil {
return err
}
return add(path, filepath.Join(base, rel), info)
})
}
func copyFile(w io.Writer, path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(w, f)
return err
}