docs: document packages, exported API and non-obvious logic
This commit is contained in:
@@ -38,6 +38,16 @@ func newDownloadCmd() *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// newDownloadSubCmd builds one download subcommand from a deezer.Kind. The
|
||||
// four kinds differ only in wording and in whether they take a track limit,
|
||||
// so they share this constructor rather than being written out four times.
|
||||
//
|
||||
// All four share one Options value through the parent's persistent flags,
|
||||
// which is safe because exactly one subcommand ever runs.
|
||||
//
|
||||
// A cancelled download is reported as success: the user pressed Ctrl-C and
|
||||
// has already seen the progress output, so an error on top of it would be
|
||||
// noise, and a non-zero exit would misreport a deliberate stop as a failure.
|
||||
func newDownloadSubCmd(kind deezer.Kind, opts *download.Options) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: fmt.Sprintf("%s <%s_id>", kind, kind),
|
||||
|
||||
@@ -49,6 +49,16 @@ func runLogin(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// promptCredentials reads the email and password, giving up if ctx is
|
||||
// cancelled.
|
||||
//
|
||||
// Reading stdin cannot itself be cancelled, so the read runs in a goroutine
|
||||
// and this selects on whichever finishes first. That goroutine outlives a
|
||||
// cancelled prompt, which is why the channel is buffered.
|
||||
//
|
||||
// Terminal state is captured up front and restored on cancellation: Ctrl-C
|
||||
// during the password prompt would otherwise leave echo disabled and the
|
||||
// user's shell silently typing blind.
|
||||
func promptCredentials(ctx context.Context) (string, string, error) {
|
||||
oldState, stateErr := term.GetState(int(os.Stdin.Fd()))
|
||||
|
||||
@@ -77,6 +87,8 @@ func promptCredentials(ctx context.Context) (string, string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// readCredentials prompts on the terminal. The password is read with echo
|
||||
// off so it neither appears on screen nor reaches the shell history.
|
||||
func readCredentials() (string, string, error) {
|
||||
fmt.Print("Email: ")
|
||||
line, err := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
|
||||
+24
@@ -1,3 +1,6 @@
|
||||
// Package cmd defines the godeez command line: the root command and its
|
||||
// download, login, logout, update and version subcommands. It is a thin layer
|
||||
// that parses flags and delegates to the internal packages.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@@ -12,8 +15,16 @@ import (
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// updateNoticeAnnotation marks the commands that may print an update notice.
|
||||
// It is an annotation rather than a field because it is inherited: marking
|
||||
// the download command opts in all of its subcommands.
|
||||
const updateNoticeAnnotation = "godeez:update-notice"
|
||||
|
||||
// Execute runs the CLI.
|
||||
//
|
||||
// The update check is started before the command and collected after it, so
|
||||
// the network round trip overlaps with work the user actually asked for
|
||||
// instead of adding to the startup time.
|
||||
func Execute(ctx context.Context) error {
|
||||
root := newRootCmd()
|
||||
|
||||
@@ -47,6 +58,13 @@ func newRootCmd() *cobra.Command {
|
||||
return root
|
||||
}
|
||||
|
||||
// wantsUpdateNotice decides whether this invocation should check for updates.
|
||||
//
|
||||
// The aim is to nag only during real interactive use. Notices are suppressed
|
||||
// when stderr is not a terminal, so they cannot corrupt piped or scripted
|
||||
// output; on help output, where they are noise; on commands that only print
|
||||
// their usage; and on anything not explicitly opted in via the annotation,
|
||||
// which notably keeps `godeez version` and `godeez update` quiet.
|
||||
func wantsUpdateNotice(root *cobra.Command) bool {
|
||||
if !term.IsTerminal(int(os.Stderr.Fd())) {
|
||||
return false
|
||||
@@ -75,6 +93,12 @@ func wantsUpdateNotice(root *cobra.Command) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// printUpdateNotice prints the notice only if the check has already finished.
|
||||
//
|
||||
// The non-blocking receive is the point: the command is done and the user
|
||||
// should get their prompt back, so a check that is still in flight is
|
||||
// dropped rather than waited on. A nil channel, meaning no check was started,
|
||||
// takes the same path.
|
||||
func printUpdateNotice(notice <-chan string) {
|
||||
select {
|
||||
case latest := <-notice:
|
||||
|
||||
@@ -38,6 +38,14 @@ func newUpdateCmd() *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runUpdate reports the current and latest versions and installs the update.
|
||||
//
|
||||
// Whether the binary can be replaced at all is checked before the network
|
||||
// call, so a package-managed install is told so straight away instead of
|
||||
// after a pointless round trip.
|
||||
//
|
||||
// The force check comes before the check-only one so that `--check --force`
|
||||
// still just reports rather than installing.
|
||||
func runUpdate(ctx context.Context, opts *updateOptions) error {
|
||||
if err := update.CheckUpdatable(); err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user