122 lines
2.9 KiB
Go
122 lines
2.9 KiB
Go
package archive
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"log/slog"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
|
|
"code.icb4dc0.de/buildr/buildr/internal/ioutils"
|
|
"code.icb4dc0.de/buildr/buildr/modules"
|
|
|
|
"github.com/klauspost/compress/zip"
|
|
)
|
|
|
|
var _ modules.Module = (*ZipArchive)(nil)
|
|
|
|
type ZipArchive struct {
|
|
Name string `hcl:"archive_name"`
|
|
WrapInDir string `hcl:"wrap_in_directory,optional"`
|
|
}
|
|
|
|
func (z ZipArchive) Category() modules.Category {
|
|
return modules.CategoryPackage
|
|
}
|
|
|
|
func (z ZipArchive) Type() string {
|
|
return "zip_archive"
|
|
}
|
|
|
|
func (z ZipArchive) Execute(ctx modules.ExecutionContext) (err error) {
|
|
if z.Name == "" {
|
|
return fmt.Errorf("zip archive name may not be empty")
|
|
}
|
|
|
|
logger := ctx.Logger()
|
|
logger.Info("Packaging archive", slog.String("archive_name", z.Name))
|
|
|
|
outFilePath := filepath.Join(ctx.OutDir(), z.Name)
|
|
logger.Debug("Creating out file", slog.String("out_file", outFilePath))
|
|
zipFile, err := os.Create(outFilePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create out file for zip archive: %w", err)
|
|
}
|
|
defer func() {
|
|
err = errors.Join(err, zipFile.Close())
|
|
}()
|
|
|
|
zipWriter := zip.NewWriter(zipFile)
|
|
|
|
defer func() {
|
|
if zipCloseErr := zipWriter.Close(); zipCloseErr != nil {
|
|
err = errors.Join(err, fmt.Errorf("failed to close zip archive: %w", zipCloseErr))
|
|
}
|
|
}()
|
|
|
|
if err = filepath.WalkDir(ctx.WorkingDir(), z.walker(logger, zipWriter, ctx.WorkingDir(), z.WrapInDir)); err != nil {
|
|
return fmt.Errorf("failed to recursively add files to zip archive: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (z ZipArchive) walker(logger *slog.Logger, zipWriter *zip.Writer, basePath, commonPrefix string) fs.WalkDirFunc {
|
|
return func(currentPath string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
logger.Debug("Adding file to zip archive", slog.String("file", currentPath))
|
|
archivePath := filepath.ToSlash(currentPath)
|
|
|
|
if filepath.IsAbs(archivePath) {
|
|
if archivePath, err = filepath.Rel(basePath, archivePath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if commonPrefix != "" {
|
|
archivePath = path.Join(commonPrefix, archivePath)
|
|
}
|
|
|
|
info, err := d.Info()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get file info for file %s: %w", currentPath, err)
|
|
}
|
|
|
|
header, err := zip.FileInfoHeader(info)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get file header for file %s: %w", currentPath, err)
|
|
}
|
|
|
|
sourceFile, err := os.Open(currentPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open file %s: %w", currentPath, err)
|
|
}
|
|
|
|
defer sourceFile.Close()
|
|
|
|
header.Method = zip.Deflate
|
|
header.Name = filepath.ToSlash(archivePath)
|
|
|
|
zipFileWriter, err := zipWriter.CreateHeader(header)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write header for file %s: %w", currentPath, err)
|
|
}
|
|
|
|
_, err = ioutils.CopyWithPooledBuffer(zipFileWriter, sourceFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy file to zip archive %s: %w", currentPath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|