Skip to content

Commit 5137e09

Browse files
allmightyspiffGitHub Enterprise
authored andcommitted
Merge pull request #830 from SoftLayer/docs
Automated Docs
2 parents 15cdfe5 + 59e7144 commit 5137e09

19 files changed

Lines changed: 350 additions & 211 deletions

README.md

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,39 @@
44
# softlayer-cli
55

66
This repository houses the code that powers the [ibmcloud-cli sl](https://github.ibm.com/Bluemix/bluemix-cli) command.
7+
[CLI Documentation](https://pages.github.ibm.com/SoftLayer/softlayer-cli)
78

8-
# Project Setup
9+
## Installation (official)
10+
11+
The Classic Infrastructure commands are a plugin for the `ibmcloud` cli. First you need to [Install the IBMCLOUD CLI](https://cloud.ibm.com/docs/cli?topic=cli-install-ibmcloud-cli). Then simply install the `sl` plugin with the following command:
12+
13+
```bash
14+
ibmcloud plugin install sl
15+
```
16+
17+
To update, simply run
18+
19+
```bash
20+
ibmcloud plugin update sl
21+
```
22+
23+
## Installation (source build)
24+
25+
To install a version of the plugin built locally, you can do the following:
26+
27+
1. Build the `sl` plugin binary
28+
2. `go build`
29+
3. Install the new `softlayer` binary
30+
4. `ibmcloud plugin install ./softlayer` (might need to put `./softlayer.exe` for windows installs)
31+
32+
When building from source, the plugin gets its version information from `plugin/metadata/sl.go`. You may want to update that number to not get confused with official versions.
33+
34+
# Development Project Setup
935

1036
Clone the repo, then just run `go mod vendor` and `go build` and you should have a running binary for the `sl` plugin.
1137

1238

13-
# Testing
39+
## Testing
1440
Before making a pull request, make sure everything looks good with these tools.
1541
Working directory: `$GO_PATH/src/github.ibm.com/SoftLayer/softlayer-cli`
1642

@@ -448,5 +474,24 @@ ENV Variables that need to be set:
448474
2. `IBMCLOUD_APIKEY` : API key for using `ibmcloud`. This is how we upload to COS. The COS plugin needs to be installed as well. `ibmcloud plugin install cloud-object-storage`
449475

450476

477+
# Documentation
478+
479+
`/docs/docs` is a command that will generate markdown documentation. This documentation needs to be copied and updated in the https://github.ibm.com/cloud-docs/cli repo (draft branch).
480+
481+
To build the full docs locally, see https://test.cloud.ibm.com/docs-internal/writing?topic=writing-transform-local
482+
483+
```bash
484+
➜ md-source pwd
485+
/Users/chris/Code/md-source
486+
➜ md-source ls -lh
487+
total 0
488+
drwxr-xr-x 3 chris staff 96B Nov 30 12:58 build
489+
drwxr-xr-x 3 chris staff 96B Nov 30 13:01 input
490+
drwxr-xr-x 4 chris staff 128B Nov 30 13:01 output
491+
492+
➜ marked-it-cli input --output=output --footer-file=build/markdown/footer.txt --extension-file=build/markdown/headerFooterExt.js --extension-file=build/markdown/generateSectionsExt.js --extension-file=build/markdown/accessibilityExt.js --extension-file=build/markdown/jsonTocExt.js --keyref-file=build/markdown/cloudoekeyrefs.yml --overwrite --verbose --toc-json --extension-file=build/markdown/videoExt.js --extension-file=build/markdown/terraformExt.js --extension-file=build/markdown/includesExt.js --extension-file=build/markdown/glossaryExt.js --@glossary:definitions-file=/Users/chris/Code/md-source/build/markdown/glossary.json
493+
```
494+
495+
451496
## TODO
452497
Automate build with https://github.ibm.com/coligo/cli/tree/main/script

docs/main.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
"encoding/json"
8+
"regexp"
9+
10+
"text/template"
11+
// "sort"
12+
"github.com/spf13/cobra"
13+
"github.com/spf13/pflag"
14+
// "github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin"
15+
sl_plugin "github.ibm.com/SoftLayer/softlayer-cli/plugin"
16+
)
17+
18+
var fileName string
19+
var rootCmd = &cobra.Command{
20+
Use: "doc-gen",
21+
Short: "Generate the documentation for the sl plugin",
22+
RunE: func(Cmd *cobra.Command, args []string) error {
23+
CliDocs()
24+
return nil
25+
},
26+
}
27+
28+
func main() {
29+
err := rootCmd.Execute()
30+
checkError(err)
31+
return
32+
}
33+
34+
func checkError(err error) {
35+
if err != nil {
36+
panic(err)
37+
}
38+
}
39+
40+
// For top level commands, like `sl account` or `sl hardware`
41+
type SlCmdGroup struct {
42+
Name string
43+
CommandShortLink string
44+
Commands []SlCmdDoc
45+
Help string
46+
}
47+
48+
// For specific commands
49+
type SlCmdDoc struct {
50+
Name string
51+
CommandShortLink string
52+
Use string
53+
Flags []SlCmdFlag
54+
Help string
55+
LongHelp string
56+
Backtick string
57+
CommandPath string
58+
}
59+
60+
// For a commands flags
61+
type SlCmdFlag struct {
62+
Name string
63+
Help string
64+
Default string
65+
}
66+
67+
68+
69+
// This function builds the documentation for IBMCLOUD docs
70+
func CliDocs() {
71+
// fmt.Printf("IBMCLOUD SL Command Directory\n")
72+
SlCommands := sl_plugin.GetTopCobraCommand(nil, nil)
73+
CmdGroups := []SlCmdGroup{}
74+
for _, iCmd := range SlCommands.Commands() {
75+
shortName := strings.ReplaceAll(iCmd.Name(), " ", "_")
76+
shortName = strings.ReplaceAll(iCmd.Name(), "-", "_")
77+
thisCmdGroup := SlCmdGroup{
78+
Name: iCmd.Name(),
79+
CommandShortLink: fmt.Sprintf("sl_%v", shortName),
80+
Commands: nil,
81+
Help: iCmd.Short,
82+
83+
}
84+
if len(iCmd.Commands()) > 0 {
85+
thisCmdGroup.Commands = buildSlCmdDoc(iCmd)
86+
} else {
87+
// This is a single command, like 'call-api' and doesn't have sub-commands
88+
thisCmdGroup.Commands = []SlCmdDoc{cobraToSl(iCmd, "")}
89+
}
90+
PrintMakrdown(thisCmdGroup)
91+
CmdGroups = append(CmdGroups, thisCmdGroup)
92+
}
93+
jOut, err := json.MarshalIndent(CmdGroups, "", " ")
94+
checkError(err)
95+
err = os.WriteFile("sl.json", jOut, 0755) //#nosec G306 -- This is a false positive
96+
checkError(err)
97+
// fmt.Println(string(jOut))
98+
}
99+
100+
// Generates the Markdown
101+
func PrintMakrdown(cmd SlCmdGroup) {
102+
103+
var cmdTemplate = `<!-- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT -->
104+
<!-- markdownlint-disable first-heading-h1 lastupdated-misformat-or-missing frontmatter-yaml -->
105+
{{range .Commands}}
106+
## ibmcloud {{.CommandPath}}
107+
{: #{{.CommandShortLink}}}
108+
109+
{{.Help}}
110+
111+
{{.LongHelp}}
112+
113+
{{.Backtick}}bash
114+
ibmcloud {{.Use}}
115+
{{.Backtick}}
116+
{: codeblock}
117+
118+
{{if .Flags}}
119+
**Command options**:
120+
{{range .Flags}}
121+
--{{.Name}}
122+
: {{.Help}}
123+
{{end}}{{end}}{{end}}`
124+
125+
mdTemplate, err := template.New("cmd template").Parse(cmdTemplate)
126+
checkError(err)
127+
filename := fmt.Sprintf("%v.md", cmd.CommandShortLink)
128+
outfile, err := os.Create(filename) //#nosec G304 -- This is a false positive
129+
defer outfile.Close()
130+
err = mdTemplate.Execute(outfile, cmd)
131+
checkError(err)
132+
133+
}
134+
135+
func getLongHelp(helpString string) string {
136+
// Removes some ugly empty lines sometimes.
137+
empty_line := regexp.MustCompile(`(?m)^(\t)+$`)
138+
helpString = empty_line.ReplaceAllString(helpString, "")
139+
helpString = strings.ReplaceAll(helpString, "${COMMAND_NAME}", "ibmcloud")
140+
example_regex := regexp.MustCompile(`(?i)Example:+`)
141+
helpString = example_regex.ReplaceAllString(helpString, "**Examples**:\n")
142+
// for 'indented-by-two'
143+
indent_regex := regexp.MustCompile(`(?m)^ {2}(\S)`)
144+
helpString = indent_regex.ReplaceAllString(helpString, " $1")
145+
146+
return getFlagHelp(helpString)
147+
}
148+
149+
func getFlagHelp(helpString string) string {
150+
// for 'no-inline-html' errors
151+
fake_html_regex := regexp.MustCompile(`<([0-9A-Za-z_\-\.\s]+)>`)
152+
helpString = fake_html_regex.ReplaceAllString(helpString, "$1")
153+
// for 'no-reversed-links'
154+
fake_link_regex := regexp.MustCompile(`\)\[`)
155+
helpString = fake_link_regex.ReplaceAllString(helpString, ") [")
156+
return helpString
157+
}
158+
159+
func buildSlCmdDoc(topCommand *cobra.Command) []SlCmdDoc {
160+
docs := []SlCmdDoc{}
161+
for _, iCmd := range topCommand.Commands() {
162+
thisDoc := cobraToSl(iCmd, topCommand.Name())
163+
docs = append(docs, thisDoc)
164+
}
165+
return docs
166+
}
167+
168+
func cobraToSl(iCmd *cobra.Command, tlcmd string) SlCmdDoc {
169+
170+
shortName := fmt.Sprintf("sl_%s_%s", tlcmd, iCmd.Name())
171+
shortName = strings.ReplaceAll(shortName, " ", "_")
172+
shortName = strings.ReplaceAll(shortName, "-", "_")
173+
longHelp := getLongHelp(iCmd.Long)
174+
thisDoc := SlCmdDoc{
175+
Name: iCmd.Name(),
176+
CommandShortLink: shortName,
177+
CommandPath: iCmd.CommandPath(),
178+
Use: iCmd.UseLine(),
179+
Flags: nil,
180+
Help: iCmd.Short,
181+
LongHelp: longHelp,
182+
Backtick: "```",
183+
}
184+
thisDoc.Flags = buildSlCmdFlag(iCmd)
185+
186+
return thisDoc
187+
188+
}
189+
190+
func buildSlCmdFlag(topCommand *cobra.Command) []SlCmdFlag {
191+
flags := []SlCmdFlag{}
192+
flagSet := topCommand.Flags()
193+
flagSet.VisitAll(func(pflag *pflag.Flag) {
194+
flagName := pflag.Name
195+
if pflag.Shorthand != "" {
196+
flagName = fmt.Sprintf("%s, %s", pflag.Shorthand, flagName)
197+
}
198+
thisFlag := SlCmdFlag{
199+
Name:flagName,
200+
Help: getFlagHelp(pflag.Usage),
201+
Default: pflag.DefValue,
202+
}
203+
flags = append(flags, thisFlag)
204+
})
205+
return flags
206+
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
)
2323

2424
require (
25+
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
2526
github.com/davecgh/go-spew v1.1.1 // indirect
2627
github.com/fatih/color v1.10.0 // indirect
2728
github.com/fatih/structs v1.1.0 // indirect
@@ -37,6 +38,7 @@ require (
3738
github.com/pelletier/go-toml v1.2.0 // indirect
3839
github.com/pmezard/go-difflib v1.0.0 // indirect
3940
github.com/rivo/uniseg v0.1.0 // indirect
41+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
4042
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
4143
golang.org/x/crypto v0.17.0 // indirect
4244
golang.org/x/mod v0.12.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.1.0 h1:m2VQ7wYE8k3ZuV0iIuye5QIK/t6QLZa
55
github.com/IBM-Cloud/ibm-cloud-cli-sdk v1.1.0/go.mod h1:/xwZEX9hm7/YFFEEiFa0Bes2YP5OWmXvgf9D/0o9jic=
66
github.com/Xuanwo/go-locale v1.1.0 h1:51gUxhxl66oXAjI9uPGb2O0qwPECpriKQb2hl35mQkg=
77
github.com/Xuanwo/go-locale v1.1.0/go.mod h1:UKrHoZB3FPIk9wIG2/tVSobnHgNnceGSH3Y8DY5cASs=
8+
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
89
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
910
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1011
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -84,6 +85,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
8485
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8586
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
8687
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
88+
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
8789
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
8890
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
8991
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=

plugin/commands/subnet/create.go

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package subnet
22

33
import (
4-
"strconv"
5-
4+
"fmt"
65
"github.com/spf13/cobra"
76
"github.ibm.com/SoftLayer/softlayer-cli/plugin/errors"
87
slErr "github.ibm.com/SoftLayer/softlayer-cli/plugin/errors"
98
. "github.ibm.com/SoftLayer/softlayer-cli/plugin/i18n"
109
"github.ibm.com/SoftLayer/softlayer-cli/plugin/managers"
1110
"github.ibm.com/SoftLayer/softlayer-cli/plugin/metadata"
1211
"github.ibm.com/SoftLayer/softlayer-cli/plugin/utils"
12+
"strconv"
13+
"strings"
1314
)
1415

1516
type CreateCommand struct {
@@ -27,22 +28,16 @@ func NewCreateCommand(sl *metadata.SoftlayerCommand) *CreateCommand {
2728
NetworkManager: managers.NewNetworkManager(sl.Session),
2829
}
2930
cobraCmd := &cobra.Command{
30-
Use: "create",
31+
Use: "create" + strings.ToUpper(fmt.Sprintf(" %s %s %s", T("Network"), T("Quantity"), T("VLAN"))),
3132
Short: T("Add a new subnet to your account"),
32-
Long: T(`${COMMAND_NAME} sl subnet create NETWORK QUANTITY VLAN_ID [OPTIONS]
33-
34-
Add a new subnet to your account. Valid quantities vary by type.
35-
36-
Type - Valid Quantities (IPv4)
37-
public - 4, 8, 16, 32
38-
private - 4, 8, 16, 32, 64
39-
40-
Type - Valid Quantities (IPv6)
41-
public - 64
33+
Long: T(`Valid quantities vary by type.
34+
- public IPv4: 4, 8, 16, 32
35+
- private IPv4: 4, 8, 16, 32, 64
36+
- public IPv6: 64
4237
4338
EXAMPLE:
44-
${COMMAND_NAME} sl subnet create public 16 567
45-
This command creates a public subnet with 16 IPv4 addresses and places it on vlan with ID 567.`),
39+
${COMMAND_NAME} sl subnet create public 16 567
40+
This command creates a public subnet with 16 IPv4 addresses and places it on vlan with ID 567.`),
4641
Args: metadata.ThreeArgs,
4742
RunE: func(cmd *cobra.Command, args []string) error {
4843
return thisCmd.Run(args)

plugin/commands/user/edit_notifications.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ func NewEditNotificationsCommand(sl *metadata.SoftlayerCommand) (cmd *EditNotifi
2929
cobraCmd := &cobra.Command{
3030
Use: "edit-notifications",
3131
Short: T("Enable or Disable specific notifications for the active user."),
32-
Long: T(`${COMMAND_NAME} sl user edit-notifications [OPTIONS] NOTIFICATIONS
33-
34-
Notification names should be enclosed in quotation marks. Examples:
35-
slcli user edit-notifications --enable 'Order Approved'
36-
slcli user edit-notifications --enable 'Order Approved' --enable 'Reload Complete'`),
32+
Long: T(`Notification names should be enclosed in quotation marks.
33+
EXAMPLE:
34+
slcli user edit-notifications --enable 'Order Approved'
35+
slcli user edit-notifications --enable 'Order Approved' --enable 'Reload Complete'`),
3736
RunE: func(cmd *cobra.Command, args []string) error {
3837
return thisCmd.Run(args)
3938
},

plugin/commands/virtual/upgrade.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,12 @@ func NewUpgradeCommand(sl *metadata.SoftlayerCommand) (cmd *UpgradeCommand) {
3535
cobraCmd := &cobra.Command{
3636
Use: "upgrade " + T("IDENTIFIER"),
3737
Short: T("Upgrade a virtual server instance"),
38-
Long: T(`${COMMAND_NAME} sl vs upgrade IDENTIFIER [OPTIONS]
39-
Note: Classic infrastructure service automatically reboots the instance once upgrade request is
40-
placed. The instance is halted until the upgrade transaction is completed.
41-
However for Network, no reboot is required.
38+
Long: T(`Note: This virtual server will be rebooted once the upgrade order is placed.
39+
The instance is halted until the upgrade transaction is completed. However for Network, no reboot is required.
4240
4341
EXAMPLE:
44-
${COMMAND_NAME} sl vs upgrade 12345678 -c 8 -m 8192 --network 1000
45-
This commands upgrades virtual server instance with ID 12345678 and set number of CPU cores to 8, memory to 8192M, network port speed to 1000 Mbps.`),
42+
${COMMAND_NAME} sl vs upgrade 12345678 -c 8 -m 8192 --network 1000
43+
This commands upgrades virtual server instance with ID 12345678 and set number of CPU cores to 8, memory to 8192M, network port speed to 1000 Mbps.`),
4644
Args: metadata.OneArgs,
4745
RunE: func(cmd *cobra.Command, args []string) error {
4846
return thisCmd.Run(args)

0 commit comments

Comments
 (0)