Implement collect CLI tool
- Add Go implementation with modular architecture - Support --name flag for exact filename matching - Support --match flag for directory glob pattern matching - Create tar.gz and zip archives preserving directory structure - Handle errors with appropriate exit codes - Skip files with permission errors gracefully - Add comprehensive test suite with 11 test cases
This commit is contained in:
parent
216461fa96
commit
eb88ef97c0
8 changed files with 684 additions and 0 deletions
86
archiver/tar.go
Normal file
86
archiver/tar.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package archiver
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/atomaka/collect/collector"
|
||||
)
|
||||
|
||||
// TarArchiver creates tar.gz archives
|
||||
type TarArchiver struct{}
|
||||
|
||||
// NewTarArchiver creates a new tar archiver
|
||||
func NewTarArchiver() *TarArchiver {
|
||||
return &TarArchiver{}
|
||||
}
|
||||
|
||||
// Create creates a tar.gz archive with the collected files
|
||||
func (a *TarArchiver) Create(outputPath string, files []collector.FileEntry) error {
|
||||
// Create output file
|
||||
outFile, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create output file: %w", err)
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Create gzip writer
|
||||
gzipWriter := gzip.NewWriter(outFile)
|
||||
defer gzipWriter.Close()
|
||||
|
||||
// Create tar writer
|
||||
tarWriter := tar.NewWriter(gzipWriter)
|
||||
defer tarWriter.Close()
|
||||
|
||||
// Add each file to the archive
|
||||
for _, file := range files {
|
||||
if err := a.addFileToTar(tarWriter, file); err != nil {
|
||||
return fmt.Errorf("failed to add file %s: %w", file.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addFileToTar adds a single file to the tar archive
|
||||
func (a *TarArchiver) addFileToTar(tw *tar.Writer, file collector.FileEntry) error {
|
||||
// Open the file
|
||||
f, err := os.Open(file.FullPath)
|
||||
if err != nil {
|
||||
// Skip files we can't read with a warning
|
||||
if os.IsPermission(err) {
|
||||
fmt.Fprintf(os.Stderr, "Warning: Cannot read file: %s\n", file.FullPath)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Get file info
|
||||
info, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create tar header
|
||||
header, err := tar.FileInfoHeader(info, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use the relative path in the archive
|
||||
header.Name = filepath.ToSlash(file.Path)
|
||||
|
||||
// Write header
|
||||
if err := tw.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy file contents
|
||||
_, err = io.Copy(tw, f)
|
||||
return err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue