{"id":23138231,"url":"https://github.com/softlayer/softlayer-cli","last_synced_at":"2025-10-24T07:36:12.384Z","repository":{"id":49522863,"uuid":"396955695","full_name":"softlayer/softlayer-cli","owner":"softlayer","description":"This repository houses the code that powers the ibmcloud-cli sl command","archived":false,"fork":false,"pushed_at":"2024-11-25T16:18:38.000Z","size":247357,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-25T16:42:17.800Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/softlayer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-16T20:12:07.000Z","updated_at":"2024-11-25T15:58:20.000Z","dependencies_parsed_at":"2023-02-08T14:46:26.407Z","dependency_job_id":"a4720442-369d-4fa8-be8a-95d6c9d3c206","html_url":"https://github.com/softlayer/softlayer-cli","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softlayer%2Fsoftlayer-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softlayer%2Fsoftlayer-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softlayer%2Fsoftlayer-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softlayer%2Fsoftlayer-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/softlayer","download_url":"https://codeload.github.com/softlayer/softlayer-cli/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230121666,"owners_count":18176477,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-17T13:10:12.264Z","updated_at":"2025-10-24T07:36:07.349Z","avatar_url":"https://github.com/softlayer.png","language":"Go","readme":"[![Build Status](https://v3.travis.ibm.com/SoftLayer/softlayer-cli.svg?token=96jYRp3ei3sE2H3zUgDN\u0026branch=master)](https://travis.ibm.com/SoftLayer/softlayer-cli)\n\n\n# softlayer-cli\n\nThis repository houses the code that powers the [ibmcloud-cli sl](https://github.ibm.com/Bluemix/bluemix-cli) command.\n[CLI Documentation](https://pages.github.ibm.com/SoftLayer/softlayer-cli)\n\n## Installation (official)\n\nThe 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:\n\n```bash\nibmcloud plugin install sl\n```\n\nTo update, simply run\n\n```bash\nibmcloud plugin update sl\n```\n\n## Installation (source build)\n\nTo install a version of the plugin built locally, you can do the following:\n\n1. Build the `sl` plugin binary\n2. `go build`\n3. Install the new `softlayer` binary\n4. `ibmcloud plugin install ./softlayer` (might need to put `./softlayer.exe` for windows installs)\n\nWhen 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.\n\n# Development Project Setup\n\nClone the repo, then just run `go mod vendor` and `go build` and you should have a running binary for the `sl` plugin.\n\n\n## Testing\nBefore making a pull request, make sure everything looks good with these tools.\nWorking directory: `$GO_PATH/src/github.ibm.com/SoftLayer/softlayer-cli`\n\n### What the build runs\n\n```\ngo vet $(go list ./... | grep -v \"fixtures\" | grep -v \"vendor\")\ngo test $(go list ./... | grep -v \"fixtures\" | grep -v \"vendor\")\n```\n\n### Individual Tests\n\nThis will test all the block commands, with verbose output\n```\ngo test -v github.ibm.com/SoftLayer/softlayer-cli/plugin/commands/\u003ccommand_group\u003e\ngo test -v github.ibm.com/SoftLayer/softlayer-cli/plugin/managers\n```\n\nThis will test only the block commands that have \"Access Password\" in their test name, and stop after 1 failure\n```\ngo test -v  github.ibm.com/SoftLayer/softlayer-cli/plugin/commands/block -ginkgo.failFast  -ginkgo.focus \"Access Password\"\n```\n\n### Code Coverage\n\nThis will generate a code coverage report for all the file commands\n\n```\n$\u003e go test -coverprofile=coverage.out github.ibm.com/SoftLayer/softlayer-cli/plugin/commands/file\nok      github.ibm.com/SoftLayer/softlayer-cli/plugin/commands/file       1.225s\n```\n\nCoverage report\n\nFor basic information\n```\ngo tool cover -func=coverage.out\n```\n\nDetailed HTML output\n\n```\ngo tool cover -html=coverage.out\n```\n\nSpecific Tests\n\n```\ngo test -v -coverprofile=coverage.out github.ibm.com/SoftLayer/softlayer-cli/plugin/managers -ginkgo.focus Issues3190\n\n```\n\n### Fake Session And Handlers\n\nTo force API errors, or api results that you don't want to put in a fixture, you need to get the testhelper transport handler, something like this.\n\n```go\n    var (\n        fakeSLSession  *session.Session\n        fakeHandler     *testhelpers.FakeTransportHandler\n        // Other fake managers/CLI things go here\n    )\n    BeforeEach(func() {\n        fakeSLSession = testhelpers.NewFakeSoftlayerSession(nil)\n        fakeHandler = testhelpers.GetSessionHandler(fakeSLSession)\n        // Other fake managers/CLI things go here\n    })\n    AfterEach(func() {\n        fakeHandler.ClearApiCallLogs()\n        fakeHandler.ClearErrors()\n    })\n```\n\n`fakeSLSession` will get used anytime something requres a softlayer-go session.\n`fakeHandler` is responsible for \"faking\" the API requests. By default it does this by looking up the appropriate JSON file in `testfixtures/\u003cSERVICE\u003e/\u003cMETHOD\u003e.json`. It is also possible to specify specific IDs by using this format: `testfixtures/\u003cSERVICE\u003e/\u003cMETHOD\u003e-\u003cID\u003e.json` which if you call `SoftLayer_Hardware/getObject(id=1234)` it will load `testfixtures/SoftLayer_Hardware/GetObject-1234.json`\n\n\n\n#### Forcing an API Error\n\nIf you want to force an API error, do something like the following.\n\n(From managers/hardware_tests.go)\n```go\n// Add the API error to the handler\nfakeHandler.AddApiError(\"SoftLayer_Hardware_Server\", \"toggleManagementInterface\", 500, \"IPMI ERROR\")\n// Make the API call\nerr := hardwareManager.ToggleIPMI(123456, false)\n// Make sure the error happened\nExpect(err).To(HaveOccurred())\n// Check the error message is as expected. The format will be similar to this\nExpect(err.Error()).To(Equal(\"IPMI ERROR: IPMI ERROR (HTTP 500)\"))\n```\n\n#### Checking for API calls\n\n(from managers/hardware_test.go)\n\nIf you want to make sure an API call was properly formatted and made, do the following\n```go\n// Make the API call\nhws, err := hardwareManager.ListHardware(...args)\n// Normal Checks...\nExpect(err).NotTo(HaveOccurred())\nExpect(len(hws)).To(Equal(2))\n// Get the apiCalls from the fakeHandler\napiCalls := fakeHandler.ApiCallLogs\n// Make sure there was the right number of calls\nExpect(len(apiCalls)).To(Equal(1))\n// Check the service is correct \nExpect(apiCalls[0].Service).To(Equal(\"SoftLayer_Account\"))\n// get the slOptions\nslOptions := apiCalls[0].Options\n// Check to make sure all object filters get set properly.\nExpect(slOptions.Filter).To(ContainSubstring(`\"id\":{\"operation\":\"orderBy\",\"options\":[{\"name\":\"sort\",\"value\":[\"DESC\"]}]}`))\n```\nCheck testhelpers/fake_softlayer_session.go for all the fields that get recorded with an API call.\n\n\nHeres a fancy way to test an API call matches a few different properties at the same time:\n\n```go\n// This is where the MatchFields/PointTo come from\n. \"github.com/onsi/gomega/gstruct\"\n\nIt(\"it returns dedicatedhost verify response\", func() {\n    err := dedicatedhostManager.DeleteHost(12345)\n    Expect(err).NotTo(HaveOccurred())\n    apiCalls := fakeHandler.ApiCallLogs\n    Expect(len(apiCalls)).To(Equal(1))\n    Expect(apiCalls[0]).To(MatchFields(IgnoreExtras, Fields{\n        \"Service\": Equal(\"SoftLayer_Virtual_DedicatedHost\"),\n        \"Method\":  Equal(\"deleteObject\"),\n        \"Options\": PointTo(MatchFields(IgnoreExtras, Fields{\"Id\": PointTo(Equal(12345))})),\n    }))\n})\n\n```\n\n### Test Fakes\n\n\nCLI calls to manager functions need an entry in `plugin\\testhelpers\\fake_manager.go` \nManagers have a fake/test interface that is autogenerate with a program called [couterfieter](https://github.com/maxbrunsfeld/counterfeiter)\n\n```\ngo generate ./...\n```\n\n\neach manager and defined interface should have this line in it to be automatically generated. After the imports, before any interfaces\n\n```\n//counterfeiter:generate -o ../testhelpers/ . \u003cWhatever\u003eManager\n```\n\n\n\nIf you want to use the real manager but fixture API data, just initialize the manager like this in the CLI test\n\nThis example is from `plugin\\commands\\account\\invoice-detail_test.go`\n```go\nvar _ = Describe(\"\u003cCOMMAND\u003e Tests\", func() {\n    var (\n        fakeUI          *terminal.FakeUI\n        cliCommand      *account.InvoiceDetailCommand\n        fakeSession     *session.Session\n        slCommand       *metadata.SoftlayerCommand\n        fakeHandler     *testhelpers.FakeTransportHandler\n    )\n    BeforeEach(func() {\n        // Fake UI to capture output of comamnds\n        fakeUI = terminal.NewFakeUI()\n        // Fake session to handle loading data from testfixtures\n        fakeSession = testhelpers.NewFakeSoftlayerSession(nil)\n        // Fake handler to control error generation\n        fakeHandler = testhelpers.GetSessionHandler(fakeSession)\n        // Real parent command, with fake UI and Fake Session being passed in\n        slCommand = metadata.NewSoftlayerCommand(fakeUI, fakeSession)\n        // Real actual command\n        cliCommand = account.NewInvoiceDetailCommand(slCommand)\n        // Need to set output flag since its set manually in the parent command normally.\n        cliCommand.Command.PersistentFlags().Var(cliCommand.OutputFlag, \"output\", \"--output=JSON for json output.\")\n    })\n    AfterEach(func() {\n        // Clear API call logs and any errors that might have been set after every test\n        fakeHandler.ClearApiCallLogs()\n        fakeHandler.ClearErrors()\n    })\n```\n`plugin\\commands\\user\\details_test.go` is also a good example test file for CLI commands.\n\n### `[no tests to run]`\nNew commands needs a `command_test.go` file in the CLI directory.\n\nIf you added `slplugin/commands/new/` then there needs to be a `slplugin/commands/new/new_test.go` file. Copy the content from one of the other command test files and just change the name and package.\n\n### Fake Transports\n\nIn unit tests, you will want to establish a FakeSoftLayerSession object so that API requests faked from test fixtures.\n\nSomething like this.\n```go\nBeforeEach(func() {\n    fakeSLSession = testhelpers.NewFakeSoftlayerSession(nil)\n    networkManager = managers.NewNetworkManager(fakeSLSession)\n})\n```\n\nBy default, every API call made to the SoftLayer API will load in the appropraite JSON file from `testfixtures/SoftLayer_Service/method.json`\n\nTo force errors:\n\n```go\n// In the Top level BeforeEach\nfakeSession = testhelpers.NewFakeSoftlayerSession(nil)\nfakeHandler = testhelpers.GetSessionHandler(fakeSession)\n\n// Then in a BeforeEach for the specific test...\nBeforeEach(func() {\n    fakeHandler.AddApiError(\"SoftLayer_User_Customer\", \"getObject\", 500, \"Internal Server Error\")\n})\n```\n\nTo force a non-default JSON file to be loaded\n\nThis will load `testfixtures/SoftLayer_Network_Vlan/getObject-noBilling.json` when SoftLayer_Network_Vlan::getObject is called next.\n\n```go\nfakeSLSession = testhelpers.NewFakeSoftlayerSession([]string{\"getObject-noBilling.json\"})\nnetworkManager = managers.NewNetworkManager(fakeSLSession)\n```\n\nFixutres can also be loaded by ID automatically with the format `testfixtures/SoftLayer_Service/getObject-1234.json` where 1234 is the ID you passed into the API call.\n\n\n# Development\n\n![Basic Architecture](./cli_arch.png)\n![Code Flow](./cli_codeflow.png)\n\n\u003e Terminology:\n\u003e `ibmcloud sl \u003cCOMMAND\u003e \u003cACTION\u003e`\n\u003e *COMMAND*: is a collection of actions here.\n\u003e *ACTION*: What part of the command you are running.\n\n## Adding new commands to slplugin\n\n1. Add an entry to `plugin/plugin.go` in the `getTopCobraCommand()` function that follows this pattern\n`cobraCmd.AddCommand(newcommand.SetupCobraCommands(slCommand))`\n\n2. Create a new folder `plugin/commands/newcommand/`\n3. Create a new file `plugin/commands/newcommand/newcommand.go` Which will look like this:\n```go\npackage newcommand\n\nimport (\n    \"github.com/spf13/cobra\"\n    \"github.com/IBM-Cloud/ibm-cloud-cli-sdk/plugin\"\n    . \"github.ibm.com/SoftLayer/softlayer-cli/plugin/i18n\"\n    \"github.ibm.com/SoftLayer/softlayer-cli/plugin/metadata\"\n)\n\nfunc SetupCobraCommands(sl *metadata.SoftlayerCommand) *cobra.Command {\n    cobraCmd := \u0026cobra.Command{\n        Use:   \"newcommand\",\n        Short: T(\"A description of the new command\"),\n        RunE:  nil,\n    }\n    cobraCmd.AddCommand(NewSomeNewCommand(sl).Command)\n    return cobraCmd\n}\n\nfunc AccountNamespace() plugin.Namespace {\n    return plugin.Namespace{\n        ParentName:  \"sl\",\n        Name:        \"newcommand\",\n        Description: T(\"A description of the new command\"),\n    }\n}\n```\nfor tests, copy from one of the other command main test functions. Make sure to add any actions to the actions list.\n\n## Adding new actions to slplugin\n\n1. Create a new files `plugin/commands/the_command/action.go`\n2. It should have its own type\n```go\ntype ActionNameCommand struct {\n    *metadata.SoftlayerCommand\n    Command *cobra.Command\n    Manager managers.SomeManager\n    // Flags go here as well\n}\n```\n\n3. It should have a function to create an instance of the type called `NewActionNameCommand`\n```go\nfunc NewActionNameCommand(sl *metadata.SoftlayerCommand) *ActionNameCommand {\n    thisCmd := \u0026ActionNameCommand{\n        SoftlayerCommand: sl,\n        Manager: managers.NewSomeManager(sl.Session),\n    }\n    cobraCmd := \u0026cobra.Command{\n        Use: \"command-name\",\n        Short: T(\"A description of the command\"),\n        Long: T(`This is an optional field, you can remove it if the command is simple.\nOtherwise create a nice long description of how to use this command. Its good to add some examples.`),\n        Example: fmt.Sprintf(`${COMMAND_NAME} sl newcommand command-name --someFlag test --soomethingElse\n    %s`, T(`This sets a flag and does something else.`)),\n        Args: metadata.NoArgs,\n        RunE: func(cmd *cobra.Command, args []string) error {\n            return thisCmd.Run(args)\n        },\n    }\n    thisCmd.Command = cobraCmd\n    return thisCmd\n}\n```\n\n\n4. It should have a `Run()` function\n```go\nfunc (cmd *BandwidthPoolsCommand) Run(args []string) error {\n    // do some stuff\n    return nil\n}\n```\n\n5. Add the function to `command.go` in the `SetupCobraCommand` function\n```go\ncobraCmd.AddCommand(NewActionNameCommand(sl).Command)\n```\n\n## i18n stuff\n\nanything with `T(\"some string here\")` uses the internationalization system. Specifically we use the [goi18n/v2](https://github.com/nicksnyder/go-i18n) library for most work here.\n\nCurrently we use a custom version of [goi18n](https://github.com/allmightyspiff/go-i18n/tree/Tfunctions) which can parse `T()` functions like we use (an artiface of migrating from v1 to v2). The custom binary (`bin/goi18n2*`) has some code that forces the .json file it generates to be like the following, because otherwise the translations don't get loaded properly.\n\n```json\n{\n    \"words you want translated\" : {\n        \"other\": \"words you want translated\"\n    }\n}\n```\n\nThe changes are this for future reference:\n\n```\n ~/go/src/github.com/allmightyspiff/go-i18n (Tfunctions)\n$\u003e git diff goi18n/marshal.go\ndiff --git a/goi18n/marshal.go b/goi18n/marshal.go\nindex a6cc762..a256f2b 100644\n--- a/goi18n/marshal.go\n+++ b/goi18n/marshal.go\n@@ -28,7 +28,9 @@ func marshalValue(messageTemplates map[string]*i18n.MessageTemplate, sourceLangu\n        for id, template := range messageTemplates {\n                if other := template.PluralTemplates[plural.Other]; sourceLanguage \u0026\u0026 len(template.PluralTemplates) == 1 \u0026\u0026\n                        other != nil \u0026\u0026 template.Description == \"\" \u0026\u0026 template.LeftDelim == \"\" \u0026\u0026 template.RightDelim == \"\" {\n-                       v[id] = other.Src\n+                       m := map[string]string{}\n+                       m[\"other\"] = other.Src\n+                       v[id] = m\n                } else {\n                        m := map[string]string{}\n                        if template.Description != \"\" {\n```\n\nTo generate the en-US.json file, just run\n\n```bash\npython bin/buildAndDeploy.py i18n\n```\n\nThe `plugin/i18n/v2Resources/active.*.json` files are all compiled into the binary automatically.\n\n### Basic Patterns and Tips\n\nWhere possible, you should try to minimize the number of unique strings we need to translate. To do this, make use of substitutions. For example:\n\nBAD:\n```go\nT(\"This is some output for a file command\")\nT(\"This is some output for a block command\")\n```\n\nGOOD:\n```go\nsubs := map[string]interface{}{\"CMDTYPE\": \"block\"}\nT(\"This is some output for a {{.CMDTYPE}} command\", subs)\n```\n\n*NOTICE* goi18n/v2 has some newer features that can make this a bit easier to deal with, but I'm not sure they are currently supported, so procede with caution in you make use of them.\n\nCommands that accept a list of options to display should have those subbed into the string, not hard coded.\n\nBAD:\n```go\ncobraCmd.Flags().StringVar(\u0026thisCmd.Sortby, \"sortby\", \"id\", T(\"Column to sort by. Options are: id, name, type, private_ip_address, source_subnet, host_iqn, username, password, allowed_host_id.\"))\n\n```\n\nGOOD:\n\n```go\ndefaultColumns := []string{\n    \"id\", \"name\", \"type\", \"private_ip_address\", \"source_subnet\",\n    \"host_iqn\", \"username\", \"password\",\"allowed_host_id\"\n}\n\ndefault_subs := map[string]interface{}{\"COLUMNS\": strings.join(defaultColumns, \", \")}\n\ncobraCmd.Flags().StringVar(\u0026thisCmd.Sortby, \"sortby\", \"id\",\n    T(\"Column to sort by. Options are: {{.COLUMNS}}.\", default_subs))\n```\n\n### Useful Scripts\n\n#### `./bin/buildAndDeploy.py i18n`\n\nShould in general take care of all these steps for you. The binaries for win/mac/linux should be in the repo.\n```\n$\u003e python bin/buildAndDeploy.py i18n\nRunning: C:\\Users\\allmi\\go\\src\\github.ibm.com\\softlayer\\softlayer-cli\\bin\\i18n4go.exe -c=checkup -q=i18n -v -d=C:\\Users\\allmi\\go\\src\\github.ibm.com\\softlayer\\softlayer-cli\\plugin\n        No Changes Needed!\nBuilding I18N: ./bin/go-bindata.exe -pkg=resources -o=plugin/resources/i18n_resources.go plugin/i18n/resources\n        OK!\n```\n\n\n\n# Vendor\n\nVendor files are now managed by `go mod vendor`, I had to set these environment variables to download github.ibm.com vendor objects. To update the github.com/softlayer/softlayer-go dependancy, update `go.mod` file.\n\n\nhttps://golang.org/doc/faq#git_https\n\n\n```bash\nexport GOPROXY=direct\nexport GOPRIVATE=github.ibm.com/*\n# Make sure you gitconfig has these lines\ncat ~/.gitconfig\n[url \"ssh://git@github.ibm.com/\"]\n        insteadOf = https://github.ibm.com/\ngo mod vendor\n\n```\n\nIf you get this error, check your GOPROXY and GOPRIVATE settings.\n```\n$ go mod vendor\ngo: github.ibm.com/Bluemix/cf-admin-cli@v0.0.0-20200515160705-accb00409d86: verifying go.mod: github.ibm.com/Bluemix/cf-admin-cli@v0.0.0-20200515160705-accb00409d86/go.mod: reading https://sum.golang.org/lookup/github.ibm.com/!bluemix/cf-admin-cli@v0.0.0-20200515160705-accb00409d86: 410 Gone\n        server response:\n        not found: github.ibm.com/Bluemix/cf-admin-cli@v0.0.0-20200515160705-accb00409d86: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /tmp/gopath/pkg/mod/cache/vcs/7c3b4597f53c7708d8d63068430570d5325f6ceef4fb0e2076cc6c593df4c01a: exit status 128:\n                fatal: could not read Username for 'https://github.ibm.com': terminal prompts disabled\n\n```\n\nALSO:\n\n\n\n## CLI Documentation\nTo make changes to the cli documentation, do so here: https://github.ibm.com/cloud-docs/cli/tree/draft/reference/ibmcloud\n\nDocs for the `sl` plugin specifically live in this repo: https://github.ibm.com/cloud-docs/cli\n\nin `docs/` there is a utility that will generate the appropriate pages for the sl plugin.\n\n(assuming `~/Code/ibm-cloud-docs-cl` is where the cloud-docs have been checked out to.)\n```\n./bin/buildAndDeploy.py docs ~/Code/ibm-cloud-docs-cli\n Building documentation builder:  go build -o docBuilder docs/main.go\n Building documentation: ./docBuilder -o C:/Users/allmi/Code/ibm-cloud-docs-cli/_include-segments -v\n```\n\n# Code Patterns\n\nHere are a list of common problems and what the code should look like if you need to solve them.\n\n## Checking that IDENTIFIER is an `id` on the CLI\n\n```go\nid, err := strconv.Atoi(args[0])\nif err != nil {\n    return slErrors.NewInvalidSoftlayerIdInputError(T(\"IDENTIFIER\"))\n}\n```\n\n## Setting fake manager returns for multiple method calls\n\n```go\nIt(\"return error\", func() {\n    fakeUserManager.GetUserReturnsOnCall(0, testUser, nil)\n    fakeUserManager.GetUserReturnsOnCall(1, datatypes.User_Customer{}, errors.New(\"BAD HARDWARE\"))\n    err := testhelpers.RunCobraCommand(cliCommand.Command, \"5555\", \"--hardware\")\n    Expect(err).To(HaveOccurred())\n    Expect(err.Error()).To(ContainSubstring(\"Failed to show hardware.\"))\n})\n```\n\n## Adding Examples\n\nUse the CobraCLI Example property when possible. `vs upgrade` for an example:\n\n```go\n    cobraCmd := \u0026cobra.Command{\n        Use:   \"upgrade \" + T(\"IDENTIFIER\"),\n        Short: T(\"Upgrade a virtual server instance\"),\n        Long: T(`Note: This virtual server will be rebooted once the upgrade order is placed.\nThe instance is halted until the upgrade transaction is completed. However for Network, no reboot is required.`) + `\n` + T(`The -c and -m options are for dedicated VSI upgrade, most VSIs will need to upgrade with --flavor.\nSee '${COMMAND_NAME} sl vs options' for flavor keyNames to use.`),\n        Example: `${COMMAND_NAME} sl vs upgrade 12345678 --flavor B1_8X32X25`,\n```\n\nThis helps make the strings a bit easier to translate.\n\n\n# Plugin Support / Release Process\nAfter v1.4.1 `sl` will be a normal plugin, so where are the instructions to build the plugin. \n\nUse the `./bin/buildAndDeploy.py` script to do a release:\n```\n$\u003e python bin/buildAndDeploy.py\nUsage: buildAndDeploy.py [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  build    Builds the SL binaries\n  deploy   Deploys the SL binaries\n  i18n     Checks and builds the i18n files\n  jenkins  Trigger a Jenkins build with existing files.\n  release  Builds, then deploys the release\n  test     Runs the tests\n```\n\n1. `./bin/buildAndDeploy.py test` : Runs all the tests, required to pass\n2. `./bin/buildAndDeploy.py i18n` : Fixing missing i18n problems, builds the i18n gobindata\n3. `./bin/buildAndDeploy.py build` : Generates binaries for all architectures in `./out`\n4. `./bin/buildAndDeploy.py deploy` : Uploads binaries to our object storage account [softlayer-cli-binaries](https://s3.us-east.cloud-object-storage.appdomain.cloud/softlayer-cli-binaries/index.html): \n5. `./bin/buildAndDeploy.py release` : Spins up the [Jenkins](https://wcp-cloud-foundry-jenkins.swg-devops.com/job/Publish%20Plugin%20to%20YS1/build ) job to publish a release\n6. Test then manually promote from staging to production to actually release the plugin. [Pomotion Jenkins](https://wcp-cloud-foundry-jenkins.swg-devops.com/job/Promote%20Plugin%20from%20staging%20to%20production/)\n\nENV Variables that need to be set:\n1. `JENKINS_TOKEN` : Auth token to run Jenkins. Username is hardcoded for me at the moment.\n2. `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`\n\n## Manually\n\nProcess is described in more detail on [bluemix-cli-repo](https://github.ibm.com/ibmcloud-cli/bluemix-cli-repo)\n\n1. Create the plugin changes with this [Jenkins Job](https://wcp-cloud-foundry-jenkins.swg-devops.com/job/Publish%20Plugin%20to%20YS1/build). \n2. That will create a pull request on the [bluemix-cli-repo](https://github.ibm.com/ibmcloud-cli/bluemix-cli-repo/pulls) for a STAGING version\n3. TEST\n4. [Promote from staging to product](https://wcp-cloud-foundry-jenkins.swg-devops.com/job/Promote%20Plugin%20from%20staging%20to%20production/) This will open a pull request that needs to be approved.\n\n# Documentation\n\n`/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).\n\nTo build the full docs locally, see https://test.cloud.ibm.com/docs-internal/writing?topic=writing-transform-local\n\n```bash\n➜  md-source pwd\n/Users/chris/Code/md-source\n➜  md-source ls -lh\ntotal 0\ndrwxr-xr-x  3 chris  staff    96B Nov 30 12:58 build\ndrwxr-xr-x  3 chris  staff    96B Nov 30 13:01 input\ndrwxr-xr-x  4 chris  staff   128B Nov 30 13:01 output\n\n➜ 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\n```\n\n\n## TODO\nAutomate build with https://github.ibm.com/coligo/cli/tree/main/script\n\n\n## Detect Secrets\nMake sure to add the pre-commit hook by running  `pre-commit install`\n\nTo run a scan do:\n```bash\ndetect-secrets scan --update .secrets.baseline\n```\n\nIf we need to update the excluded files (these are saved in the .secrets.baseline file) do this:\n```bash\ndetect-secrets -v scan --update .secrets.baseline  --exclude-files \"plugin/i18n/v1Resources/|plugin/i18n/v2Resources/|(.*test.*)|(vendor)|(go.sum)|bin/\"\n```\n\n\n# Commit Signing\nAlways a good idea to sign commits, even if it takes a bit to setup.\n\n1. `git config --global commit.gpgsign true` Enabled signed commits\n2. `gpg --full-generate-key` Create a GPG key to sign commits with (use your github email)\n3. `git commit --message=\"THE MESSAGE\"` is your normal commit, but should now be signed! You may be prompted for a password\n4. `git log --show-signature` confirm its signed.\n5. `gpg --output email.pgp --armor --export email@domain.com` will export your PUBLIC key so you can upload it to github.\n\n\n## Windows setup\n\nIn windows GPG pops up an annoying window for password prompting. Following [Using Command-Line Passphrase Input for GPG with Git](https://betakuang.medium.com/using-command-line-passphrase-input-for-gpg-with-git-for-windows-f78ae2c7cd2e) to be prompted on the CLI\n\n```\n$\u003e  cat ~/.gnupg/gpg-agent.conf\nallow-loopback-pinentry\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftlayer%2Fsoftlayer-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoftlayer%2Fsoftlayer-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftlayer%2Fsoftlayer-cli/lists"}