docs: extract goget manpage to standalone troff source
This commit is contained in:
@@ -3,6 +3,13 @@
|
|||||||
# Build output
|
# Build output
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
|
# Generated copy of man/goget.1 (regenerated by `just build`).
|
||||||
|
# Required because //go:embed cannot reference files outside the
|
||||||
|
# embedding package, so man/goget.1 is copied into the package
|
||||||
|
# tree at build time and embedded into the binary.
|
||||||
|
cmd/goget/man-page-data/*.1
|
||||||
|
!cmd/goget/man-page-data/.gitkeep
|
||||||
|
|
||||||
# Test artifacts
|
# Test artifacts
|
||||||
*.test
|
*.test
|
||||||
*.out
|
*.out
|
||||||
|
|||||||
@@ -9,6 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
**Documentation**
|
||||||
|
|
||||||
|
- **Standalone troff manpage** — `man/goget.1` is now the single source
|
||||||
|
of truth for the goget(1) manual. The previous embedded manpage listed
|
||||||
|
only ~30 of the 138 CLI flags and was missing every standard section;
|
||||||
|
the new file documents every flag with a dedicated entry, the
|
||||||
|
full set of conventional `.SH` sections (NAME, SYNOPSIS, DESCRIPTION,
|
||||||
|
OPTIONS, EXAMPLES, EXIT STATUS, FILES, ENVIRONMENT, SEE ALSO, HISTORY,
|
||||||
|
AUTHORS, REPORTING BUGS, COPYRIGHT), curl/wget compatibility notes, and
|
||||||
|
a release-history entry. The manpage is embedded into the binary via
|
||||||
|
`//go:embed`, so `--generate-man-page` and `man goget` now agree.
|
||||||
|
- **`just man`** — new task that runs `groff -man -ww -Kutf-8 -z` over
|
||||||
|
`man/goget.1` to validate the troff source. Skips cleanly when `groff`
|
||||||
|
is not installed (CI on minimal images still passes).
|
||||||
|
- **`just install-man`** and **`just uninstall-man`** — install the
|
||||||
|
manpage to `$HOME/.local/share/man/man1/goget.1.gz` (XDG rootless,
|
||||||
|
gzipped, reinstall-safe). After `just install-man`, `man goget`
|
||||||
|
resolves the page automatically via the user's `MANPATH`.
|
||||||
|
|
||||||
**Core engine**
|
**Core engine**
|
||||||
|
|
||||||
- **Unified worker pool** — single protocol-agnostic worker pool (`internal/pool`)
|
- **Unified worker pool** — single protocol-agnostic worker pool (`internal/pool`)
|
||||||
@@ -53,6 +72,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
output when combined with `--json`. Credentials embedded in the stored
|
output when combined with `--json`. Credentials embedded in the stored
|
||||||
URL are masked before display (the password placeholder is `xxxxx`).
|
URL are masked before display (the password placeholder is `xxxxx`).
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- `cmd/goget/man_page_test.go` — new test file. Asserts the embedded
|
||||||
|
manpage is non-empty, carries a `.TH GOGET 1` header, contains every
|
||||||
|
mandatory `.SH` section, and documents every flag registered by
|
||||||
|
`defineFlags` (so adding a new flag without documenting it now breaks
|
||||||
|
the build). When `groff` is installed, also runs `groff -man -ww
|
||||||
|
-Kutf-8 -z` over the embedded source and fails on any warning.
|
||||||
|
|
||||||
## [1.0.0] — 2026-06-26
|
## [1.0.0] — 2026-06-26
|
||||||
|
|
||||||
First public release: a modern IPv6-first command-line download utility built on
|
First public release: a modern IPv6-first command-line download utility built on
|
||||||
|
|||||||
@@ -271,13 +271,18 @@ just install # go mod download
|
|||||||
just run # run from source (development)
|
just run # run from source (development)
|
||||||
just build # go vet + gofmt check + build → bin/goget
|
just build # go vet + gofmt check + build → bin/goget
|
||||||
just test # go test -race -count=1 ./...
|
just test # go test -race -count=1 ./...
|
||||||
|
just man # validate man/goget.1 with groff
|
||||||
|
just install-man # install manpage to ~/.local/share/man/man1/
|
||||||
just uninstall # remove build artifacts
|
just uninstall # remove build artifacts
|
||||||
|
just uninstall-man# remove installed manpage
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
- [`man/goget.1`](man/goget.1) — troff manual page (`man goget` after
|
||||||
|
`just install-man`)
|
||||||
- [`docs/architecture.md`](docs/architecture.md) — components and request flow
|
- [`docs/architecture.md`](docs/architecture.md) — components and request flow
|
||||||
- [`docs/configuration.md`](docs/configuration.md) — config schema (TOML)
|
- [`docs/configuration.md`](docs/configuration.md) — config schema (TOML)
|
||||||
- [`docs/api.md`](docs/api.md) — public Go library API
|
- [`docs/api.md`](docs/api.md) — public Go library API
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Placeholder so the directory is tracked by git. The actual file
|
||||||
|
generated here by `just build` is `goget.1`, copied from
|
||||||
|
`../../../man/goget.1`.
|
||||||
+13
-206
@@ -1,217 +1,24 @@
|
|||||||
//go:build linux || freebsd
|
//go:build linux || freebsd
|
||||||
// +build linux freebsd
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeberg.org/petrbalvin/goget/internal/core"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// manPage holds the canonical goget(1) manual page in troff/groff format.
|
||||||
|
// The source of truth is ../../man/goget.1; a copy is kept here so it can
|
||||||
|
// be embedded into the binary at build time (//go:embed cannot reference
|
||||||
|
// files outside the embedding package). The copy is refreshed by the
|
||||||
|
// `cp` step in `just build`.
|
||||||
|
//
|
||||||
|
//go:embed man-page-data/goget.1
|
||||||
|
var manPage string
|
||||||
|
|
||||||
|
// handleGenManPage implements the --generate-man-page flag. It writes the
|
||||||
|
// embedded manpage to standard output and exits 0.
|
||||||
func handleGenManPage() int {
|
func handleGenManPage() int {
|
||||||
fmt.Printf(`.TH GOGET 1 "2026-06" "goget %s" "User Commands"
|
fmt.Print(manPage)
|
||||||
.SH NAME
|
|
||||||
goget \- modern IPv6-first command-line download utility
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B goget
|
|
||||||
\fB\-\-url\fR \fIURL\fR [\fIOPTIONS\fR]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
goget is a modern replacement for curl and wget built on Go stdlib.
|
|
||||||
Features IPv6-first connections, parallel downloads, resume support,
|
|
||||||
compression, checksum verification, and recursive mirroring.
|
|
||||||
.SH OPTIONS
|
|
||||||
.SS "Target"
|
|
||||||
.TP
|
|
||||||
\fB\-\-url\fR=\fIURL\fR
|
|
||||||
URL to download (can be specified multiple times)
|
|
||||||
.SS "Basic Options"
|
|
||||||
.TP
|
|
||||||
\fB\-\-output\fR=\fIFILE\fR
|
|
||||||
Output filename (default: auto from URL)
|
|
||||||
.TP
|
|
||||||
\fB\-\-verbose\fR
|
|
||||||
Verbose output with progress bar (default: enabled)
|
|
||||||
.TP
|
|
||||||
\fB\-\-no-progress\fR
|
|
||||||
Hide progress bar (keep other output)
|
|
||||||
.TP
|
|
||||||
\fB\-\-quiet\fR
|
|
||||||
Suppress all output except errors
|
|
||||||
.TP
|
|
||||||
\fB\-\-debug\fR
|
|
||||||
Debug mode with detailed info
|
|
||||||
.TP
|
|
||||||
\fB\-\-resume\fR
|
|
||||||
Resume interrupted download
|
|
||||||
.TP
|
|
||||||
\fB\-\-restart\fR
|
|
||||||
Delete existing file and restart download
|
|
||||||
.TP
|
|
||||||
\fB\-\-overwrite\fR
|
|
||||||
Overwrite existing file
|
|
||||||
.TP
|
|
||||||
\fB\-\-no-decompress\fR
|
|
||||||
Disable automatic decompression
|
|
||||||
.TP
|
|
||||||
\fB\-\-parallel\fR=\fIN\fR
|
|
||||||
Parallel connections (0=auto, 1=sequential)
|
|
||||||
.TP
|
|
||||||
\fB\-\-json\fR
|
|
||||||
Output progress as JSON (for scripting)
|
|
||||||
.TP
|
|
||||||
\fB\-\-info\fR
|
|
||||||
Show file information without downloading
|
|
||||||
.TP
|
|
||||||
\fB\-\-benchmark\fR=\fIURL\fR
|
|
||||||
Benchmark download speed
|
|
||||||
.SS "Network"
|
|
||||||
.TP
|
|
||||||
\fB\-\-timeout\fR=\fIDURATION\fR
|
|
||||||
Connection timeout (0 = auto based on size)
|
|
||||||
.TP
|
|
||||||
\fB\-\-proxy\fR=\fIURL\fR
|
|
||||||
Proxy server URL (http://, https://, socks5://, socks5h://)
|
|
||||||
.TP
|
|
||||||
\fB\-\-dns-servers\fR=\fIIPs\fR
|
|
||||||
Custom DNS servers (comma-separated)
|
|
||||||
.TP
|
|
||||||
\fB\-\-max-retries\fR=\fIN\fR
|
|
||||||
Maximum number of retries on failure
|
|
||||||
.TP
|
|
||||||
\fB\-\-no-ipv4\fR
|
|
||||||
Disable IPv4 fallback
|
|
||||||
.TP
|
|
||||||
\fB\-\-no-redirect\fR
|
|
||||||
Disable following HTTP redirects
|
|
||||||
.TP
|
|
||||||
\fB\-\-show-headers\fR
|
|
||||||
Print HTTP response headers
|
|
||||||
.TP
|
|
||||||
\fB\-\-keep-timestamps\fR
|
|
||||||
Set file modification time from Last-Modified header
|
|
||||||
.TP
|
|
||||||
\fB\-\-content-disposition\fR
|
|
||||||
Use server-provided filename from Content-Disposition header
|
|
||||||
.SS "Authentication"
|
|
||||||
.TP
|
|
||||||
Credentials are loaded automatically from \fB~/.netrc\fR when no \fB\-\-username\fR is given.
|
|
||||||
.TP
|
|
||||||
\fB\-\-username\fR=\fIUSER\fR
|
|
||||||
Username for HTTP Basic/Digest Auth
|
|
||||||
.TP
|
|
||||||
\fB\-\-password\fR=\fIPASS\fR
|
|
||||||
Password for HTTP Basic/Digest Auth
|
|
||||||
.TP
|
|
||||||
\fB\-\-password-file\fR=\fIFILE\fR
|
|
||||||
Read password from file
|
|
||||||
.TP
|
|
||||||
\fB\-\-auth-type\fR=\fITYPE\fR
|
|
||||||
Auth type: basic, digest, auto
|
|
||||||
.TP
|
|
||||||
\fB\-\-cookie-jar\fR=\fIFILE\fR
|
|
||||||
File for loading/saving cookies
|
|
||||||
.TP
|
|
||||||
\fB\-\-cookie\fR=\fINAME=VALUE\fR
|
|
||||||
Set cookie from CLI (repeatable, curl-compatible)
|
|
||||||
.TP
|
|
||||||
\fB\-\-retry-all-errors\fR
|
|
||||||
Retry on any HTTP 4xx/5xx response
|
|
||||||
.TP
|
|
||||||
\fB\-\-ssl-key-log\fR=\fIFILE\fR
|
|
||||||
Log TLS session keys to file (SSLKEYLOGFILE for Wireshark)
|
|
||||||
.SS "Config"
|
|
||||||
.TP
|
|
||||||
\fB\-\-config\fR=\fIFILE\fR
|
|
||||||
Path to config file
|
|
||||||
.TP
|
|
||||||
\fB\-\-config-get\fR=\fIKEY\fR
|
|
||||||
Get a config value
|
|
||||||
.TP
|
|
||||||
\fB\-\-config-set\fR=\fIKEY\fR \fIVALUE\fR
|
|
||||||
Set a config value
|
|
||||||
.TP
|
|
||||||
\fB\-\-config-list\fR
|
|
||||||
List all config values
|
|
||||||
.TP
|
|
||||||
\fB\-\-config-unset\fR=\fIKEY\fR
|
|
||||||
Unset a config value
|
|
||||||
.TP
|
|
||||||
\fB\-\-init-config\fR
|
|
||||||
Generate default config file
|
|
||||||
.SS "Mirror & Recursive"
|
|
||||||
.TP
|
|
||||||
\fB\-\-mirror\fR
|
|
||||||
Mirror entire website (infinite depth)
|
|
||||||
.TP
|
|
||||||
\fB\-\-recursive\fR
|
|
||||||
Enable recursive downloading
|
|
||||||
.TP
|
|
||||||
\fB\-\-max-depth\fR=\fIN\fR
|
|
||||||
Maximum recursion depth
|
|
||||||
.SS "Scheduling & Hooks"
|
|
||||||
.TP
|
|
||||||
\fB\-\-schedule\fR=\fITIME\fR
|
|
||||||
Schedule download ("YYYY-MM-DD HH:MM" or cron "0 2 * * *")
|
|
||||||
.TP
|
|
||||||
\fB\-\-on-complete\fR=\fICMD\fR
|
|
||||||
Run shell command after successful download
|
|
||||||
.TP
|
|
||||||
\fB\-\-bind-interface\fR=\fIIFACE\fR
|
|
||||||
Bind to specific network interface
|
|
||||||
.SS "Other"
|
|
||||||
.TP
|
|
||||||
\fB\-\-batch-file\fR=\fIFILE\fR
|
|
||||||
Download multiple URLs from a file
|
|
||||||
.TP
|
|
||||||
\fB\-\-checksum-file\fR=\fIFILE\fR
|
|
||||||
Read expected checksum from SHA256SUMS file
|
|
||||||
.TP
|
|
||||||
\fB\-\-generate-man-page\fR
|
|
||||||
Generate this man page
|
|
||||||
.TP
|
|
||||||
\fB\-\-help\fR
|
|
||||||
Show help
|
|
||||||
.TP
|
|
||||||
\fB\-\-version\fR
|
|
||||||
Show version
|
|
||||||
.SH EXAMPLES
|
|
||||||
.TP
|
|
||||||
Download a file:
|
|
||||||
goget --url https://example.com/file.zip
|
|
||||||
.TP
|
|
||||||
Parallel download:
|
|
||||||
goget --url https://example.com/large.iso --parallel 4
|
|
||||||
.TP
|
|
||||||
Resume interrupted download:
|
|
||||||
goget --url https://example.com/file.zip --resume
|
|
||||||
.TP
|
|
||||||
Mirror website:
|
|
||||||
goget --url https://example.com --mirror --output ./mirror
|
|
||||||
.TP
|
|
||||||
Recursive download:
|
|
||||||
goget --url https://example.com --recursive --max-depth 2
|
|
||||||
.TP
|
|
||||||
JSON progress (for scripting):
|
|
||||||
goget --url https://example.com/file.zip --json --no-progress > progress.jsonl
|
|
||||||
.TP
|
|
||||||
Scheduled download at 2 AM:
|
|
||||||
goget --url https://example.com/daily.zip --schedule "0 2 * * *"
|
|
||||||
.TP
|
|
||||||
Run import script after download:
|
|
||||||
goget --url https://example.com/data.csv --on-complete "import.sh"
|
|
||||||
.SH FILES
|
|
||||||
.TP
|
|
||||||
~/.config/goget/config.json
|
|
||||||
Configuration file
|
|
||||||
.TP
|
|
||||||
<file>.goget.meta
|
|
||||||
Resume metadata file
|
|
||||||
.SH BUGS
|
|
||||||
Report bugs at https://codeberg.org/petrbalvin/goget/issues
|
|
||||||
.SH AUTHOR
|
|
||||||
Petr Balvin
|
|
||||||
.SH LICENSE
|
|
||||||
MIT License`, core.Version)
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
//go:build linux || freebsd
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os/exec"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"codeberg.org/petrbalvin/goget/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// manPageRequiredSections lists the conventional manpage sections that
|
||||||
|
// goget(1) must include. Every section in this list must be present
|
||||||
|
// in the manpage source as a ".SH <NAME>" header. Adding a new section
|
||||||
|
// here forces the manpage author to document it.
|
||||||
|
var manPageRequiredSections = []string{
|
||||||
|
"NAME",
|
||||||
|
"SYNOPSIS",
|
||||||
|
"DESCRIPTION",
|
||||||
|
"OPTIONS",
|
||||||
|
"EXAMPLES",
|
||||||
|
"EXIT STATUS",
|
||||||
|
"FILES",
|
||||||
|
"ENVIRONMENT",
|
||||||
|
"SEE ALSO",
|
||||||
|
"HISTORY",
|
||||||
|
"AUTHORS",
|
||||||
|
"REPORTING BUGS",
|
||||||
|
"COPYRIGHT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManPageEmbedded is the simplest smoke test: the embed succeeded
|
||||||
|
// and the variable is not empty. Any first line beginning with `.`
|
||||||
|
// (either a `.\"` comment or a macro such as `.TH`) is accepted as
|
||||||
|
// proof the embedded blob is troff source.
|
||||||
|
func TestManPageEmbedded(t *testing.T) {
|
||||||
|
if manPage == "" {
|
||||||
|
t.Fatal("embedded manPage is empty — //go:embed failed at build time")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(manPage, ".") {
|
||||||
|
t.Errorf("embedded manPage does not look like troff (got prefix %q)", firstLine(manPage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManPageHasTHHeader verifies the .TH macro is present with the
|
||||||
|
// expected program name, section, and version. The .TH line is the
|
||||||
|
// entry point that `man` uses to identify the page.
|
||||||
|
func TestManPageHasTHHeader(t *testing.T) {
|
||||||
|
if !strings.Contains(manPage, ".TH GOGET 1 ") {
|
||||||
|
t.Error("manpage is missing the .TH GOGET 1 header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManPageHasMandatorySections asserts that every section in
|
||||||
|
// manPageRequiredSections exists as a .SH heading. This keeps the page
|
||||||
|
// discoverable in `man goget` and ensures consistency with project
|
||||||
|
// documentation conventions.
|
||||||
|
//
|
||||||
|
// In troff, multi-word section names must be quoted to keep the
|
||||||
|
// spaces inside a single argument: `.SH "SEE ALSO"`. The matcher
|
||||||
|
// accepts both the bare form (`.SH SEE ALSO`) and the quoted form
|
||||||
|
// (`.SH "SEE ALSO"`), so authors can use either convention.
|
||||||
|
func TestManPageHasMandatorySections(t *testing.T) {
|
||||||
|
for _, sec := range manPageRequiredSections {
|
||||||
|
bare := "\n.SH " + sec + "\n"
|
||||||
|
quoted := "\n.SH \"" + sec + "\"\n"
|
||||||
|
if !strings.Contains(manPage, bare) && !strings.Contains(manPage, quoted) {
|
||||||
|
t.Errorf("manpage is missing required section: .SH %s (or .SH \"%s\")", sec, sec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManPageDocumentsEveryFlag is a guard against drift between the
|
||||||
|
// CLI surface (defineFlags) and the manpage. For every flag
|
||||||
|
// registered on the FlagSet, the manpage must contain the
|
||||||
|
// corresponding option entry. The expected on-page form escapes
|
||||||
|
// every hyphen in the flag name as `\-` (per the convention used
|
||||||
|
// throughout the manpage) and prefixes the entry with `\-\-`.
|
||||||
|
//
|
||||||
|
// Adding a new flag in defineFlags but forgetting the manpage (or
|
||||||
|
// vice versa) makes this test fail with a clear missing-flag list.
|
||||||
|
func TestManPageDocumentsEveryFlag(t *testing.T) {
|
||||||
|
cfg := config.DefaultConfig()
|
||||||
|
fs := newTestFlagSet("goget-test-man-page")
|
||||||
|
defineFlags(fs, cfg)
|
||||||
|
|
||||||
|
var missing []string
|
||||||
|
fs.VisitAll(func(f *flag.Flag) {
|
||||||
|
escaped := strings.ReplaceAll(f.Name, "-", `\-`)
|
||||||
|
needle := `\-\-` + escaped
|
||||||
|
if !strings.Contains(manPage, needle) {
|
||||||
|
missing = append(missing, f.Name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if len(missing) > 0 {
|
||||||
|
sort.Strings(missing)
|
||||||
|
t.Errorf("manpage does not document %d flag(s):\n --%s",
|
||||||
|
len(missing), strings.Join(missing, "\n --"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManPageRendersWithGroff runs `groff -man -ww -Kutf-8 -z` on the
|
||||||
|
// embedded manpage and fails if it reports any warnings or errors.
|
||||||
|
// The `-z` flag suppresses output; `-ww` enables maximum warning
|
||||||
|
// verbosity; `-Kutf-8` accepts UTF-8 source (em-dash, etc.).
|
||||||
|
//
|
||||||
|
// If `groff` is not installed on the test machine the test is
|
||||||
|
// skipped — we still want it to pass on minimal CI images — but a
|
||||||
|
// CI image with `groff` available will catch regressions in the
|
||||||
|
// troff source.
|
||||||
|
func TestManPageRendersWithGroff(t *testing.T) {
|
||||||
|
if _, err := exec.LookPath("groff"); err != nil {
|
||||||
|
t.Skip("groff not installed; skipping manpage render check")
|
||||||
|
}
|
||||||
|
cmd := exec.Command("groff", "-man", "-ww", "-Kutf-8", "-z")
|
||||||
|
cmd.Stdin = strings.NewReader(manPage)
|
||||||
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
t.Fatalf("groff rejected the manpage: %v\n%s", err, out)
|
||||||
|
} else if len(out) > 0 {
|
||||||
|
t.Fatalf("groff produced unexpected output:\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstLine returns the first line of s, trimmed of the trailing
|
||||||
|
// newline. Used only for error messages.
|
||||||
|
func firstLine(s string) string {
|
||||||
|
if i := strings.IndexByte(s, '\n'); i >= 0 {
|
||||||
|
return s[:i]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
@@ -11,6 +11,10 @@ run:
|
|||||||
|
|
||||||
# Build the binary
|
# Build the binary
|
||||||
build:
|
build:
|
||||||
|
# Keep the embedded manpage in sync with man/goget.1. //go:embed
|
||||||
|
# cannot reference files outside the embedding package, so we copy
|
||||||
|
# the source-of-truth manpage into the package tree before compiling.
|
||||||
|
cp man/goget.1 cmd/goget/man-page-data/goget.1
|
||||||
go vet ./...
|
go vet ./...
|
||||||
gofmt -l $(find cmd internal pkg -name '*.go') | grep . && exit 1 || true
|
gofmt -l $(find cmd internal pkg -name '*.go') | grep . && exit 1 || true
|
||||||
go build -ldflags "-s -w" -o bin/goget ./cmd/goget
|
go build -ldflags "-s -w" -o bin/goget ./cmd/goget
|
||||||
@@ -22,3 +26,22 @@ test:
|
|||||||
# Remove build artifacts
|
# Remove build artifacts
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -rf bin/ coverage.out
|
rm -rf bin/ coverage.out
|
||||||
|
|
||||||
|
# Validate the manpage with groff (zero warnings on success). Requires
|
||||||
|
# `groff` to be installed — skipped silently otherwise.
|
||||||
|
man:
|
||||||
|
@command -v groff >/dev/null || { echo "groff not installed, skipping"; exit 0; }
|
||||||
|
groff -man -ww -Kutf-8 -z man/goget.1
|
||||||
|
|
||||||
|
# Install the manpage to ~/.local/share/man/man1/ (XDG rootless).
|
||||||
|
# After install, `man goget` resolves it automatically. Re-run safely to
|
||||||
|
# pick up edits.
|
||||||
|
install-man: man
|
||||||
|
@mkdir -p "$HOME/.local/share/man/man1"
|
||||||
|
gzip -c -f man/goget.1 > "$HOME/.local/share/man/man1/goget.1.gz"
|
||||||
|
@echo "Installed: $HOME/.local/share/man/man1/goget.1.gz"
|
||||||
|
|
||||||
|
# Remove the manpage installed by `just install-man`.
|
||||||
|
uninstall-man:
|
||||||
|
rm -f "$HOME/.local/share/man/man1/goget.1.gz"
|
||||||
|
@echo "Removed: $HOME/.local/share/man/man1/goget.1.gz"
|
||||||
|
|||||||
+950
@@ -0,0 +1,950 @@
|
|||||||
|
.\" Manpage for goget.
|
||||||
|
.\" Source of truth — kept in sync with cmd/goget/man-page-data/goget.1
|
||||||
|
.\" by the `cp` step in `just build`. The duplicate is required because
|
||||||
|
.\" //go:embed cannot reference files outside the embedding package.
|
||||||
|
.\"
|
||||||
|
.\" Conventions used in this file:
|
||||||
|
.\" .TP tag-paragraph (one option per entry)
|
||||||
|
.\" \fB...\fR bold (the flag itself)
|
||||||
|
.\" \fI...\fR italic (the argument placeholder)
|
||||||
|
.\" \- literal hyphen (escapes groff's dash-to-endash conversion)
|
||||||
|
.\" \\- alternative escape for the same purpose
|
||||||
|
.\"
|
||||||
|
.\" Validate with: groff -man -ww man/goget.1 > /dev/null
|
||||||
|
.\" Render with: groff -man -Tutf8 man/goget.1 | less
|
||||||
|
.\"
|
||||||
|
.TH GOGET 1 "2026-06-26" "goget 1.0.0" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
goget \- modern IPv6-first command-line download utility
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B goget
|
||||||
|
[\fIOPTION\fR]... \fIURL\fR...
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B goget
|
||||||
|
is a modern replacement for
|
||||||
|
.BR curl (1)
|
||||||
|
and
|
||||||
|
.BR wget (1)
|
||||||
|
built on the Go standard library.
|
||||||
|
It provides IPv6-first connections (with automatic IPv4 fallback), parallel
|
||||||
|
chunked downloads, resume support, checksum verification, recursive website
|
||||||
|
mirroring, and support for nine network protocols
|
||||||
|
.RB ( HTTP / HTTPS ", " FTP / FTPS ", " SFTP ", " WebDAV / WebDAVS ", "
|
||||||
|
.BR file:// ", " data: ", " Gopher ", and " Gemini ") \(em all in a
|
||||||
|
single static binary with zero runtime dependencies.
|
||||||
|
.PP
|
||||||
|
Safety features include TLS 1.2 minimum, certificate verification, SSRF
|
||||||
|
protection (private/internal addresses are blocked by default), SSH host-key
|
||||||
|
verification for SFTP, HSTS cache (RFC 6797), and certificate or host-key
|
||||||
|
pinning.
|
||||||
|
.PP
|
||||||
|
URLs are specified positionally after the flags; the
|
||||||
|
.B \-\-url
|
||||||
|
flag is an alias for the first positional URL and may be repeated to download
|
||||||
|
several files in one invocation.
|
||||||
|
.PP
|
||||||
|
Configuration is loaded from
|
||||||
|
.IR ~/.config/goget/config.toml
|
||||||
|
(overridable with
|
||||||
|
.BR \-\-config " or the " GOGET_CONFIG
|
||||||
|
environment variable) and merged on top of compiled-in defaults. Command-line
|
||||||
|
flags always take precedence over configuration values.
|
||||||
|
.SH OPTIONS
|
||||||
|
.SS "Target"
|
||||||
|
.TP
|
||||||
|
\fB\-\-url\fR=\fIURL\fR
|
||||||
|
URL to download. May be repeated for multiple URLs, or supplied as positional
|
||||||
|
arguments after the flags.
|
||||||
|
.SS "Output"
|
||||||
|
.TP
|
||||||
|
\fB\-\-output\fR=\fIFILE\fR
|
||||||
|
Output filename or directory. When omitted, the filename is derived from the
|
||||||
|
URL.
|
||||||
|
.TP
|
||||||
|
\fB\-\-restart\fR
|
||||||
|
Delete any existing output file and restart the download from byte zero.
|
||||||
|
.TP
|
||||||
|
\fB\-\-overwrite\fR
|
||||||
|
Overwrite an existing output file without using atomic rename.
|
||||||
|
.TP
|
||||||
|
\fB\-\-content\-disposition\fR
|
||||||
|
Use the filename from the server's
|
||||||
|
.B Content\-Disposition
|
||||||
|
header instead of the one derived from the URL.
|
||||||
|
.TP
|
||||||
|
\fB\-\-create\-dirs\fR
|
||||||
|
Create missing parent directories of
|
||||||
|
.BR \-\-output
|
||||||
|
(enabled by default; disable with
|
||||||
|
.B \-\-create\-dirs=false
|
||||||
|
for
|
||||||
|
.BR curl (1) " /" wget (1)
|
||||||
|
compatibility). Ignored for stdout output.
|
||||||
|
.TP
|
||||||
|
\fB\-\-keep\-timestamps\fR
|
||||||
|
Set the output file's modification time to the server's
|
||||||
|
.B Last\-Modified
|
||||||
|
value.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-clobber\fR
|
||||||
|
Skip the download if the output file already exists.
|
||||||
|
.TP
|
||||||
|
\fB\-\-continue\fR
|
||||||
|
Auto-resume the download if a partial output file is present.
|
||||||
|
.TP
|
||||||
|
\fB\-\-timestamping\fR
|
||||||
|
Download only when the remote file is newer than the local copy.
|
||||||
|
.SS "Progress and output"
|
||||||
|
.TP
|
||||||
|
\fB\-\-verbose\fR
|
||||||
|
Verbose output (default: enabled when set in the config file).
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-progress\fR
|
||||||
|
Hide the progress bar; keep every other piece of output.
|
||||||
|
.TP
|
||||||
|
\fB\-\-progress\fR=\fISTYLE\fR
|
||||||
|
Progress bar style. One of
|
||||||
|
.B bar
|
||||||
|
(default) or
|
||||||
|
.BR dot .
|
||||||
|
.TP
|
||||||
|
\fB\-\-quiet\fR
|
||||||
|
Suppress all output except errors.
|
||||||
|
.TP
|
||||||
|
\fB\-\-json\fR
|
||||||
|
Emit progress and completion events as newline-delimited JSON on stdout.
|
||||||
|
Each event is a single JSON object with
|
||||||
|
.BR type " (" progress " or " complete "), " downloaded ", " total ", " speed ,
|
||||||
|
and (for the
|
||||||
|
.B progress
|
||||||
|
event) an
|
||||||
|
.B eta_ms
|
||||||
|
field when the total size and speed are known.
|
||||||
|
.TP
|
||||||
|
\fB\-\-debug\fR
|
||||||
|
Enable debug output with detailed connection and protocol information.
|
||||||
|
.TP
|
||||||
|
\fB\-\-show\-headers\fR
|
||||||
|
Print HTTP response headers during the download.
|
||||||
|
.TP
|
||||||
|
\fB\-\-write\-out\fR=\fIFORMAT\fR
|
||||||
|
Print metadata after the transfer, using a
|
||||||
|
.BR curl (1)\-compatible
|
||||||
|
format string. Supported directives:
|
||||||
|
.BR %\{http_code\} ", " %\{size_download\} ", " %\{time_total\} ", "
|
||||||
|
.BR %\{speed_download\} .
|
||||||
|
.TP
|
||||||
|
\fB\-\-trace\-time\fR
|
||||||
|
Print an HTTP timing breakdown after the download:
|
||||||
|
.BR DNS ", " TCP ", " TLS ", " TTFB ", and " Total .
|
||||||
|
.SS "Network"
|
||||||
|
.TP
|
||||||
|
\fB\-\-timeout\fR=\fIDURATION\fR
|
||||||
|
Overall connection timeout.
|
||||||
|
.B 0
|
||||||
|
selects a smart auto-calculation based on the file size.
|
||||||
|
.TP
|
||||||
|
\fB\-\-connect\-timeout\fR=\fIDURATION\fR
|
||||||
|
Timeout for the TCP (or SSH) connection establishment phase.
|
||||||
|
.TP
|
||||||
|
\fB\-\-max\-time\fR=\fIDURATION\fR
|
||||||
|
Maximum total transfer time; abort if exceeded.
|
||||||
|
.B 0
|
||||||
|
disables the limit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-max\-retries\fR=\fIN\fR
|
||||||
|
Maximum number of retry attempts on failure.
|
||||||
|
.B 0
|
||||||
|
selects the built-in default.
|
||||||
|
.TP
|
||||||
|
\fB\-\-retry\-delay\fR=\fIDURATION\fR
|
||||||
|
Delay inserted between retry attempts.
|
||||||
|
.TP
|
||||||
|
\fB\-\-retry\-all\-errors\fR
|
||||||
|
Retry on any HTTP 4xx or 5xx response (in addition to network errors).
|
||||||
|
.TP
|
||||||
|
\fB\-\-retry\-on\-http\-error\fR=\fICODES\fR
|
||||||
|
Retry on the given HTTP status codes (comma-separated; e.g.
|
||||||
|
.BR 503,429 ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-max\-filesize\fR=\fISIZE\fR
|
||||||
|
Abort the transfer if the remote file exceeds this size (e.g.
|
||||||
|
.BR 100MB ", " 1GB ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-proxy\fR=\fIURL\fR
|
||||||
|
Proxy URL.
|
||||||
|
.B http://
|
||||||
|
and
|
||||||
|
.B https://
|
||||||
|
trigger HTTP CONNECT;
|
||||||
|
.B socks5://
|
||||||
|
uses local DNS,
|
||||||
|
.B socks5h://
|
||||||
|
uses remote DNS.
|
||||||
|
Optional
|
||||||
|
.B user:pass@
|
||||||
|
credentials are supported.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-ipv4\fR
|
||||||
|
Disable IPv4 fallback; connect over IPv6 only.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-redirect\fR
|
||||||
|
Do not follow HTTP 3xx redirects.
|
||||||
|
.TP
|
||||||
|
\fB\-\-dns\-servers\fR=\fIIPS\fR
|
||||||
|
Custom DNS servers, comma-separated (e.g.
|
||||||
|
.BR 1.1.1.1,8.8.8.8 ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-bind\-interface\fR=\fIIFACE\fR
|
||||||
|
Bind outgoing connections to a specific network interface (Linux only).
|
||||||
|
.TP
|
||||||
|
\fB\-\-allow\-private\fR
|
||||||
|
Allow connections to private or internal IP addresses. By default these
|
||||||
|
are blocked as an SSRF safeguard.
|
||||||
|
.TP
|
||||||
|
\fB\-\-pinned\-cert\fR=\fISHA256\fR
|
||||||
|
Pin the server certificate by its leaf SHA-256 fingerprint. Applies to all
|
||||||
|
TLS-bearing protocols (HTTPS, FTPS, Gemini, WebDAVS); abort on mismatch.
|
||||||
|
.TP
|
||||||
|
\fB\-\-pinned\-host\-key\fR=\fIFINGERPRINT\fR
|
||||||
|
Pin the SSH host key (SFTP). Accepts
|
||||||
|
.BR SHA256:<base64>
|
||||||
|
(as emitted by
|
||||||
|
.BR "ssh\-keygen \-lf" )
|
||||||
|
or a raw lowercase-hex SHA-256 of the key wire format. Pinning takes
|
||||||
|
precedence over
|
||||||
|
.B \-\-ssh\-insecure
|
||||||
|
and any
|
||||||
|
.I known_hosts
|
||||||
|
entry.
|
||||||
|
.TP
|
||||||
|
\fB\-\-rate\-limit\fR=\fIRATE\fR
|
||||||
|
Maximum download speed (e.g.
|
||||||
|
.BR 1MB/s ", " 500KB/s ).
|
||||||
|
.B 0
|
||||||
|
disables the limit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-max\-speed\fR=\fIRATE\fR
|
||||||
|
Alias for
|
||||||
|
.B \-\-rate\-limit
|
||||||
|
in the legacy "bytes per second" form.
|
||||||
|
.TP
|
||||||
|
\fB\-\-range\fR=\fIN\fB\-\fIN\fR
|
||||||
|
Download only a byte range (e.g.
|
||||||
|
.BR 0\-999 ).
|
||||||
|
.SS "HTTP and request shaping"
|
||||||
|
.TP
|
||||||
|
\fB\-\-header\fR=\fI"Key: Value"\fR
|
||||||
|
Custom HTTP header. May be repeated.
|
||||||
|
.TP
|
||||||
|
\fB\-\-insecure\fR
|
||||||
|
Skip TLS certificate verification. Use only for testing.
|
||||||
|
.TP
|
||||||
|
\fB\-\-cert\fR=\fIFILE\fR
|
||||||
|
Client certificate in PEM format (for mTLS).
|
||||||
|
.TP
|
||||||
|
\fB\-\-key\fR=\fIFILE\fR
|
||||||
|
Client private key in PEM format (for mTLS).
|
||||||
|
.TP
|
||||||
|
\fB\-\-ca\-certificate\fR=\fIFILE\fR
|
||||||
|
Path to a custom CA certificate bundle.
|
||||||
|
.TP
|
||||||
|
\fB\-\-ssl\-key\-log\fR=\fIFILE\fR
|
||||||
|
Log TLS session keys to
|
||||||
|
.I FILE
|
||||||
|
in the NSS Key Log format used by Wireshark
|
||||||
|
.RB ( SSLKEYLOGFILE ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-data\fR=\fI"key=value"\fR
|
||||||
|
Send
|
||||||
|
.I key=value
|
||||||
|
as the request body. Implies HTTP POST.
|
||||||
|
.TP
|
||||||
|
\fB\-\-form\fR=\fI"field=value"\fR
|
||||||
|
Multipart form field; repeatable. A value of
|
||||||
|
.B field=@/path
|
||||||
|
attaches a file upload.
|
||||||
|
.TP
|
||||||
|
\fB\-\-post\-file\fR=\fIFILE\fR
|
||||||
|
POST the contents of
|
||||||
|
.I FILE
|
||||||
|
as the request body.
|
||||||
|
.TP
|
||||||
|
\fB\-\-referer\fR=\fIURL\fR
|
||||||
|
Set the
|
||||||
|
.B Referer
|
||||||
|
request header.
|
||||||
|
.TP
|
||||||
|
\fB\-\-compressed\fR
|
||||||
|
Send an
|
||||||
|
.B Accept\-Encoding
|
||||||
|
header so the server returns a compressed response. By default
|
||||||
|
.B goget
|
||||||
|
already handles compressed responses transparently; this flag is provided
|
||||||
|
for
|
||||||
|
.BR curl (1)
|
||||||
|
compatibility.
|
||||||
|
.TP
|
||||||
|
\fB\-\-spider\fR
|
||||||
|
Check whether the URL exists via a HEAD request; do not download the body.
|
||||||
|
.TP
|
||||||
|
\fB\-\-fail\fR
|
||||||
|
Exit with a non-zero status on HTTP 4xx or 5xx responses.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-decompress\fR
|
||||||
|
Disable automatic decompression of compressed response bodies.
|
||||||
|
.SS "Recursive downloading and mirroring"
|
||||||
|
.TP
|
||||||
|
\fB\-\-recursive\fR
|
||||||
|
Follow links discovered in HTML pages.
|
||||||
|
.TP
|
||||||
|
\fB\-\-mirror\fR
|
||||||
|
Mirror an entire website. Shorthand for
|
||||||
|
.BR "\-\-recursive \-\-convert\-links \-\-page\-requisites" .
|
||||||
|
.TP
|
||||||
|
\fB\-\-max\-depth\fR=\fIN\fR
|
||||||
|
Maximum recursion depth.
|
||||||
|
.TP
|
||||||
|
\fB\-\-recursive\-parallel\fR=\fIN\fR
|
||||||
|
Number of concurrent file downloads in recursive mode across all protocols
|
||||||
|
(HTTP, WebDAV, FTP, SFTP).
|
||||||
|
.B 0
|
||||||
|
runs sequentially (default).
|
||||||
|
.TP
|
||||||
|
\fB\-\-follow\-external\fR
|
||||||
|
Follow links to domains other than the starting domain.
|
||||||
|
.TP
|
||||||
|
\fB\-\-span\-hosts\fR
|
||||||
|
Alias for
|
||||||
|
.B \-\-follow\-external
|
||||||
|
in the
|
||||||
|
.BR wget (1)
|
||||||
|
style.
|
||||||
|
.TP
|
||||||
|
\fB\-\-domains\fR=\fILIST\fR
|
||||||
|
Restrict recursive crawling to the listed comma-separated domains.
|
||||||
|
.TP
|
||||||
|
\fB\-\-accept\fR=\fIPATTERN\fR
|
||||||
|
Accept patterns (filename glob or MIME type), comma-separated. Only
|
||||||
|
matching resources are downloaded.
|
||||||
|
.TP
|
||||||
|
\fB\-\-reject\fR=\fIPATTERN\fR
|
||||||
|
Reject patterns (glob), comma-separated. Matching resources are skipped.
|
||||||
|
.TP
|
||||||
|
\fB\-\-exclude\-pattern\fR=\fIPATTERN\fR
|
||||||
|
Alias for
|
||||||
|
.B \-\-reject
|
||||||
|
used by the recursive crawler.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-parent\fR
|
||||||
|
Do not ascend to parent directories during recursive download.
|
||||||
|
.TP
|
||||||
|
\fB\-\-page\-requisites\fR
|
||||||
|
Download CSS, JavaScript, and images required to render the HTML pages.
|
||||||
|
.TP
|
||||||
|
\fB\-\-convert\-links\fR
|
||||||
|
Rewrite links in downloaded HTML for local offline viewing.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-convert\-links\fR
|
||||||
|
Skip HTML link rewriting (faster; output is not offline-ready).
|
||||||
|
.TP
|
||||||
|
\fB\-\-backup\-converted\fR
|
||||||
|
Keep a
|
||||||
|
.B .orig
|
||||||
|
backup of each file before link conversion.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-mirror\-assets\fR
|
||||||
|
Do not download CSS, JavaScript, and images during a mirror.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-robots\fR
|
||||||
|
Do not honour
|
||||||
|
.I robots.txt
|
||||||
|
during mirroring.
|
||||||
|
.TP
|
||||||
|
\fB\-\-wait\fR=\fIDURATION\fR
|
||||||
|
Delay between requests in recursive or mirror mode (e.g.
|
||||||
|
.BR 1s ", " 500ms ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-random\-wait\fR
|
||||||
|
Randomize the wait time between 0.5x and 1.5x of
|
||||||
|
.BR \-\-wait .
|
||||||
|
.TP
|
||||||
|
\fB\-\-adjust\-extension\fR
|
||||||
|
Append a
|
||||||
|
.B .html
|
||||||
|
extension to text/html files that lack one.
|
||||||
|
.TP
|
||||||
|
\fB\-\-cut\-dirs\fR=\fIN\fR
|
||||||
|
Ignore the first
|
||||||
|
.I N
|
||||||
|
path components when constructing local filenames.
|
||||||
|
.TP
|
||||||
|
\fB\-\-protocol\-directories\fR
|
||||||
|
Create protocol-prefixed subdirectories
|
||||||
|
.RB ( .http/ ", " .ftp/ )
|
||||||
|
in recursive mode.
|
||||||
|
.TP
|
||||||
|
\fB\-\-dry\-run\fR
|
||||||
|
List the URLs that would be downloaded in recursive or mirror mode without
|
||||||
|
writing any files to disk.
|
||||||
|
.TP
|
||||||
|
\fB\-\-warc\-file\fR=\fIFILE\fR
|
||||||
|
Write a WARC (Web ARChive) record of the transfer to
|
||||||
|
.IR FILE .
|
||||||
|
.SS "Download management"
|
||||||
|
.TP
|
||||||
|
\fB\-\-batch\-file\fR=\fIFILE\fR
|
||||||
|
Read URLs from
|
||||||
|
.IR FILE ,
|
||||||
|
one per line. Lines beginning with
|
||||||
|
.B #
|
||||||
|
are treated as comments and skipped.
|
||||||
|
.TP
|
||||||
|
\fB\-\-input\-file\fR=\fIFILE\fR
|
||||||
|
Alias for
|
||||||
|
.BR \-\-batch\-file .
|
||||||
|
A value of
|
||||||
|
.B \-
|
||||||
|
reads from standard input.
|
||||||
|
.TP
|
||||||
|
\fB\-\-glob\fR
|
||||||
|
Expand
|
||||||
|
.B [1-3]
|
||||||
|
and
|
||||||
|
.B {a,b,c}
|
||||||
|
patterns in the URL before issuing requests.
|
||||||
|
.TP
|
||||||
|
\fB\-\-queue\fR
|
||||||
|
Add the URL to the persistent download queue instead of downloading it
|
||||||
|
immediately.
|
||||||
|
.TP
|
||||||
|
\fB\-\-queue\-list\fR
|
||||||
|
List all items in the download queue and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-queue\-process\fR
|
||||||
|
Process (download) every pending item in the queue and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-queue\-clear\fR
|
||||||
|
Remove completed items from the queue.
|
||||||
|
.TP
|
||||||
|
\fB\-\-queue\-file\fR=\fIPATH\fR
|
||||||
|
Use a custom queue file (default
|
||||||
|
.IR ~/.config/goget/queue.json ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-queue\-priority\fR=\fIN\fR
|
||||||
|
Priority for the queued item (1\(en10; higher values are processed first).
|
||||||
|
Default: 5.
|
||||||
|
.TP
|
||||||
|
\fB\-\-schedule\fR=\fITIME\fR
|
||||||
|
Schedule the download. Accepts an absolute timestamp
|
||||||
|
.RB ( " YYYY\-MM\-DD HH:MM ")
|
||||||
|
or a cron expression in the standard
|
||||||
|
.B MIN HOUR DOM MON DOW
|
||||||
|
format (e.g.
|
||||||
|
.BR "0 2 * * *" ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-on\-complete\fR=\fICMD\fR
|
||||||
|
Run
|
||||||
|
.I CMD
|
||||||
|
after a successful download. The following environment variables are
|
||||||
|
exported into the hook:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
.B GOGET_OUTPUT
|
||||||
|
Absolute path of the downloaded file.
|
||||||
|
.TP
|
||||||
|
.B GOGET_SIZE
|
||||||
|
Downloaded size in bytes.
|
||||||
|
.TP
|
||||||
|
.B GOGET_URL
|
||||||
|
The (credential-stripped) source URL.
|
||||||
|
.RE
|
||||||
|
.IP
|
||||||
|
The command is split into tokens with shell-like quoting rules but is
|
||||||
|
executed directly via
|
||||||
|
.BR exec (3)
|
||||||
|
\(em no shell is invoked, so metacharacters in arguments are not
|
||||||
|
re-interpreted.
|
||||||
|
.SS "Authentication"
|
||||||
|
.TP
|
||||||
|
\fB\-\-username\fR=\fIUSER\fR
|
||||||
|
Username for HTTP Basic or Digest authentication. When omitted, credentials
|
||||||
|
are auto-loaded from
|
||||||
|
.I ~/.netrc
|
||||||
|
if a matching machine entry is present.
|
||||||
|
.TP
|
||||||
|
\fB\-\-password\fR=\fIPASS\fR
|
||||||
|
Password for HTTP Basic or Digest authentication. Prefer
|
||||||
|
.B \-\-password\-file
|
||||||
|
to avoid exposing the secret on the command line.
|
||||||
|
.TP
|
||||||
|
\fB\-\-password\-file\fR=\fIFILE\fR
|
||||||
|
Read the password from
|
||||||
|
.I FILE
|
||||||
|
(the first line, with the trailing newline stripped).
|
||||||
|
.TP
|
||||||
|
\fB\-\-auth\-type\fR=\fITYPE\fR
|
||||||
|
Authentication scheme:
|
||||||
|
.BR basic ", " digest ", or " auto
|
||||||
|
(default).
|
||||||
|
.TP
|
||||||
|
\fB\-\-cookie\-jar\fR=\fIFILE\fR
|
||||||
|
Load cookies from and save cookies to
|
||||||
|
.I FILE
|
||||||
|
in Netscape format.
|
||||||
|
.TP
|
||||||
|
\fB\-\-cookie\fR=\fI"name=value"\fR
|
||||||
|
Set a cookie on the request. May be repeated.
|
||||||
|
.TP
|
||||||
|
\fB\-\-no\-private\fR
|
||||||
|
Disable
|
||||||
|
.I .netrc
|
||||||
|
and other credential-file auto-loading for this invocation.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-client\-id\fR=\fIID\fR
|
||||||
|
OAuth 2.0 client ID.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-client\-secret\fR=\fISECRET\fR
|
||||||
|
OAuth 2.0 client secret.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-token\-url\fR=\fIURL\fR
|
||||||
|
OAuth 2.0 token endpoint.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-auth\-url\fR=\fIURL\fR
|
||||||
|
OAuth 2.0 authorization endpoint (used by the
|
||||||
|
.B authorization_code
|
||||||
|
grant).
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-redirect\-uri\fR=\fIURI\fR
|
||||||
|
OAuth 2.0 redirect URI.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-scopes\fR=\fISCOPES\fR
|
||||||
|
OAuth 2.0 scopes, comma-separated.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-grant\-type\fR=\fITYPE\fR
|
||||||
|
OAuth 2.0 grant type:
|
||||||
|
.BR client_credentials ", " authorization_code ", " password ", or "
|
||||||
|
.BR refresh_token .
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-access\-token\fR=\fITOKEN\fR
|
||||||
|
OAuth 2.0 access token supplied directly.
|
||||||
|
.TP
|
||||||
|
\fB\-\-oauth\-refresh\-token\fR=\fITOKEN\fR
|
||||||
|
OAuth 2.0 refresh token used to obtain a new access token.
|
||||||
|
.SS "Upload"
|
||||||
|
.TP
|
||||||
|
\fB\-\-upload\fR
|
||||||
|
Upload the file in
|
||||||
|
.B \-\-upload\-file
|
||||||
|
instead of downloading.
|
||||||
|
.TP
|
||||||
|
\fB\-\-upload\-method\fR=\fIMETHOD\fR
|
||||||
|
HTTP method for the upload:
|
||||||
|
.B PUT
|
||||||
|
(default) or
|
||||||
|
.BR POST .
|
||||||
|
.TP
|
||||||
|
\fB\-\-upload\-file\fR=\fIFILE\fR
|
||||||
|
Path to the file to upload.
|
||||||
|
.TP
|
||||||
|
\fB\-\-form\-data\fR=\fI"k=v"\fR
|
||||||
|
Additional multipart form data, comma-separated.
|
||||||
|
.TP
|
||||||
|
\fB\-\-file\-field\fR=\fINAME\fR
|
||||||
|
Form field name used for the file part. Default:
|
||||||
|
.BR file .
|
||||||
|
.SS "Verification"
|
||||||
|
.TP
|
||||||
|
\fB\-\-checksum\fR=\fIHASH\fR
|
||||||
|
Expected checksum, as a hex string.
|
||||||
|
.TP
|
||||||
|
\fB\-\-checksum\-algo\fR=\fIALG\fR
|
||||||
|
Checksum algorithm:
|
||||||
|
.BR sha256 ", " sha512 ", " blake2b ", " sha3\-256 ", " sha3\-512 ", or " md5 .
|
||||||
|
Default:
|
||||||
|
.BR sha256 .
|
||||||
|
.TP
|
||||||
|
\fB\-\-checksum\-file\fR=\fIFILE\fR
|
||||||
|
Read expected checksums from
|
||||||
|
.I FILE
|
||||||
|
in the standard
|
||||||
|
.B SHA256SUMS
|
||||||
|
format.
|
||||||
|
.SS "PGP / OpenPGP"
|
||||||
|
.TP
|
||||||
|
\fB\-\-pgp\-verify\fR
|
||||||
|
Verify a PGP detached or inline signature after download.
|
||||||
|
.TP
|
||||||
|
\fB\-\-pgp\-decrypt\fR
|
||||||
|
Decrypt a PGP-encrypted file after download.
|
||||||
|
.TP
|
||||||
|
\fB\-\-pgp\-sig\fR=\fIFILE\fR
|
||||||
|
Path to a detached signature file (e.g.
|
||||||
|
.BR .asc ", " .sig ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-pgp\-key\fR=\fIFILE\fR
|
||||||
|
Path to a PGP key (public or private, depending on the operation).
|
||||||
|
.TP
|
||||||
|
\fB\-\-pgp\-passphrase\fR=\fIPASS\fR
|
||||||
|
Passphrase for the private key.
|
||||||
|
.TP
|
||||||
|
\fB\-\-pgp\-passphrase\-file\fR=\fIFILE\fR
|
||||||
|
Read the private-key passphrase from
|
||||||
|
.IR FILE .
|
||||||
|
.SS "Metalink"
|
||||||
|
.TP
|
||||||
|
\fB\-\-metalink\fR
|
||||||
|
Treat the URL as a Metalink file (multi-source download).
|
||||||
|
.TP
|
||||||
|
\fB\-\-metalink\-file\fR=\fIPATH\fR
|
||||||
|
Read a Metalink description from
|
||||||
|
.IR PATH .
|
||||||
|
Accepts
|
||||||
|
.B .meta4
|
||||||
|
and
|
||||||
|
.B .metalink
|
||||||
|
formats.
|
||||||
|
.SS "TLS and SSH"
|
||||||
|
.TP
|
||||||
|
\fB\-\-known\-hosts\fR=\fIFILE\fR
|
||||||
|
Use a custom
|
||||||
|
.I known_hosts
|
||||||
|
file for SFTP (default
|
||||||
|
.IR ~/.ssh/known_hosts ).
|
||||||
|
.TP
|
||||||
|
\fB\-\-ssh\-insecure\fR
|
||||||
|
Skip SSH host key verification for SFTP (accept any key). Mutually
|
||||||
|
incompatible with
|
||||||
|
.BR \-\-pinned\-host\-key .
|
||||||
|
.TP
|
||||||
|
\fB\-\-hsts\-file\fR=\fIFILE\fR
|
||||||
|
Use a custom HSTS cache file (default
|
||||||
|
.IR ~/.config/goget/hsts ).
|
||||||
|
.SS "Archive extraction"
|
||||||
|
.TP
|
||||||
|
\fB\-\-extract\fR
|
||||||
|
Automatically extract recognized archives (TAR, TAR.GZ, TAR.BZ2, ZIP)
|
||||||
|
after a successful download.
|
||||||
|
.TP
|
||||||
|
\fB\-\-extract\-dir\fR=\fIDIR\fR
|
||||||
|
Directory to extract into. Default: alongside the downloaded archive.
|
||||||
|
.TP
|
||||||
|
\fB\-\-strip\-components\fR=\fIN\fR
|
||||||
|
Strip the first
|
||||||
|
.I N
|
||||||
|
path components during archive extraction.
|
||||||
|
.SS "Configuration"
|
||||||
|
.TP
|
||||||
|
\fB\-\-config\fR=\fIFILE\fR
|
||||||
|
Path to the TOML configuration file.
|
||||||
|
.TP
|
||||||
|
\fB\-\-init\-config\fR
|
||||||
|
Write a default configuration file to
|
||||||
|
.I ~/.config/goget/config.toml
|
||||||
|
and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-config\-get\fR=\fIKEY\fR
|
||||||
|
Print the value of a single configuration key (e.g.
|
||||||
|
.BR \-\-config\-get timeout )
|
||||||
|
and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-config\-set\fR=\fIKEY\fR \fIVALUE\fR
|
||||||
|
Persist
|
||||||
|
.I VALUE
|
||||||
|
under
|
||||||
|
.I KEY
|
||||||
|
in the configuration file and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-config\-list\fR
|
||||||
|
Print every configuration key and value and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-config\-unset\fR=\fIKEY\fR
|
||||||
|
Remove
|
||||||
|
.I KEY
|
||||||
|
from the configuration file and exit.
|
||||||
|
.SS "Information and shell integration"
|
||||||
|
.TP
|
||||||
|
\fB\-\-info\fR=\fIURL\fR
|
||||||
|
Show file metadata (size, content type, server, last-modified) without
|
||||||
|
downloading the body.
|
||||||
|
.TP
|
||||||
|
\fB\-\-benchmark\fR=\fIURL\fR
|
||||||
|
Benchmark the download speed by transferring the first 10 MB and reporting
|
||||||
|
the average throughput.
|
||||||
|
.TP
|
||||||
|
\fB\-\-show\-metadata\fR=\fIPATH\fR
|
||||||
|
Display the contents of a
|
||||||
|
.B .goget.meta
|
||||||
|
sidecar for debugging interrupted or resumed downloads. Accepts either a
|
||||||
|
path to the downloaded file (the sidecar is located next to it) or a direct
|
||||||
|
path to the sidecar. Renders a human-readable summary by default; combines
|
||||||
|
with
|
||||||
|
.B \-\-json
|
||||||
|
for raw output. Credentials embedded in the stored URL are masked.
|
||||||
|
.TP
|
||||||
|
\fB\-\-generate\-man\-page\fR
|
||||||
|
Print this manual page in troff format to standard output and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-completion\fR=\fISHELL\fR
|
||||||
|
Print a shell completion script for
|
||||||
|
.IR SHELL \(lqbbash\(rqq, \(rqqzsh\(rqq, or \(rqqfish\(rqq\(en to standard output and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-help\fR
|
||||||
|
Print the short help summary and exit.
|
||||||
|
.TP
|
||||||
|
\fB\-\-version\fR
|
||||||
|
Print the program name and version and exit.
|
||||||
|
.SH EXAMPLES
|
||||||
|
.SS "Simple download"
|
||||||
|
.PP
|
||||||
|
Download a single file to the current directory:
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-url https://example.com/file.zip
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SS "Parallel chunks and resume"
|
||||||
|
.PP
|
||||||
|
Files larger than 100 MB are automatically split into parallel chunks. To
|
||||||
|
force four connections explicitly and resume an interrupted transfer:
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-url https://example.com/large.iso \-\-parallel 4
|
||||||
|
goget \-\-url https://example.com/file.zip \-\-resume
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SS "Verification"
|
||||||
|
.PP
|
||||||
|
Verify a download against a known checksum and PGP signature:
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-url https://example.com/release.tar.gz \\
|
||||||
|
\-\-checksum\-algo sha256 \-\-checksum 5b9d...e2 \\
|
||||||
|
\-\-pgp\-verify \-\-pgp\-sig release.tar.gz.asc \\
|
||||||
|
\-\-pgp\-key maintainer.asc
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SS "Recursive mirror"
|
||||||
|
.PP
|
||||||
|
Mirror a documentation site, convert links for offline use, and respect
|
||||||
|
.IR robots.txt :
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-url https://example.com/docs/ \\
|
||||||
|
\-\-mirror \-\-convert\-links \-\-output ./mirror
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SS "Scripting with JSON progress"
|
||||||
|
.PP
|
||||||
|
Stream machine-readable progress to a file for a downstream tool:
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-url https://example.com/data.csv \\
|
||||||
|
\-\-json \-\-no\-progress > progress.jsonl
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SS "Scheduled download with a post-hook"
|
||||||
|
.PP
|
||||||
|
Fetch a daily report at 02:00 and feed it into a processor:
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-url https://example.com/daily.zip \\
|
||||||
|
\-\-schedule "0 2 * * *" \\
|
||||||
|
\-\-on\-complete "import.sh"
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SS "Batch file"
|
||||||
|
.PP
|
||||||
|
Read URLs from a file (one per line; lines starting with
|
||||||
|
.B #
|
||||||
|
are comments):
|
||||||
|
.PP
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
goget \-\-batch\-file urls.txt
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
.SH EXIT STATUS
|
||||||
|
.TP
|
||||||
|
.B 0
|
||||||
|
Success.
|
||||||
|
.TP
|
||||||
|
.B 1
|
||||||
|
Generic failure (network error, protocol error, invalid input).
|
||||||
|
.TP
|
||||||
|
.B 2
|
||||||
|
Invalid command-line arguments or configuration.
|
||||||
|
.TP
|
||||||
|
.B 3
|
||||||
|
File I/O error (cannot write output, cannot read source).
|
||||||
|
.TP
|
||||||
|
.B 4
|
||||||
|
Authentication failure.
|
||||||
|
.TP
|
||||||
|
.B 5
|
||||||
|
TLS or certificate error (including pinning mismatch).
|
||||||
|
.TP
|
||||||
|
.B 6
|
||||||
|
Checksum or PGP verification failed.
|
||||||
|
.TP
|
||||||
|
.B 7
|
||||||
|
Operation cancelled (SIGINT, SIGTERM, context cancellation).
|
||||||
|
.TP
|
||||||
|
.B 8
|
||||||
|
Resource limit exceeded
|
||||||
|
.RB ( \-\-max\-filesize ", " \-\-max\-time ", etc.).
|
||||||
|
.PP
|
||||||
|
Only the generic
|
||||||
|
.B 0
|
||||||
|
and
|
||||||
|
.B 1
|
||||||
|
codes are guaranteed; the project reserves the other values and their
|
||||||
|
meaning is part of the stable interface.
|
||||||
|
.SH FILES
|
||||||
|
.TP
|
||||||
|
.I ~/.config/goget/config.toml
|
||||||
|
Default configuration file. Overridden by
|
||||||
|
.B \-\-config
|
||||||
|
or the
|
||||||
|
.B GOGET_CONFIG
|
||||||
|
environment variable.
|
||||||
|
.TP
|
||||||
|
.I ~/.config/goget/queue.json
|
||||||
|
Persistent download queue. Path can be overridden with
|
||||||
|
.BR \-\-queue\-file .
|
||||||
|
.TP
|
||||||
|
.I ~/.config/goget/hsts
|
||||||
|
HSTS cache (RFC 6797). Path can be overridden with
|
||||||
|
.BR \-\-hsts\-file .
|
||||||
|
.TP
|
||||||
|
.I ~/.ssh/known_hosts
|
||||||
|
Default SFTP host-key database. Path can be overridden with
|
||||||
|
.BR \-\-known\-hosts .
|
||||||
|
.TP
|
||||||
|
.I <output>.goget.meta
|
||||||
|
Resume metadata sidecar written next to a partial output file. Deleted on
|
||||||
|
successful completion; consulted by
|
||||||
|
.B \-\-resume
|
||||||
|
or
|
||||||
|
.BR \-\-continue .
|
||||||
|
.TP
|
||||||
|
.I ~/.netrc
|
||||||
|
Auto-loaded credentials. Disabled for the current invocation by
|
||||||
|
.BR \-\-no\-private .
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.TP
|
||||||
|
.B GOGET_CONFIG
|
||||||
|
Path to the configuration file. Takes precedence over
|
||||||
|
.IR ~/.config/goget/config.toml
|
||||||
|
and
|
||||||
|
.BR \-\-config .
|
||||||
|
.TP
|
||||||
|
.B NO_COLOR
|
||||||
|
When set to a non-empty value, colour output is disabled regardless of the
|
||||||
|
.BR color " setting in the config file (see " https://no\-color.org )."
|
||||||
|
.TP
|
||||||
|
.B SSLKEYLOGFILE
|
||||||
|
Equivalent to passing
|
||||||
|
.B \-\-ssl\-key\-log
|
||||||
|
with the same value. Used by Wireshark to decrypt TLS traffic.
|
||||||
|
.TP
|
||||||
|
.B GOGET_OUTPUT
|
||||||
|
Exported into the environment of
|
||||||
|
.B \-\-on\-complete
|
||||||
|
hooks; contains the absolute path of the downloaded file.
|
||||||
|
.TP
|
||||||
|
.B GOGET_SIZE
|
||||||
|
Exported into
|
||||||
|
.B \-\-on\-complete
|
||||||
|
hooks; contains the downloaded size in bytes.
|
||||||
|
.TP
|
||||||
|
.B GOGET_URL
|
||||||
|
Exported into
|
||||||
|
.B \-\-on\-complete
|
||||||
|
hooks; contains the source URL with credentials stripped
|
||||||
|
.RB ( core.SafeURL ).
|
||||||
|
.TP
|
||||||
|
.B HOME
|
||||||
|
Used to locate
|
||||||
|
.I ~/.config/goget/
|
||||||
|
and
|
||||||
|
.IR ~/.ssh/known_hosts .
|
||||||
|
.TP
|
||||||
|
.B HTTP_PROXY
|
||||||
|
.IR "https_proxy" ", " ALL_PROXY ", " NO_PROXY
|
||||||
|
Standard proxy environment variables; respected unless
|
||||||
|
.B \-\-proxy
|
||||||
|
is given explicitly.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR curl (1),
|
||||||
|
.BR wget (1),
|
||||||
|
.BR sftp (1),
|
||||||
|
.BR ssh\-keygen (1),
|
||||||
|
.BR groff (1),
|
||||||
|
.BR man (1)
|
||||||
|
.PP
|
||||||
|
Project home page and issue tracker:
|
||||||
|
.RI < https://codeberg.org/petrbalvin/goget >
|
||||||
|
.PP
|
||||||
|
Full documentation set:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
.I docs/configuration.md
|
||||||
|
Configuration schema.
|
||||||
|
.TP
|
||||||
|
.I docs/cli-reference.md
|
||||||
|
Full flag reference.
|
||||||
|
.TP
|
||||||
|
.I docs/protocols.md
|
||||||
|
Protocol handler details.
|
||||||
|
.TP
|
||||||
|
.I docs/recursive-mirror.md
|
||||||
|
Recursive and mirror behaviour.
|
||||||
|
.TP
|
||||||
|
.I docs/security.md
|
||||||
|
Threat model and security controls.
|
||||||
|
.TP
|
||||||
|
.I docs/api.md
|
||||||
|
Public Go library API.
|
||||||
|
.RE
|
||||||
|
.SH HISTORY
|
||||||
|
goget 1.0.0 (2026-06-26) is the first public release.
|
||||||
|
The project was created as a modern, IPv6-first, Go-stdlib-only replacement
|
||||||
|
for
|
||||||
|
.BR curl (1)
|
||||||
|
and
|
||||||
|
.BR wget (1).
|
||||||
|
.SH AUTHORS
|
||||||
|
Petr Balvín
|
||||||
|
.RI < opensource@petrbalvin.org >
|
||||||
|
\(em upstream maintainer.
|
||||||
|
.PP
|
||||||
|
See
|
||||||
|
.RI < https://codeberg.org/petrbalvin/goget >
|
||||||
|
for the full contributor list.
|
||||||
|
.SH "REPORTING BUGS"
|
||||||
|
Report bugs to the issue tracker at
|
||||||
|
.RI < https://codeberg.org/petrbalvin/goget/issues >.
|
||||||
|
.PP
|
||||||
|
For security issues, do not open a public issue; email
|
||||||
|
.RI < opensource@petrbalvin.org >
|
||||||
|
instead.
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright \(co 2026 Petr Balvín.
|
||||||
|
.PP
|
||||||
|
Licensed under the MIT License.
|
||||||
|
Permission is granted to use, copy, modify, and distribute this software
|
||||||
|
without restriction, provided that the above copyright notice and this
|
||||||
|
permission notice appear in all copies.
|
||||||
|
.PP
|
||||||
|
.B goget
|
||||||
|
is provided
|
||||||
|
.B "AS IS"
|
||||||
|
without any warranty of any kind. See the MIT License text distributed with
|
||||||
|
the source for the full disclaimer.
|
||||||
Reference in New Issue
Block a user