105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
package upload
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"compress/gzip"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/distributor/pkg/bundle"
|
|
)
|
|
|
|
func archiveBundle(root string, manifest bundle.Manifest) ([]byte, error) {
|
|
var output bytes.Buffer
|
|
gzipWriter := gzip.NewWriter(&output)
|
|
tarWriter := tar.NewWriter(gzipWriter)
|
|
|
|
manifestData, err := bundle.MarshalManifest(manifest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := writeTarEntry(tarWriter, bundle.ManifestName, manifestData, 0o600, manifest.Created); err != nil {
|
|
_ = tarWriter.Close()
|
|
_ = gzipWriter.Close()
|
|
return nil, err
|
|
}
|
|
for _, manifestFile := range manifest.Files {
|
|
fullPath := filepath.Join(root, filepath.FromSlash(manifestFile.Path))
|
|
info, err := os.Lstat(fullPath)
|
|
if err != nil {
|
|
_ = tarWriter.Close()
|
|
_ = gzipWriter.Close()
|
|
return nil, fmt.Errorf("file %q stat: %w", manifestFile.Path, err)
|
|
}
|
|
if !info.Mode().IsRegular() {
|
|
_ = tarWriter.Close()
|
|
_ = gzipWriter.Close()
|
|
return nil, fmt.Errorf("file %q must be a regular file", manifestFile.Path)
|
|
}
|
|
file, err := os.Open(fullPath)
|
|
if err != nil {
|
|
_ = tarWriter.Close()
|
|
_ = gzipWriter.Close()
|
|
return nil, fmt.Errorf("file %q open: %w", manifestFile.Path, err)
|
|
}
|
|
if err := writeTarFile(tarWriter, manifestFile.Path, file, info.Mode().Perm(), info.ModTime(), info.Size()); err != nil {
|
|
_ = file.Close()
|
|
_ = tarWriter.Close()
|
|
_ = gzipWriter.Close()
|
|
return nil, err
|
|
}
|
|
if err := file.Close(); err != nil {
|
|
_ = tarWriter.Close()
|
|
_ = gzipWriter.Close()
|
|
return nil, fmt.Errorf("file %q close: %w", manifestFile.Path, err)
|
|
}
|
|
}
|
|
if err := tarWriter.Close(); err != nil {
|
|
_ = gzipWriter.Close()
|
|
return nil, fmt.Errorf("close tar archive: %w", err)
|
|
}
|
|
if err := gzipWriter.Close(); err != nil {
|
|
return nil, fmt.Errorf("close gzip archive: %w", err)
|
|
}
|
|
return output.Bytes(), nil
|
|
}
|
|
|
|
func writeTarEntry(writer *tar.Writer, name string, data []byte, mode int64, modTime time.Time) error {
|
|
header := &tar.Header{
|
|
Name: name,
|
|
Mode: mode,
|
|
Size: int64(len(data)),
|
|
ModTime: modTime,
|
|
}
|
|
if err := writer.WriteHeader(header); err != nil {
|
|
return fmt.Errorf("write tar header %q: %w", name, err)
|
|
}
|
|
if _, err := writer.Write(data); err != nil {
|
|
return fmt.Errorf("write tar entry %q: %w", name, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeTarFile(writer *tar.Writer, name string, file *os.File, mode os.FileMode, modTime time.Time, size int64) error {
|
|
if mode == 0 {
|
|
mode = 0o600
|
|
}
|
|
header := &tar.Header{
|
|
Name: name,
|
|
Mode: int64(mode),
|
|
Size: size,
|
|
ModTime: modTime,
|
|
}
|
|
if err := writer.WriteHeader(header); err != nil {
|
|
return fmt.Errorf("write tar header %q: %w", name, err)
|
|
}
|
|
if _, err := io.Copy(writer, file); err != nil {
|
|
return fmt.Errorf("write tar entry %q: %w", name, err)
|
|
}
|
|
return nil
|
|
}
|