gen_docs.go

  1package main
  2
  3import (
  4	"fmt"
  5	"os"
  6	"path"
  7	"path/filepath"
  8	"strings"
  9	"time"
 10
 11	"github.com/pkg/errors"
 12	"github.com/spf13/cobra/doc"
 13
 14	"github.com/MichaelMure/git-bug/bridge"
 15	"github.com/MichaelMure/git-bug/commands"
 16)
 17
 18const bridgeExampleFilePath = "commands/bridge_configure_doc.go"
 19
 20func main() {
 21	fmt.Println("Generating documentation ...")
 22
 23	tasks := map[string]func() error{
 24		"BridgeConfig": genBridgeConfig,
 25		"ManPage":      genManPage,
 26		"Markdown":     genMarkdown,
 27	}
 28
 29	// Due to concurrency issues in cobra, the following can't be concurrent :(
 30
 31	// var wg sync.WaitGroup
 32	for name, f := range tasks {
 33		// wg.Add(1)
 34		// go func(name string, f func() error) {
 35		// 	defer wg.Done()
 36		err := f()
 37		if err != nil {
 38			fmt.Printf("  - %s: %v\n", name, err)
 39			return
 40		}
 41		fmt.Printf("  - %s: ok\n", name)
 42		// }(name, f)
 43	}
 44
 45	// wg.Wait()
 46}
 47
 48// If a flag is not listed in flagInfos, docs will generate the default values for the flag:
 49// flagName = lowercase name
 50// defaultVal = $({uppercase name})
 51// paramConflicts = none
 52var flagInfos = map[string]BridgeFlagInfo{
 53	"BaseURL": {
 54		flagName:       "base-url",
 55		defaultVal:     "$(BASE_URL)",
 56		paramConflicts: []string{},
 57	},
 58	"CredPrefix": {
 59		flagName:   "credential",
 60		defaultVal: "$(CREDENTIALS)",
 61		paramConflicts: []string{
 62			"TokenRaw",
 63		},
 64	},
 65	"TokenRaw": {
 66		flagName:       "token",
 67		defaultVal:     "$(TOKEN)",
 68		paramConflicts: []string{},
 69	},
 70}
 71
 72type BridgeFlagInfo struct {
 73	flagName       string
 74	defaultVal     string
 75	paramConflicts []string
 76}
 77
 78var bridgeUrls = map[string]string{
 79	"github":            "https://github.com/MichaelMure/git-bug",
 80	"gitlab":            "https://gitlab.com/gitlab-org/gitlab",
 81	"launchpad-preview": "https://bugs.launchpad.net/ubuntu/",
 82	// TODO: Insert URL for Jira Project
 83}
 84
 85// genBridgeConfig generates the bridge configuration documentation on go generate
 86// Documentation is stored in commands/bridge_configure_doc.go
 87func genBridgeConfig() error {
 88	var exampleText strings.Builder
 89	exampleText.WriteString("`")
 90	exampleText.WriteString(`# Interactive example
 91[1]: github
 92[2]: gitlab
 93[3]: jira
 94[4]: launchpad-preview
 95
 96target: 1
 97name [default]: default
 98
 99Detected projects:
100[1]: github.com/a-hilaly/git-bug
101[2]: github.com/MichaelMure/git-bug
102
103[0]: Another project
104
105Select option: 1
106
107[1]: user provided token
108[2]: interactive token creation
109Select option: 1
110
111You can generate a new token by visiting https://github.com/settings/tokens.
112Choose 'Generate new token' and set the necessary access scope for your repository.
113
114The access scope depend on the type of repository.
115Public:
116	- 'public_repo': to be able to read public repositories
117Private:
118	- 'repo'       : to be able to read private repositories
119
120Enter token: 87cf5c03b64029f18ea5f9ca5679daa08ccbd700
121Successfully configured bridge: default
122
123`)
124	targets := bridge.Targets()
125	for i, b := range targets {
126		if i != 0 {
127			exampleText.WriteString("\n\n")
128		}
129		exampleText.WriteString("# For ")
130		exampleText.WriteString(strings.Title(b))
131		exampleText.WriteString("\ngit bug bridge configure \\\n")
132
133		exampleText.WriteString("    --target=")
134		exampleText.WriteString(strings.ToLower(b))
135		exampleText.WriteString(" \\\n")
136
137		params, err := bridge.ValidParams(b)
138		if err != nil {
139			return errors.Wrap(err, "bridge parameters")
140		}
141
142		for _, param := range params {
143			if param == "BaseURL" {
144				continue
145			}
146
147			paramString := formatParam(param, params, b)
148			if paramString == "" {
149				continue
150			}
151
152			exampleText.WriteString(paramString)
153		}
154	}
155	exampleText.WriteString("`")
156
157	_ = os.Remove(bridgeExampleFilePath)
158
159	f, err := os.Create(bridgeExampleFilePath)
160	if err != nil {
161		return err
162	}
163	defer f.Close()
164
165	_, err = f.WriteString(`// Code generated by doc/gen_docs.go; DO NOT EDIT.
166
167package commands
168
169var bridgeConfigureExample =`)
170	if err != nil {
171		return err
172	}
173	_, err = f.WriteString(exampleText.String())
174	if err != nil {
175		return err
176	}
177
178	return nil
179}
180
181const paramFormatString = "    --%s=%s \\\n"
182
183// formatParam formats a parameter into a flag example in the command line
184func formatParam(param string, params []string, bridge string) string {
185	if flagInfo, ok := flagInfos[param]; ok {
186		if checkParamConflicts(flagInfo.paramConflicts, params) {
187			return ""
188		}
189
190		return fmt.Sprintf(paramFormatString, flagInfo.flagName, flagInfo.defaultVal)
191	} else if param == "URL" {
192		if exampleUrl, ok := bridgeUrls[bridge]; ok {
193			return fmt.Sprintf(paramFormatString, "url", exampleUrl)
194		}
195	}
196
197	return fmt.Sprintf(paramFormatString, strings.ToLower(param), "=$("+strings.ToUpper(param)+")")
198}
199
200// checkParamConflicts checks the parameter conflicts against the list of present parameters
201// If a conflict is found, it returns true. Otherwise, it returns false
202func checkParamConflicts(paramConflicts []string, params []string) bool {
203	if len(paramConflicts) == 0 {
204		return false
205	}
206
207	for p := range params {
208		for conflict := range paramConflicts {
209			if p == conflict {
210				return true
211			}
212		}
213	}
214	return false
215}
216
217func genManPage() error {
218	cwd, _ := os.Getwd()
219	dir := path.Join(cwd, "doc", "man")
220
221	date := time.Date(2019, 4, 1, 12, 0, 0, 0, time.UTC)
222
223	header := &doc.GenManHeader{
224		Title:   "GIT-BUG",
225		Section: "1",
226		Date:    &date,
227		Source:  "Generated from git-bug's source code",
228	}
229
230	files, err := filepath.Glob(dir + "/*.1")
231	if err != nil {
232		return err
233	}
234	for _, f := range files {
235		if err := os.Remove(f); err != nil {
236			return err
237		}
238	}
239
240	return doc.GenManTree(commands.NewRootCommand(), header, dir)
241}
242
243func genMarkdown() error {
244	cwd, _ := os.Getwd()
245	dir := path.Join(cwd, "doc", "md")
246
247	files, err := filepath.Glob(dir + "/*.md")
248	if err != nil {
249		return err
250	}
251	for _, f := range files {
252		if err := os.Remove(f); err != nil {
253			return err
254		}
255	}
256
257	return doc.GenMarkdownTree(commands.NewRootCommand(), dir)
258}