package main import ( "bytes" "compress/gzip" "context" "fmt" "io" "io/fs" "os" "path/filepath" "strings" "dev.mediocregopher.com/mediocre-blog.git/src/post/asset" "dev.mediocregopher.com/mediocre-go-lib.git/mctx" "dev.mediocregopher.com/mediocre-go-lib.git/mlog" "github.com/nlepage/go-tarfs" ) func writeArchiveAsset( assetStore asset.Store, assetsDirPath string, id string, ) error { buf := new(bytes.Buffer) if err := assetStore.Get(id, buf); err != nil { return fmt.Errorf("loading into buffer: %w", err) } gzipR, err := gzip.NewReader(buf) if err != nil { return fmt.Errorf("decompressing as gzip: %w", err) } tarFS, err := tarfs.New(gzipR) if err != nil { return fmt.Errorf("parsing as tar: %w", err) } return fs.WalkDir(tarFS, ".", func(path string, d fs.DirEntry, err error) error { if err != nil { return fmt.Errorf("walking path %q: %w", path, err) } else if d.IsDir() { return nil } var ( dirPath = filepath.Join(assetsDirPath, id, filepath.Dir(path)) dstPath = filepath.Join(dirPath, d.Name()) srcPath = path ) if err := os.MkdirAll(dirPath, 0755); err != nil { return fmt.Errorf("creating directory %q: %w", dirPath, err) } dstF, err := os.Create(dstPath) if err != nil { return fmt.Errorf("opening dst path %q: %w", dstPath, err) } defer dstF.Close() srcF, err := tarFS.Open(srcPath) if err != nil { return fmt.Errorf("opening path %q within the tar: %w", srcPath, err) } defer srcF.Close() if _, err = io.Copy(dstF, srcF); err != nil { return fmt.Errorf("copying %q into %q: %w", srcPath, dstPath, err) } return nil }) } func writeAsset( assetStore asset.Store, assetsDirPath string, id string, ) error { if strings.HasSuffix(id, ".tgz") { return writeArchiveAsset(assetStore, assetsDirPath, id) } assetPath := filepath.Join(assetsDirPath, id) f, err := os.Create(assetPath) if err != nil { return fmt.Errorf("creating file %q: %w", assetPath, err) } defer func() { _ = f.Close() }() if err := assetStore.Get(id, f); err != nil { return fmt.Errorf("writing asset to %q: %w", assetPath, err) } return nil } func exportAssets( ctx context.Context, logger *mlog.Logger, assetStore asset.Store, exportDirPath string, ) error { var ( assetsDirPath = filepath.Join(exportDirPath, "assets") ) if err := os.MkdirAll(assetsDirPath, 0755); err != nil { return fmt.Errorf("creating asset dir %q: %w", assetsDirPath, err) } logger.Info(ctx, "Listing assets") assets, err := assetStore.List() if err != nil { return fmt.Errorf("listing assets: %w", err) } for _, id := range assets { logger.Info(mctx.Annotate(ctx, "assetID", id), "Writing asset") if err := writeAsset(assetStore, assetsDirPath, id); err != nil { return fmt.Errorf("writing asset %q: %w", id, err) } } return nil }