Add initialization comments for command registration in CLI

This commit is contained in:
Snider 2025-11-03 03:42:21 +00:00
parent 057d4a55d0
commit e7db83bc96
20 changed files with 119 additions and 19 deletions

View file

@ -60,6 +60,7 @@ var allCmd = &cobra.Command{
},
}
// init registers the 'all' command and its flags with the root command.
func init() {
RootCmd.AddCommand(allCmd)
allCmd.PersistentFlags().String("output", ".", "Output directory for the DataNodes")

View file

@ -11,6 +11,7 @@ var collectCmd = &cobra.Command{
Long: `Collect a resource from a git repository, a website, or other URI and store it in a DataNode.`,
}
// init registers the collect command with the root.
func init() {
RootCmd.AddCommand(collectCmd)
}

View file

@ -11,6 +11,7 @@ var collectGithubCmd = &cobra.Command{
Long: `Collect a resource from a GitHub repository, such as a repository or a release.`,
}
// init registers the GitHub collection parent command.
func init() {
collectCmd.AddCommand(collectGithubCmd)
}

View file

@ -126,6 +126,7 @@ var collectGithubReleaseCmd = &cobra.Command{
},
}
// init registers the 'collect github release' subcommand and its flags.
func init() {
collectGithubCmd.AddCommand(collectGithubReleaseCmd)
collectGithubReleaseCmd.PersistentFlags().String("output", ".", "Output directory for the downloaded file")

View file

@ -89,6 +89,7 @@ var collectGithubRepoCmd = &cobra.Command{
},
}
// init registers the 'collect github repo' subcommand and its flags.
func init() {
collectGithubCmd.AddCommand(collectGithubRepoCmd)
collectGithubRepoCmd.PersistentFlags().String("output", "", "Output file for the DataNode")

View file

@ -28,6 +28,7 @@ var collectGithubReposCmd = &cobra.Command{
},
}
// init registers the 'collect github repos' subcommand.
func init() {
collectGithubCmd.AddCommand(collectGithubReposCmd)
}

View file

