package main import ( "errors" "fmt" "io" "log/slog" "net/http" "os" "path/filepath" "sync" ) // Asset directories, relative to project root. const ( jsDir = "assets/js" cssDir = "assets/css" ) // files maps download URLs to local destination paths. var files = map[string]string{ "https://raw.githubusercontent.com/starfederation/datastar/main/bundles/datastar.js": jsDir + "/datastar.js", "https://raw.githubusercontent.com/starfederation/datastar/main/bundles/datastar.js.map": jsDir + "/datastar.js.map", "https://github.com/saadeghi/daisyui/releases/latest/download/daisyui.mjs": cssDir + "/daisyui.mjs", "https://github.com/saadeghi/daisyui/releases/latest/download/daisyui-theme.mjs": cssDir + "/daisyui-theme.mjs", } func main() { if err := run(); err != nil { slog.Error("failure", "error", err) os.Exit(1) } } func run() error { dirs := []string{jsDir, cssDir} for _, dir := range dirs { if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("create directory %s: %w", dir, err) } } return download(files) } func download(files map[string]string) error { var wg sync.WaitGroup errCh := make(chan error, len(files)) for url, dest := range files { wg.Go(func() { base := filepath.Base(dest) slog.Info("downloading...", "file", base, "url", url) if err := downloadFile(url, dest); err != nil { errCh <- fmt.Errorf("download %s: %w", base, err) } else { slog.Info("finished", "file", base) } }) } wg.Wait() close(errCh) var errs []error for err := range errCh { errs = append(errs, err) } return errors.Join(errs...) } func downloadFile(url, dest string) error { resp, err := http.Get(url) //nolint:gosec,noctx // static URLs, simple tool if err != nil { return fmt.Errorf("GET %s: %w", url, err) } defer resp.Body.Close() //nolint:errcheck if resp.StatusCode != http.StatusOK { return fmt.Errorf("GET %s: status %s", url, resp.Status) } out, err := os.Create(dest) //nolint:gosec // paths are hardcoded constants if err != nil { return fmt.Errorf("create %s: %w", dest, err) } if _, err := io.Copy(out, resp.Body); err != nil { out.Close() //nolint:errcheck return fmt.Errorf("write %s: %w", dest, err) } if err := out.Close(); err != nil { return fmt.Errorf("close %s: %w", dest, err) } return nil }