buildr/modules/packaging/archive/zip.go
Peter e60726ef9e
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
feat: implement new and man for plugin modules
- use extracted shared libraries
2023-08-23 22:06:26 +02:00

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
}
}