diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..2fa7ce4d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,31 @@ +# Aviator CLI manual pages + +The Markdown files that end with `.\d.md` in this directory are manual pages for +Aviator CLI. + +## Online manual documentation pages + +Run `man man` in your system to see the overview of the online manual pages. The +number in the filename is the manual page sections. Typically, the manual pages +for shell commands are in section 1. You will likely write a page under this +section. In the manual pages, other pages are referenced like `git(1)`, where +the number after the page name is the section number. The section number is used +to distinguish pages with the same name. For example, `man 1 exit` will show the +help document for the shell command `exit`, and `man 3 exit` will show the help +for the C POSIX API's `void exit(int)`. + +Git's reference pages are good references for the manual pages. Try `man 1 git` +or `man 1 git-commit`. + +## Converting the Markdown files + +Go script `convert-manpages.go` has two modes. + +1. Preview mode: Run `go run ./convert-manpages.go FILE`, and you'll see the + preview of the converted man page. +2. Conversion mode: Run `go run ./convert-manpages.go --output-dir=DIR`, and + you'll get all files in the directory converted to the manual pages in `DIR`. + +The conversion output directory can be used as a `MANPATH`. For example, run +`MANPATH=$PWD/DIR man 1 av` will search for the page for `av(1)` in the +directory. You can use this for checking the final output. diff --git a/docs/av.1.md b/docs/av.1.md new file mode 100644 index 00000000..a2a9e2ea --- /dev/null +++ b/docs/av.1.md @@ -0,0 +1,31 @@ +# av 1 "" av-cli "Aviator CLI User Manual" + +# NAME + +av - Aviator CLI + +# DESCRIPTION + +**av** allows you to manage stacked pull requests with Aviator. + +# SUBCOMMANDS + +* av-commit-split(1): Split a commit into multiple commits. +* av-fetch(1): Fetch latest state from GitHub. +* av-init(1): Initialize the Git repository for Aviator CLI. +* av-pr-create(1): Create a pull request for the current branch. +* av-stack-branch(1): Create a new stacked branch. +* av-stack-branch-commit(1): Create a new stacked branch and commit staged + changes to it. +* av-stack-diff(1): Generate diff between working tree and the parent branch. +* av-stack-next(1): Checkout the next branch in the stack. +* av-stack-prev(1): Checkout the previous branch in the stack. +* av-stack-submit(1): Create/synchronize pull requests for the current stack. +* av-stack-sync(1): Synchronize stacked branches. +* av-stack-tidy(1): Tidy up the branch metadata. +* av-stack-tree(1): Show the tree of stacked branches. + +# FURTHER DOCUMENTATION + +See [Aviator documentation](https://docs.aviator.co) for the help document +beyond Aviator CLI. diff --git a/docs/convert-manpages.go b/docs/convert-manpages.go new file mode 100644 index 00000000..adc9ef06 --- /dev/null +++ b/docs/convert-manpages.go @@ -0,0 +1,101 @@ +// Binary convert-manpages converts specified man page Markdown files. +package main + +import ( + "bytes" + "errors" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strings" + + "github.com/cpuguy83/go-md2man/v2/md2man" + "github.com/spf13/pflag" +) + +var ( + preview = pflag.Bool("preview", false, "Preview the converted man page") + outputDir = pflag.String("output-dir", "", "Output directory") + + manpageMarkdownPattern = regexp.MustCompile(`[.](\d)[.]md`) +) + +func main() { + pflag.Parse() + + if *outputDir == "" { + // If the output directory is not specified, assume it's for preview. + *preview = true + } + + if *preview { + args := pflag.Args() + if len(args) != 1 { + pflag.Usage() + os.Exit(1) + return + } + previewMarkdown(args[0]) + return + } + + wd, err := os.Getwd() + if err != nil { + log.Fatalf("Cannot get the current wd: %v", err) + } + ents, err := os.ReadDir(wd) + if err != nil { + log.Fatal(err) + } + + for _, ent := range ents { + matches := manpageMarkdownPattern.FindStringSubmatch(ent.Name()) + if len(matches) == 0 { + continue + } + bs, err := os.ReadFile(ent.Name()) + if err != nil { + log.Fatalf("Cannot read a file %q: %v", ent.Name(), err) + } + roff := convertMarkdown(bs) + outFilePath := filepath.Join(*outputDir, "man"+matches[1], strings.TrimSuffix(ent.Name(), ".md")) + if err := os.MkdirAll(filepath.Dir(outFilePath), 0755); err != nil { + log.Fatalf("Cannot create the output directory: %v", err) + } + if err := os.WriteFile(outFilePath, roff, 0644); err != nil { + log.Fatalf("Cannot write the conversion result to %q: %v", outFilePath, err) + } + } +} + +func previewMarkdown(fp string) error { + bs, err := os.ReadFile(fp) + if err != nil { + return err + } + + roff := convertMarkdown(bs) + _ = roff + + if runtime.GOOS == "darwin" || runtime.GOOS == "freebsd" { + cmd := exec.Command("mandoc", "-a") + cmd.Stdin = bytes.NewBuffer(roff) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() + } else if runtime.GOOS == "linux" { + cmd := exec.Command("man", "-l", "-") + cmd.Stdin = bytes.NewBuffer(roff) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() + } + return errors.New("operating system not supported for preview") +} + +func convertMarkdown(bs []byte) []byte { + return md2man.Render(bs) +} diff --git a/go.mod b/go.mod index a3e70dfe..7dd3ca57 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( emperror.dev/errors v0.8.1 + github.com/cpuguy83/go-md2man/v2 v2.0.2 github.com/fatih/color v1.15.0 github.com/golangci/golangci-lint v1.52.2 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 @@ -12,6 +13,7 @@ require ( github.com/shurcooL/githubv4 v0.0.0-20220115235240-a14260e6f8a2 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.7.0 + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.2 github.com/whilp/git-urls v1.0.0 @@ -140,6 +142,7 @@ require ( github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryancurrah/gomodguard v1.3.0 // indirect github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect @@ -156,7 +159,6 @@ require ( github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stretchr/objx v0.5.0 // indirect diff --git a/go.sum b/go.sum index 713a9ada..21be908f 100644 --- a/go.sum +++ b/go.sum @@ -109,6 +109,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= @@ -460,6 +461,7 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.3.0 h1:q15RT/pd6UggBXVBuLps8BXRvl5GPBcwVA7BJHMLuTw= github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50=