Skip to content

Commit

Permalink
temp: further organization and polishing
Browse files Browse the repository at this point in the history
  • Loading branch information
telemachus committed Nov 29, 2024
1 parent 7450a26 commit 408eaef
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 58 deletions.
16 changes: 9 additions & 7 deletions internal/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ const (
)

type appEnv struct {
homeDir string
cmd string
subCmd string
version string
usage string
subCmd string
homeDir string
exitValue int
quietWanted bool
}
Expand All @@ -27,18 +28,19 @@ func (app *appEnv) noOp() bool {
return app.exitValue != exitSuccess
}

func newAppEnv(subCmd string) *appEnv {
func newAppEnv(cmd, usage, version, subCmd string) *appEnv {
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Fprintf(os.Stderr, "%s %s: %s\n", gitmirrorName, subCmd, err)
fmt.Fprintf(os.Stderr, "%s %s: %s\n", cmd, subCmd, err)
return &appEnv{exitValue: exitFailure}
}
return &appEnv{
cmd: gitmirrorName,
cmd: cmd,
usage: usage,
version: version,
subCmd: subCmd,
usage: gitmirrorUsage,
exitValue: exitSuccess,
homeDir: homeDir,
exitValue: exitSuccess,
}
}

Expand Down
27 changes: 22 additions & 5 deletions internal/cli/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ func (app *appEnv) clone(repos []Repo) {
}
err := os.MkdirAll(filepath.Join(app.homeDir, defaultStorage), os.ModePerm)
if err != nil {
fmt.Fprintf(
os.Stderr,
"%s %s: %s\n",
app.cmd,
app.subCmd,
err,
)
app.exitValue = exitFailure
return
}
Expand All @@ -39,7 +46,13 @@ func (app *appEnv) cloneOne(repo Repo, ch chan<- result) {
prettyPath := app.prettyPath(storagePath)
ch <- result{
isErr: false,
msg: fmt.Sprintf("%s: already present in %s", repo.Name, prettyPath),
msg: fmt.Sprintf(
"%s %s: %s already present in %s",
app.cmd,
app.subCmd,
repo.Name,
prettyPath,
),
}
return
}
Expand All @@ -54,7 +67,13 @@ func (app *appEnv) cloneOne(repo Repo, ch chan<- result) {
app.exitValue = exitFailure
ch <- result{
isErr: true,
msg: fmt.Sprintf("%s: %s", repo.Name, err),
msg: fmt.Sprintf(
"%s %s: %s: %s",
app.cmd,
app.subCmd,
repo.Name,
err,
),
}
return
}
Expand All @@ -64,10 +83,8 @@ func (app *appEnv) cloneOne(repo Repo, ch chan<- result) {
}
}

