package main import ( "archive/tar" "compress/gzip" "flag" "fmt" "io" "os" "path/filepath" ) func main() { // Define command line flags sourceDir := flag.String("source", "", "Source directory to compress") outputFile := flag.String("output", "output.tar.gz", "Output tarball filename") prefixDir := flag.String("prefix", "myapp", "Directory prefix in tarball") flag.Parse() if *sourceDir == "" { fmt.Println("Please specify a source directory using -source") flag.Usage() os.Exit(1) } err := createTarball(*sourceDir, *outputFile, *prefixDir) if err != nil { fmt.Printf("Error creating tarball: %v\n", err) os.Exit(1) } fmt.Printf("Successfully created %s with prefix %s\n", *outputFile, *prefixDir) } func createTarball(sourceDir, outputFile, prefix string) error { // Create output file out, err := os.Create(outputFile) if err != nil { return err } defer out.Close() // Create gzip writer gw := gzip.NewWriter(out) defer gw.Close() // Create tar writer tw := tar.NewWriter(gw) defer tw.Close() // Resolve absolute source path to handle relative symlinks correctly sourceDir, err = filepath.Abs(sourceDir) if err != nil { return err } // Walk through source directory err = filepath.Walk(sourceDir, func(filePath string, info os.FileInfo, err error) error { if err != nil { return err } // Get relative path to use in the tarball relPath, err := filepath.Rel(sourceDir, filePath) if err != nil { return err } // Create tar header using original file info header, err := tar.FileInfoHeader(info, "") if err != nil { return err } // Update the name with the prefix and relative path header.Name = filepath.Join(prefix, relPath) // Special handling for symbolic links if info.Mode()&os.ModeSymlink != 0 { // Read link target linkTarget, err := os.Readlink(filePath) if err != nil { return err } // Store the link target in the header header.Linkname = linkTarget // Make sure the link type is set correctly header.Typeflag = tar.TypeSymlink } // Write header if err := tw.WriteHeader(header); err != nil { return err } // If it's a file (not a directory or symlink), copy contents if !info.IsDir() && info.Mode()&os.ModeSymlink == 0 { file, err := os.Open(filePath) if err != nil { return err } defer file.Close() _, err = io.Copy(tw, file) if err != nil { return err } } return nil }) return err }