@ -94,6 +94,7 @@ Example:
},
}
// init registers the 'collect pwa' subcommand and its flags.
func init() {
collectCmd.AddCommand(collectPWACmd)
collectPWACmd.Flags().String("uri", "", "The URI of the PWA to collect")

View file

@ -4,11 +4,11 @@ import (
"fmt"
"os"
"github.com/schollz/progressbar/v3"
"github.com/Snider/Borg/pkg/compress"
"github.com/Snider/Borg/pkg/matrix"
"github.com/Snider/Borg/pkg/ui"
"github.com/Snider/Borg/pkg/website"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
)
@ -83,6 +83,7 @@ var collectWebsiteCmd = &cobra.Command{
},
}
// init registers the 'collect website' subcommand and its flags.
func init() {
collectCmd.AddCommand(collectWebsiteCmd)
collectWebsiteCmd.PersistentFlags().String("output", "", "Output file for the DataNode")

View file

@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"
)
// NewRootCmd constructs the root cobra.Command for the Borg CLI and wires common flags.
func NewRootCmd() *cobra.Command {
rootCmd := &cobra.Command{
Use: "borg",

View file

@ -62,6 +62,7 @@ var serveCmd = &cobra.Command{
},
}
// init registers the 'serve' command and its flags with the root command.
func init() {
RootCmd.AddCommand(serveCmd)
serveCmd.PersistentFlags().String("port", "8080", "Port to serve the PWA on")

View file

@ -7,6 +7,7 @@ import (
"github.com/Snider/Borg/pkg/logger"
)
// main is the entry point for the Borg CLI application.
func main() {
verbose, _ := cmd.RootCmd.PersistentFlags().GetBool("verbose")
log := logger.New(verbose)

View file

@ -260,19 +260,35 @@ type dataFile struct {
modTime time.Time
}
// Stat implements fs.File.Stat for a dataFile and returns its FileInfo.
func (d *dataFile) Stat() (fs.FileInfo, error) { return &dataFileInfo{file: d}, nil }
// Read implements fs.File.Read for a dataFile and reports EOF as files are in-memory.
func (d *dataFile) Read(p []byte) (int, error) { return 0, io.EOF }
func (d *dataFile) Close() error { return nil }
// Close implements fs.File.Close for a dataFile; it's a no-op.
func (d *dataFile) Close() error { return nil }
// dataFileInfo implements fs.FileInfo for a dataFile.
type dataFileInfo struct{ file *dataFile }
func (d *dataFileInfo) Name() string { return path.Base(d.file.name) }
func (d *dataFileInfo) Size() int64 { return int64(len(d.file.content)) }
func (d *dataFileInfo) Mode() fs.FileMode { return 0444 }
// Name returns the base name of the data file.
func (d *dataFileInfo) Name() string { return path.Base(d.file.name) }
// Size returns the size of the data file in bytes.
func (d *dataFileInfo) Size() int64 { return int64(len(d.file.content)) }
// Mode returns the file mode for data files (read-only).
func (d *dataFileInfo) Mode() fs.FileMode { return 0444 }
// ModTime returns the modification time of the data file.
func (d *dataFileInfo) ModTime() time.Time { return d.file.modTime }
func (d *dataFileInfo) IsDir() bool { return false }
func (d *dataFileInfo) Sys() interface{} { return nil }
// IsDir reports whether the entry is a directory (false for data files).
func (d *dataFileInfo) IsDir() bool { return false }
// Sys returns system-specific data, which is nil for data files.
func (d *dataFileInfo) Sys() interface{} { return nil }
// dataFileReader implements fs.File for a dataFile.
type dataFileReader struct {
@ -280,13 +296,18 @@ type dataFileReader struct {
reader *bytes.Reader
}
// Stat returns the FileInfo for the underlying data file.
func (d *dataFileReader) Stat() (fs.FileInfo, error) { return d.file.Stat() }
// Read reads from the underlying data file content.
func (d *dataFileReader) Read(p []byte) (int, error) {
if d.reader == nil {
d.reader = bytes.NewReader(d.file.content)
}
return d.reader.Read(p)
}
// Close closes the data file reader (no-op).
func (d *dataFileReader) Close() error { return nil }
// dirInfo implements fs.FileInfo for an implicit directory.
@ -295,12 +316,23 @@ type dirInfo struct {
modTime time.Time
}
func (d *dirInfo) Name() string { return d.name }
func (d *dirInfo) Size() int64 { return 0 }
func (d *dirInfo) Mode() fs.FileMode { return fs.ModeDir | 0555 }
// Name returns the directory name.
func (d *dirInfo) Name() string { return d.name }
// Size returns the size of the directory entry (always 0 for virtual dirs).
func (d *dirInfo) Size() int64 { return 0 }
// Mode returns the file mode for directories.
func (d *dirInfo) Mode() fs.FileMode { return fs.ModeDir | 0555 }
// ModTime returns the modification time of the directory.
func (d *dirInfo) ModTime() time.Time { return d.modTime }
func (d *dirInfo) IsDir() bool { return true }
func (d *dirInfo) Sys() interface{} { return nil }
// IsDir reports that the entry is a directory.
func (d *dirInfo) IsDir() bool { return true }
// Sys returns system-specific data, which is nil for dirs.
func (d *dirInfo) Sys() interface{} { return nil }
// dirFile implements fs.File for a directory.
type dirFile struct {
@ -308,10 +340,15 @@ type dirFile struct {
modTime time.Time
}
// Stat returns the FileInfo for the directory.
func (d *dirFile) Stat() (fs.FileInfo, error) {
return &dirInfo{name: path.Base(d.path), modTime: d.modTime}, nil
}
// Read is invalid for directories and returns an error.
func (d *dirFile) Read([]byte) (int, error) {
return 0, &fs.PathError{Op: "read", Path: d.path, Err: fs.ErrInvalid}
}
// Close closes the directory file (no-op).
func (d *dirFile) Close() error { return nil }

View file

@ -31,6 +31,8 @@ func (g *githubClient) GetPublicRepos(ctx context.Context, userOrOrg string) ([]
return g.getPublicReposWithAPIURL(ctx, "https://api.github.com", userOrOrg)
}
// newAuthenticatedClient returns an *http.Client that uses GITHUB_TOKEN if set.
// When no token is present, it falls back to http.DefaultClient.
func (g *githubClient) newAuthenticatedClient(ctx context.Context) *http.Client {
token := os.Getenv("GITHUB_TOKEN")
if token == "" {
@ -42,6 +44,8 @@ func (g *githubClient) newAuthenticatedClient(ctx context.Context) *http.Client
return oauth2.NewClient(ctx, ts)
}
// getPublicReposWithAPIURL fetches public repository clone URLs for the given user/org
// using the provided GitHub API base URL, following pagination links as needed.
func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, userOrOrg string) ([]string, error) {
client := g.newAuthenticatedClient(ctx)
var allCloneURLs []string
@ -106,6 +110,7 @@ func (g *githubClient) getPublicReposWithAPIURL(ctx context.Context, apiURL, use
return allCloneURLs, nil
}
// findNextURL parses the HTTP Link header and returns the URL with rel="next", if any.
func (g *githubClient) findNextURL(linkHeader string) string {
links := strings.Split(linkHeader, ",")
for _, link := range links {

View file

@ -5,6 +5,8 @@ import (
"os"
)
// New constructs a slog.Logger configured for stderr with the given verbosity.
// When verbose is true, the logger emits debug-level messages; otherwise info-level.
func New(verbose bool) *slog.Logger {
level := slog.LevelInfo
if verbose {

View file

@ -158,6 +158,7 @@ func (p *pwaClient) DownloadAndPackagePWA(baseURL string, manifestURL string, ba
return dn, nil
}
// resolveURL resolves ref against base and returns the absolute URL.
func (p *pwaClient) resolveURL(base, ref string) (*url.URL, error) {
baseURL, err := url.Parse(base)
if err != nil {
@ -170,6 +171,7 @@ func (p *pwaClient) resolveURL(base, ref string) (*url.URL, error) {
return baseURL.ResolveReference(refURL), nil
}
// downloadAndAddFile downloads fileURL and adds it to dn at internalPath, updating the progress bar.
func (p *pwaClient) downloadAndAddFile(dn *datanode.DataNode, fileURL *url.URL, internalPath string, bar *progressbar.ProgressBar) error {
resp, err := p.client.Get(fileURL.String())
if err != nil {

View file

@ -67,16 +67,23 @@ type tarFile struct {
modTime time.Time
}
func (f *tarFile) Close() error { return nil }
// Close implements http.File Close with a no-op for tar-backed files.
func (f *tarFile) Close() error { return nil }
// Read implements io.Reader by delegating to the underlying bytes.Reader.
func (f *tarFile) Read(p []byte) (int, error) { return f.content.Read(p) }
// Seek implements io.Seeker by delegating to the underlying bytes.Reader.
func (f *tarFile) Seek(offset int64, whence int) (int64, error) {
return f.content.Seek(offset, whence)
}
// Readdir is unsupported for files in the tar filesystem and returns os.ErrInvalid.
func (f *tarFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, os.ErrInvalid
}
// Stat returns a FileInfo describing the tar-backed file.
func (f *tarFile) Stat() (os.FileInfo, error) {
return &tarFileInfo{
name: path.Base(f.header.Name),
@ -92,9 +99,20 @@ type tarFileInfo struct {
modTime time.Time
}
func (i *tarFileInfo) Name() string { return i.name }
func (i *tarFileInfo) Size() int64 { return i.size }
func (i *tarFileInfo) Mode() os.FileMode { return 0444 }
// Name returns the base name of the tar file.
func (i *tarFileInfo) Name() string { return i.name }
// Size returns the size of the tar file in bytes.
func (i *tarFileInfo) Size() int64 { return i.size }
// Mode returns a read-only file mode for tar entries.
func (i *tarFileInfo) Mode() os.FileMode { return 0444 }
// ModTime returns the modification time recorded in the tar header.
func (i *tarFileInfo) ModTime() time.Time { return i.modTime }
func (i *tarFileInfo) IsDir() bool { return false }
func (i *tarFileInfo) Sys() interface{} { return nil }
// IsDir reports whether the entry is a directory (always false for files here).
func (i *tarFileInfo) IsDir() bool { return false }
// Sys returns underlying data source (unused for tar entries).
func (i *tarFileInfo) Sys() interface{} { return nil }

View file

@ -10,6 +10,8 @@ import (
"github.com/mattn/go-isatty"
)
// NonInteractivePrompter periodically prints status quotes when the terminal is non-interactive.
// It is intended to give the user feedback during long-running operations.
type NonInteractivePrompter struct {
stopChan chan struct{}
quoteFunc func() (string, error)
@ -18,6 +20,8 @@ type NonInteractivePrompter struct {
stopOnce sync.Once
}
// NewNonInteractivePrompter returns a new NonInteractivePrompter that will call
// the provided quoteFunc to retrieve text to display.
func NewNonInteractivePrompter(quoteFunc func() (string, error)) *NonInteractivePrompter {
return &NonInteractivePrompter{
stopChan: make(chan struct{}),
@ -25,6 +29,7 @@ func NewNonInteractivePrompter(quoteFunc func() (string, error)) *NonInteractive
}
}
// Start begins the periodic display of quotes until Stop is called.
func (p *NonInteractivePrompter) Start() {
p.mu.Lock()
if p.started {
@ -59,6 +64,7 @@ func (p *NonInteractivePrompter) Start() {
}()
}
// Stop signals the prompter to stop printing further messages.
func (p *NonInteractivePrompter) Stop() {
if p.IsInteractive() {
return
@ -68,6 +74,7 @@ func (p *NonInteractivePrompter) Stop() {
})
}
// IsInteractive reports whether stdout is attached to an interactive terminal.
func (p *NonInteractivePrompter) IsInteractive() bool {
return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
}

View file

@ -1,16 +1,19 @@
package ui
import "github.com/schollz/progressbar/v3"
// progressWriter implements io.Writer to update a progress bar with textual status.
type progressWriter struct {
bar *progressbar.ProgressBar
}
// NewProgressWriter returns a writer that updates the provided progress bar's description.
func NewProgressWriter(bar *progressbar.ProgressBar) *progressWriter {
return &progressWriter{bar: bar}
}
// Write updates the progress bar description with the provided bytes as a string.
// It returns the length of p to satisfy io.Writer semantics.
func (pw *progressWriter) Write(p []byte) (n int, err error) {
if pw == nil || pw.bar == nil {
return len(p), nil

View file

@ -16,6 +16,7 @@ var (
quotesErr error
)
// init seeds the random number generator for quote selection.
func init() {
rand.Seed(time.Now().UnixNano())
}
@ -41,6 +42,7 @@ type Quotes struct {
} `json:"image_related"`
}
// loadQuotes reads and unmarshals the embedded quotes.json file into a Quotes struct.
func loadQuotes() (*Quotes, error) {
quotesFile, err := QuotesJSON.ReadFile("quotes.json")
if err != nil {
@ -54,6 +56,7 @@ func loadQuotes() (*Quotes, error) {
return &quotes, nil
}
// getQuotes returns the cached Quotes, loading them once on first use.
func getQuotes() (*Quotes, error) {
quotesOnce.Do(func() {
cachedQuotes, quotesErr = loadQuotes()
@ -61,6 +64,7 @@ func getQuotes() (*Quotes, error) {
return cachedQuotes, quotesErr
}
// GetRandomQuote returns a random quote string from the combined quote sets.
func GetRandomQuote() (string, error) {
quotes, err := getQuotes()
if err != nil {
@ -82,6 +86,7 @@ func GetRandomQuote() (string, error) {
return allQuotes[rand.Intn(len(allQuotes))], nil
}
// PrintQuote prints a random quote to stdout in green for user feedback.
func PrintQuote() {
quote, err := GetRandomQuote()
if err != nil {
@ -92,6 +97,7 @@ func PrintQuote() {
c.Println(quote)
}
// GetVCSQuote returns a random quote related to VCS processing.
func GetVCSQuote() (string, error) {
quotes, err := getQuotes()
if err != nil {
@ -103,6 +109,7 @@ func GetVCSQuote() (string, error) {
return quotes.VCSProcessing[rand.Intn(len(quotes.VCSProcessing))], nil
}
// GetPWAQuote returns a random quote related to PWA processing.
func GetPWAQuote() (string, error) {
quotes, err := getQuotes()
if err != nil {
@ -114,6 +121,7 @@ func GetPWAQuote() (string, error) {
return quotes.PWAProcessing[rand.Intn(len(quotes.PWAProcessing))], nil
}
// GetWebsiteQuote returns a random quote related to website processing.
func GetWebsiteQuote() (string, error) {
quotes, err := getQuotes()
if err != nil {

View file

@ -53,6 +53,8 @@ func DownloadAndPackageWebsite(startURL string, maxDepth int, bar *progressbar.P
return d.dn, nil
}
// crawl traverses the website starting from pageURL up to maxDepth,
// storing pages and discovered local assets into the DataNode.
func (d *Downloader) crawl(pageURL string, depth int) {
if depth > d.maxDepth || d.visited[pageURL] {
return
@ -110,6 +112,7 @@ func (d *Downloader) crawl(pageURL string, depth int) {
f(doc)
}
// downloadAsset fetches a single asset URL and stores it into the DataNode.
func (d *Downloader) downloadAsset(assetURL string) {
if d.visited[assetURL] {
return
@ -136,6 +139,7 @@ func (d *Downloader) downloadAsset(assetURL string) {
d.dn.AddData(relPath, body)
}
// getRelativePath returns a path relative to the site's root for the given URL.
func (d *Downloader) getRelativePath(pageURL string) string {
u, err := url.Parse(pageURL)
if err != nil {
@ -144,6 +148,7 @@ func (d *Downloader) getRelativePath(pageURL string) string {
return strings.TrimPrefix(u.Path, "/")
}
// resolveURL resolves ref against base and returns the absolute URL string.
func (d *Downloader) resolveURL(base, ref string) (string, error) {
baseURL, err := url.Parse(base)
if err != nil {
@ -156,6 +161,7 @@ func (d *Downloader) resolveURL(base, ref string) (string, error) {
return baseURL.ResolveReference(refURL).String(), nil
}
// isLocal reports whether pageURL belongs to the same host as the base URL.
func (d *Downloader) isLocal(pageURL string) bool {
u, err := url.Parse(pageURL)
if err != nil {
@ -164,6 +170,7 @@ func (d *Downloader) isLocal(pageURL string) bool {
return u.Hostname() == d.baseURL.Hostname()
}
// isAsset reports whether the URL likely points to a static asset we should download.
func isAsset(pageURL string) bool {
ext := []string{".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico"}
for _, e := range ext {