From 0fc1cd727c327d50c99a772f05135ed9d09bd153 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Wed, 2 Sep 2026 21:14:37 -0700 Subject: Add a describe command to set repo description --- git-shell-commands.go | 127 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 44 deletions(-) (limited to 'git-shell-commands.go') diff --git a/git-shell-commands.go b/git-shell-commands.go index 720fb69..cbcc32c 100644 --- a/git-shell-commands.go +++ b/git-shell-commands.go @@ -15,6 +15,10 @@ import ( const codeDir = "/nezuko/code/" +func shellQuote(s string) string { + return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'" +} + func checkRepoName(name string) (string, error) { if strings.Contains(name, "/") { return "", fmt.Errorf("invalid name %q: contains slashes", name) @@ -25,55 +29,42 @@ func checkRepoName(name string) (string, error) { return name, nil } -func createRepo(name string) error { - dir := filepath.Join(codeDir, name) - fmt.Fprintf(os.Stderr, "mkdir %s\n", dir) - if err := os.Mkdir(dir, 0755); err != nil { - return err - } - fmt.Fprintf(os.Stderr, "cd %s && git init --bare\n", dir) - git := exec.Command("git", "init", "--bare") - git.Dir = dir - git.Stdout = os.Stdout - git.Stderr = os.Stderr - if err := git.Run(); err != nil { - return err - } - return nil -} - func create() error { flag.Usage = func() { - fmt.Fprintf(os.Stderr, `Usage: create [REPO]... + fmt.Fprintf(os.Stderr, `Usage: create REPO Create a new git repo named REPO. `) } + d := flag.String("d", "", "description") flag.Parse() args := flag.Args() if len(args) == 0 { return fmt.Errorf("missing operand\nUse create -help for usage info") } - names := make([]string, len(args)) - var errs []error - for i, arg := range args { - var err error - names[i], err = checkRepoName(arg) - if err != nil { - errs = append(errs, err) - } + if len(args) > 1 { + return fmt.Errorf("too many positional arguments\nUse create -help for usage info") } - if len(errs) > 0 { - return errors.Join(errs...) + name, err := checkRepoName(args[0]) + if err != nil { + return err } - failed := false - for _, name := range names { - if err := createRepo(name); err != nil { - fmt.Fprintln(os.Stderr, err) - failed = true - } + dir := filepath.Join(codeDir, name) + fmt.Fprintf(os.Stderr, "mkdir %s\n", shellQuote(dir)) + if err := os.Mkdir(dir, 0755); err != nil { + return err + } + fmt.Fprintf(os.Stderr, "cd %s && git init --bare\n", shellQuote(dir)) + git := exec.Command("git", "init", "--bare") + git.Dir = dir + git.Stdout = os.Stdout + git.Stderr = os.Stderr + if err := git.Run(); err != nil { + return err } - if failed { - return fmt.Errorf("failed to create repo") + if *d != "" { + if err := setRepoDescription(name, *d); err != nil { + return err + } } return nil } @@ -85,7 +76,7 @@ func fileExists(path string) bool { func delete() error { flag.Usage = func() { - fmt.Fprintf(os.Stderr, `Usage: delete [REPO] + fmt.Fprintf(os.Stderr, `Usage: delete REPO Delete the git repo named REPO. `) } @@ -108,7 +99,7 @@ Delete the git repo named REPO. if !scanner.Scan() || scanner.Text() != shortName { return fmt.Errorf("name does not match") } - fmt.Fprintf(os.Stderr, "rm -rf %s\n", dir) + fmt.Fprintf(os.Stderr, "rm -rf %s\n", shellQuote(dir)) if !fileExists(dir) { return fmt.Errorf("repo %q does not exist", name) } @@ -118,8 +109,53 @@ Delete the git repo named REPO. return nil } +func setRepoDescription(repo, description string) error { + descriptionPath := filepath.Join(codeDir, repo, "description") + fmt.Fprintf(os.Stderr, "echo %s > %s\n", shellQuote(description), shellQuote(descriptionPath)) + desc, err := os.Create(descriptionPath) + if err != nil { + return err + } + defer desc.Close() + if _, err := fmt.Fprintln(desc, description); err != nil { + return err + } + if err := desc.Close(); err != nil { + return err + } + return nil +} + +func describe() error { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: describe -d DESCRIPTION REPO +Set the description for REPO. +`) + } + d := flag.String("d", "", "description") + flag.Parse() + args := flag.Args() + if len(args) == 0 { + return fmt.Errorf("missing operand\nUse describe -help for usage info") + } + if len(args) > 1 { + return fmt.Errorf("too many positional arguments\nUse describe -help for usage info") + } + if *d == "" { + return fmt.Errorf("missing required flag -d\nUse describe -help for usage info") + } + name, err := checkRepoName(args[0]) + if err != nil { + return err + } + if err := setRepoDescription(name, *d); err != nil { + return err + } + return nil +} + func installFile(srcPath, dstPath string) error { - fmt.Fprintf(os.Stderr, "cp %s %s && chmod +x %[2]s\n", srcPath, dstPath) + fmt.Fprintf(os.Stderr, "cp %s %s && chmod +x %[2]s\n", shellQuote(srcPath), shellQuote(dstPath)) src, err := os.Open(srcPath) if err != nil { return err @@ -161,9 +197,9 @@ func install() error { if err := installFile(selfPath, helpPath); err != nil { return err } - for _, cmd := range []string{"create", "delete"} { + for _, cmd := range []string{"create", "delete", "describe"} { cmdPath := filepath.Join(dir, cmd) - fmt.Fprintf(os.Stderr, "ln -f %s %s\n", helpPath, cmdPath) + fmt.Fprintf(os.Stderr, "ln -f %s %s\n", shellQuote(helpPath), shellQuote(cmdPath)) if err := os.Remove(cmdPath); err != nil && !errors.Is(err, os.ErrNotExist) { return err } @@ -176,9 +212,10 @@ func install() error { func help() { fmt.Printf(`Available commands: - create create a new git repo - delete delete a git repo - help print this message + create create a new git repo + delete delete a git repo + describe set repo description + help print this message `) } @@ -189,6 +226,8 @@ func main() { err = create() case "delete": err = delete() + case "describe": + err = describe() case "git-shell-commands": err = install() default: -- cgit v1.3.1