diff --git a/web/fup/cmd/extractstatic.go b/web/fup/cmd/extractstatic.go new file mode 100644 index 0000000000..f21b9021db --- /dev/null +++ b/web/fup/cmd/extractstatic.go @@ -0,0 +1,83 @@ +package cmd + +import ( + "fmt" + "io" + "io/fs" + "log" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "hg.lukegb.com/lukegb/depot/web/fup/fupstatic" +) + +func init() { + rootCmd.AddCommand(extractstaticCmd) + + extractstaticCmd.Flags().BoolVarP(&extractstaticForceOverwrite, "force", "f", false, "If set, will overwrite existing files.") +} + +var ( + extractstaticForceOverwrite bool + + extractstaticCmd = &cobra.Command{ + Use: "extractstatic dir", + Example: "extractstatic /srv/http/static", + Short: "Extracts static assets into a target directory.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + targetDir := args[0] + log.Printf("extracting assets to %v", targetDir) + return fs.WalkDir(fupstatic.Static, ".", func(fpath string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + targetPath := filepath.Join(targetDir, fpath) + fi, err := os.Stat(targetPath) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("checking for existence of %q: %w", targetPath, err) + } + if d.IsDir() { + switch { + case fi != nil && !fi.IsDir(): + return fmt.Errorf("%q already exists and is not a directory", targetPath) + case fi != nil: + return nil + default: + // Ensure directory exists. + return os.Mkdir(filepath.Join(targetDir, fpath), 0755) + } + } + + switch { + case fi != nil && fi.IsDir(): + return fmt.Errorf("%q already exists and is a directory", targetPath) + case fi != nil && !extractstaticForceOverwrite: + return nil + } + + src, err := fupstatic.Static.Open(fpath) + if err != nil { + return fmt.Errorf("open source %q: %w", fpath, err) + } + defer src.Close() // Don't care about errors for closing read-only file. + + dst, err := os.Create(targetPath) + if err != nil { + return fmt.Errorf("create target %q: %w", targetPath, err) + } + + if _, err := io.Copy(dst, src); err != nil { + return fmt.Errorf("copying from source %q to target %q: %w", fpath, targetPath, err) + } + + if err := dst.Close(); err != nil { + return fmt.Errorf("close target %q: %w", targetPath, err) + } + + return nil + }) + }, + } +)