• v1.1.0 b5fa009ec5

    v1.1.0 Stable

    petrbalvin released this 2026-06-30 19:46:23 +00:00 | 0 commits to main since this release

    Added

    Documentation

    • Standalone troff manpageman/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

    • Unified worker pool — single protocol-agnostic worker pool (internal/pool)
      used by FTP, SFTP, and WebDAV recursive-parallel downloads. Replaces three
      near-identical semaphore+WaitGroup implementations with a shared, tested
      concurrency primitive.
    • Graceful shutdown on SIGINT — persist .goget.meta sidecar on interrupt
      for SFTP single-file downloads, ensuring progress is saved for resume.
    • Gemini resume & metadata — Gemini downloads now persist .goget.meta
      sidecar on interrupt and delete it on successful completion. --resume
      continues interrupted Gemini transfers from the saved byte offset.
    • Gopher resume & metadata — Gopher typeHTML and typeTextFile
      responses now persist .goget.meta sidecar on interrupt and delete it on
      successful completion. --resume continues interrupted Gopher text
      transfers, skipping already-written output bytes (post-transform length
      because Gopher line metadata is stripped before writing).

    Security

    • Certificate pinning across all TLS protocols--pinned-cert now
      applies to FTPS, Gemini, and WebDAVS in addition to HTTP/HTTPS. The leaf
      certificate SHA-256 hash is verified via tls.Config.VerifyPeerCertificate
      on every TLS-bearing protocol.
    • SSH host-key pinning for SFTP — new --pinned-host-key flag pins the
      SSH host key by its OpenSSH fingerprint (SHA256:<base64>, as produced by
      ssh-keygen -lf). Raw lowercase-hex SHA-256 of the key wire format is also
      accepted. Pinning takes precedence over --ssh-insecure and known_hosts.
    • WebDAV TLS config fixwebdav.Protocol.getHTTPTransport now builds
      its tls.Config from the caller-provided transport.TLSConfig instead of
      a fresh config that silently dropped CA certs, mTLS, keylog, and pinned-cert
      settings.

    Recursive downloading & mirroring

    • --adjust-extension extended to non-HTTP protocols — applies .html
      extension to text/html files downloaded via Gopher and WebDAV.
    • --backup-converted — keep .orig backups of files before link conversion
      during recursive mirroring. Previously only available for HTTP/HTTPS;
      now works for WebDAV mirrors too.

    Download management

    • --show-metadata — displays .goget.meta sidecar contents for debugging
      interrupted / resumed downloads. Accepts a path to the downloaded output
      (the sidecar is located next to it) or a direct path to the sidecar file.
      Renders a human-readable summary by default and switches to raw JSON
      output when combined with --json. Credentials embedded in the stored
      URL are masked before display (the password placeholder is xxxxx).

    Fixed

    • Gopher: ctx.Done() cancellation goroutine leak — the goroutine
      spawned in Download to close the socket on ctx.Done() previously
      waited indefinitely on the parent context, leaking every time the
      context outlived the function. Now bounded by a defer-closed
      done channel so the goroutine exits on every return path.

    • Gemini: ctx.Done() cancellation goroutine leak — same bug,
      same fix in downloadWithRedirects.

    • Gopher: partial state lost on cancel-then-close — when the server
      closed the connection after ctx was cancelled, downloadText
      treated the resulting EOF as a successful end-of-stream and never
      persisted .goget.meta. Mirrors the equivalent Gemini behavior
      (already present from the Gemini resume feature): if ctx.Err() != nil,
      save partial state and return the context error.

    • Gopher: --resume ignored when caller supplies Writer — when
      --resume was set together with a caller-supplied Writer, the
      partial-state sidecar would track bytes that landed in the Writer
      rather than Output. --resume is now silently disabled in that
      case (with a --verbose warning), keeping metadata consistent with
      the actual on-disk state.

    • Gemini: --resume ignored when caller supplies Writer — same
      fix applied to downloadWithRedirects.

    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.
    • Resume tests deterministic across the protocol stack
      (TestGeminiResume{SaveMetadata,Download} and
      TestGopherResume{SaveMetadata,Download}) — the four tests previously
      relied on a fixed time.Sleep before calling cancel(), making them
      flaky on slow CI schedulers. They now wait for a ProgressCallback
      signal, which fires synchronously after the first writer.Write
      lands on disk.
    • Goroutine leak regression suite
      (TestGeminiCancelGoroutineExits, TestGopherCancelGoroutineExits)
      — runs 50 downloads back-to-back, settles the runtime via
      runtime.GC(), and asserts runtime.NumGoroutine() returns to
      baseline + 1 within a 2-second window.
    • TestGopherResumeInformation — new regression test covering the
      typeInformation text-mode path through resume, which differs from
      typeHTML/typeTextFile because lines are passed through verbatim
      (no metadata strip).
    • TestGopherResumeStaleMetadata now asserts that an orphan
      .goget.meta carried over from a different URL is deleted once a
      successful fresh download completes, preventing it from being
      picked up by a future --resume run.
    Downloads