func cmdClone(args []string) int {
app := newAppEnv("clone")
func cmdClone(app *appEnv, args []string) {
configFile, configIsDefault := app.parse(args)
repos := app.getRepos(configFile, configIsDefault)
app.clone(repos)
return app.exitValue
}
11 changes: 4 additions & 7 deletions internal/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,22 @@ func (app *appEnv) parse(args []string) (string, bool) {
case helpWanted:
fmt.Fprintf(
os.Stderr,
"%s: use 'help' not '-help' or '-h'\n",
"%s: use 'help' not '-help' or '-h'\n%s",
app.cmd,
app.usage,
)
fmt.Fprint(os.Stderr, app.usage)
app.exitValue = exitFailure
case configFile == "":
configFile = defaultConfig
configIsDefault = true
}
extraArgs := cmdFlags.Args()
if len(extraArgs) > 0 {
if len(cmdFlags.Args()) > 0 {
fmt.Fprintf(
os.Stderr,
"%s %s: unrecognized arguments: %+v\n",
"%s %s: no arguments accepted\n",
app.cmd,
app.subCmd,
extraArgs,
)
fmt.Fprint(os.Stderr, app.usage)
app.exitValue = exitFailure
}
return configFile, configIsDefault
Expand Down
12 changes: 6 additions & 6 deletions internal/cli/unmarshal_test.go → internal/cli/getrepos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ func makeRepos() []Repo {
}
}

func TestLoadSuccess(t *testing.T) {
func TestGetReposSuccess(t *testing.T) {
expected := makeRepos()
app := newAppEnv("test")
app := newAppEnv("test", "usage", "v0.0.0", "whatever")
actual := app.getRepos("testdata/backups.json", false)
if app.exitValue != exitSuccess {
t.Fatal("app.exitValue != exitSuccess")
Expand All @@ -26,16 +26,16 @@ func TestLoadSuccess(t *testing.T) {
}
}

func TestLoadFailure(t *testing.T) {
app := newAppEnv("test")
func TestGetReposFailure(t *testing.T) {
app := newAppEnv("test", "usage", "v0.0.0", "whatever")
app.getRepos("testdata/nope.json", false)
if app.exitValue != exitFailure {
t.Errorf("app.getRepos(\"testdata/nope.json\") exit value: %d; expected %d", app.exitValue, exitFailure)
}
}

func TestLoadRepoChecks(t *testing.T) {
app := newAppEnv("test")
func TestRepoChecks(t *testing.T) {
app := newAppEnv("test", "usage", "v0.0.0", "whatever")
actual := app.getRepos("testdata/repo-checks.json", false)
if len(actual) != 0 {
t.Errorf("app.getRepos(\"testdata/repo-checks.json\") expected len(repos) = 0; actual: %d", len(actual))
Expand Down
57 changes: 39 additions & 18 deletions internal/cli/gitmirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,60 @@ package cli
import (
"fmt"
"os"
)

const (
gitmirrorName = "gitmirror"
gitmirrorVersion = "v0.9.0"
"github.com/MakeNowJust/heredoc"
)

// CmdGitmirror runs a subcommand and returns success or failure to the shell.
func CmdGitmirror(args []string) int {
cmd := "gitmirror"
usage := heredoc.Docf(`
Usage: gitmirror <subcommand> [options]
Back up git repositories using git itself.
Subcommands:
clone Clone repositories to mirror
update|up Update mirrored repositories
help Show detailed information about a subcommand
version Show version
Run %[1]sgitmirror help <subcommand>%[1]s for more information about a subcommand.
`, "`")
version := "v0.9.0"
if len(args) < 1 {
fmt.Fprint(os.Stderr, gitmirrorUsage)
fmt.Fprintf(
os.Stderr,
"%s: required subcommand missing\n%s",
cmd,
usage,
)
return exitFailure
}
app := newAppEnv(cmd, usage, version, args[0])
// Give users a hand if they try, e.g., `gitmirror -(-)help|-h update`.
subCmd := args[0]
if subCmd == "--help" || subCmd == "-help" || subCmd == "-h" {
args[0] = "help"
if app.subCmd == "--help" || app.subCmd == "-help" || app.subCmd == "-h" {
app.subCmd = "help"
}
var exitValue int
switch subCmd {
switch app.subCmd {
case "clone":
exitValue = cmdClone(args[1:])
cmdClone(app, args[1:])
case "update", "up":
exitValue = cmdUpdate(args[1:])
cmdUpdate(app, args[1:])
case "version":
exitValue = cmdVersion(args[1:])
cmdVersion(app, args[1:])
case "help":
// TODO: write the help in Asciidoc?
exitValue = cmdHelp(args[1:])
cmdHelp(app, args[1:])
default:
fmt.Fprintf(os.Stderr, "gitmirror: unrecognized subcommand: \"%s\"\n", subCmd)
fmt.Fprint(os.Stderr, gitmirrorUsage)
exitValue = exitFailure
fmt.Fprintf(
os.Stderr,
"%s: unrecognized subcommand: \"%s\"\n%s",
app.cmd,
app.subCmd,
app.usage,
)
app.exitValue = exitFailure
}
return exitValue
return app.exitValue
}
25 changes: 19 additions & 6 deletions internal/cli/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ func (app *appEnv) help(args []string) {
return
}
if len(args) < 1 {
fmt.Fprintf(os.Stderr, "%s help: no subcommand given\n", gitmirrorName)
fmt.Fprint(os.Stderr, helpUsage)
fmt.Fprintf(
os.Stderr,
"%s help: no subcommand given\n%s",
app.cmd,
helpUsage,
)
app.exitValue = exitFailure
return
}
if len(args) > 1 {
fmt.Fprintf(os.Stderr, "%s help: too many arguments: %+v\n", gitmirrorName, args)
fmt.Fprintf(
os.Stderr,
"%s help: too many arguments: %+v\n",
app.cmd,
args,
)
fmt.Fprint(os.Stderr, helpUsage)
app.exitValue = exitFailure
return
Expand All @@ -31,14 +40,18 @@ func (app *appEnv) help(args []string) {
case "version":
fmt.Fprint(os.Stdout, versionUsage)
default:
fmt.Fprintf(os.Stderr, "%s help: unrecognized subcommand: \"%s\"\n", gitmirrorName, args[0])
fmt.Fprintf(
os.Stderr,
"%s help: unrecognized subcommand: \"%s\"\n",
app.cmd,
args[0],
)
fmt.Fprint(os.Stderr, helpUsage)
app.exitValue = exitFailure
}
}

func cmdHelp(args []string) int {
app := newAppEnv("help")
func cmdHelp(app *appEnv, args []string) int {
app.help(args)
return app.exitValue
}
3 changes: 1 addition & 2 deletions internal/cli/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ func (app *appEnv) updateOne(repo Repo, ch chan<- result) {
}
}

func cmdUpdate(args []string) int {
app := newAppEnv("update")
func cmdUpdate(app *appEnv, args []string) int {
configFile, configIsDefault := app.parse(args)
repos := app.getRepos(configFile, configIsDefault)
app.update(repos)
Expand Down
17 changes: 10 additions & 7 deletions internal/cli/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ import (
"os"
)

func (app *appEnv) version(args []string) {
func (app *appEnv) showVersion(args []string) {
if app.noOp() {
return
}
if len(args) != 0 {
fmt.Fprintf(os.Stderr, "%s version: no arguments accepted\n", gitmirrorName)
fmt.Fprint(os.Stderr, versionUsage)
fmt.Fprintf(
os.Stderr,
"%s version: no arguments accepted\n%s",
app.cmd,
versionUsage,
)
app.exitValue = exitFailure
return
}
fmt.Fprintf(os.Stdout, "%s: %s\n", gitmirrorName, gitmirrorVersion)
fmt.Fprintf(os.Stdout, "%s: %s\n", app.cmd, app.version)
}

func cmdVersion(args []string) int {
app := newAppEnv("version")
app.version(args)
func cmdVersion(app *appEnv, args []string) int {
app.showVersion(args)
return app.exitValue
}
4 changes: 4 additions & 0 deletions internal/git/fetchhead.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// Package git represents and manipulates git commands and objects.
package git

import (
"bytes"
"os"
)

// FetchHead represents a git FETCH_HEAD as a []byte.
type FetchHead []byte

// NewFetchHead reads a git FETCH_HEAD and returns a FetchHead and possible error.
func NewFetchHead(f string) (FetchHead, error) {
fh, err := os.ReadFile(f)
if err != nil {
Expand All @@ -15,6 +18,7 @@ func NewFetchHead(f string) (FetchHead, error) {
return FetchHead(fh), nil
}

// Equals checks whether one FetchHead is identical to another.
func (fh FetchHead) Equals(fhOther FetchHead) bool {
return bytes.Equal(fh, fhOther)
}

0 comments on commit 408eaef

Please sign in to comment.