9c9576b5b9
ci / test (macos-latest) (push) Has been cancelled
ci / test (ubuntu-latest) (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / govulncheck (push) Has been cancelled
ci / tidy (push) Has been cancelled
ci / webview (push) Has been cancelled
ci / windows-gui (push) Has been cancelled
release / check (push) Has been cancelled
release / release (push) Has been cancelled
Docs / build (push) Has been cancelled
Docs / deploy-pages (push) Has been cancelled
Docs / deploy-cloudflare (push) Has been cancelled
112 lines
3.3 KiB
Go
112 lines
3.3 KiB
Go
package clone
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// maxRecordedFailures caps how many individual failures Run keeps for the final
|
|
// report, so a huge broken site cannot grow the slice without bound. The error
|
|
// counters still count every failure.
|
|
const maxRecordedFailures = 100
|
|
|
|
// stats are the live counters of a run, read by the CLI's progress ticker.
|
|
type stats struct {
|
|
pages atomic.Int64 // page documents written (one per output file)
|
|
pagePaths atomic.Int64 // distinct URL paths among those, ignoring query
|
|
pagesLinked atomic.Int64 // pages stored as a hard link to identical content
|
|
assets atomic.Int64
|
|
assetSkipped atomic.Int64 // assets left on the live web (over the size cap)
|
|
pageErrors atomic.Int64
|
|
assetErrors atomic.Int64
|
|
skipped atomic.Int64 // robots-disallowed or out of budget
|
|
|
|
muPaths sync.Mutex
|
|
seenPath map[string]struct{}
|
|
|
|
muFail sync.Mutex
|
|
failures []Failure
|
|
}
|
|
|
|
// recordPage counts a freshly written page. Every write bumps pages; the first
|
|
// write for a given query-stripped path also bumps pagePaths, so the display can
|
|
// separate real pages from the query-string variants (?q=…, ?page=…) that a
|
|
// single path can spawn by the thousand on a faceted site. deduped marks a page
|
|
// whose bytes were stored as a hard link to identical content already on disk.
|
|
func (s *stats) recordPage(pathKey string, deduped bool) {
|
|
s.pages.Add(1)
|
|
if deduped {
|
|
s.pagesLinked.Add(1)
|
|
}
|
|
s.muPaths.Lock()
|
|
if s.seenPath == nil {
|
|
s.seenPath = make(map[string]struct{})
|
|
}
|
|
if _, ok := s.seenPath[pathKey]; !ok {
|
|
s.seenPath[pathKey] = struct{}{}
|
|
s.pagePaths.Add(1)
|
|
}
|
|
s.muPaths.Unlock()
|
|
}
|
|
|
|
// Failure is one thing that went wrong, kept for the end-of-run report so the
|
|
// errors are visible as a list rather than only as a count.
|
|
type Failure struct {
|
|
Kind string // "page" or "asset"
|
|
URL string
|
|
Referer string // the page that referenced it, when known
|
|
Reason string // e.g. "HTTP 403 Forbidden"
|
|
}
|
|
|
|
func (s *stats) recordFailure(f Failure) {
|
|
s.muFail.Lock()
|
|
if len(s.failures) < maxRecordedFailures {
|
|
s.failures = append(s.failures, f)
|
|
}
|
|
s.muFail.Unlock()
|
|
}
|
|
|
|
func (s *stats) recordedFailures() []Failure {
|
|
s.muFail.Lock()
|
|
defer s.muFail.Unlock()
|
|
out := make([]Failure, len(s.failures))
|
|
copy(out, s.failures)
|
|
return out
|
|
}
|
|
|
|
// Progress is a snapshot of a run for display. Pages is every page document
|
|
// written (it equals the count of HTML files on disk); PagePaths is how many
|
|
// distinct URL paths those represent once query strings are ignored. The
|
|
// difference, Pages-PagePaths, is the number of query-string variants.
|
|
type Progress struct {
|
|
Pages int64
|
|
PagePaths int64
|
|
PagesLinked int64
|
|
Assets int64
|
|
AssetSkipped int64
|
|
PageErrors int64
|
|
AssetErrors int64
|
|
Skipped int64
|
|
}
|
|
|
|
func (s *stats) snapshot() Progress {
|
|
return Progress{
|
|
Pages: s.pages.Load(),
|
|
PagePaths: s.pagePaths.Load(),
|
|
PagesLinked: s.pagesLinked.Load(),
|
|
Assets: s.assets.Load(),
|
|
AssetSkipped: s.assetSkipped.Load(),
|
|
PageErrors: s.pageErrors.Load(),
|
|
AssetErrors: s.assetErrors.Load(),
|
|
Skipped: s.skipped.Load(),
|
|
}
|
|
}
|
|
|
|
// Result is the final outcome returned by Run.
|
|
type Result struct {
|
|
Progress
|
|
OutDir string
|
|
// Failures is a capped sample of what went wrong, for the final report.
|
|
Failures []Failure
|
|
}
|