├── .github
├── ISSUE_TEMPLATE
│ └── issue.md
└── workflows
│ ├── dist.yml
│ ├── generate-docs.yml
│ ├── go-releaser.yml
│ ├── integration-test.yml
│ ├── pr-validation.yml
│ └── release-please.yml
├── .gitignore
├── .goreleaser.yaml
├── .octopus
├── deployment_process.ocl
├── deployment_settings.ocl
└── schema_version.ocl
├── CHANGELOG.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── DISTRIBUTION.md
├── LICENSE
├── Makefile
├── README.md
├── build
├── cli.nuspec
├── tools
│ ├── LICENSE.txt
│ ├── VERIFICATION.txt
│ ├── chocolateyInstall.ps1
│ └── icon.png
└── windows
│ ├── octopus.wixproj
│ ├── octopus.wxs
│ └── ui.wxs
├── cmd
├── gen-docs
│ └── main.go
└── octopus
│ └── main.go
├── examples.md
├── go.mod
├── go.sum
├── logo.png
├── pkg
├── apiclient
│ ├── apiclient_test.go
│ ├── client_factory.go
│ ├── client_factory_test.go
│ ├── requester.go
│ └── spinner_round_tripper.go
├── cmd
│ ├── account
│ │ ├── account.go
│ │ ├── aws
│ │ │ ├── aws.go
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ └── list
│ │ │ │ └── list.go
│ │ ├── azure-oidc
│ │ │ ├── azure-oidc.go
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ └── list
│ │ │ │ └── list.go
│ │ ├── azure
│ │ │ ├── azure.go
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ └── list
│ │ │ │ └── list.go
│ │ ├── create
│ │ │ └── create.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── gcp
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── gcp.go
│ │ │ └── list
│ │ │ │ └── list.go
│ │ ├── generic-oidc
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── generic-oidc.go
│ │ │ └── list
│ │ │ │ └── list.go
│ │ ├── helper
│ │ │ └── helper.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── shared
│ │ │ └── shared.go
│ │ ├── ssh
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ └── ssh.go
│ │ ├── token
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ └── token.go
│ │ └── username
│ │ │ ├── create
│ │ │ ├── create.go
│ │ │ └── create_test.go
│ │ │ ├── list
│ │ │ └── list.go
│ │ │ └── username.go
│ ├── buildinformation
│ │ ├── build-information.go
│ │ ├── bulkdelete
│ │ │ └── bulk-delete.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── shared
│ │ │ └── shared.go
│ │ ├── upload
│ │ │ └── upload.go
│ │ └── view
│ │ │ └── view.go
│ ├── channel
│ │ ├── channel.go
│ │ └── create
│ │ │ └── create.go
│ ├── config
│ │ ├── config.go
│ │ ├── get
│ │ │ └── get.go
│ │ ├── list
│ │ │ └── list.go
│ │ └── set
│ │ │ └── set.go
│ ├── dependencies.go
│ ├── environment
│ │ ├── create
│ │ │ └── create.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── environment.go
│ │ └── list
│ │ │ └── list.go
│ ├── login
│ │ ├── login.go
│ │ └── login_test.go
│ ├── logout
│ │ ├── logout.go
│ │ └── logout_test.go
│ ├── model
│ │ └── entity.go
│ ├── package
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── list
│ │ │ ├── list.go
│ │ │ └── list_test.go
│ │ ├── nuget
│ │ │ ├── create
│ │ │ │ └── create.go
│ │ │ └── nuget.go
│ │ ├── package.go
│ │ ├── support
│ │ │ ├── pack.go
│ │ │ └── pack_test.go
│ │ ├── upload
│ │ │ ├── upload.go
│ │ │ └── upload_test.go
│ │ ├── versions
│ │ │ ├── versions.go
│ │ │ └── versions_test.go
│ │ └── zip
│ │ │ ├── create
│ │ │ └── create.go
│ │ │ └── zip.go
│ ├── project
│ │ ├── branch
│ │ │ ├── branch.go
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ └── shared
│ │ │ │ └── shared.go
│ │ ├── clone
│ │ │ └── clone.go
│ │ ├── connect
│ │ │ └── connect.go
│ │ ├── convert
│ │ │ ├── convert.go
│ │ │ └── convert_test.go
│ │ ├── create
│ │ │ ├── create.go
│ │ │ ├── create_opts.go
│ │ │ └── create_test.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── disable
│ │ │ └── disable.go
│ │ ├── disconnect
│ │ │ └── disconnect.go
│ │ ├── enable
│ │ │ └── enable.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── project.go
│ │ ├── shared
│ │ │ ├── shared.go
│ │ │ └── shared_test.go
│ │ ├── variables
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── delete
│ │ │ │ └── delete.go
│ │ │ ├── exclude
│ │ │ │ └── exclude.go
│ │ │ ├── include
│ │ │ │ └── include.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── shared
│ │ │ │ ├── input.go
│ │ │ │ ├── input_test.go
│ │ │ │ └── scopes.go
│ │ │ ├── update
│ │ │ │ ├── update.go
│ │ │ │ └── update_test.go
│ │ │ ├── variables.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ └── view
│ │ │ └── view.go
│ ├── projectgroup
│ │ ├── create
│ │ │ └── create.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── project-group.go
│ │ └── view
│ │ │ └── view.go
│ ├── release
│ │ ├── create
│ │ │ ├── create.go
│ │ │ └── create_test.go
│ │ ├── delete
│ │ │ ├── delete.go
│ │ │ └── delete_test.go
│ │ ├── deploy
│ │ │ ├── deploy.go
│ │ │ └── deploy_test.go
│ │ ├── list
│ │ │ ├── list.go
│ │ │ └── list_test.go
│ │ ├── progression
│ │ │ ├── allow
│ │ │ │ └── allow.go
│ │ │ ├── prevent
│ │ │ │ └── prevent.go
│ │ │ ├── progression.go
│ │ │ └── shared
│ │ │ │ └── shared.go
│ │ └── release.go
│ ├── root
│ │ ├── help.go
│ │ └── root.go
│ ├── runbook
│ │ ├── delete
│ │ │ ├── delete.go
│ │ │ └── delete_test.go
│ │ ├── list
│ │ │ ├── list.go
│ │ │ └── list_test.go
│ │ ├── run
│ │ │ ├── run.go
│ │ │ └── run_test.go
│ │ ├── runbook.go
│ │ ├── shared
│ │ │ └── shared.go
│ │ └── snapshot
│ │ │ ├── create
│ │ │ └── create.go
│ │ │ ├── list
│ │ │ └── list.go
│ │ │ ├── publish
│ │ │ └── publish.go
│ │ │ └── snapshot.go
│ ├── space
│ │ ├── create
│ │ │ ├── create.go
│ │ │ └── create_test.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── space.go
│ │ └── view
│ │ │ └── view.go
│ ├── target
│ │ ├── azure-web-app
│ │ │ ├── azure-web-app.go
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── cloud-region
│ │ │ ├── cloud-region.go
│ │ │ ├── create
│ │ │ │ └── create.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── kubernetes
│ │ │ ├── create
│ │ │ │ ├── create.go
│ │ │ │ └── create_test.go
│ │ │ ├── kubernetes.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── listening-tentacle
│ │ │ ├── create
│ │ │ │ └── create.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── listening-tentacle.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── polling-tentacle
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── polling-tentacle.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── shared
│ │ │ ├── environment.go
│ │ │ ├── environment_test.go
│ │ │ ├── role.go
│ │ │ ├── role_test.go
│ │ │ ├── target.go
│ │ │ ├── tenant.go
│ │ │ ├── tenant_test.go
│ │ │ ├── view.go
│ │ │ ├── workerpool.go
│ │ │ └── workerpool_test.go
│ │ ├── ssh
│ │ │ ├── create
│ │ │ │ └── create.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── ssh.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── target.go
│ │ └── view
│ │ │ └── view.go
│ ├── task
│ │ ├── task.go
│ │ └── wait
│ │ │ ├── task_output_formatter.go
│ │ │ ├── wait.go
│ │ │ └── wait_test.go
│ ├── tenant
│ │ ├── clone
│ │ │ ├── clone.go
│ │ │ └── clone_test.go
│ │ ├── connect
│ │ │ ├── connect.go
│ │ │ └── connect_test.go
│ │ ├── create
│ │ │ ├── create.go
│ │ │ ├── create_opts.go
│ │ │ └── create_test.go
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── disable
│ │ │ └── disable.go
│ │ ├── disconnect
│ │ │ ├── disconnect.go
│ │ │ └── disconnect_test.go
│ │ ├── enable
│ │ │ └── enable.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── shared
│ │ │ └── shared.go
│ │ ├── tag
│ │ │ ├── tag.go
│ │ │ └── tag_opts.go
│ │ ├── tenant.go
│ │ ├── variables
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── update
│ │ │ │ ├── update.go
│ │ │ │ └── update_test.go
│ │ │ └── variables.go
│ │ └── view
│ │ │ └── view.go
│ ├── user
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── list
│ │ │ └── list.go
│ │ └── user.go
│ ├── version
│ │ └── version.go
│ ├── worker
│ │ ├── delete
│ │ │ └── delete.go
│ │ ├── list
│ │ │ └── list.go
│ │ ├── listening-tentacle
│ │ │ ├── create
│ │ │ │ └── create.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── listening-tentacle.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── polling-tentacle
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── polling-tentacle.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── shared
│ │ │ ├── view.go
│ │ │ ├── worker.go
│ │ │ ├── workerpool.go
│ │ │ └── workerpool_test.go
│ │ ├── ssh
│ │ │ ├── create
│ │ │ │ └── create.go
│ │ │ ├── list
│ │ │ │ └── list.go
│ │ │ ├── ssh.go
│ │ │ └── view
│ │ │ │ └── view.go
│ │ ├── view
│ │ │ └── view.go
│ │ └── worker.go
│ └── workerpool
│ │ ├── delete
│ │ └── delete.go
│ │ ├── dynamic
│ │ ├── create
│ │ │ ├── create.go
│ │ │ └── create_test.go
│ │ ├── dynamic.go
│ │ └── view
│ │ │ └── view.go
│ │ ├── list
│ │ └── list.go
│ │ ├── shared
│ │ ├── view.go
│ │ └── workerpool.go
│ │ ├── static
│ │ ├── create
│ │ │ ├── create.go
│ │ │ └── create_test.go
│ │ ├── static.go
│ │ └── view
│ │ │ └── view.go
│ │ ├── view
│ │ └── view.go
│ │ └── workerpool.go
├── config
│ ├── config.go
│ └── provider.go
├── constants
│ ├── annotations
│ │ └── annotations.go
│ └── constants.go
├── errors
│ └── errors.go
├── executionscommon
│ ├── executionscommon.go
│ └── executionscommon_test.go
├── executor
│ ├── account.go
│ ├── executor.go
│ ├── release.go
│ └── runbook.go
├── factory
│ └── factory.go
├── gitresources
│ ├── gitResources.go
│ └── gitResources_test.go
├── machinescommon
│ ├── comms.go
│ ├── machinepolicy.go
│ ├── machinepolicy_test.go
│ ├── proxy.go
│ ├── proxy_test.go
│ ├── ssh_common.go
│ ├── ssh_common_test.go
│ └── web.go
├── output
│ ├── color.go
│ ├── format.go
│ ├── print_array.go
│ ├── print_resource.go
│ ├── shared.go
│ ├── table.go
│ └── truncate.go
├── packages
│ └── packages.go
├── question
│ ├── ask.go
│ ├── helpers_test.go
│ ├── input.go
│ ├── input_test.go
│ ├── select.go
│ ├── select_test.go
│ ├── selectors
│ │ ├── channels.go
│ │ ├── environments.go
│ │ ├── gitCredentialStorage.go
│ │ ├── git_references.go
│ │ ├── lifecycles.go
│ │ ├── projects.go
│ │ ├── runbooks.go
│ │ ├── selector_test.go
│ │ └── selectors.go
│ └── shared
│ │ └── variables
│ │ └── variables.go
├── surveyext
│ ├── datepicker.go
│ ├── editor.go
│ ├── multiselectwithadd.go
│ ├── paginate.go
│ └── select.go
├── usage
│ └── usage.go
├── util
│ ├── featuretoggle
│ │ └── feature_toggles.go
│ ├── flag
│ │ └── flag.go
│ ├── pflagaliases.go
│ ├── pflagaliases_test.go
│ ├── pipe.go
│ ├── util.go
│ └── util_test.go
└── validation
│ ├── validation.go
│ └── validation_test.go
├── releases.json
├── scripts
└── install.sh
├── test
├── fixtures
│ └── projects.go
├── integration
│ ├── account_test.go
│ ├── fixtures.go
│ ├── integrationtestutil.go
│ └── release_test.go
└── testutil
│ ├── fakefactory.go
│ ├── fakeoctopusserver.go
│ ├── fakesurvey.go
│ └── testutil.go
├── version.go
└── version.txt
/.github/ISSUE_TEMPLATE/issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: For reporting bug/issues
4 | title: ""
5 | labels: kind/bug
6 | assignees: ""
7 | ---
8 |
9 | ## The bug
10 |
11 |
12 |
13 | ## Command to reproduce
14 |
15 |
16 |
17 | ```
18 | insert command here
19 | ```
20 |
21 | ## Outcome
22 |
23 |
24 |
25 | ```
26 | insert output here
27 | ```
28 |
29 | ## Versions
30 |
31 | **cli**:
32 |
33 | **Octopus Server**:
34 |
35 | ## Links
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.github/workflows/dist.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - release-please--**
5 | name: "Update Install Script Version"
6 | jobs:
7 | build:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v3
11 | with:
12 | token: ${{ secrets.INTEGRATIONS_FNM_BOT_TOKEN }}
13 | - name: Update Install Script
14 | run: |-
15 | NEW_VERSION="v$(cat version.txt)"
16 | sed -i "s/\(VERSION:-\)v[0-9]*\.[0-9]*\.[0-9]*/\1${NEW_VERSION}/g" scripts/install.sh
17 | - name: Commit
18 | run: |-
19 | git config --global user.name "team-integrations-fnm-bot"
20 | git config user.email 'integrationsfnmbot@octopus.com'
21 | git add scripts/install.sh
22 | git diff-index --quiet HEAD || (git commit -m "chore: update install script version" && git push origin)
23 |
24 |
--------------------------------------------------------------------------------
/.github/workflows/generate-docs.yml:
--------------------------------------------------------------------------------
1 | name: 'Generate Docs'
2 | on:
3 | workflow_dispatch:
4 | env:
5 | DOCS_TOKEN: ${{ secrets.DOCS_TOKEN }}
6 | jobs:
7 | generate-docs:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Calculate branch name
11 | id: branch-name
12 | run: echo "BRANCH_NAME=enh-cliupdate-octopus-$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_ENV
13 |
14 | - name: Checkout CLI
15 | uses: actions/checkout@v3
16 | with:
17 | path: cli
18 |
19 | - name: Checkout docs
20 | uses: actions/checkout@v3
21 | with:
22 | repository: OctopusDeploy/docs
23 | token: ${{ env.DOCS_TOKEN }}
24 | path: docs
25 |
26 | - name: Setup docs repo
27 | working-directory: docs
28 | run: |
29 | git config user.email 'bob@octopus.com'
30 | git config user.name octobob
31 | git checkout main
32 | git checkout -b $BRANCH_NAME
33 |
34 | - name: Set up Go
35 | uses: actions/setup-go@v3
36 | with:
37 | go-version: 1.21
38 |
39 | - name: Generate cli docs
40 | working-directory: cli
41 | run: mkdir -p ../docs/src/pages/docs/octopus-rest-api/cli && go run cmd/gen-docs/main.go --website --doc-path ../docs/src/pages/docs/octopus-rest-api/cli --relative-base-path /docs/octopus-rest-api/cli
42 |
43 | - name: Commit
44 | uses: EndBug/add-and-commit@v9
45 | with:
46 | message: 'Update cli docs for octopus(go)'
47 | author_name: Bob
48 | author_email: bob@octopus.com
49 | committer_name: bob
50 | cwd: docs
51 | add: src/pages/docs/octopus-rest-api/cli
52 | push: false
53 |
54 | - run: git push --repo https://octobob:$DOCS_TOKEN@github.com/OctopusDeploy/docs.git --set-upstream origin $BRANCH_NAME
55 | working-directory: docs
56 |
57 | - name: Create PR
58 | run: |
59 | curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $DOCS_TOKEN" \
60 | https://api.github.com/repos/OctopusDeploy/docs/pulls \
61 | -d '{"title":"update go-cli docs","body":"An automated update of the command line docs for octopus CLI\nCreated by GitHub Actions [Generate Docs](https://github.com/OctopusDeploy/cli/actions/workflows/generate-docs.yml)","head":"'$BRANCH_NAME'","base":"main"}'
62 |
--------------------------------------------------------------------------------
/.github/workflows/pr-validation.yml:
--------------------------------------------------------------------------------
1 | name: 'PR and Commit Validation'
2 | on:
3 | workflow_dispatch:
4 | push:
5 | paths-ignore:
6 | - '**.md'
7 | - "releases.json"
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 |
12 | permissions: # https://github.com/dorny/test-reporter/issues/168
13 | statuses: write
14 | checks: write
15 | steps:
16 | - uses: actions/checkout@v3
17 |
18 | - name: Set up Go
19 | uses: actions/setup-go@v3
20 | with:
21 | go-version: 1.21
22 |
23 | # if we just run the unit tests then go doesn't compile the parts of the app that aren't covered by
24 | # unit tests; this forces it
25 | - name: Build binary
26 | run: go build -o bin/octopus cmd/octopus/main.go
27 |
28 | - name: Setup gotestsum
29 | run: go install gotest.tools/gotestsum@latest
30 |
31 | - name: Unit Tests
32 | run: gotestsum --format testname --junitfile ../unit-tests.xml
33 | working-directory: ./pkg
34 |
35 | - name: Test Report
36 | uses: dorny/test-reporter@v1
37 | if: success() || failure()
38 | with:
39 | name: Test Results
40 | path: '*-tests.xml'
41 | reporter: java-junit
42 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - main
5 | name: release-please
6 | jobs:
7 | release-please-release:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: google-github-actions/release-please-action@v3
11 | id: release
12 | with:
13 | release-type: simple
14 | token: ${{ secrets.INTEGRATIONS_FNM_BOT_TOKEN }}
15 | command: github-release
16 | release-please-pr:
17 | runs-on: ubuntu-latest
18 | needs:
19 | - release-please-release
20 | steps:
21 | - id: release-pr
22 | uses: google-github-actions/release-please-action@v3
23 | with:
24 | token: ${{ secrets.INTEGRATIONS_FNM_BOT_TOKEN }}
25 | release-type: simple
26 | command: release-pr
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 | bin/
8 |
9 | # Test binary, built with `go test -c`
10 | *.test
11 |
12 | # Output of the go coverage tool, specifically when used with LiteIDE
13 | *.out
14 |
15 | # Dependency directories (remove the comment below to include it)
16 | # vendor/
17 | .vscode
18 | dist/
19 | .idea
20 | .env
21 |
22 | cmd/octopus/octopus
23 | cmd/gen-docs/gen-docs
--------------------------------------------------------------------------------
/.octopus/deployment_settings.ocl:
--------------------------------------------------------------------------------
1 | deployment_changes_template = <<-EOT
2 | #{each release in Octopus.Deployment.Changes}
3 | #{release.ReleaseNotes}
4 | #{/each}
5 | EOT
6 |
7 | connectivity_policy {
8 | }
9 |
10 | versioning_strategy {
11 |
12 | donor_package {
13 | package = "cli"
14 | step = "push-cli-to-chocolatey"
15 | }
16 | }
--------------------------------------------------------------------------------
/.octopus/schema_version.ocl:
--------------------------------------------------------------------------------
1 | version = 9
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @OctopusDeploy/team-integrations-fnm
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) Octopus Deploy and contributors. All rights reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4 | these files except in compliance with the License. You may obtain a copy of the
5 | License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software distributed
10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
12 | specific language governing permissions and limitations under the License.
13 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | EXE=
2 | ifeq ($(GOOS),windows)
3 | EXE = .exe
4 | endif
5 |
6 | RMCMD = rm -rf
7 | ifeq ($(GOOS),windows)
8 | rmdir /s /q bin/
9 | endif
10 |
11 | ifeq (run,$(firstword $(MAKECMDGOALS)))
12 | RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
13 | $(eval $(RUN_ARGS):;@:)
14 | endif
15 |
16 | .PHONY: bin/octopus$(EXE)
17 | bin/octopus$(EXE):
18 | go build -o bin/octopus cmd/octopus/main.go
19 |
20 | .PHONY: run
21 | run:
22 | go run cmd/octopus/main.go $(RUN_ARGS)
23 |
24 | .PHONY: clean
25 | clean:
26 | $(RMCMD) bin/
27 |
28 | ## Install/Uninstall (*nix Only)
29 |
30 | DESTDIR :=
31 | prefix := /usr/local
32 | bindir := ${prefix}/bin
33 |
34 | .PHONY: install
35 | install: bin/octopus
36 | install -d ${DESTDIR}${bindir}
37 | install -m755 bin/octopus ${DESTDIR}${bindir}/
38 |
39 | .PHONY: uninstall
40 | uninstall:
41 | rm -f ${DESTDIR}${bindir}/octopus
42 |
--------------------------------------------------------------------------------
/build/cli.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | octopus-cli
5 | Octopus CLI
6 | $version$
7 | Octopus Deploy
8 | Octopus Deploy
9 | Command line wrapper for continuous deployments that makes you better at Octopus Deploy.
10 | octopus is Octopus Deploy on the command line. It brings releases, deployments, and other Octopus Deploy concepts to the terminal next to where you are already working with projects and your continuous deployment processes.
11 |
12 | ### Usage
13 | ```
14 | octopus release [list, create, delete]
15 | octopus account [aws|azure|gcp|ssh|token|username] [list, create]
16 | octopus help
17 | ```
18 |
19 | https://github.com/OctopusDeploy/cli/releases/latest
20 | en-US
21 | false
22 | Apache-2.0
23 | https://github.com/OctopusDeploy/cli/
24 |
25 | tools\icon.png
26 | automation deployment
27 | true
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/build/tools/LICENSE.txt:
--------------------------------------------------------------------------------
1 | From: https://github.com/OctopusDeploy/cli/blob/main/LICENSE
2 |
3 | LICENSE
4 |
5 | Copyright (c) Octopus Deploy and contributors. All rights reserved.
6 |
7 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
8 | these files except in compliance with the License. You may obtain a copy of the
9 | License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software distributed
14 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
15 | CONDITIONS OF ANY KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations under the License.
17 |
--------------------------------------------------------------------------------
/build/tools/VERIFICATION.txt:
--------------------------------------------------------------------------------
1 | VERIFICATION
2 | Verification is intended to assist the Chocolatey moderators and community
3 | in verifying that this package's contents are trustworthy.
4 |
5 | This package is published by the Octopus Deploy team itself.
--------------------------------------------------------------------------------
/build/tools/chocolateyInstall.ps1:
--------------------------------------------------------------------------------
1 | $ErrorActionPreference = 'Stop'
2 |
3 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
4 | $logMsi = Join-Path -Path $env:TEMP -ChildPath ("{0}-{1}-MsiInstall.log" -f $env:ChocolateyPackageName, $env:chocolateyPackageVersion)
5 |
6 | $packageArgs = @{
7 | packageName = $env:ChocolateyPackageName
8 | fileType = 'MSI'
9 | silentArgs = "/qn /norestart /log `"$logMsi`""
10 | file64 = Join-Path -Path $toolsDir -ChildPath "octopus_$($env:ChocolateyPackageVersion)_windows_amd64.msi"
11 | }
12 |
13 | Install-ChocolateyInstallPackage @packageArgs
--------------------------------------------------------------------------------
/build/tools/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctopusDeploy/cli/c42d7d39b1151125e52e1f3bede75fcb107c3c1f/build/tools/icon.png
--------------------------------------------------------------------------------
/build/windows/octopus.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | x64
6 | 0.1.0
7 | $(MSBuildProjectName)
8 | package
9 | $([MSBuild]::NormalizeDirectory($(MSBuildProjectDirectory)\..\..))
10 | $(RepoPath)bin\$(Platform)\
11 | $(RepoPath)bin\obj\$(Platform)\
12 |
13 | $(DefineConstants);
14 | ProductVersion=$(ProductVersion);
15 |
16 | ICE39
17 | false
18 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OctopusDeploy/cli/c42d7d39b1151125e52e1f3bede75fcb107c3c1f/logo.png
--------------------------------------------------------------------------------
/pkg/apiclient/requester.go:
--------------------------------------------------------------------------------
1 | package apiclient
2 |
3 | import (
4 | "fmt"
5 | version "github.com/OctopusDeploy/cli"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/spf13/cobra"
8 | "strings"
9 | )
10 |
11 | type Requester interface {
12 | GetRequester() string
13 | }
14 |
15 | type RequesterContext struct {
16 | cmd *cobra.Command
17 | }
18 |
19 | type FakeRequesterContext struct {
20 | }
21 |
22 | func NewRequester(c *cobra.Command) *RequesterContext {
23 | return &RequesterContext{
24 | cmd: c,
25 | }
26 | }
27 |
28 | func (r *FakeRequesterContext) GetRequester() string { return "octopus/0.0.0" }
29 |
30 | func (r *RequesterContext) GetRequester() string {
31 | versionStr := strings.TrimSpace(version.Version)
32 |
33 | if r.cmd == nil {
34 | if versionStr == "" {
35 | return constants.ExecutableName
36 | }
37 | return fmt.Sprintf("%s/%s", constants.ExecutableName, versionStr)
38 | }
39 |
40 | commands := []string{r.cmd.Name()}
41 | var rootCmd string
42 | parentCmd := r.cmd.Parent()
43 | for parentCmd != nil {
44 | name := parentCmd.Name()
45 | if name == constants.ExecutableName && versionStr != "" {
46 | rootCmd = fmt.Sprintf("%s/%s", name, versionStr)
47 | } else {
48 | commands = append([]string{name}, commands...)
49 | }
50 | parentCmd = parentCmd.Parent()
51 | }
52 | return fmt.Sprintf("%s (%s)", rootCmd, strings.Join(commands, ";"))
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/apiclient/spinner_round_tripper.go:
--------------------------------------------------------------------------------
1 | package apiclient
2 |
3 | import (
4 | "net/http"
5 | "time"
6 |
7 | "github.com/briandowns/spinner"
8 | )
9 |
10 | type SpinnerRoundTripper struct {
11 | Next http.RoundTripper
12 | Spinner *spinner.Spinner
13 | }
14 |
15 | func NewSpinnerRoundTripper() *SpinnerRoundTripper {
16 | return &SpinnerRoundTripper{
17 | Next: http.DefaultTransport,
18 | Spinner: spinner.New(spinner.CharSets[11], 100*time.Millisecond, spinner.WithColor("cyan")),
19 | }
20 | }
21 |
22 | func (c *SpinnerRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
23 | c.Spinner.Start()
24 | defer c.Spinner.Stop()
25 | return c.Next.RoundTrip(r)
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/cmd/account/account.go:
--------------------------------------------------------------------------------
1 | package account
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdAWS "github.com/OctopusDeploy/cli/pkg/cmd/account/aws"
6 | cmdAzure "github.com/OctopusDeploy/cli/pkg/cmd/account/azure"
7 | cmdAzureOidc "github.com/OctopusDeploy/cli/pkg/cmd/account/azure-oidc"
8 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/create"
9 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/account/delete"
10 | cmdGCP "github.com/OctopusDeploy/cli/pkg/cmd/account/gcp"
11 | cmdGenericOidc "github.com/OctopusDeploy/cli/pkg/cmd/account/generic-oidc"
12 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/list"
13 | cmdSSH "github.com/OctopusDeploy/cli/pkg/cmd/account/ssh"
14 | cmdToken "github.com/OctopusDeploy/cli/pkg/cmd/account/token"
15 | cmdUsr "github.com/OctopusDeploy/cli/pkg/cmd/account/username"
16 | "github.com/OctopusDeploy/cli/pkg/constants"
17 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
18 | "github.com/OctopusDeploy/cli/pkg/factory"
19 | "github.com/spf13/cobra"
20 | )
21 |
22 | func NewCmdAccount(f factory.Factory) *cobra.Command {
23 | cmd := &cobra.Command{
24 | Use: "account ",
25 | Short: "Manage accounts",
26 | Long: "Manage accounts in Octopus Deploy",
27 | Example: heredoc.Docf("$ %s account list", constants.ExecutableName),
28 | Annotations: map[string]string{
29 | annotations.IsInfrastructure: "true",
30 | },
31 | }
32 |
33 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
34 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
35 | cmd.AddCommand(cmdList.NewCmdList(f))
36 | cmd.AddCommand(cmdAWS.NewCmdAws(f))
37 | cmd.AddCommand(cmdAzure.NewCmdAzure(f))
38 | cmd.AddCommand(cmdAzureOidc.NewCmdAzureOidc(f))
39 | cmd.AddCommand(cmdGenericOidc.NewCmdGenericOidc(f))
40 | cmd.AddCommand(cmdGCP.NewCmdGcp(f))
41 | cmd.AddCommand(cmdSSH.NewCmdSsh(f))
42 | cmd.AddCommand(cmdUsr.NewCmdUsername(f))
43 | cmd.AddCommand(cmdToken.NewCmdToken(f))
44 | return cmd
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/cmd/account/aws/aws.go:
--------------------------------------------------------------------------------
1 | package aws
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/aws/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/aws/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdAws(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "aws ",
15 | Short: "Manage AWS accounts",
16 | Long: "Manage AWS accounts in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s account aws list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 | return cmd
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/cmd/account/aws/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdList(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "list",
17 | Short: "List AWS accounts",
18 | Long: "List AWS accounts in Octopus Deploy",
19 | Example: heredoc.Docf("$ %s account aws list", constants.ExecutableName),
20 | Aliases: []string{"ls"},
21 | RunE: func(cmd *cobra.Command, _ []string) error {
22 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
23 | if err != nil {
24 | return err
25 | }
26 | return listAwsAccounts(client, cmd)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
33 | func listAwsAccounts(client *client.Client, cmd *cobra.Command) error {
34 | accountResources, err := client.Accounts.Get(accounts.AccountsQuery{
35 | AccountType: accounts.AccountTypeAmazonWebServicesAccount,
36 | })
37 | if err != nil {
38 | return err
39 | }
40 | items, err := accountResources.GetAllPages(client.Accounts.GetClient())
41 | if err != nil {
42 | return err
43 | }
44 |
45 | output.PrintArray(items, cmd, output.Mappers[accounts.IAccount]{
46 | Json: func(item accounts.IAccount) any {
47 | acc := item.(*accounts.AmazonWebServicesAccount)
48 | return &struct {
49 | Id string
50 | Slug string
51 | Name string
52 | AccessKey string
53 | }{
54 | Id: acc.GetID(),
55 | Slug: acc.GetSlug(),
56 | Name: acc.GetName(),
57 | AccessKey: acc.AccessKey,
58 | }
59 | },
60 | Table: output.TableDefinition[accounts.IAccount]{
61 | Header: []string{"NAME", "SLUG", "ACCESS KEY"},
62 | Row: func(item accounts.IAccount) []string {
63 | acc := item.(*accounts.AmazonWebServicesAccount)
64 | return []string{output.Bold(acc.GetName()), acc.GetSlug(), acc.AccessKey}
65 | }},
66 | Basic: func(item accounts.IAccount) string {
67 | return item.GetName()
68 | },
69 | })
70 | return nil
71 | }
72 |
--------------------------------------------------------------------------------
/pkg/cmd/account/azure-oidc/azure-oidc.go:
--------------------------------------------------------------------------------
1 | package azure
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/azure-oidc/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/azure-oidc/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdAzureOidc(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "azure-oidc ",
15 | Short: "Manage Azure OpenID Connect accounts",
16 | Long: "Manage Azure OpenID Connect accounts in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s account azure-oidc list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/account/azure/azure.go:
--------------------------------------------------------------------------------
1 | package azure
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/azure/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/azure/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdAzure(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "azure ",
15 | Short: "Manage Azure subscription accounts",
16 | Long: "Manage Azure subscription accounts in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s account azure list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/account/gcp/gcp.go:
--------------------------------------------------------------------------------
1 | package gcp
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/gcp/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/gcp/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdGcp(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "gcp ",
15 | Short: "Manage Google Cloud accounts",
16 | Long: "Manage Google Cloud accounts in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s account gcp list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/account/gcp/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdList(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "list",
17 | Short: "List Google Cloud accounts",
18 | Long: "List Google Cloud accounts in Octopus Deploy",
19 | Example: heredoc.Docf("$ %s account gcp list", constants.ExecutableName),
20 | Aliases: []string{"ls"},
21 | RunE: func(cmd *cobra.Command, _ []string) error {
22 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
23 | if err != nil {
24 | return err
25 | }
26 | return listGcpAccounts(client, cmd)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
33 | func listGcpAccounts(client *client.Client, cmd *cobra.Command) error {
34 | accountResources, err := client.Accounts.Get(accounts.AccountsQuery{
35 | AccountType: accounts.AccountTypeGoogleCloudPlatformAccount,
36 | })
37 | if err != nil {
38 | return err
39 | }
40 | items, err := accountResources.GetAllPages(client.Accounts.GetClient())
41 | if err != nil {
42 | return err
43 | }
44 |
45 | output.PrintArray(items, cmd, output.Mappers[accounts.IAccount]{
46 | Json: func(item accounts.IAccount) any {
47 | acc := item.(*accounts.GoogleCloudPlatformAccount)
48 | return &struct {
49 | Id string
50 | Name string
51 | Slug string
52 | }{
53 | Id: acc.GetID(),
54 | Name: acc.GetName(),
55 | Slug: acc.GetSlug(),
56 | }
57 | },
58 | Table: output.TableDefinition[accounts.IAccount]{
59 | Header: []string{"NAME", "SLUG"},
60 | Row: func(item accounts.IAccount) []string {
61 | acc := item.(*accounts.GoogleCloudPlatformAccount)
62 | return []string{output.Bold(acc.GetName()), acc.GetSlug()}
63 | }},
64 | Basic: func(item accounts.IAccount) string {
65 | return item.GetName()
66 | },
67 | })
68 | return nil
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/account/generic-oidc/generic-oidc.go:
--------------------------------------------------------------------------------
1 | package generic_oidc
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/generic-oidc/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/generic-oidc/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdGenericOidc(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "generic-oidc ",
15 | Short: "Manage Generic OpenID Connect accounts",
16 | Long: "Manage Generic OpenID Connect accounts in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s account generic-oidc list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/account/helper/helper.go:
--------------------------------------------------------------------------------
1 | package helper
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
8 | )
9 |
10 | // ResolveEnvironmentNames takes in an array of names and trys to find an exact match.
11 | // If a match is found it will return its corresponding ID. If no match is found
12 | // it will return the name as is, in assumption it is an ID.
13 | func ResolveEnvironmentNames(envs []string, octopus *client.Client) ([]string, error) {
14 | envIds := make([]string, 0, len(envs))
15 | loop:
16 | for _, envName := range envs {
17 | matches, err := octopus.Environments.Get(environments.EnvironmentsQuery{
18 | Name: envName,
19 | })
20 | if err != nil {
21 | return nil, err
22 | }
23 | allMatches, err := matches.GetAllPages(octopus.Environments.GetClient())
24 | if err != nil {
25 | return nil, err
26 | }
27 | for _, match := range allMatches {
28 | if strings.EqualFold(envName, match.Name) {
29 | envIds = append(envIds, match.ID)
30 | continue loop
31 | }
32 | }
33 | envIds = append(envIds, envName)
34 | }
35 | return envIds, nil
36 | }
37 |
--------------------------------------------------------------------------------
/pkg/cmd/account/shared/shared.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | var AzureEnvMap = map[string]string{
4 | "Global Cloud (Default)": "AzureCloud",
5 | "China Cloud": "AzureChinaCloud",
6 | "German Cloud": "AzureGermanCloud",
7 | "US Government": "AzureUSGovernment",
8 | }
9 | var AzureADEndpointBaseUri = map[string]string{
10 | "AzureCloud": "https://login.microsoftonline.com/",
11 | "AzureChinaCloud": "https://login.chinacloudapi.cn/",
12 | "AzureGermanCloud": "https://login.microsoftonline.de/",
13 | "AzureUSGovernment": "https://login.microsoftonline.us/",
14 | }
15 | var AzureResourceManagementBaseUri = map[string]string{
16 | "AzureCloud": "https://management.azure.com/",
17 | "AzureChinaCloud": "https://management.chinacloudapi.cn/",
18 | "AzureGermanCloud": "https://management.microsoftazure.de/",
19 | "AzureUSGovernment": "https://management.usgovcloudapi.net/",
20 | }
21 |
--------------------------------------------------------------------------------
/pkg/cmd/account/ssh/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdList(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "list",
17 | Short: "List SSH Key Pair accounts",
18 | Long: "List SSH Key Pair accounts in Octopus Deploy",
19 | Example: heredoc.Docf("$ %s account ssh list", constants.ExecutableName),
20 | Aliases: []string{"ls"},
21 | RunE: func(cmd *cobra.Command, _ []string) error {
22 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
23 | if err != nil {
24 | return err
25 | }
26 | return listSshAccounts(client, cmd)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
33 | func listSshAccounts(client *client.Client, cmd *cobra.Command) error {
34 | accountResources, err := client.Accounts.Get(accounts.AccountsQuery{
35 | AccountType: accounts.AccountTypeSSHKeyPair,
36 | })
37 | if err != nil {
38 | return err
39 | }
40 | items, err := accountResources.GetAllPages(client.Accounts.GetClient())
41 | if err != nil {
42 | return err
43 | }
44 |
45 | output.PrintArray(items, cmd, output.Mappers[accounts.IAccount]{
46 | Json: func(item accounts.IAccount) any {
47 | acc := item.(*accounts.SSHKeyAccount)
48 | return &struct {
49 | Id string
50 | Name string
51 | Slug string
52 | Username string
53 | }{
54 | Id: acc.GetID(),
55 | Name: acc.GetName(),
56 | Slug: acc.GetSlug(),
57 | Username: acc.Username,
58 | }
59 | },
60 | Table: output.TableDefinition[accounts.IAccount]{
61 | Header: []string{"NAME", "SLUG", "USERNAME"},
62 | Row: func(item accounts.IAccount) []string {
63 | acc := item.(*accounts.SSHKeyAccount)
64 | return []string{output.Bold(acc.GetName()), acc.GetSlug(), acc.Username}
65 | }},
66 | Basic: func(item accounts.IAccount) string {
67 | return item.GetName()
68 | },
69 | })
70 | return nil
71 | }
72 |
--------------------------------------------------------------------------------
/pkg/cmd/account/ssh/ssh.go:
--------------------------------------------------------------------------------
1 | package ssh
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/ssh/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/ssh/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdSsh(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "ssh ",
15 | Short: "Manage SSH Key Pair accounts",
16 | Long: "Manage SSH Key Pair accounts in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s account ssh list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/account/token/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdList(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "list",
17 | Short: "List Token accounts",
18 | Long: "List Token accounts in Octopus Deploy",
19 | Example: heredoc.Docf("$ %s account token list", constants.ExecutableName),
20 | Aliases: []string{"ls"},
21 | RunE: func(cmd *cobra.Command, _ []string) error {
22 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
23 | if err != nil {
24 | return err
25 | }
26 | return listTokenAccounts(client, cmd)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
33 | func listTokenAccounts(client *client.Client, cmd *cobra.Command) error {
34 | accountResources, err := client.Accounts.Get(accounts.AccountsQuery{
35 | AccountType: accounts.AccountTypeToken,
36 | })
37 | if err != nil {
38 | return err
39 | }
40 | items, err := accountResources.GetAllPages(client.Accounts.GetClient())
41 | if err != nil {
42 | return err
43 | }
44 |
45 | output.PrintArray(items, cmd, output.Mappers[accounts.IAccount]{
46 | Json: func(item accounts.IAccount) any {
47 | acc := item.(*accounts.TokenAccount)
48 | return &struct {
49 | Id string
50 | Name string
51 | Slug string
52 | }{
53 | Id: acc.GetID(),
54 | Name: acc.GetName(),
55 | Slug: acc.GetSlug(),
56 | }
57 | },
58 | Table: output.TableDefinition[accounts.IAccount]{
59 | Header: []string{"NAME", "SLUG"},
60 | Row: func(item accounts.IAccount) []string {
61 | acc := item.(*accounts.TokenAccount)
62 | return []string{output.Bold(acc.GetName()), acc.GetSlug()}
63 | }},
64 | Basic: func(item accounts.IAccount) string {
65 | return item.GetName()
66 | },
67 | })
68 | return nil
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/account/token/token.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/token/create"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/token/list"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdToken(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "token ",
16 | Short: "Manage Token accounts",
17 | Long: "Manage Token accounts in Octopus Deploy",
18 | Example: fmt.Sprintf("$ %s account token list", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdList.NewCmdList(f))
22 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
23 |
24 | return cmd
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/account/username/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdList(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "list",
17 | Short: "List Username/Password accounts",
18 | Long: "List Username/Password accounts in Octopus Deploy",
19 | Example: heredoc.Docf(`
20 | $ %s account username list"
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(cmd *cobra.Command, _ []string) error {
24 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
25 | if err != nil {
26 | return err
27 | }
28 | return listUsernameAccounts(client, cmd)
29 | },
30 | }
31 |
32 | return cmd
33 | }
34 |
35 | func listUsernameAccounts(client *client.Client, cmd *cobra.Command) error {
36 | accountResources, err := client.Accounts.Get(accounts.AccountsQuery{
37 | AccountType: accounts.AccountTypeUsernamePassword,
38 | })
39 | if err != nil {
40 | return err
41 | }
42 | items, err := accountResources.GetAllPages(client.Accounts.GetClient())
43 | if err != nil {
44 | return err
45 | }
46 |
47 | output.PrintArray(items, cmd, output.Mappers[accounts.IAccount]{
48 | Json: func(item accounts.IAccount) any {
49 | acc := item.(*accounts.UsernamePasswordAccount)
50 | return &struct {
51 | Id string
52 | Name string
53 | Slug string
54 | Username string
55 | }{
56 | Id: acc.GetID(),
57 | Name: acc.GetName(),
58 | Slug: acc.GetSlug(),
59 | Username: acc.Username,
60 | }
61 | },
62 | Table: output.TableDefinition[accounts.IAccount]{
63 | Header: []string{"NAME", "SLUG", "USERNAME"},
64 | Row: func(item accounts.IAccount) []string {
65 | acc := item.(*accounts.UsernamePasswordAccount)
66 | return []string{output.Bold(acc.GetName()), acc.GetSlug(), acc.Username}
67 | }},
68 | Basic: func(item accounts.IAccount) string {
69 | return item.GetName()
70 | },
71 | })
72 | return nil
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/cmd/account/username/username.go:
--------------------------------------------------------------------------------
1 | package username
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/account/username/create"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/account/username/list"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdUsername(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "username ",
16 | Short: "Manage Username/Password accounts",
17 | Long: "Manage Username/Password accounts in Octopus Deploy",
18 | Example: fmt.Sprintf("$ %s account username list", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdList.NewCmdList(f))
22 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
23 |
24 | return cmd
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/buildinformation/build-information.go:
--------------------------------------------------------------------------------
1 | package buildinformation
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdBulkDelete "github.com/OctopusDeploy/cli/pkg/cmd/buildinformation/bulkdelete"
7 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/buildinformation/delete"
8 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/buildinformation/list"
9 | cmdUpload "github.com/OctopusDeploy/cli/pkg/cmd/buildinformation/upload"
10 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/buildinformation/view"
11 | "github.com/OctopusDeploy/cli/pkg/constants"
12 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
13 | "github.com/OctopusDeploy/cli/pkg/factory"
14 | "github.com/spf13/cobra"
15 | )
16 |
17 | func NewCmdBuildInformation(f factory.Factory) *cobra.Command {
18 | cmd := &cobra.Command{
19 | Use: "build-information ",
20 | Short: "Manage build information",
21 | Long: "Manage build information in Octopus Deploy",
22 | Example: fmt.Sprintf("$ %s build-information upload", constants.ExecutableName),
23 | Aliases: []string{"build-info"},
24 | Annotations: map[string]string{
25 | annotations.IsCore: "true",
26 | },
27 | }
28 |
29 | cmd.AddCommand(cmdBulkDelete.NewCmdBulkDelete(f))
30 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
31 | cmd.AddCommand(cmdView.NewCmdView(f))
32 | cmd.AddCommand(cmdList.NewCmdList(f))
33 | cmd.AddCommand(cmdUpload.NewCmdUpload(f))
34 | return cmd
35 | }
36 |
--------------------------------------------------------------------------------
/pkg/cmd/buildinformation/shared/shared.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | type WorkItemAsJson struct {
4 | Id string `json:"Id"`
5 | Source string `json:"Source"`
6 | Description string `json:"Description"`
7 | }
8 |
9 | type CommitAsJson struct {
10 | Id string `json:"Id"`
11 | Comment string `json:"Comment"`
12 | }
13 |
14 | type BuildInfoAsJson struct {
15 | Id string `json:"Id"`
16 | PackageId string `json:"PackageId"`
17 | Version string `json:"Version"`
18 | Branch string `json:"Branch"`
19 | BuildEnvironment string `json:"BuildEnvironment"`
20 | VcsCommitNumber string `json:"VcsCommitNumber"`
21 | VcsType string `json:"VcsType"`
22 | VcsRoot string `json:"VcsRoot"`
23 | Commits []*CommitAsJson `json:"Commits,omitempty"`
24 | WorkItems []*WorkItemAsJson `json:"WorkItems,omitempty"`
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/channel/channel.go:
--------------------------------------------------------------------------------
1 | package channel
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/channel/create"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdChannel(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "channel ",
15 | Short: "Manage channels",
16 | Long: "Manage channels in Octopus Deploy",
17 | Example: heredoc.Docf(`
18 | $ %[1]s channel create
19 | `, constants.ExecutableName),
20 | Annotations: map[string]string{
21 | annotations.IsCore: "true",
22 | },
23 | }
24 |
25 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
26 |
27 | return cmd
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/cmd/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | getCmd "github.com/OctopusDeploy/cli/pkg/cmd/config/get"
5 | listCmd "github.com/OctopusDeploy/cli/pkg/cmd/config/list"
6 | setCmd "github.com/OctopusDeploy/cli/pkg/cmd/config/set"
7 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdConfig(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "config ",
15 | Short: "Manage CLI configuration",
16 | Long: "Manage the CLI configuration",
17 | Annotations: map[string]string{
18 | annotations.IsConfiguration: "true",
19 | },
20 | }
21 |
22 | cmd.AddCommand(getCmd.NewCmdGet(f))
23 | cmd.AddCommand(setCmd.NewCmdSet(f))
24 | cmd.AddCommand(listCmd.NewCmdList(f))
25 | return cmd
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/cmd/config/get/get.go:
--------------------------------------------------------------------------------
1 | package get
2 |
3 | import (
4 | "fmt"
5 | "io"
6 |
7 | "github.com/AlecAivazis/survey/v2"
8 | "github.com/OctopusDeploy/cli/pkg/config"
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 | "github.com/OctopusDeploy/cli/pkg/factory"
11 | "github.com/OctopusDeploy/cli/pkg/question"
12 | "github.com/spf13/cobra"
13 | "github.com/spf13/viper"
14 | )
15 |
16 | func NewCmdGet(f factory.Factory) *cobra.Command {
17 | cmd := &cobra.Command{
18 | Use: "get [key]",
19 | Short: "Gets the value of config key for Octopus CLI",
20 | RunE: func(cmd *cobra.Command, args []string) error {
21 | key := ""
22 | if len(args) > 0 {
23 | key = args[0]
24 | }
25 | return getRun(f.IsPromptEnabled(), f.Ask, key, cmd.OutOrStdout())
26 |
27 | },
28 | }
29 | return cmd
30 | }
31 |
32 | func getRun(isPromptEnabled bool, ask question.Asker, key string, out io.Writer) error {
33 | if key != "" {
34 | if !config.IsValidKey(key) {
35 | return fmt.Errorf("the key '%s' is not a valid", key)
36 | }
37 | }
38 | value := ""
39 | configFile := viper.New()
40 | configFile.SetConfigFile(viper.ConfigFileUsed())
41 | configFile.ReadInConfig()
42 | if isPromptEnabled && key == "" {
43 | k, err := promptMissing(ask)
44 | if err != nil {
45 | return err
46 | }
47 | key = k
48 | }
49 | value = configFile.GetString(key)
50 | if value == "" && !configFile.InConfig(key) {
51 | return fmt.Errorf("unable to get value for key: %s", key)
52 | }
53 |
54 | fmt.Fprintln(out, value)
55 | return nil
56 | }
57 |
58 | func promptMissing(ask question.Asker) (string, error) {
59 | keys := []string{
60 | constants.ConfigApiKey,
61 | constants.ConfigSpace,
62 | constants.ConfigNoPrompt,
63 | constants.ConfigUrl,
64 | constants.ConfigOutputFormat,
65 | constants.ConfigShowOctopus,
66 | constants.ConfigEditor,
67 | // constants.ConfigProxyUrl,
68 | }
69 |
70 | var selectKey string
71 | if err := ask(&survey.Select{
72 | Options: keys,
73 | Message: "What key you would you would like to see the value of?",
74 | }, &selectKey); err != nil {
75 | return "", err
76 | }
77 | return selectKey, nil
78 | }
79 |
--------------------------------------------------------------------------------
/pkg/cmd/dependencies.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/apiclient"
5 | "io"
6 |
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/question"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/spaces"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | type Dependable interface {
15 | Commit() error
16 | GenerateAutomationCmd()
17 | }
18 |
19 | type Dependencies struct {
20 | Out io.Writer
21 | Client *client.Client
22 | Host string
23 | Space *spaces.Space
24 | NoPrompt bool
25 | Ask question.Asker
26 | CmdPath string
27 | ShowMessagePrefix bool
28 | }
29 |
30 | func NewDependencies(f factory.Factory, cmd *cobra.Command) *Dependencies {
31 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
32 | if err != nil {
33 | panic(err)
34 | }
35 |
36 | return newDependencies(f, cmd, client)
37 | }
38 |
39 | func NewSystemDependencies(f factory.Factory, cmd *cobra.Command) *Dependencies {
40 | client, err := f.GetSystemClient(apiclient.NewRequester(cmd))
41 | if err != nil {
42 | panic(err)
43 | }
44 | return newDependencies(f, cmd, client)
45 | }
46 |
47 | func newDependencies(f factory.Factory, cmd *cobra.Command, client *client.Client) *Dependencies {
48 | return &Dependencies{
49 | Ask: f.Ask,
50 | CmdPath: cmd.CommandPath(),
51 | Out: cmd.OutOrStdout(),
52 | Client: client,
53 | Host: f.GetCurrentHost(),
54 | NoPrompt: !f.IsPromptEnabled(),
55 | Space: f.GetCurrentSpace(),
56 | }
57 | }
58 |
59 | func NewDependenciesFromExisting(opts *Dependencies, cmdPath string) *Dependencies {
60 | return &Dependencies{
61 | Ask: opts.Ask,
62 | CmdPath: cmdPath,
63 | Out: opts.Out,
64 | Client: opts.Client,
65 | Host: opts.Host,
66 | NoPrompt: opts.NoPrompt,
67 | Space: opts.Space,
68 | ShowMessagePrefix: true,
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/pkg/cmd/environment/environment.go:
--------------------------------------------------------------------------------
1 | package environment
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/environment/create"
6 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/environment/delete"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/environment/list"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
10 | "github.com/OctopusDeploy/cli/pkg/factory"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdEnvironment(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "environment ",
17 | Short: "Manage environments",
18 | Long: "Manage environments in Octopus Deploy",
19 | Example: heredoc.Docf(`
20 | $ %[1]s environment list
21 | $ %[1]s environment ls
22 | `, constants.ExecutableName),
23 | Annotations: map[string]string{
24 | annotations.IsInfrastructure: "true",
25 | },
26 | }
27 |
28 | cmd.AddCommand(cmdList.NewCmdList(f))
29 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
30 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
31 | return cmd
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/cmd/environment/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/apiclient"
5 | "strconv"
6 |
7 | "github.com/MakeNowJust/heredoc/v2"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | func NewCmdList(f factory.Factory) *cobra.Command {
16 | cmd := &cobra.Command{
17 | Use: "list",
18 | Short: "List environments",
19 | Long: "List environments in Octopus Deploy",
20 | Example: heredoc.Docf(`
21 | $ %[1]s environment list
22 | $ %[1]s environment ls"
23 | `, constants.ExecutableName),
24 | Aliases: []string{"ls"},
25 | RunE: func(cmd *cobra.Command, args []string) error {
26 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
27 | if err != nil {
28 | return err
29 | }
30 |
31 | envResources, err := client.Environments.Get(environments.EnvironmentsQuery{})
32 | if err != nil {
33 | return err
34 | }
35 | allEnvs, err := envResources.GetAllPages(client.Environments.GetClient())
36 | if err != nil {
37 | return err
38 | }
39 |
40 | return output.PrintArray(allEnvs, cmd, output.Mappers[*environments.Environment]{
41 | Json: func(item *environments.Environment) any {
42 | return output.IdAndName{Id: item.GetID(), Name: item.Name}
43 | },
44 | Table: output.TableDefinition[*environments.Environment]{
45 | Header: []string{"NAME", "GUIDED FAILURE"},
46 | Row: func(item *environments.Environment) []string {
47 |
48 | return []string{output.Bold(item.Name), strconv.FormatBool(item.UseGuidedFailure)}
49 | },
50 | },
51 | Basic: func(item *environments.Environment) string {
52 | return item.Name
53 | },
54 | })
55 | },
56 | }
57 |
58 | return cmd
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/cmd/logout/logout.go:
--------------------------------------------------------------------------------
1 | package logout
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
5 | "github.com/OctopusDeploy/cli/pkg/factory"
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | func NewCmdLogout(f factory.Factory) *cobra.Command {
10 | cmd := &cobra.Command{
11 | Use: "logout",
12 | Short: "Logout of Octopus",
13 | Long: "Logout of your Octopus server",
14 | RunE: func(cmd *cobra.Command, args []string) error {
15 | return logoutRun(f, cmd)
16 | },
17 | Annotations: map[string]string{
18 | annotations.IsConfiguration: "true",
19 | },
20 | }
21 |
22 | return cmd
23 | }
24 |
25 | func logoutRun(f factory.Factory, cmd *cobra.Command) error {
26 | configProvider, err := f.GetConfigProvider()
27 |
28 | if err != nil {
29 | return err
30 | }
31 | configProvider.Set("Url", "")
32 | configProvider.Set("ApiKey", "")
33 | configProvider.Set("AccessToken", "")
34 |
35 | cmd.Printf("Logout successful")
36 |
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/cmd/logout/logout_test.go:
--------------------------------------------------------------------------------
1 | package logout_test
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 |
7 | logoutCmd "github.com/OctopusDeploy/cli/pkg/cmd/logout"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/test/testutil"
10 | "github.com/spf13/cobra"
11 | "github.com/spf13/viper"
12 | "github.com/stretchr/testify/assert"
13 | )
14 |
15 | func TestLogout_SetsConfigCorrectly(t *testing.T) {
16 | api := testutil.NewMockHttpServer()
17 | fac := testutil.NewMockFactory(api)
18 |
19 | configProvider, err := fac.GetConfigProvider()
20 |
21 | assert.Nil(t, err)
22 |
23 | configProvider.Set(constants.ConfigUrl, "https://some.octopus.app")
24 | configProvider.Set(constants.ConfigApiKey, "API-APIKEY01")
25 | configProvider.Set(constants.ConfigUrl, "accesstoken")
26 |
27 | logoutCmd := logoutCmd.NewCmdLogout(fac)
28 | stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
29 | logoutCmd.SetOut(stdout)
30 | logoutCmd.SetErr(stderr)
31 | cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
32 | return logoutCmd.ExecuteC()
33 | })
34 |
35 | _, err = testutil.ReceivePair(cmdReceiver)
36 | assert.Nil(t, err)
37 | assert.Empty(t, viper.GetString(constants.ConfigUrl))
38 | assert.Empty(t, viper.GetString(constants.ConfigApiKey))
39 | assert.Empty(t, viper.GetString(constants.ConfigAccessToken))
40 | }
41 |
--------------------------------------------------------------------------------
/pkg/cmd/model/entity.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type Entity struct {
4 | Id string `json:"Id"`
5 | Name string `json:"Name"`
6 | }
7 |
--------------------------------------------------------------------------------
/pkg/cmd/package/nuget/nuget.go:
--------------------------------------------------------------------------------
1 | package nuget
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdNugetCreate "github.com/OctopusDeploy/cli/pkg/cmd/package/nuget/create"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | func NewCmdPackageNuget(f factory.Factory) *cobra.Command {
12 | cmd := &cobra.Command{
13 | Use: "nuget ",
14 | Short: "Package as NuPkg",
15 | Long: "Package as NuPkg for Octopus Deploy",
16 | Example: heredoc.Docf("$ %s package nuget create", constants.ExecutableName),
17 | }
18 |
19 | cmd.AddCommand(cmdNugetCreate.NewCmdCreate(f))
20 |
21 | return cmd
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/cmd/package/package.go:
--------------------------------------------------------------------------------
1 | package _package
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/package/delete"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/package/list"
8 | cmdNuget "github.com/OctopusDeploy/cli/pkg/cmd/package/nuget"
9 | cmdUpload "github.com/OctopusDeploy/cli/pkg/cmd/package/upload"
10 | cmdVersions "github.com/OctopusDeploy/cli/pkg/cmd/package/versions"
11 | cmdZip "github.com/OctopusDeploy/cli/pkg/cmd/package/zip"
12 | "github.com/OctopusDeploy/cli/pkg/constants"
13 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
14 | "github.com/OctopusDeploy/cli/pkg/factory"
15 | "github.com/spf13/cobra"
16 | )
17 |
18 | func NewCmdPackage(f factory.Factory) *cobra.Command {
19 | cmd := &cobra.Command{
20 | Use: "package ",
21 | Short: "Manage packages",
22 | Long: "Manage packages in Octopus Deploy",
23 | Example: fmt.Sprintf("$ %s package upload", constants.ExecutableName),
24 | Annotations: map[string]string{
25 | annotations.IsCore: "true",
26 | },
27 | }
28 |
29 | cmd.AddCommand(cmdUpload.NewCmdUpload(f))
30 | cmd.AddCommand(cmdList.NewCmdList(f))
31 | cmd.AddCommand(cmdVersions.NewCmdVersions(f))
32 | cmd.AddCommand(cmdNuget.NewCmdPackageNuget(f))
33 | cmd.AddCommand(cmdZip.NewCmdPackageZip(f))
34 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
35 |
36 | return cmd
37 | }
38 |
--------------------------------------------------------------------------------
/pkg/cmd/package/zip/zip.go:
--------------------------------------------------------------------------------
1 | package zip
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdZipCreate "github.com/OctopusDeploy/cli/pkg/cmd/package/zip/create"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | func NewCmdPackageZip(f factory.Factory) *cobra.Command {
12 | cmd := &cobra.Command{
13 | Use: "zip ",
14 | Short: "Package as zip",
15 | Long: "Package as zip for Octopus Deploy",
16 | Example: heredoc.Docf("$ %s package zip create", constants.ExecutableName),
17 | }
18 |
19 | cmd.AddCommand(cmdZipCreate.NewCmdCreate(f))
20 |
21 | return cmd
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/cmd/project/branch/branch.go:
--------------------------------------------------------------------------------
1 | package branch
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/constants"
6 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/spf13/cobra"
9 |
10 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/project/branch/create"
11 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/project/branch/list"
12 | )
13 |
14 | func NewCmdBranch(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "branch ",
17 | Short: "Manage project branches",
18 | Long: "Manage project branches in Octopus Deploy",
19 | Example: heredoc.Docf(`
20 | $ %[1]s project branch list "Deploy Web App"
21 | $ %[1]s project branch create -p "Deploy Web App" --new-branch-name add-name-variable --base-branch refs/heads/main -
22 | `, constants.ExecutableName),
23 | Annotations: map[string]string{
24 | annotations.IsCore: "true",
25 | },
26 | }
27 |
28 | cmd.AddCommand(cmdList.NewCmdList(f))
29 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
30 |
31 | return cmd
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/cmd/project/branch/shared/shared.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projectbranches"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
8 | )
9 |
10 | type GetAllBranchesCallback func(projectId string) ([]*projects.GitReference, error)
11 |
12 | type ProjectBranchCallbacks struct {
13 | GetAllBranchesCallback GetAllBranchesCallback
14 | }
15 |
16 | func NewProjectBranchCallbacks(dependencies *cmd.Dependencies) *ProjectBranchCallbacks {
17 | return &ProjectBranchCallbacks{
18 | GetAllBranchesCallback: func(projectId string) ([]*projects.GitReference, error) {
19 | return getAllBranches(dependencies.Client, dependencies.Space.GetID(), projectId)
20 | },
21 | }
22 | }
23 |
24 | func getAllBranches(client *client.Client, spaceId string, projectId string) ([]*projects.GitReference, error) {
25 | branches, err := client.ProjectBranches.Get(spaceId, projectId, projectbranches.ProjectBranchQuery{Skip: 0, Take: 9999})
26 | if err != nil {
27 | return nil, err
28 | }
29 | return branches.Items, nil
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/cmd/project/connect/connect.go:
--------------------------------------------------------------------------------
1 | package connect
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | connectTenant "github.com/OctopusDeploy/cli/pkg/cmd/tenant/connect"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdConnect(f factory.Factory) *cobra.Command {
13 | connectFlags := connectTenant.NewConnectFlags()
14 | cmd := &cobra.Command{
15 | Use: "connect",
16 | Short: "Connect a tenant to a project",
17 | Long: "Connect a tenant to a project in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s project connect
20 | $ %[1]s project connect --tenant "Bobs Wood Shop" --project "Deploy web site" --environment "Production"
21 | `, constants.ExecutableName),
22 | RunE: func(c *cobra.Command, args []string) error {
23 | opts := connectTenant.NewConnectOptions(connectFlags, cmd.NewDependencies(f, c))
24 |
25 | return connectTenant.ConnectRun(opts)
26 | },
27 | }
28 |
29 | connectTenant.ConfigureFlags(cmd, connectFlags)
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/project/create/create_test.go:
--------------------------------------------------------------------------------
1 | package create_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/OctopusDeploy/cli/pkg/cmd"
7 | projectCreate "github.com/OctopusDeploy/cli/pkg/cmd/project/create"
8 | "github.com/OctopusDeploy/cli/test/testutil"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | func TestPromptForConfigAsCode_NotUsingCac(t *testing.T) {
13 | pa := []*testutil.PA{
14 | testutil.NewConfirmPrompt("Would you like to use Config as Code?", "", false),
15 | }
16 |
17 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
18 | flags := projectCreate.NewCreateFlags()
19 | flags.ConfigAsCode.Value = false
20 |
21 | var convertCallbackCalled bool
22 | opts := projectCreate.NewCreateOptions(flags, &cmd.Dependencies{Ask: asker})
23 | opts.ConvertProjectCallback = func() (cmd.Dependable, error) {
24 | convertCallbackCalled = true
25 | return nil, nil
26 | }
27 |
28 | _, err := projectCreate.PromptForConfigAsCode(opts)
29 | checkRemainingPrompts()
30 | assert.NoError(t, err)
31 | assert.False(t, convertCallbackCalled)
32 | assert.False(t, opts.ConfigAsCode.Value)
33 | }
34 |
--------------------------------------------------------------------------------
/pkg/cmd/project/disable/disable.go:
--------------------------------------------------------------------------------
1 | package disable
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/MakeNowJust/heredoc/v2"
7 | "github.com/OctopusDeploy/cli/pkg/cmd"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
11 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | type DisableOptions struct {
16 | *cmd.Dependencies
17 | IdOrName string
18 | }
19 |
20 | func NewDisableOptions(args []string, dependencies *cmd.Dependencies) *DisableOptions {
21 | return &DisableOptions{
22 | Dependencies: dependencies,
23 | IdOrName: args[0],
24 | }
25 | }
26 |
27 | func NewCmdDisable(f factory.Factory) *cobra.Command {
28 | cmd := &cobra.Command{
29 | Use: "disable",
30 | Short: "Disable a project",
31 | Long: "Disable a project in Octopus Deploy",
32 | Example: heredoc.Docf("$ %[1]s project disable", constants.ExecutableName),
33 | RunE: func(c *cobra.Command, args []string) error {
34 | if len(args) == 0 {
35 | args = append(args, "")
36 | }
37 |
38 | opts := NewDisableOptions(args, cmd.NewDependencies(f, c))
39 | return disableRun(opts)
40 | },
41 | }
42 |
43 | return cmd
44 | }
45 |
46 | func disableRun(opts *DisableOptions) error {
47 | if !opts.NoPrompt {
48 | if err := PromptMissing(opts); err != nil {
49 | return err
50 | }
51 | }
52 |
53 | if opts.IdOrName == "" {
54 | return fmt.Errorf("project identifier is required but was not provided")
55 | }
56 |
57 | projectToDisable, err := projects.GetByIdentifier(opts.Client, opts.Client.GetSpaceID(), opts.IdOrName)
58 | if err != nil {
59 | return err
60 | }
61 |
62 | projectToDisable.IsDisabled = true
63 | _, err = projects.Update(opts.Client, projectToDisable)
64 | if err != nil {
65 | return err
66 | }
67 |
68 | return nil
69 | }
70 |
71 | func PromptMissing(opts *DisableOptions) error {
72 | if opts.IdOrName == "" {
73 | existingProjects, err := opts.Client.Projects.GetAll()
74 | if err != nil {
75 | return err
76 | }
77 | selectedProject, err := selectors.ByName(opts.Ask, existingProjects, "Select the project you wish to disable:")
78 | if err != nil {
79 | return err
80 | }
81 | opts.IdOrName = selectedProject.GetID()
82 | }
83 |
84 | return nil
85 | }
86 |
--------------------------------------------------------------------------------
/pkg/cmd/project/disconnect/disconnect.go:
--------------------------------------------------------------------------------
1 | package disconnect
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | tenantDisconnect "github.com/OctopusDeploy/cli/pkg/cmd/tenant/disconnect"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdDisconnect(f factory.Factory) *cobra.Command {
13 | disconnectFlags := tenantDisconnect.NewDisconnectFlags()
14 |
15 | cmd := &cobra.Command{
16 | Use: "disconnect",
17 | Short: "Disconnect a tenant from a project",
18 | Long: "Disconnect a tenant from a project in Octopus Deploy",
19 | Example: heredoc.Docf(`
20 | $ %[1]s project disconnect
21 | $ %[1]s project disconnect --tenant "Test Tenant" --project "Deploy web site" --confirm
22 | `, constants.ExecutableName),
23 | RunE: func(c *cobra.Command, args []string) error {
24 | opts := tenantDisconnect.NewDisconnectOptions(disconnectFlags, cmd.NewDependencies(f, c))
25 | return tenantDisconnect.DisconnectRun(opts)
26 | },
27 | }
28 |
29 | tenantDisconnect.ConfigureFlags(cmd, disconnectFlags)
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/project/enable/enable.go:
--------------------------------------------------------------------------------
1 | package enable
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/MakeNowJust/heredoc/v2"
7 | "github.com/OctopusDeploy/cli/pkg/cmd"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
11 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | type EnableOptions struct {
16 | *cmd.Dependencies
17 | IdOrName string
18 | }
19 |
20 | func NewEnableOptions(args []string, dependencies *cmd.Dependencies) *EnableOptions {
21 | return &EnableOptions{
22 | Dependencies: dependencies,
23 | IdOrName: args[0],
24 | }
25 | }
26 |
27 | func NewCmdEnable(f factory.Factory) *cobra.Command {
28 | cmd := &cobra.Command{
29 | Use: "enable",
30 | Short: "Enable a project",
31 | Long: "Enable a project in Octopus Deploy",
32 | Example: heredoc.Docf("$ %[1]s project enable", constants.ExecutableName),
33 | RunE: func(c *cobra.Command, args []string) error {
34 | if len(args) == 0 {
35 | args = append(args, "")
36 | }
37 |
38 | opts := NewEnableOptions(args, cmd.NewDependencies(f, c))
39 | return disableRun(opts)
40 | },
41 | }
42 |
43 | return cmd
44 | }
45 |
46 | func disableRun(opts *EnableOptions) error {
47 | if !opts.NoPrompt {
48 | if err := PromptMissing(opts); err != nil {
49 | return err
50 | }
51 | }
52 |
53 | if opts.IdOrName == "" {
54 | return fmt.Errorf("project identifier is required but was not provided")
55 | }
56 |
57 | projectToEnable, err := projects.GetByIdentifier(opts.Client, opts.Client.GetSpaceID(), opts.IdOrName)
58 | if err != nil {
59 | return err
60 | }
61 |
62 | projectToEnable.IsDisabled = false
63 | _, err = projects.Update(opts.Client, projectToEnable)
64 | if err != nil {
65 | return err
66 | }
67 |
68 | return nil
69 | }
70 |
71 | func PromptMissing(opts *EnableOptions) error {
72 | if opts.IdOrName == "" {
73 | existingProjects, err := opts.Client.Projects.GetAll()
74 | if err != nil {
75 | return err
76 | }
77 | selectedProject, err := selectors.ByName(opts.Ask, existingProjects, "Select the project you wish to enable:")
78 | if err != nil {
79 | return err
80 | }
81 | opts.IdOrName = selectedProject.GetID()
82 | }
83 |
84 | return nil
85 | }
86 |
--------------------------------------------------------------------------------
/pkg/cmd/project/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List projects",
17 | Long: "List projects in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s project list
20 | $ %[1]s project ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(cmd *cobra.Command, args []string) error {
24 | return listRun(cmd, f)
25 | },
26 | }
27 |
28 | return cmd
29 | }
30 |
31 | type ProjectAsJson struct {
32 | Id string `json:"Id"`
33 | Name string `json:"Name"`
34 | Description string `json:"Description"`
35 | }
36 |
37 | func listRun(cmd *cobra.Command, f factory.Factory) error {
38 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
39 | if err != nil {
40 | return err
41 | }
42 |
43 | allProjects, err := client.Projects.GetAll()
44 | if err != nil {
45 | return err
46 | }
47 |
48 | return output.PrintArray(allProjects, cmd, output.Mappers[*projects.Project]{
49 | Json: func(p *projects.Project) any {
50 | return ProjectAsJson{
51 | Id: p.GetID(),
52 | Name: p.GetName(),
53 | Description: p.Description,
54 | }
55 | },
56 | Table: output.TableDefinition[*projects.Project]{
57 | Header: []string{"NAME", "DESCRIPTION"},
58 | Row: func(p *projects.Project) []string {
59 | return []string{output.Bold(p.Name), p.Description}
60 | },
61 | },
62 | Basic: func(p *projects.Project) string {
63 | return p.GetName()
64 | },
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/cmd/project/project.go:
--------------------------------------------------------------------------------
1 | package project
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdBranch "github.com/OctopusDeploy/cli/pkg/cmd/project/branch"
6 | cmdClone "github.com/OctopusDeploy/cli/pkg/cmd/project/clone"
7 | cmdConnect "github.com/OctopusDeploy/cli/pkg/cmd/project/connect"
8 | cmdConvert "github.com/OctopusDeploy/cli/pkg/cmd/project/convert"
9 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/project/create"
10 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/project/delete"
11 | cmdDisable "github.com/OctopusDeploy/cli/pkg/cmd/project/disable"
12 | cmdDisconnect "github.com/OctopusDeploy/cli/pkg/cmd/project/disconnect"
13 | cmdEnable "github.com/OctopusDeploy/cli/pkg/cmd/project/enable"
14 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/project/list"
15 | cmdVariables "github.com/OctopusDeploy/cli/pkg/cmd/project/variables"
16 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/project/view"
17 | "github.com/OctopusDeploy/cli/pkg/constants"
18 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
19 | "github.com/OctopusDeploy/cli/pkg/factory"
20 | "github.com/spf13/cobra"
21 | )
22 |
23 | func NewCmdProject(f factory.Factory) *cobra.Command {
24 | cmd := &cobra.Command{
25 | Use: "project ",
26 | Aliases: []string{"proj"},
27 | Short: "Manage projects",
28 | Long: "Manage projects in Octopus Deploy",
29 | Example: heredoc.Docf(`
30 | $ %[1]s project list
31 | $ %[1]s project ls
32 | `, constants.ExecutableName),
33 | Annotations: map[string]string{
34 | annotations.IsCore: "true",
35 | },
36 | }
37 |
38 | cmd.AddCommand(cmdList.NewCmdList(f))
39 | cmd.AddCommand(cmdView.NewCmdView(f))
40 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
41 | cmd.AddCommand(cmdDelete.NewCmdList(f))
42 | cmd.AddCommand(cmdDisable.NewCmdDisable(f))
43 | cmd.AddCommand(cmdEnable.NewCmdEnable(f))
44 | cmd.AddCommand(cmdConnect.NewCmdConnect(f))
45 | cmd.AddCommand(cmdDisconnect.NewCmdDisconnect(f))
46 | cmd.AddCommand(cmdConvert.NewCmdConvert(f))
47 | cmd.AddCommand(cmdVariables.NewCmdVariables(f))
48 | cmd.AddCommand(cmdClone.NewCmdClone(f))
49 | cmd.AddCommand(cmdBranch.NewCmdBranch(f))
50 |
51 | return cmd
52 | }
53 |
--------------------------------------------------------------------------------
/pkg/cmd/project/shared/shared.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | projectGroupCreate "github.com/OctopusDeploy/cli/pkg/cmd/projectgroup/create"
6 | "github.com/OctopusDeploy/cli/pkg/question"
7 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projectgroups"
10 | )
11 |
12 | type CreateProjectGroupCallback func() (string, cmd.Dependable, error)
13 | type GetAllGroupsCallback func() ([]*projectgroups.ProjectGroup, error)
14 |
15 | func GetAllGroups(client client.Client) ([]*projectgroups.ProjectGroup, error) {
16 | res, err := client.ProjectGroups.GetAll()
17 | if err != nil {
18 | return nil, err
19 | }
20 | return res, nil
21 | }
22 |
23 | func CreateProjectGroup(dependencies *cmd.Dependencies) (string, cmd.Dependable, error) {
24 | optValues := projectGroupCreate.NewCreateFlags()
25 | projectGroupOpts := cmd.NewDependenciesFromExisting(dependencies, "octopus project-group create")
26 |
27 | projectGroupCreateOpts := projectGroupCreate.NewCreateOptions(optValues, projectGroupOpts)
28 | projectGroupCreate.PromptMissing(projectGroupCreateOpts)
29 | returnValue := projectGroupCreateOpts.Name.Value
30 | return returnValue, projectGroupCreateOpts, nil
31 | }
32 |
33 | func AskProjectGroups(ask question.Asker, value string, getAllGroupsCallback GetAllGroupsCallback, createProjectGroupCallback CreateProjectGroupCallback) (string, cmd.Dependable, error) {
34 | if value != "" {
35 | return value, nil, nil
36 | }
37 | g, shouldCreateNew, err := selectors.SelectOrNew(ask, "You have not specified a Project group for this project. Please select one:", getAllGroupsCallback, func(pg *projectgroups.ProjectGroup) string {
38 | return pg.Name
39 | })
40 | if err != nil {
41 | return "", nil, err
42 | }
43 | if shouldCreateNew {
44 | return createProjectGroupCallback()
45 | }
46 | return g.Name, nil, nil
47 | }
48 |
--------------------------------------------------------------------------------
/pkg/cmd/project/variables/variables.go:
--------------------------------------------------------------------------------
1 | package variables
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/create"
6 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/delete"
7 | cmdExclude "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/exclude"
8 | cmdInclude "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/include"
9 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/list"
10 | cmdUpdate "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/update"
11 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/project/variables/view"
12 | "github.com/OctopusDeploy/cli/pkg/constants"
13 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
14 | "github.com/OctopusDeploy/cli/pkg/factory"
15 | "github.com/spf13/cobra"
16 | )
17 |
18 | func NewCmdVariables(f factory.Factory) *cobra.Command {
19 | cmd := &cobra.Command{
20 | Use: "variables ",
21 | Aliases: []string{"variable"},
22 | Short: "Manage project variables",
23 | Long: "Manage project variables in Octopus Deploy",
24 | Example: heredoc.Docf(`
25 | $ %[1]s project variable list "Deploy Web App"
26 | $ %[1]s project variable view --name "DatabaseName" --project Deploy
27 | $ %[1]s project variable update
28 | `, constants.ExecutableName),
29 | Annotations: map[string]string{
30 | annotations.IsCore: "true",
31 | },
32 | }
33 |
34 | cmd.AddCommand(cmdUpdate.NewUpdateCmd(f))
35 | cmd.AddCommand(cmdCreate.NewCreateCmd(f))
36 | cmd.AddCommand(cmdList.NewCmdList(f))
37 | cmd.AddCommand(cmdView.NewCmdView(f))
38 | cmd.AddCommand(cmdDelete.NewDeleteCmd(f))
39 | cmd.AddCommand(cmdInclude.NewIncludeVariableSetCmd(f))
40 | cmd.AddCommand(cmdExclude.NewExcludeVariableSetCmd(f))
41 |
42 | return cmd
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/cmd/projectgroup/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projectgroups"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | type ProjectGroupAsJson struct {
14 | Id string `json:"Id"`
15 | Name string `json:"Name"`
16 | Description string `json:"Description"`
17 | }
18 |
19 | func NewCmdList(f factory.Factory) *cobra.Command {
20 | cmd := &cobra.Command{
21 | Use: "list",
22 | Short: "List project groups",
23 | Long: "List project groups in Octopus Deploy",
24 | Example: heredoc.Docf(`
25 | $ %[1]s project-group list
26 | $ %[1]s project-group ls
27 | `, constants.ExecutableName),
28 | Aliases: []string{"ls"},
29 | RunE: func(cmd *cobra.Command, args []string) error {
30 | return listRun(cmd, f)
31 | },
32 | }
33 |
34 | return cmd
35 | }
36 |
37 | func listRun(cmd *cobra.Command, f factory.Factory) error {
38 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
39 | if err != nil {
40 | return err
41 | }
42 |
43 | allProjects, err := client.ProjectGroups.GetAll()
44 | if err != nil {
45 | return err
46 | }
47 |
48 | return output.PrintArray(allProjects, cmd, output.Mappers[*projectgroups.ProjectGroup]{
49 | Json: func(p *projectgroups.ProjectGroup) any {
50 | return ProjectGroupAsJson{
51 | Id: p.GetID(),
52 | Name: p.Name,
53 | Description: p.Description,
54 | }
55 | },
56 | Table: output.TableDefinition[*projectgroups.ProjectGroup]{
57 | Header: []string{"NAME", "DESCRIPTION"},
58 | Row: func(p *projectgroups.ProjectGroup) []string {
59 | return []string{output.Bold(p.Name), p.Description}
60 | },
61 | },
62 | Basic: func(p *projectgroups.ProjectGroup) string {
63 | return p.Name
64 | },
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/cmd/projectgroup/project-group.go:
--------------------------------------------------------------------------------
1 | package projectgroup
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | createCmd "github.com/OctopusDeploy/cli/pkg/cmd/projectgroup/create"
6 | deleteCmd "github.com/OctopusDeploy/cli/pkg/cmd/projectgroup/delete"
7 | listCmd "github.com/OctopusDeploy/cli/pkg/cmd/projectgroup/list"
8 | viewCmd "github.com/OctopusDeploy/cli/pkg/cmd/projectgroup/view"
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
11 | "github.com/OctopusDeploy/cli/pkg/factory"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | func NewCmdProjectGroup(f factory.Factory) *cobra.Command {
16 | cmd := &cobra.Command{
17 | Use: "project-group ",
18 | Short: "Manage project groups",
19 | Long: "Manage project groups in Octopus Deploy",
20 | Example: heredoc.Docf(`
21 | $ %[1]s project-group list
22 | $ %[1]s project-group ls
23 | `, constants.ExecutableName),
24 | Annotations: map[string]string{
25 | annotations.IsCore: "true",
26 | },
27 | }
28 |
29 | cmd.AddCommand(createCmd.NewCmdCreate(f))
30 | cmd.AddCommand(listCmd.NewCmdList(f))
31 | cmd.AddCommand(deleteCmd.NewCmdList(f))
32 | cmd.AddCommand(viewCmd.NewCmdView(f))
33 |
34 | return cmd
35 | }
36 |
--------------------------------------------------------------------------------
/pkg/cmd/release/progression/progression.go:
--------------------------------------------------------------------------------
1 | package progression
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdAllow "github.com/OctopusDeploy/cli/pkg/cmd/release/progression/allow"
6 | cmdPrevent "github.com/OctopusDeploy/cli/pkg/cmd/release/progression/prevent"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdProgression(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "progression ",
15 | Short: "Manage progression of a release",
16 | Long: "Manage progression of a release in Octopus Deploy",
17 | Example: heredoc.Docf(`
18 | $ %[1]s release progression prevent
19 | $ %[1]s release progression allow
20 | `, constants.ExecutableName),
21 | }
22 |
23 | cmd.AddCommand(cmdAllow.NewCmdAllow(f))
24 | cmd.AddCommand(cmdPrevent.NewCmdPrevent(f))
25 |
26 | return cmd
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/cmd/release/progression/shared/shared.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/OctopusDeploy/cli/pkg/question"
7 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/releases"
11 | )
12 |
13 | func GetReleaseID(octopus *client.Client, spaceID string, projectIdentifier string, version string) (string, error) {
14 | selectedProject, err := selectors.FindProject(octopus, projectIdentifier)
15 | if err != nil {
16 | return "", err
17 | }
18 |
19 | selectedRelease, err := FindRelease(octopus, selectedProject, version)
20 | if err != nil {
21 | return "", err
22 | }
23 | return selectedRelease.GetID(), nil
24 | }
25 |
26 | func SelectRelease(octopus *client.Client, project *projects.Project, ask question.Asker, action string) (*releases.Release, error) {
27 | existingReleases, err := octopus.Projects.GetReleases(project)
28 | if err != nil {
29 | return nil, err
30 | }
31 |
32 | selectedRelease, err := question.SelectMap(ask, fmt.Sprintf("Select Release to %s Progression for", action), existingReleases, func(r *releases.Release) string {
33 | return r.Version
34 | })
35 | if err != nil {
36 | return nil, err
37 | }
38 |
39 | return selectedRelease, nil
40 | }
41 |
42 | func FindRelease(octopus *client.Client, project *projects.Project, version string) (*releases.Release, error) {
43 | existingRelease, err := releases.GetReleaseInProject(octopus, octopus.GetSpaceID(), project.GetID(), version)
44 | if err != nil {
45 | return nil, err
46 | }
47 |
48 | if existingRelease == nil {
49 | return nil, fmt.Errorf("unable to locate a release with version/release number '%s'", version)
50 | }
51 |
52 | return existingRelease, nil
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/cmd/release/release.go:
--------------------------------------------------------------------------------
1 | package release
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/release/create"
6 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/release/delete"
7 | cmdDeploy "github.com/OctopusDeploy/cli/pkg/cmd/release/deploy"
8 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/release/list"
9 | cmdProgression "github.com/OctopusDeploy/cli/pkg/cmd/release/progression"
10 | "github.com/OctopusDeploy/cli/pkg/constants"
11 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
12 | "github.com/OctopusDeploy/cli/pkg/factory"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdRelease(f factory.Factory) *cobra.Command {
17 | cmd := &cobra.Command{
18 | Use: "release ",
19 | Short: "Manage releases",
20 | Long: "Manage releases in Octopus Deploy",
21 | Example: heredoc.Docf("$ %s release list", constants.ExecutableName),
22 | Annotations: map[string]string{
23 | annotations.IsCore: "true",
24 | },
25 | }
26 |
27 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
28 | cmd.AddCommand(cmdDeploy.NewCmdDeploy(f))
29 | cmd.AddCommand(cmdList.NewCmdList(f))
30 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
31 | cmd.AddCommand(cmdProgression.NewCmdProgression(f))
32 |
33 | return cmd
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/cmd/runbook/runbook.go:
--------------------------------------------------------------------------------
1 | package runbook
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/runbook/delete"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/runbook/list"
7 | cmdRun "github.com/OctopusDeploy/cli/pkg/cmd/runbook/run"
8 | cmdSnapshot "github.com/OctopusDeploy/cli/pkg/cmd/runbook/snapshot"
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
11 | "github.com/OctopusDeploy/cli/pkg/factory"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | func NewCmdRunbook(f factory.Factory) *cobra.Command {
16 | cmd := &cobra.Command{
17 | Use: "runbook ",
18 | Short: "Manage runbooks",
19 | Long: "Manage runbooks in Octopus Deploy",
20 | Example: heredoc.Docf(`
21 | $ %[1]s runbook list
22 | $ %[1]s runbook run
23 | `, constants.ExecutableName),
24 | Annotations: map[string]string{
25 | annotations.IsCore: "true",
26 | },
27 | }
28 |
29 | cmd.AddCommand(cmdList.NewCmdList(f))
30 | cmd.AddCommand(cmdRun.NewCmdRun(f))
31 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
32 | cmd.AddCommand(cmdSnapshot.NewCmdSnapshot(f))
33 | return cmd
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/cmd/runbook/snapshot/snapshot.go:
--------------------------------------------------------------------------------
1 | package snapshot
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/runbook/snapshot/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/runbook/snapshot/list"
7 | cmdPublish "github.com/OctopusDeploy/cli/pkg/cmd/runbook/snapshot/publish"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdSnapshot(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "snapshot ",
16 | Short: "Manage runbook snapshots",
17 | Long: "Manage runbook snapshots in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s runbook snapshot create
20 | $ %[1]s runbook snapshot list
21 | `, constants.ExecutableName),
22 | }
23 |
24 | cmd.AddCommand(cmdList.NewCmdList(f))
25 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
26 | cmd.AddCommand(cmdPublish.NewCmdPublish(f))
27 | return cmd
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/cmd/space/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/spaces"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List spaces",
17 | Long: "List spaces in Octopus Deploy",
18 | Example: heredoc.Docf("$ %s space list", constants.ExecutableName),
19 | Aliases: []string{"ls"},
20 | RunE: func(cmd *cobra.Command, args []string) error {
21 | return listRun(f, cmd)
22 | },
23 | }
24 |
25 | return cmd
26 | }
27 |
28 | func listRun(f factory.Factory, cmd *cobra.Command) error {
29 | client, err := f.GetSystemClient(apiclient.NewRequester(cmd))
30 | if err != nil {
31 | return err
32 | }
33 |
34 | allSpaces, err := client.Spaces.GetAll()
35 | if err != nil {
36 | return err
37 | }
38 |
39 | type SpaceAsJson struct {
40 | Id string `json:"Id"`
41 | Name string `json:"Name"`
42 | Description string `json:"Description"`
43 | TaskQueue string `json:"TaskQueue"`
44 | }
45 |
46 | return output.PrintArray(allSpaces, cmd, output.Mappers[*spaces.Space]{
47 | Json: func(item *spaces.Space) any {
48 | taskQueue := "Running"
49 | if item.TaskQueueStopped {
50 | taskQueue = "Stopped"
51 | }
52 | return SpaceAsJson{
53 | Id: item.GetID(),
54 | Name: item.Name,
55 | Description: item.Description,
56 | TaskQueue: taskQueue,
57 | }
58 | },
59 | Table: output.TableDefinition[*spaces.Space]{
60 | Header: []string{"NAME", "DESCRIPTION", "TASK QUEUE"},
61 | Row: func(item *spaces.Space) []string {
62 | taskQueue := output.Green("Running")
63 | if item.TaskQueueStopped {
64 | taskQueue = output.Yellow("Stopped")
65 | }
66 |
67 | return []string{output.Bold(item.Name), item.Description, taskQueue}
68 | },
69 | },
70 | Basic: func(item *spaces.Space) string {
71 | return item.Name
72 | },
73 | })
74 | }
75 |
--------------------------------------------------------------------------------
/pkg/cmd/space/space.go:
--------------------------------------------------------------------------------
1 | package space
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/space/create"
6 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/space/delete"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/space/list"
8 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/space/view"
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
11 | "github.com/OctopusDeploy/cli/pkg/factory"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | func NewCmdSpace(f factory.Factory) *cobra.Command {
16 | cmd := &cobra.Command{
17 | Use: "space ",
18 | Short: "Manage spaces",
19 | Long: "Manage spaces in Octopus Deploy",
20 | Example: heredoc.Docf(`
21 | $ %[1]s space list
22 | $ %[1]s space view Spaces-302
23 | `, constants.ExecutableName),
24 | Annotations: map[string]string{
25 | annotations.IsConfiguration: "true",
26 | },
27 | }
28 |
29 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
30 | cmd.AddCommand(cmdList.NewCmdList(f))
31 | cmd.AddCommand(cmdView.NewCmdView(f))
32 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
33 |
34 | return cmd
35 | }
36 |
--------------------------------------------------------------------------------
/pkg/cmd/target/azure-web-app/azure-web-app.go:
--------------------------------------------------------------------------------
1 | package azure_web_app
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/target/azure-web-app/create"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/azure-web-app/list"
8 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/azure-web-app/view"
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 | "github.com/OctopusDeploy/cli/pkg/factory"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdAzureWebApp(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "azure-web-app ",
17 | Short: "Manage Azure Web App deployment targets",
18 | Long: "Manage Azure Web App deployment targets in Octopus Deploy",
19 | Example: fmt.Sprintf("$ %s deployment-target azure-web-app list", constants.ExecutableName),
20 | }
21 |
22 | cmd.AddCommand(cmdList.NewCmdList(f))
23 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
24 | cmd.AddCommand(cmdView.NewCmdView(f))
25 |
26 | return cmd
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/cmd/target/azure-web-app/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Azure Web App deployment targets",
17 | Long: "List Azure Web App deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s deployment-target azure-web-app list
20 | $ %[1]s deployment-target azure-web-app ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(c *cobra.Command, args []string) error {
24 | dependencies := cmd.NewDependencies(f, c)
25 | options := list.NewListOptions(dependencies, c, machines.MachinesQuery{DeploymentTargetTypes: []string{"AzureWebApp"}})
26 | return list.ListRun(options)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/target/azure-web-app/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "fmt"
5 | "github.com/OctopusDeploy/cli/pkg/output"
6 | "strings"
7 |
8 | "github.com/MakeNowJust/heredoc/v2"
9 | "github.com/OctopusDeploy/cli/pkg/cmd"
10 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
11 | "github.com/OctopusDeploy/cli/pkg/constants"
12 | "github.com/OctopusDeploy/cli/pkg/factory"
13 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
14 | "github.com/OctopusDeploy/cli/pkg/usage"
15 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
16 | "github.com/spf13/cobra"
17 | )
18 |
19 | func NewCmdView(f factory.Factory) *cobra.Command {
20 | flags := shared.NewViewFlags()
21 | cmd := &cobra.Command{
22 | Args: usage.ExactArgs(1),
23 | Use: "view { | }",
24 | Short: "View an Azure Web App deployment target",
25 | Long: "View an Azure Web App deployment target in Octopus Deploy",
26 | Example: heredoc.Docf(`
27 | $ %[1]s deployment-target azure-web-app view 'Shop Api'
28 | $ %[1]s deployment-target azure-web-app view Machines-100
29 | `, constants.ExecutableName),
30 | RunE: func(c *cobra.Command, args []string) error {
31 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
32 | return ViewRun(opts)
33 | },
34 | }
35 |
36 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
37 |
38 | return cmd
39 | }
40 |
41 | func ViewRun(opts *shared.ViewOptions) error {
42 | return shared.ViewRun(opts, contributeEndpoint, "Azure Web App")
43 | }
44 |
45 | func contributeEndpoint(opts *shared.ViewOptions, targetEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
46 | data := []*output.DataRow{}
47 | endpoint := targetEndpoint.(*machines.AzureWebAppEndpoint)
48 | accountRows, err := shared.ContributeAccount(opts, endpoint.AccountID)
49 | if err != nil {
50 | return nil, err
51 | }
52 |
53 | data = append(data, accountRows...)
54 | data = append(data, output.NewDataRow("Web App", getWebAppDisplay(endpoint)))
55 | return data, nil
56 | }
57 |
58 | func getWebAppDisplay(endpoint *machines.AzureWebAppEndpoint) string {
59 | builder := &strings.Builder{}
60 | builder.WriteString(endpoint.WebAppName)
61 | if endpoint.WebAppSlotName != "" {
62 | builder.WriteString(fmt.Sprintf("/%s", endpoint.WebAppSlotName))
63 | }
64 |
65 | return builder.String()
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/cmd/target/cloud-region/cloud-region.go:
--------------------------------------------------------------------------------
1 | package cloud_region
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/target/cloud-region/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/cloud-region/list"
7 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/cloud-region/view"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdCloudRegion(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "cloud-region ",
16 | Short: "Manage Cloud Region deployment targets",
17 | Long: "Manage Cloud Region deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf("$ %s deployment-target cloud-region list", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 | cmd.AddCommand(cmdList.NewCmdList(f))
23 | cmd.AddCommand(cmdView.NewCmdView(f))
24 | return cmd
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/target/cloud-region/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Cloud Region deployment targets",
17 | Long: "List Cloud Region deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s deployment-target cloud-region list
20 | $ %[1]s deployment-target cloud-region ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(c *cobra.Command, args []string) error {
24 | dependencies := cmd.NewDependencies(f, c)
25 | options := list.NewListOptions(dependencies, c, machines.MachinesQuery{DeploymentTargetTypes: []string{"CloudRegion"}})
26 | return list.ListRun(options)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/target/cloud-region/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/usage"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdView(f factory.Factory) *cobra.Command {
15 | flags := shared.NewViewFlags()
16 | cmd := &cobra.Command{
17 | Args: usage.ExactArgs(1),
18 | Use: "view { | }",
19 | Short: "View a Cloud Region deployment target",
20 | Long: "View a Cloud Region deployment target in Octopus Deploy",
21 | Example: heredoc.Docf(`
22 | $ %[1]s deployment-target cloud-region view 'EU'
23 | $ %[1]s deployment-target cloud-region view Machines-100
24 | `, constants.ExecutableName),
25 | RunE: func(c *cobra.Command, args []string) error {
26 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
27 | return ViewRun(opts)
28 | },
29 | }
30 |
31 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
32 |
33 | return cmd
34 | }
35 |
36 | func ViewRun(opts *shared.ViewOptions) error {
37 | return shared.ViewRun(opts, nil, "Cloud Region")
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/cmd/target/kubernetes/kubernetes.go:
--------------------------------------------------------------------------------
1 | package kubernetes
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/target/kubernetes/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/kubernetes/list"
7 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/kubernetes/view"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdKubernetes(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "kubernetes ",
16 | Short: "Manage Kubernetes deployment targets",
17 | Long: "Manage Kubernetes deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf("$ %s deployment-target kubernetes create", constants.ExecutableName),
19 | Aliases: []string{"k8s"},
20 | }
21 |
22 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
23 | cmd.AddCommand(cmdList.NewCmdList(f))
24 | cmd.AddCommand(cmdView.NewCmdView(f))
25 |
26 | return cmd
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/cmd/target/kubernetes/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Kubernetes deployment targets",
17 | Long: "List Kubernetes deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s deployment-target kubernetes list
20 | $ %[1]s deployment-target kubernetes ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(c *cobra.Command, _ []string) error {
24 | dependencies := cmd.NewDependencies(f, c)
25 | options := list.NewListOptions(dependencies, c, machines.MachinesQuery{DeploymentTargetTypes: []string{"Kubernetes"}})
26 | return list.ListRun(options)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/target/kubernetes/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a Kubernetes deployment target",
22 | Long: "View a Kubernetes deployment target in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s deployment-target kubernetes view 'target-name'
25 | `, constants.ExecutableName),
26 | RunE: func(c *cobra.Command, args []string) error {
27 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
28 | return ViewRun(opts)
29 | },
30 | }
31 |
32 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
33 |
34 | return cmd
35 | }
36 |
37 | func ViewRun(opts *shared.ViewOptions) error {
38 | return shared.ViewRun(opts, contributeEndpoint, "Kubernetes")
39 | }
40 |
41 | func contributeEndpoint(_ *shared.ViewOptions, targetEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
42 | data := []*output.DataRow{}
43 | endpoint := targetEndpoint.(*machines.KubernetesEndpoint)
44 |
45 | data = append(data, output.NewDataRow("Authentication Type", endpoint.Authentication.GetAuthenticationType()))
46 |
47 | return data, nil
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/cmd/target/listening-tentacle/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Listening Tentacle deployment targets",
17 | Long: "List Listening Tentacle deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s deployment-target listening-tentacle list
20 | $ %[1]s deployment-target listening-tentacle ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(c *cobra.Command, args []string) error {
24 | dependencies := cmd.NewDependencies(f, c)
25 | options := list.NewListOptions(dependencies, c, machines.MachinesQuery{DeploymentTargetTypes: []string{"TentaclePassive"}})
26 | return list.ListRun(options)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/target/listening-tentacle/listening-tentacle.go:
--------------------------------------------------------------------------------
1 | package listening_tentacle
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/target/listening-tentacle/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/listening-tentacle/list"
7 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/listening-tentacle/view"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdListeningTentacle(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "listening-tentacle ",
16 | Short: "Manage Listening Tentacle deployment targets",
17 | Long: "Manage Listening Tentacle deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf("$ %s deployment-target listening-tentacle list", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdList.NewCmdList(f))
22 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
23 | cmd.AddCommand(cmdView.NewCmdView(f))
24 | return cmd
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/target/listening-tentacle/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a Listening Tentacle deployment target",
22 | Long: "View a Listening Tentacle deployment target in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s deployment-target listening-tentacle view 'EU'
25 | $ %[1]s deployment-target listening-tentacle view Machines-100
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
29 | return ViewRun(opts)
30 | },
31 | }
32 |
33 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
34 |
35 | return cmd
36 | }
37 |
38 | func ViewRun(opts *shared.ViewOptions) error {
39 | return shared.ViewRun(opts, contributeEndpoint, "Listening Tentacle")
40 | }
41 |
42 | func contributeEndpoint(opts *shared.ViewOptions, targetEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
43 | data := []*output.DataRow{}
44 |
45 | endpoint := targetEndpoint.(*machines.ListeningTentacleEndpoint)
46 | data = append(data, output.NewDataRow("URI", endpoint.URI.String()))
47 | data = append(data, output.NewDataRow("Tentacle version", endpoint.TentacleVersionDetails.Version))
48 |
49 | proxyData, err := shared.ContributeProxy(opts, endpoint.ProxyID)
50 | if err != nil {
51 | return nil, err
52 | }
53 | data = append(data, proxyData...)
54 |
55 | return data, nil
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/cmd/target/polling-tentacle/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Polling Tentacle deployment targets",
17 | Long: "List Polling Tentacle deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s deployment-target polling-tentacle list
20 | $ %[1]s deployment-target polling-tentacle ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(c *cobra.Command, args []string) error {
24 | dependencies := cmd.NewDependencies(f, c)
25 | options := list.NewListOptions(dependencies, c, machines.MachinesQuery{DeploymentTargetTypes: []string{"TentacleActive"}})
26 | return list.ListRun(options)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/target/polling-tentacle/polling-tentacle.go:
--------------------------------------------------------------------------------
1 | package polling_tentacle
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/polling-tentacle/list"
6 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/polling-tentacle/view"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdPollingTentacle(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "polling-tentacle ",
15 | Short: "Manage Polling Tentacle deployment targets",
16 | Long: "Manage Polling Tentacle deployment targets in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s deployment-target polling-tentacle list", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdList.NewCmdList(f))
21 | cmd.AddCommand(cmdView.NewCmdView(f))
22 | return cmd
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/cmd/target/polling-tentacle/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a Polling Tentacle deployment target",
22 | Long: "View a Polling Tentacle deployment target in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s deployment-target polling-tentacle view 'EU'
25 | $ %[1]s deployment-target polling-tentacle view Machines-100
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
29 | return ViewRun(opts)
30 | },
31 | }
32 |
33 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
34 |
35 | return cmd
36 | }
37 |
38 | func ViewRun(opts *shared.ViewOptions) error {
39 | return shared.ViewRun(opts, contributeEndpoint, "Polling Tentacle")
40 | }
41 |
42 | func contributeEndpoint(opts *shared.ViewOptions, targetEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
43 | data := []*output.DataRow{}
44 |
45 | endpoint := targetEndpoint.(*machines.PollingTentacleEndpoint)
46 | data = append(data, output.NewDataRow("URI", endpoint.URI.String()))
47 | data = append(data, output.NewDataRow("Tentacle version", endpoint.TentacleVersionDetails.Version))
48 |
49 | return data, nil
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/cmd/target/shared/environment.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
6 | "github.com/OctopusDeploy/cli/pkg/util"
7 | "github.com/OctopusDeploy/cli/pkg/util/flag"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | const (
13 | FlagEnvironment = "environment"
14 | )
15 |
16 | type CreateTargetEnvironmentFlags struct {
17 | Environments *flag.Flag[[]string]
18 | }
19 |
20 | type CreateTargetEnvironmentOptions struct {
21 | *cmd.Dependencies
22 | selectors.GetAllEnvironmentsCallback
23 | }
24 |
25 | func NewCreateTargetEnvironmentOptions(dependencies *cmd.Dependencies) *CreateTargetEnvironmentOptions {
26 | return &CreateTargetEnvironmentOptions{
27 | Dependencies: dependencies,
28 | GetAllEnvironmentsCallback: func() ([]*environments.Environment, error) {
29 | return selectors.GetAllEnvironments(dependencies.Client)
30 | },
31 | }
32 | }
33 |
34 | func NewCreateTargetEnvironmentFlags() *CreateTargetEnvironmentFlags {
35 | return &CreateTargetEnvironmentFlags{
36 | Environments: flag.New[[]string](FlagEnvironment, false),
37 | }
38 | }
39 |
40 | func RegisterCreateTargetEnvironmentFlags(cmd *cobra.Command, flags *CreateTargetEnvironmentFlags) {
41 | cmd.Flags().StringSliceVar(&flags.Environments.Value, FlagEnvironment, []string{}, "Choose at least one environment for the deployment target.")
42 | }
43 |
44 | func PromptForEnvironments(opts *CreateTargetEnvironmentOptions, flags *CreateTargetEnvironmentFlags) error {
45 | if util.Empty(flags.Environments.Value) {
46 | envs, err := selectors.EnvironmentsMultiSelect(opts.Ask, opts.GetAllEnvironmentsCallback,
47 | "Choose at least one environment for the deployment target.\n", true)
48 | if err != nil {
49 | return err
50 | }
51 | flags.Environments.Value = util.SliceTransform(envs, func(e *environments.Environment) string { return e.Name })
52 | }
53 |
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/cmd/target/shared/environment_test.go:
--------------------------------------------------------------------------------
1 | package shared_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
6 | "github.com/OctopusDeploy/cli/test/testutil"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestPromptEnvironments_FlagsSupplied(t *testing.T) {
13 | pa := []*testutil.PA{}
14 |
15 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
16 | flags := shared.NewCreateTargetEnvironmentFlags()
17 | flags.Environments.Value = []string{"Dev"}
18 |
19 | opts := shared.NewCreateTargetEnvironmentOptions(&cmd.Dependencies{Ask: asker})
20 |
21 | err := shared.PromptForEnvironments(opts, flags)
22 | checkRemainingPrompts()
23 |
24 | assert.NoError(t, err)
25 | }
26 |
27 | func TestPromptEnvironments_ShouldPrompt(t *testing.T) {
28 | pa := []*testutil.PA{
29 | testutil.NewMultiSelectPrompt("Choose at least one environment for the deployment target.\n", "", []string{"Dev", "Test"}, []string{"Dev"}),
30 | }
31 |
32 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
33 | flags := shared.NewCreateTargetEnvironmentFlags()
34 |
35 | opts := shared.NewCreateTargetEnvironmentOptions(&cmd.Dependencies{Ask: asker})
36 | opts.GetAllEnvironmentsCallback = func() ([]*environments.Environment, error) {
37 | return []*environments.Environment{
38 | environments.NewEnvironment("Dev"),
39 | environments.NewEnvironment("Test"),
40 | }, nil
41 | }
42 |
43 | err := shared.PromptForEnvironments(opts, flags)
44 | checkRemainingPrompts()
45 |
46 | assert.NoError(t, err)
47 |
48 | assert.Equal(t, []string{"Dev"}, flags.Environments.Value)
49 | }
50 |
--------------------------------------------------------------------------------
/pkg/cmd/target/shared/role.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/question"
6 | "github.com/OctopusDeploy/cli/pkg/util"
7 | "github.com/OctopusDeploy/cli/pkg/util/flag"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | const (
13 | FlagRole = "role"
14 | )
15 |
16 | type GetAllRolesCallback func() ([]string, error)
17 |
18 | type CreateTargetRoleFlags struct {
19 | Roles *flag.Flag[[]string]
20 | }
21 |
22 | type CreateTargetRoleOptions struct {
23 | *cmd.Dependencies
24 | GetAllRolesCallback
25 | }
26 |
27 | func NewCreateTargetRoleOptions(dependencies *cmd.Dependencies) *CreateTargetRoleOptions {
28 | return &CreateTargetRoleOptions{
29 | Dependencies: dependencies,
30 |
31 | GetAllRolesCallback: func() ([]string, error) {
32 | return getAllMachineRoles(*dependencies.Client)
33 | },
34 | }
35 | }
36 |
37 | func NewCreateTargetRoleFlags() *CreateTargetRoleFlags {
38 | return &CreateTargetRoleFlags{
39 | Roles: flag.New[[]string](FlagRole, false),
40 | }
41 | }
42 |
43 | func RegisterCreateTargetRoleFlags(cmd *cobra.Command, commonFlags *CreateTargetRoleFlags) {
44 | cmd.Flags().StringSliceVar(&commonFlags.Roles.Value, FlagRole, []string{}, "Choose at least one role that this deployment target will provide.")
45 | }
46 |
47 | func PromptForRoles(opts *CreateTargetRoleOptions, flags *CreateTargetRoleFlags) error {
48 |
49 | if util.Empty(flags.Roles.Value) {
50 | availableRoles, err := opts.GetAllRolesCallback()
51 | if err != nil {
52 | return err
53 | }
54 | roles, err := question.MultiSelectWithAddMap(opts.Ask, "Choose at least one role for the deployment target.\n", availableRoles, true)
55 |
56 | if err != nil {
57 | return err
58 | }
59 | flags.Roles.Value = roles
60 | }
61 | return nil
62 | }
63 |
64 | func getAllMachineRoles(client client.Client) ([]string, error) {
65 | res, err := client.MachineRoles.GetAll()
66 | if err != nil {
67 | return nil, err
68 | }
69 |
70 | var roles []string
71 | for _, r := range res {
72 | roles = append(roles, *r)
73 | }
74 | return roles, nil
75 | }
76 |
--------------------------------------------------------------------------------
/pkg/cmd/target/shared/role_test.go:
--------------------------------------------------------------------------------
1 | package shared_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
6 | "github.com/OctopusDeploy/cli/pkg/util"
7 | "github.com/OctopusDeploy/cli/test/testutil"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestDistinctRoles_EmptyList(t *testing.T) {
13 | result := util.SliceDistinct([]string{})
14 | assert.Empty(t, result)
15 | }
16 |
17 | func TestDistinctRoles_DuplicateValues(t *testing.T) {
18 | result := util.SliceDistinct([]string{"a", "b", "a"})
19 | assert.Equal(t, []string{"a", "b"}, result)
20 | }
21 |
22 | func TestPromptRoles_FlagsSupplied(t *testing.T) {
23 | pa := []*testutil.PA{}
24 |
25 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
26 | flags := shared.NewCreateTargetRoleFlags()
27 | flags.Roles.Value = []string{"Man with hat"}
28 |
29 | opts := shared.NewCreateTargetRoleOptions(&cmd.Dependencies{Ask: asker})
30 |
31 | err := shared.PromptForRoles(opts, flags)
32 | checkRemainingPrompts()
33 |
34 | assert.NoError(t, err)
35 | }
36 |
37 | func TestPromptRolesAndEnvironments_ShouldPrompt(t *testing.T) {
38 | pa := []*testutil.PA{
39 | testutil.NewMultiSelectWithAddPrompt("Choose at least one role for the deployment target.\n", "", []string{"Ninja #3", "Girl in crowd"}, []string{"Ninja #3"}),
40 | }
41 |
42 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
43 | flags := shared.NewCreateTargetRoleFlags()
44 |
45 | opts := shared.NewCreateTargetRoleOptions(&cmd.Dependencies{Ask: asker})
46 | opts.GetAllRolesCallback = func() ([]string, error) {
47 | return []string{"Ninja #3", "Girl in crowd"}, nil
48 | }
49 |
50 | err := shared.PromptForRoles(opts, flags)
51 | checkRemainingPrompts()
52 |
53 | assert.NoError(t, err)
54 |
55 | assert.Equal(t, []string{"Ninja #3"}, flags.Roles.Value)
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/cmd/target/shared/target.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
7 | "math"
8 | )
9 |
10 | type GetTargetsCallback func() ([]*machines.DeploymentTarget, error)
11 |
12 | type GetTargetsOptions struct {
13 | GetTargetsCallback
14 | }
15 |
16 | func NewGetTargetsOptions(dependencies *cmd.Dependencies, query machines.MachinesQuery) *GetTargetsOptions {
17 | return &GetTargetsOptions{
18 | GetTargetsCallback: func() ([]*machines.DeploymentTarget, error) {
19 | return GetAllTargets(*dependencies.Client, query)
20 | },
21 | }
22 | }
23 |
24 | func NewGetTargetsOptionsForAllTargets(dependencies *cmd.Dependencies) *GetTargetsOptions {
25 | return &GetTargetsOptions{
26 | GetTargetsCallback: func() ([]*machines.DeploymentTarget, error) {
27 | return GetAllTargets(*dependencies.Client, machines.MachinesQuery{})
28 | },
29 | }
30 | }
31 |
32 | func GetAllTargets(client client.Client, query machines.MachinesQuery) ([]*machines.DeploymentTarget, error) {
33 | query.Skip = 0
34 | query.Take = math.MaxInt32
35 | res, err := client.Machines.Get(query)
36 | if err != nil {
37 | return nil, err
38 | }
39 | return res.Items, nil
40 | }
41 |
--------------------------------------------------------------------------------
/pkg/cmd/target/ssh/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List SSH deployment targets",
17 | Long: "List SSH deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s deployment-target ssh list
20 | $ %[1]s deployment-target ssh ls
21 | `, constants.ExecutableName),
22 | Aliases: []string{"ls"},
23 | RunE: func(c *cobra.Command, args []string) error {
24 | dependencies := cmd.NewDependencies(f, c)
25 | options := list.NewListOptions(dependencies, c, machines.MachinesQuery{DeploymentTargetTypes: []string{"Ssh"}})
26 | return list.ListRun(options)
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/target/ssh/ssh.go:
--------------------------------------------------------------------------------
1 | package ssh
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/target/ssh/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/ssh/list"
7 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/ssh/view"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdSsh(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "ssh ",
16 | Short: "Manage SSH deployment targets",
17 | Long: "Manage SSH deployment targets in Octopus Deploy",
18 | Example: heredoc.Docf("$ %s deployment-target ssh create", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 | cmd.AddCommand(cmdList.NewCmdList(f))
23 | cmd.AddCommand(cmdView.NewCmdView(f))
24 | return cmd
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/target/ssh/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a SSH deployment target",
22 | Long: "View a SSH deployment target in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s deployment-target ssh view 'linux-web-server'
25 | $ %[1]s deployment-target ssh view Machines-100
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
29 | return ViewRun(opts)
30 | },
31 | }
32 |
33 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
34 |
35 | return cmd
36 | }
37 |
38 | func ViewRun(opts *shared.ViewOptions) error {
39 | return shared.ViewRun(opts, contributeEndpoint, "SSH")
40 | }
41 |
42 | func contributeEndpoint(opts *shared.ViewOptions, targetEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
43 | data := []*output.DataRow{}
44 | endpoint := targetEndpoint.(*machines.SSHEndpoint)
45 |
46 | data = append(data, output.NewDataRow("URI", endpoint.URI.String()))
47 | data = append(data, output.NewDataRow("Runtime architecture", getRuntimeArchitecture(endpoint)))
48 | accountRows, err := shared.ContributeAccount(opts, endpoint.AccountID)
49 | if err != nil {
50 | return nil, err
51 | }
52 | data = append(data, accountRows...)
53 |
54 | proxy, err := shared.ContributeProxy(opts, endpoint.ProxyID)
55 | data = append(data, proxy...)
56 | if err != nil {
57 | return nil, err
58 | }
59 |
60 | return data, nil
61 | }
62 |
63 | func getRuntimeArchitecture(endpoint *machines.SSHEndpoint) string {
64 | if endpoint.DotNetCorePlatform == "" {
65 | return "Mono"
66 | }
67 |
68 | return endpoint.DotNetCorePlatform
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/target/target.go:
--------------------------------------------------------------------------------
1 | package target
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdAzureWebApp "github.com/OctopusDeploy/cli/pkg/cmd/target/azure-web-app"
6 | cmdCloudRegion "github.com/OctopusDeploy/cli/pkg/cmd/target/cloud-region"
7 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/target/delete"
8 | cmdKubernetes "github.com/OctopusDeploy/cli/pkg/cmd/target/kubernetes"
9 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/target/list"
10 | cmdListeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/target/listening-tentacle"
11 | cmdPollingTentacle "github.com/OctopusDeploy/cli/pkg/cmd/target/polling-tentacle"
12 | cmdSsh "github.com/OctopusDeploy/cli/pkg/cmd/target/ssh"
13 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/target/view"
14 | "github.com/OctopusDeploy/cli/pkg/constants"
15 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
16 | "github.com/OctopusDeploy/cli/pkg/factory"
17 | "github.com/spf13/cobra"
18 | )
19 |
20 | func NewCmdDeploymentTarget(f factory.Factory) *cobra.Command {
21 | cmd := &cobra.Command{
22 | Use: "deployment-target ",
23 | Short: "Manage deployment targets",
24 | Long: "Manage deployment targets in Octopus Deploy",
25 | Example: heredoc.Docf("$ %s deployment-target list", constants.ExecutableName),
26 | Annotations: map[string]string{
27 | annotations.IsCore: "true",
28 | },
29 | }
30 |
31 | cmd.AddCommand(cmdListeningTentacle.NewCmdListeningTentacle(f))
32 | cmd.AddCommand(cmdPollingTentacle.NewCmdPollingTentacle(f))
33 | cmd.AddCommand(cmdSsh.NewCmdSsh(f))
34 | cmd.AddCommand(cmdCloudRegion.NewCmdCloudRegion(f))
35 | cmd.AddCommand(cmdAzureWebApp.NewCmdAzureWebApp(f))
36 | cmd.AddCommand(cmdKubernetes.NewCmdKubernetes(f))
37 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
38 | cmd.AddCommand(cmdList.NewCmdList(f))
39 | cmd.AddCommand(cmdView.NewCmdView(f))
40 |
41 | return cmd
42 | }
43 |
--------------------------------------------------------------------------------
/pkg/cmd/task/task.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | waitCmd "github.com/OctopusDeploy/cli/pkg/cmd/task/wait"
5 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
6 | "github.com/OctopusDeploy/cli/pkg/factory"
7 | "github.com/spf13/cobra"
8 | )
9 |
10 | func NewCmdTask(f factory.Factory) *cobra.Command {
11 | cmd := &cobra.Command{
12 | Use: "task ",
13 | Short: "Manage tasks",
14 | Long: "Manage tasks in Octopus Deploy",
15 | Annotations: map[string]string{
16 | annotations.IsCore: "true",
17 | },
18 | }
19 |
20 | cmd.AddCommand(waitCmd.NewCmdWait(f))
21 |
22 | return cmd
23 | }
24 |
--------------------------------------------------------------------------------
/pkg/cmd/tenant/clone/clone_test.go:
--------------------------------------------------------------------------------
1 | package clone_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/cmd/tenant/clone"
6 | "github.com/OctopusDeploy/cli/test/testutil"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestPromptMissing_AllFlagsSupplied(t *testing.T) {
13 | pa := []*testutil.PA{}
14 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
15 | flags := clone.NewCloneFlags()
16 | flags.Name.Value = "Cloned tenant"
17 | flags.Description.Value = "the description"
18 | flags.SourceTenant.Value = "source tenant"
19 |
20 | opts := clone.NewCloneOptions(flags, &cmd.Dependencies{Ask: asker})
21 |
22 | opts.GetTenantCallback = func(identifier string) (*tenants.Tenant, error) {
23 | return tenants.NewTenant("source tenant"), nil
24 | }
25 |
26 | err := clone.PromptMissing(opts)
27 | checkRemainingPrompts()
28 | assert.NoError(t, err)
29 | }
30 |
31 | func TestPromptMissing_NoFlagsSupplied(t *testing.T) {
32 | pa := []*testutil.PA{
33 | testutil.NewInputPrompt("Name", "A short, memorable, unique name for this Tenant.", "cloned tenant"),
34 | testutil.NewInputPrompt("Description", "A short, memorable, description for this Tenant.", "the description"),
35 | testutil.NewSelectPrompt("You have not specified a source Tenant to clone from. Please select one:", "", []string{"source tenant", "source tenant 2"}, "source tenant"),
36 | }
37 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
38 | flags := clone.NewCloneFlags()
39 | opts := clone.NewCloneOptions(flags, &cmd.Dependencies{Ask: asker})
40 |
41 | opts.GetTenantCallback = func(identifier string) (*tenants.Tenant, error) {
42 | return tenants.NewTenant("source tenant"), nil
43 | }
44 | opts.GetAllTenantsCallback= func () ([]*tenants.Tenant, error) {
45 | return []*tenants.Tenant{
46 | tenants.NewTenant("source tenant"),
47 | tenants.NewTenant("source tenant 2"),
48 | },nil
49 | }
50 |
51 | err := clone.PromptMissing(opts)
52 | checkRemainingPrompts()
53 | assert.NoError(t, err)
54 |
55 | assert.Equal(t, "cloned tenant", flags.Name.Value)
56 | assert.Equal(t, "the description", flags.Description.Value)
57 | assert.Equal(t, "source tenant", flags.SourceTenant.Value)
58 | }
--------------------------------------------------------------------------------
/pkg/cmd/tenant/create/create_opts.go:
--------------------------------------------------------------------------------
1 | package create
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tagsets"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
9 |
10 | "github.com/OctopusDeploy/cli/pkg/cmd"
11 | "github.com/OctopusDeploy/cli/pkg/output"
12 | "github.com/OctopusDeploy/cli/pkg/util/flag"
13 | )
14 |
15 | type GetAllTagSetsCallback func() ([]*tagsets.TagSet, error)
16 |
17 | type CreateOptions struct {
18 | *CreateFlags
19 | *cmd.Dependencies
20 | GetAllTagsCallback GetAllTagSetsCallback
21 | }
22 |
23 | func NewCreateOptions(createFlags *CreateFlags, dependencies *cmd.Dependencies) *CreateOptions {
24 | return &CreateOptions{
25 | CreateFlags: createFlags,
26 | Dependencies: dependencies,
27 | GetAllTagsCallback: getAllTagSetsCallback(dependencies.Client),
28 | }
29 | }
30 |
31 | func (co *CreateOptions) Commit() error {
32 |
33 | tenant := tenants.NewTenant(co.Name.Value)
34 | tenant.Description = co.Description.Value
35 | tenant.TenantTags = co.Tag.Value
36 |
37 | createdTenant, err := co.Client.Tenants.Add(tenant)
38 | if err != nil {
39 | return err
40 | }
41 |
42 | _, err = fmt.Fprintf(co.Out, "\nSuccessfully created tenant %s (%s).\n", createdTenant.Name, createdTenant.ID)
43 | if err != nil {
44 | return err
45 | }
46 |
47 | link := output.Bluef("%s/app#/%s/tenants/%s/overview", co.Host, co.Space.GetID(), createdTenant.GetID())
48 | fmt.Fprintf(co.Out, "View this tenant on Octopus Deploy: %s\n", link)
49 |
50 | return nil
51 | }
52 |
53 | func (co *CreateOptions) GenerateAutomationCmd() {
54 | if !co.NoPrompt {
55 | autoCmd := flag.GenerateAutomationCmd(co.CmdPath, co.Name, co.Description, co.Tag)
56 | fmt.Fprintf(co.Out, "%s\n", autoCmd)
57 | }
58 | }
59 |
60 | func getAllTagSetsCallback(client *client.Client) GetAllTagSetsCallback {
61 | return func() ([]*tagsets.TagSet, error) {
62 | tagSets, err := client.TagSets.GetAll()
63 | if err != nil {
64 | return nil, err
65 | }
66 | return tagSets, nil
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/pkg/cmd/tenant/create/create_test.go:
--------------------------------------------------------------------------------
1 | package create_test
2 |
3 | import (
4 | "testing"
5 |
6 | tenantCreate "github.com/OctopusDeploy/cli/pkg/cmd/tenant/create"
7 | "github.com/OctopusDeploy/cli/test/testutil"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tagsets"
9 | )
10 |
11 | func TestTenantCreate_Tags(t *testing.T) {
12 | tags := []string{
13 | "foo/bar",
14 | "foo/car",
15 | "bin/bop",
16 | }
17 | pa := []*testutil.PA{
18 | testutil.NewMultiSelectPrompt("Tags", "", tags, []string{"foo/bar"}),
19 | }
20 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
21 | getAllTagsCallback := func() ([]*tagsets.TagSet, error) {
22 | fooTagSet := tagsets.NewTagSet("foo")
23 | fooTagSet.Tags = []*tagsets.Tag{
24 | tagsets.NewTag("bar", "#000000"),
25 | tagsets.NewTag("car", "#0000FF"),
26 | }
27 | fooTagSet.Tags[0].CanonicalTagName = "foo/bar"
28 | fooTagSet.Tags[1].CanonicalTagName = "foo/car"
29 | binTagSet := tagsets.NewTagSet("bin")
30 | binTagSet.Tags = []*tagsets.Tag{
31 | tagsets.NewTag("bop", "#FF0000"),
32 | }
33 | binTagSet.Tags[0].CanonicalTagName = "bin/bop"
34 | return []*tagsets.TagSet{
35 | fooTagSet,
36 | binTagSet,
37 | }, nil
38 | }
39 | tenantCreate.AskTags(asker, []string{}, getAllTagsCallback)
40 | checkRemainingPrompts()
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/cmd/tenant/disable/disable.go:
--------------------------------------------------------------------------------
1 | package disable
2 |
3 | import (
4 | "fmt"
5 | "github.com/MakeNowJust/heredoc/v2"
6 | "github.com/OctopusDeploy/cli/pkg/cmd"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | type DisableOptions struct {
15 | *cmd.Dependencies
16 | IdOrName string
17 | }
18 |
19 | func NewDisableOptions(args []string, dependencies *cmd.Dependencies) *DisableOptions {
20 | return &DisableOptions{
21 | Dependencies: dependencies,
22 | IdOrName: args[0],
23 | }
24 | }
25 |
26 | func NewCmdDisable(f factory.Factory) *cobra.Command {
27 | cmd := &cobra.Command{
28 | Use: "disable { | }",
29 | Short: "Disable a tenant",
30 | Long: "Disable a tenant in Octopus Deploy",
31 | Example: heredoc.Docf(`
32 | $ %[1]s tenant disable Tenants-1
33 | $ %[1]s tenant disable 'Tenant'
34 | `, constants.ExecutableName),
35 | RunE: func(c *cobra.Command, args []string) error {
36 | if len(args) == 0 {
37 | args = append(args, "")
38 | }
39 |
40 | opts := NewDisableOptions(args, cmd.NewDependencies(f, c))
41 | return disableRun(opts)
42 | },
43 | }
44 |
45 | return cmd
46 | }
47 |
48 | func disableRun(opts *DisableOptions) error {
49 | if !opts.NoPrompt {
50 | if err := PromptMissing(opts); err != nil {
51 | return err
52 | }
53 | }
54 |
55 | if opts.IdOrName == "" {
56 | return fmt.Errorf("tenant identifier is required but was not provided")
57 | }
58 |
59 | tenantToUpdate, err := opts.Client.Tenants.GetByIdentifier(opts.IdOrName)
60 | if err != nil {
61 | return err
62 | }
63 |
64 | tenantToUpdate.IsDisabled = true
65 | _, err = tenants.Update(opts.Client, tenantToUpdate)
66 | if err != nil {
67 | return err
68 | }
69 |
70 | return nil
71 | }
72 |
73 | func PromptMissing(opts *DisableOptions) error {
74 | if opts.IdOrName == "" {
75 | existingTenants, err := opts.Client.Tenants.GetAll()
76 | if err != nil {
77 | return err
78 | }
79 | selectedTenant, err := selectors.ByName(opts.Ask, existingTenants, "Select the tenant you wish to disable:")
80 | if err != nil {
81 | return err
82 | }
83 | opts.IdOrName = selectedTenant.GetID()
84 | }
85 |
86 | return nil
87 | }
88 |
--------------------------------------------------------------------------------
/pkg/cmd/tenant/enable/enable.go:
--------------------------------------------------------------------------------
1 | package enable
2 |
3 | import (
4 | "fmt"
5 | "github.com/MakeNowJust/heredoc/v2"
6 | "github.com/OctopusDeploy/cli/pkg/cmd"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/question/selectors"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | type EnableOptions struct {
15 | *cmd.Dependencies
16 | IdOrName string
17 | }
18 |
19 | func NewEnableOptions(args []string, dependencies *cmd.Dependencies) *EnableOptions {
20 | return &EnableOptions{
21 | Dependencies: dependencies,
22 | IdOrName: args[0],
23 | }
24 | }
25 |
26 | func NewCmdEnable(f factory.Factory) *cobra.Command {
27 | cmd := &cobra.Command{
28 | Use: "enable { | }",
29 | Short: "Enable a tenant",
30 | Long: "Enable a tenant in Octopus Deploy",
31 | Example: heredoc.Docf(`
32 | $ %[1]s tenant enable Tenants-1
33 | $ %[1]s tenant enable 'Tenant'
34 | `, constants.ExecutableName),
35 | RunE: func(c *cobra.Command, args []string) error {
36 | if len(args) == 0 {
37 | args = append(args, "")
38 | }
39 |
40 | opts := NewEnableOptions(args, cmd.NewDependencies(f, c))
41 | return enableRun(opts)
42 | },
43 | }
44 |
45 | return cmd
46 | }
47 |
48 | func enableRun(opts *EnableOptions) error {
49 | if !opts.NoPrompt {
50 | if err := PromptMissing(opts); err != nil {
51 | return err
52 | }
53 | }
54 |
55 | if opts.IdOrName == "" {
56 | return fmt.Errorf("tenant identifier is required but was not provided")
57 | }
58 |
59 | tenantToUpdate, err := opts.Client.Tenants.GetByIdentifier(opts.IdOrName)
60 | if err != nil {
61 | return err
62 | }
63 |
64 | tenantToUpdate.IsDisabled = false
65 | _, err = tenants.Update(opts.Client, tenantToUpdate)
66 | if err != nil {
67 | return err
68 | }
69 |
70 | return nil
71 | }
72 |
73 | func PromptMissing(opts *EnableOptions) error {
74 | if opts.IdOrName == "" {
75 | existingTenants, err := opts.Client.Tenants.GetAll()
76 | if err != nil {
77 | return err
78 | }
79 | selectedTenant, err := selectors.ByName(opts.Ask, existingTenants, "Select the tenant you wish to enable:")
80 | if err != nil {
81 | return err
82 | }
83 | opts.IdOrName = selectedTenant.GetID()
84 | }
85 |
86 | return nil
87 | }
88 |
--------------------------------------------------------------------------------
/pkg/cmd/tenant/tenant.go:
--------------------------------------------------------------------------------
1 | package tenant
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdClone "github.com/OctopusDeploy/cli/pkg/cmd/tenant/clone"
6 | cmdConnect "github.com/OctopusDeploy/cli/pkg/cmd/tenant/connect"
7 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/tenant/create"
8 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/tenant/delete"
9 | cmdDisable "github.com/OctopusDeploy/cli/pkg/cmd/tenant/disable"
10 | cmdDisconnect "github.com/OctopusDeploy/cli/pkg/cmd/tenant/disconnect"
11 | cmdEnable "github.com/OctopusDeploy/cli/pkg/cmd/tenant/enable"
12 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/tenant/list"
13 | cmdTag "github.com/OctopusDeploy/cli/pkg/cmd/tenant/tag"
14 | cmdVariable "github.com/OctopusDeploy/cli/pkg/cmd/tenant/variables"
15 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/tenant/view"
16 | "github.com/OctopusDeploy/cli/pkg/constants"
17 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
18 | "github.com/OctopusDeploy/cli/pkg/factory"
19 | "github.com/spf13/cobra"
20 | )
21 |
22 | func NewCmdTenant(f factory.Factory) *cobra.Command {
23 | cmd := &cobra.Command{
24 | Use: "tenant ",
25 | Short: "Manage tenants",
26 | Long: "Manage tenants in Octopus Deploy",
27 | Example: heredoc.Docf(`
28 | $ %[1]s tenant list
29 | $ %[1]s tenant ls
30 | `, constants.ExecutableName),
31 | Annotations: map[string]string{
32 | annotations.IsCore: "true",
33 | },
34 | }
35 |
36 | cmd.AddCommand(cmdConnect.NewCmdConnect(f))
37 | cmd.AddCommand(cmdDisconnect.NewCmdDisconnect(f))
38 | cmd.AddCommand(cmdList.NewCmdList(f))
39 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
40 | cmd.AddCommand(cmdTag.NewCmdTag(f))
41 | cmd.AddCommand(cmdClone.NewCmdClone(f))
42 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
43 | cmd.AddCommand(cmdView.NewCmdView(f))
44 | cmd.AddCommand(cmdVariable.NewCmdVariables(f))
45 | cmd.AddCommand(cmdEnable.NewCmdEnable(f))
46 | cmd.AddCommand(cmdDisable.NewCmdDisable(f))
47 |
48 | return cmd
49 | }
50 |
--------------------------------------------------------------------------------
/pkg/cmd/tenant/variables/variables.go:
--------------------------------------------------------------------------------
1 | package variables
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/tenant/variables/list"
6 | cmdUpdate "github.com/OctopusDeploy/cli/pkg/cmd/tenant/variables/update"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdVariables(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "variables ",
16 | Aliases: []string{"variable"},
17 | Short: "Manage tenant variables",
18 | Long: "Manage tenant variables in Octopus Deploy",
19 | Example: heredoc.Docf(`
20 | $ %[1]s tenant variables list "Bobs Wood Shop"
21 | $ %[1]s tenant variables update --tenant "Bobs Fish Shack" --name "site-name" --value "Bob's Fish Shack" --project "Awesome Web Site" --environment "Test"
22 | `, constants.ExecutableName),
23 | Annotations: map[string]string{
24 | annotations.IsCore: "true",
25 | },
26 | }
27 |
28 | //cmd.AddCommand(cmdUpdate.NewUpdateCmd(f))
29 | //cmd.AddCommand(cmdCreate.NewCreateCmd(f))
30 | cmd.AddCommand(cmdList.NewCmdList(f))
31 | cmd.AddCommand(cmdUpdate.NewCmdUpdate(f))
32 | //cmd.AddCommand(cmdView.NewCmdView(f))
33 | //cmd.AddCommand(cmdDelete.NewDeleteCmd(f))
34 | //cmd.AddCommand(cmdInclude.NewIncludeVariableSetCmd(f))
35 | //cmd.AddCommand(cmdExclude.NewExcludeVariableSetCmd(f))
36 |
37 | return cmd
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/cmd/user/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/apiclient"
6 | "github.com/OctopusDeploy/cli/pkg/constants"
7 | "github.com/OctopusDeploy/cli/pkg/factory"
8 | "github.com/OctopusDeploy/cli/pkg/output"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/users"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | type UserAsJson struct {
14 | Id string `json:"Id"`
15 | Name string `json:"Name"`
16 | UserName string `json:"UserName"`
17 | Description string `json:"Description"`
18 | }
19 |
20 | func NewCmdList(f factory.Factory) *cobra.Command {
21 | cmd := &cobra.Command{
22 | Use: "list",
23 | Short: "List users",
24 | Long: "List users in Octopus Deploy",
25 | Example: heredoc.Docf(`
26 | $ %[1]s user list
27 | $ %[1]s user ls
28 | `, constants.ExecutableName),
29 | Aliases: []string{"ls"},
30 | RunE: func(cmd *cobra.Command, args []string) error {
31 | return listRun(cmd, f)
32 | },
33 | }
34 |
35 | return cmd
36 | }
37 |
38 | func listRun(cmd *cobra.Command, f factory.Factory) error {
39 | client, err := f.GetSpacedClient(apiclient.NewRequester(cmd))
40 | if err != nil {
41 | return err
42 | }
43 |
44 | allUsers, err := client.Users.GetAll()
45 | if err != nil {
46 | return err
47 | }
48 |
49 | return output.PrintArray(allUsers, cmd, output.Mappers[*users.User]{
50 | Json: func(t *users.User) any {
51 | return UserAsJson{
52 | Id: t.GetID(),
53 | Name: t.DisplayName,
54 | UserName: t.Username,
55 | // TODO other fields like isService, etc
56 | }
57 | },
58 | Table: output.TableDefinition[*users.User]{
59 | // TODO other fields like isService, etc
60 | Header: []string{"USERNAME", "NAME", "ID"},
61 | Row: func(t *users.User) []string {
62 | return []string{output.Bold(t.Username), t.DisplayName, t.GetID()}
63 | },
64 | },
65 | Basic: func(t *users.User) string {
66 | return t.Username
67 | },
68 | })
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/user/delete"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/user/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdUser(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "user ",
16 | Short: "Manage users",
17 | Long: "Manage user in Octopus Deploy",
18 | Example: heredoc.Docf(`
19 | $ %[1]s user list
20 | $ %[1]s user ls
21 | `, constants.ExecutableName),
22 | Annotations: map[string]string{
23 | annotations.IsCore: "true",
24 | },
25 | }
26 |
27 | cmd.AddCommand(cmdList.NewCmdList(f))
28 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/version/version.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/constants"
6 | "github.com/OctopusDeploy/cli/pkg/factory"
7 | "github.com/spf13/cobra"
8 | )
9 |
10 | func NewCmdVersion(f factory.Factory) *cobra.Command {
11 | cmd := &cobra.Command{
12 | Use: "version",
13 | Hidden: true,
14 | Example: heredoc.Docf("$ %s version", constants.ExecutableName),
15 | RunE: func(cmd *cobra.Command, args []string) error {
16 | cmd.Println(f.BuildVersion())
17 | return nil
18 | },
19 | }
20 |
21 | return cmd
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/listening-tentacle/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Listening Tentacle workers",
17 | Long: "List Listening Tentacle workers in Octopus Deploy",
18 | Aliases: []string{"ls"},
19 | Example: heredoc.Docf(`
20 | $ %s worker listening-tentacle list
21 | `, constants.ExecutableName),
22 | RunE: func(c *cobra.Command, args []string) error {
23 | dependencies := cmd.NewDependencies(f, c)
24 | options := list.NewListOptions(dependencies, c, func(worker *machines.Worker) bool {
25 | return worker.Endpoint.GetCommunicationStyle() == "TentaclePassive"
26 | })
27 | return list.ListRun(options)
28 | },
29 | }
30 |
31 | return cmd
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/listening-tentacle/listening-tentacle.go:
--------------------------------------------------------------------------------
1 | package listening_tentacle
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/create"
7 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/list"
8 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/view"
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 | "github.com/OctopusDeploy/cli/pkg/factory"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func NewCmdListeningTentacle(f factory.Factory) *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "listening-tentacle ",
17 | Short: "Manage Listening Tentacle workers",
18 | Long: "Manage Listening Tentacle workers in Octopus Deploy",
19 | Example: fmt.Sprintf("$ %s worker listening-tentacle list", constants.ExecutableName),
20 | }
21 |
22 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
23 | cmd.AddCommand(cmdList.NewCmdList(f))
24 | cmd.AddCommand(cmdView.NewCmdView(f))
25 |
26 | return cmd
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/listening-tentacle/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a Listening Tentacle worker",
22 | Long: "View a Listening Tentacle worker in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s worker listening-tentacle view 'WindowsWorker'
25 | $ %[1]s worker listening-tentacle view Machines-100
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
29 | return ViewRun(opts)
30 | },
31 | }
32 |
33 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
34 |
35 | return cmd
36 | }
37 |
38 | func ViewRun(opts *shared.ViewOptions) error {
39 | return shared.ViewRun(opts, contributeEndpoint, "Listening Tentacle")
40 | }
41 |
42 | func contributeEndpoint(opts *shared.ViewOptions, end machines.IEndpoint) ([]*output.DataRow, error) {
43 | data := []*output.DataRow{}
44 |
45 | endpoint := end.(*machines.ListeningTentacleEndpoint)
46 | data = append(data, output.NewDataRow("URI", endpoint.URI.String()))
47 | data = append(data, output.NewDataRow("Tentacle version", endpoint.TentacleVersionDetails.Version))
48 |
49 | proxyData, err := shared.ContributeProxy(opts, endpoint.ProxyID)
50 | if err != nil {
51 | return nil, err
52 | }
53 | data = append(data, proxyData...)
54 |
55 | return data, nil
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/polling-tentacle/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List Polling Tentacle workers",
17 | Long: "List Polling Tentacle workers in Octopus Deploy",
18 | Aliases: []string{"ls"},
19 | Example: heredoc.Docf("$ %s worker polling-tentacle list", constants.ExecutableName),
20 | RunE: func(c *cobra.Command, args []string) error {
21 | dependencies := cmd.NewDependencies(f, c)
22 | options := list.NewListOptions(dependencies, c, func(worker *machines.Worker) bool {
23 | return worker.Endpoint.GetCommunicationStyle() == "TentacleActive"
24 | })
25 | return list.ListRun(options)
26 | },
27 | }
28 |
29 | return cmd
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/polling-tentacle/polling-tentacle.go:
--------------------------------------------------------------------------------
1 | package polling_tentacle
2 |
3 | import (
4 | "fmt"
5 |
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle/list"
7 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle/view"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdPollingTentacle(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "polling-tentacle ",
16 | Short: "Manage Polling Tentacle workers",
17 | Long: "Manage Polling Tentacle workers in Octopus Deploy",
18 | Example: fmt.Sprintf("$ %s worker polling-tentacle list", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdList.NewCmdList(f))
22 | cmd.AddCommand(cmdView.NewCmdView(f))
23 |
24 | return cmd
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/polling-tentacle/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a Polling Tentacle worker",
22 | Long: "View a Polling Tentacle worker in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s worker polling-tentacle view 'WindowsWorker'
25 | $ %[1]s worker polling-tentacle view Machines-100
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
29 | return ViewRun(opts)
30 | },
31 | }
32 |
33 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
34 |
35 | return cmd
36 | }
37 |
38 | func ViewRun(opts *shared.ViewOptions) error {
39 | return shared.ViewRun(opts, contributeEndpoint, "Polling Tentacle")
40 | }
41 |
42 | func contributeEndpoint(opts *shared.ViewOptions, workerEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
43 | data := []*output.DataRow{}
44 |
45 | endpoint := workerEndpoint.(*machines.PollingTentacleEndpoint)
46 | data = append(data, output.NewDataRow("URI", endpoint.URI.String()))
47 | data = append(data, output.NewDataRow("Tentacle version", endpoint.TentacleVersionDetails.Version))
48 |
49 | return data, nil
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/shared/worker.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
7 | )
8 |
9 | type GetWorkersCallback func() ([]*machines.Worker, error)
10 | type GetWorkerCallback func(identifer string) (*machines.Worker, error)
11 |
12 | type GetWorkersOptions struct {
13 | GetWorkersCallback
14 | GetWorkerCallback
15 | }
16 |
17 | func NewGetWorkersOptions(dependencies *cmd.Dependencies, filter func(*machines.Worker) bool) *GetWorkersOptions {
18 | return &GetWorkersOptions{
19 | GetWorkersCallback: func() ([]*machines.Worker, error) {
20 | return GetWorkers(*dependencies.Client, filter)
21 | },
22 | GetWorkerCallback: func(identifier string) (*machines.Worker, error) {
23 | return GetWorker(*dependencies.Client, identifier)
24 | },
25 | }
26 | }
27 |
28 | func NewGetWorkersOptionsForAllWorkers(dependencies *cmd.Dependencies) *GetWorkersOptions {
29 | return &GetWorkersOptions{
30 | GetWorkersCallback: func() ([]*machines.Worker, error) {
31 | return GetWorkers(*dependencies.Client, nil)
32 | },
33 | }
34 | }
35 |
36 | func GetWorkers(client client.Client, filter func(*machines.Worker) bool) ([]*machines.Worker, error) {
37 | allWorkers, err := client.Workers.GetAll()
38 | if err != nil {
39 | return nil, err
40 | }
41 |
42 | if filter == nil {
43 | return allWorkers, nil
44 | }
45 |
46 | var workers []*machines.Worker
47 | for _, w := range allWorkers {
48 | filterResult := filter(w)
49 | if filterResult {
50 | workers = append(workers, w)
51 | }
52 | }
53 |
54 | return workers, nil
55 | }
56 |
57 | func GetWorker(client client.Client, identifier string) (*machines.Worker, error) {
58 | worker, err := client.Workers.GetByIdentifier(identifier)
59 | if err != nil {
60 | return nil, err
61 | }
62 |
63 | return worker, nil
64 | }
65 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/shared/workerpool_test.go:
--------------------------------------------------------------------------------
1 | package shared_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared"
6 | "github.com/OctopusDeploy/cli/test/testutil"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestPromptForWorkerPool_FlagsSupplied(t *testing.T) {
13 | pa := []*testutil.PA{}
14 |
15 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
16 | flags := shared.NewWorkerPoolFlags()
17 | flags.WorkerPools.Value = []string{"Head lifeguard"}
18 |
19 | opts := shared.NewWorkerPoolOptions(&cmd.Dependencies{Ask: asker})
20 | err := shared.PromptForWorkerPools(opts, flags)
21 | checkRemainingPrompts()
22 | assert.NoError(t, err)
23 | }
24 |
25 | func TestPromptForWorkerPool_NoFlagsSupplied(t *testing.T) {
26 | pa := []*testutil.PA{
27 | testutil.NewMultiSelectPrompt("Select the worker pools to assign to the worker", "", []string{"Groundskeeper", "Swim instructor"}, []string{"Groundskeeper", "Swim instructor"}),
28 | }
29 |
30 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
31 | flags := shared.NewWorkerPoolFlags()
32 |
33 | opts := shared.NewWorkerPoolOptions(&cmd.Dependencies{Ask: asker})
34 | opts.GetAllWorkerPoolsCallback = func() ([]*workerpools.WorkerPoolListResult, error) {
35 | poolWorker1 := &workerpools.WorkerPoolListResult{
36 | ID: "WorkerPools-1",
37 | Name: "Groundskeeper",
38 | WorkerPoolType: workerpools.WorkerPoolTypeStatic,
39 | CanAddWorkers: true,
40 | }
41 | poolWorker2 := &workerpools.WorkerPoolListResult{
42 | ID: "WorkerPools-2",
43 | Name: "Swim instructor",
44 | WorkerPoolType: workerpools.WorkerPoolTypeStatic,
45 | CanAddWorkers: true,
46 | }
47 | return []*workerpools.WorkerPoolListResult{poolWorker1, poolWorker2}, nil
48 | }
49 | err := shared.PromptForWorkerPools(opts, flags)
50 | checkRemainingPrompts()
51 | assert.NoError(t, err)
52 | assert.Equal(t, []string{"Groundskeeper", "Swim instructor"}, flags.WorkerPools.Value)
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/ssh/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/list"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdList(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "list",
16 | Short: "List SSH workers",
17 | Long: "List SSH workers in Octopus Deploy",
18 | Aliases: []string{"ls"},
19 | Example: heredoc.Docf("$ %s worker ssh list", constants.ExecutableName),
20 | RunE: func(c *cobra.Command, args []string) error {
21 | dependencies := cmd.NewDependencies(f, c)
22 | options := list.NewListOptions(dependencies, c, func(worker *machines.Worker) bool {
23 | return worker.Endpoint.GetCommunicationStyle() == "Ssh"
24 | })
25 | return list.ListRun(options)
26 | },
27 | }
28 |
29 | return cmd
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/ssh/ssh.go:
--------------------------------------------------------------------------------
1 | package ssh
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/create"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/list"
7 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/view"
8 | "github.com/OctopusDeploy/cli/pkg/constants"
9 | "github.com/OctopusDeploy/cli/pkg/factory"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func NewCmdSsh(f factory.Factory) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "ssh ",
16 | Short: "Manage SSH workers",
17 | Long: "Manage SSH workers in Octopus Deploy",
18 | Example: heredoc.Docf("$ %s worker SSH list", constants.ExecutableName),
19 | }
20 |
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 | cmd.AddCommand(cmdList.NewCmdList(f))
23 | cmd.AddCommand(cmdView.NewCmdView(f))
24 |
25 | return cmd
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/ssh/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a SSH worker",
22 | Long: "View a SSH worker in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s worker ssh view 'linux-worker'
25 | $ %[1]s worker ssh view Machines-100
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
29 | return ViewRun(opts)
30 | },
31 | }
32 |
33 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
34 |
35 | return cmd
36 | }
37 |
38 | func ViewRun(opts *shared.ViewOptions) error {
39 | return shared.ViewRun(opts, contributeEndpoint, "SSH")
40 | }
41 |
42 | func contributeEndpoint(opts *shared.ViewOptions, workerEndpoint machines.IEndpoint) ([]*output.DataRow, error) {
43 | data := []*output.DataRow{}
44 | endpoint := workerEndpoint.(*machines.SSHEndpoint)
45 |
46 | data = append(data, output.NewDataRow("URI", endpoint.URI.String()))
47 | data = append(data, output.NewDataRow("Runtime architecture", GetRuntimeArchitecture(endpoint)))
48 | accountRows, err := shared.ContributeAccount(opts, endpoint.AccountID)
49 | if err != nil {
50 | return nil, err
51 | }
52 | data = append(data, accountRows...)
53 |
54 | proxy, err := shared.ContributeProxy(opts, endpoint.ProxyID)
55 | data = append(data, proxy...)
56 | if err != nil {
57 | return nil, err
58 | }
59 |
60 | return data, nil
61 | }
62 |
63 | func GetRuntimeArchitecture(endpoint *machines.SSHEndpoint) string {
64 | if endpoint.DotNetCorePlatform == "" {
65 | return "Mono"
66 | }
67 |
68 | return endpoint.DotNetCorePlatform
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/MakeNowJust/heredoc/v2"
7 | "github.com/OctopusDeploy/cli/pkg/cmd"
8 | listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle/view"
9 | pollingTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle/view"
10 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/shared"
11 | ssh "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh/view"
12 | "github.com/OctopusDeploy/cli/pkg/constants"
13 | "github.com/OctopusDeploy/cli/pkg/factory"
14 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
15 | "github.com/OctopusDeploy/cli/pkg/usage"
16 | "github.com/spf13/cobra"
17 | )
18 |
19 | func NewCmdView(f factory.Factory) *cobra.Command {
20 | flags := shared.NewViewFlags()
21 | cmd := &cobra.Command{
22 | Args: usage.ExactArgs(1),
23 | Use: "view { | }",
24 | Short: "View a worker",
25 | Long: "View a worker in Octopus Deploy",
26 | Example: heredoc.Docf(`
27 | $ %[1]s worker view Machines-100
28 | $ %[1]s worker view 'worker'
29 | `, constants.ExecutableName),
30 | RunE: func(c *cobra.Command, args []string) error {
31 | return ViewRun(shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args))
32 | },
33 | }
34 |
35 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
36 |
37 | return cmd
38 | }
39 |
40 | func ViewRun(opts *shared.ViewOptions) error {
41 | var worker, err = opts.Client.Workers.GetByIdentifier(opts.IdOrName)
42 | if err != nil {
43 | return err
44 | }
45 |
46 | switch worker.Endpoint.GetCommunicationStyle() {
47 | case "TentaclePassive":
48 | return listeningTentacle.ViewRun(opts)
49 | case "TentacleActive":
50 | return pollingTentacle.ViewRun(opts)
51 | case "Ssh":
52 | return ssh.ViewRun(opts)
53 | }
54 |
55 | return fmt.Errorf("unsupported worker '%s'", worker.Endpoint.GetCommunicationStyle())
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/cmd/worker/worker.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdDelete "github.com/OctopusDeploy/cli/pkg/cmd/worker/delete"
6 | cmdList "github.com/OctopusDeploy/cli/pkg/cmd/worker/list"
7 | listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/listening-tentacle"
8 | pollingTentacle "github.com/OctopusDeploy/cli/pkg/cmd/worker/polling-tentacle"
9 | "github.com/OctopusDeploy/cli/pkg/cmd/worker/ssh"
10 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/worker/view"
11 | "github.com/OctopusDeploy/cli/pkg/constants"
12 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
13 | "github.com/OctopusDeploy/cli/pkg/factory"
14 | "github.com/spf13/cobra"
15 | )
16 |
17 | func NewCmdWorker(f factory.Factory) *cobra.Command {
18 | cmd := &cobra.Command{
19 | Use: "worker ",
20 | Short: "Manage workers",
21 | Long: "Manage workers in Octopus Deploy",
22 | Example: heredoc.Docf(`
23 | $ %[1]s worker list
24 | $ %[1]s worker ls
25 | `, constants.ExecutableName),
26 | Annotations: map[string]string{
27 | annotations.IsCore: "true",
28 | },
29 | }
30 |
31 | cmd.AddCommand(listeningTentacle.NewCmdListeningTentacle(f))
32 | cmd.AddCommand(pollingTentacle.NewCmdPollingTentacle(f))
33 | cmd.AddCommand(ssh.NewCmdSsh(f))
34 | cmd.AddCommand(cmdList.NewCmdList(f))
35 | cmd.AddCommand(cmdDelete.NewCmdDelete(f))
36 | cmd.AddCommand(cmdView.NewCmdView(f))
37 |
38 | return cmd
39 | }
40 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/dynamic/create/create_test.go:
--------------------------------------------------------------------------------
1 | package create_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/dynamic/create"
6 | "github.com/OctopusDeploy/cli/test/testutil"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestPromptMissing_FlagsSupplied(t *testing.T) {
13 | pa := []*testutil.PA{}
14 |
15 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
16 | flags := create.NewCreateFlags()
17 | flags.Name.Value = "name"
18 | flags.Description.Value = "description"
19 | flags.Type.Value = "Ubuntu1804"
20 |
21 | opts := create.NewCreateOptions(flags, &cmd.Dependencies{Ask: asker})
22 |
23 | err := create.PromptMissing(opts)
24 | checkRemainingPrompts()
25 | assert.NoError(t, err)
26 | }
27 |
28 | func TestPromptMissing_ShouldPrompt(t *testing.T) {
29 | pa := []*testutil.PA{
30 | testutil.NewInputPrompt("Name", "A short, memorable, unique name for this Dynamic Worker Pool.", "name"),
31 | testutil.NewInputPrompt("Description", "A short, memorable, description for this Dynamic Worker Pool.", "description"),
32 | testutil.NewSelectPrompt("Select the worker type to use", "", []string{"Ubuntu (UbuntuDefault)", "Windows (WindowsDefault)"}, "Windows (WindowsDefault)"),
33 | }
34 |
35 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
36 | flags := create.NewCreateFlags()
37 | opts := create.NewCreateOptions(flags, &cmd.Dependencies{Ask: asker})
38 | opts.GetDynamicWorkerPoolTypes = func() ([]*workerpools.DynamicWorkerPoolType, error) {
39 | return []*workerpools.DynamicWorkerPoolType{
40 | &workerpools.DynamicWorkerPoolType{
41 | ID: "UbuntuDefault",
42 | Type: "UbuntuDefault",
43 | Description: "Ubuntu",
44 | },
45 | &workerpools.DynamicWorkerPoolType{
46 | ID: "WindowsDefault",
47 | Type: "WindowsDefault",
48 | Description: "Windows",
49 | },
50 | }, nil
51 | }
52 |
53 | err := create.PromptMissing(opts)
54 | checkRemainingPrompts()
55 | assert.NoError(t, err)
56 |
57 | assert.Equal(t, "name", flags.Name.Value)
58 | assert.Equal(t, "description", flags.Description.Value)
59 | assert.Equal(t, "WindowsDefault", flags.Type.Value)
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/dynamic/dynamic.go:
--------------------------------------------------------------------------------
1 | package dynamic
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/dynamic/create"
6 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/dynamic/view"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdSsh(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "dynamic ",
15 | Short: "Manage dynamic worker pools",
16 | Long: "Manage dynamic worker pools in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s worker-pool dynamic view", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
21 | cmd.AddCommand(cmdView.NewCmdView(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/dynamic/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
10 | "github.com/OctopusDeploy/cli/pkg/output"
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdView(f factory.Factory) *cobra.Command {
17 | flags := shared.NewViewFlags()
18 | cmd := &cobra.Command{
19 | Args: usage.ExactArgs(1),
20 | Use: "view { | }",
21 | Short: "View a dynamic worker pool",
22 | Long: "View a dynamic worker pool in Octopus Deploy",
23 | Example: heredoc.Docf(`
24 | $ %[1]s worker-pool dynamic view WorkerPools-3
25 | $ %[1]s worker-pool dynamic view 'Hosted Workers'
26 | `, constants.ExecutableName),
27 | RunE: func(c *cobra.Command, args []string) error {
28 | return ViewRun(shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args))
29 | },
30 | }
31 |
32 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
33 |
34 | return cmd
35 | }
36 |
37 | func ViewRun(opts *shared.ViewOptions) error {
38 | return shared.ViewRun(opts, contributeDetails)
39 | }
40 |
41 | func contributeDetails(opts *shared.ViewOptions, workerPool workerpools.IWorkerPool) ([]*output.DataRow, error) {
42 | workerType := workerPool.(*workerpools.DynamicWorkerPool).WorkerType
43 |
44 | data := []*output.DataRow{}
45 | data = append(data, output.NewDataRow("Worker Type", string(workerType)))
46 |
47 | return data, nil
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/list/list.go:
--------------------------------------------------------------------------------
1 | package list
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/shared"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/OctopusDeploy/cli/pkg/output"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | type ListOptions struct {
15 | *shared.GetWorkerPoolsOptions
16 | *cobra.Command
17 | *cmd.Dependencies
18 | }
19 |
20 | func NewListOptions(dependencies *cmd.Dependencies, command *cobra.Command) *ListOptions {
21 | return &ListOptions{
22 | GetWorkerPoolsOptions: shared.NewGetWorkerPoolsOptions(dependencies),
23 | Command: command,
24 | Dependencies: dependencies,
25 | }
26 | }
27 |
28 | func NewCmdList(f factory.Factory) *cobra.Command {
29 | cmd := &cobra.Command{
30 | Use: "list",
31 | Short: "List worker pools",
32 | Long: "List worker pools in Octopus Deploy",
33 | Aliases: []string{"ls"},
34 | Example: heredoc.Docf("$ %s worker-pool list", constants.ExecutableName),
35 | RunE: func(c *cobra.Command, args []string) error {
36 | return ListRun(NewListOptions(cmd.NewDependencies(f, c), c))
37 | },
38 | }
39 |
40 | return cmd
41 | }
42 |
43 | func ListRun(opts *ListOptions) error {
44 | allPools, err := opts.GetWorkerPoolsCallback()
45 | if err != nil {
46 | return err
47 | }
48 |
49 | type TargetAsJson struct {
50 | Id string `json:"Id"`
51 | Name string `json:"Name"`
52 | Type string `json:"Type"`
53 | Slug string `json:"Slug"`
54 | }
55 |
56 | return output.PrintArray(allPools, opts.Command, output.Mappers[*workerpools.WorkerPoolListResult]{
57 | Json: func(item *workerpools.WorkerPoolListResult) any {
58 |
59 | return TargetAsJson{
60 | Id: item.ID,
61 | Name: item.Name,
62 | Type: string(item.WorkerPoolType),
63 | Slug: item.Slug,
64 | }
65 | },
66 | Table: output.TableDefinition[*workerpools.WorkerPoolListResult]{
67 | Header: []string{"NAME", "TYPE", "SLUG"},
68 | Row: func(item *workerpools.WorkerPoolListResult) []string {
69 | return []string{output.Bold(item.Name), string(item.WorkerPoolType), output.Dim(item.Slug)}
70 | },
71 | },
72 | Basic: func(item *workerpools.WorkerPoolListResult) string {
73 | return item.Name
74 | },
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/shared/view.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "fmt"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
7 | "github.com/OctopusDeploy/cli/pkg/output"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
9 | )
10 |
11 | type ContributeDetailsCallback func(opts *ViewOptions, workerPool workerpools.IWorkerPool) ([]*output.DataRow, error)
12 |
13 | type ViewFlags struct {
14 | *machinescommon.WebFlags
15 | }
16 |
17 | type ViewOptions struct {
18 | *cmd.Dependencies
19 | IdOrName string
20 | *ViewFlags
21 | }
22 |
23 | func NewViewFlags() *ViewFlags {
24 | return &ViewFlags{
25 | WebFlags: machinescommon.NewWebFlags(),
26 | }
27 | }
28 |
29 | func NewViewOptions(viewFlags *ViewFlags, dependencies *cmd.Dependencies, args []string) *ViewOptions {
30 | return &ViewOptions{
31 | ViewFlags: viewFlags,
32 | Dependencies: dependencies,
33 | IdOrName: args[0],
34 | }
35 | }
36 |
37 | func ViewRun(opts *ViewOptions, contributeDetails ContributeDetailsCallback) error {
38 | var workerPool, err = opts.Client.WorkerPools.GetByIdentifier(opts.IdOrName)
39 | if err != nil {
40 | return err
41 | }
42 |
43 | data := []*output.DataRow{}
44 |
45 | data = append(data, output.NewDataRow("Name", fmt.Sprintf("%s %s", output.Bold(workerPool.GetName()), output.Dimf("(%s)", workerPool.GetID()))))
46 | data = append(data, output.NewDataRow("Worker Pool Type", getWorkerPoolTypeDescription(workerPool.GetWorkerPoolType())))
47 | if workerPool.GetIsDefault() {
48 | data = append(data, output.NewDataRow("Default", output.Green("Yes")))
49 | }
50 |
51 | if contributeDetails != nil {
52 | newRows, err := contributeDetails(opts, workerPool)
53 | if err != nil {
54 | return err
55 | }
56 | for _, r := range newRows {
57 | data = append(data, r)
58 | }
59 | }
60 |
61 | t := output.NewTable(opts.Out)
62 | for _, row := range data {
63 | t.AddRow(row.Name, row.Value)
64 | }
65 | t.Print()
66 |
67 | fmt.Fprintf(opts.Out, "\n")
68 | machinescommon.DoWebForWorkerPools(workerPool, opts.Dependencies, opts.WebFlags)
69 | return nil
70 |
71 | return nil
72 | }
73 |
74 | func getWorkerPoolTypeDescription(poolType workerpools.WorkerPoolType) string {
75 | if poolType == workerpools.WorkerPoolTypeDynamic {
76 | return "Dynamic"
77 | }
78 |
79 | return "Static"
80 | }
81 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/shared/workerpool.go:
--------------------------------------------------------------------------------
1 | package shared
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
7 | )
8 |
9 | type GetWorkerPoolsCallback func() ([]*workerpools.WorkerPoolListResult, error)
10 | type GetWorkerPoolCallback func(identifer string) (workerpools.IWorkerPool, error)
11 |
12 | type GetWorkerPoolsOptions struct {
13 | GetWorkerPoolsCallback
14 | GetWorkerPoolCallback
15 | }
16 |
17 | func NewGetWorkerPoolsOptions(dependencies *cmd.Dependencies) *GetWorkerPoolsOptions {
18 | return &GetWorkerPoolsOptions{
19 | GetWorkerPoolsCallback: func() ([]*workerpools.WorkerPoolListResult, error) {
20 | return GetWorkerPools(*dependencies.Client)
21 | },
22 | GetWorkerPoolCallback: func(identifier string) (workerpools.IWorkerPool, error) {
23 | return GetWorker(*dependencies.Client, identifier)
24 | },
25 | }
26 | }
27 |
28 | func GetWorkerPools(client client.Client) ([]*workerpools.WorkerPoolListResult, error) {
29 | allWorkerPools, err := client.WorkerPools.GetAll()
30 | if err != nil {
31 | return nil, err
32 | }
33 |
34 | return allWorkerPools, nil
35 | }
36 |
37 | func GetWorker(client client.Client, identifier string) (workerpools.IWorkerPool, error) {
38 | workerPool, err := client.WorkerPools.GetByIdentifier(identifier)
39 | if err != nil {
40 | return nil, err
41 | }
42 |
43 | return workerPool, nil
44 | }
45 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/static/create/create_test.go:
--------------------------------------------------------------------------------
1 | package create_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/static/create"
6 | "github.com/OctopusDeploy/cli/test/testutil"
7 | "github.com/stretchr/testify/assert"
8 | "testing"
9 | )
10 |
11 | func TestPromptMissing_FlagsSupplied(t *testing.T) {
12 | pa := []*testutil.PA{}
13 |
14 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
15 | flags := create.NewCreateFlags()
16 | flags.Name.Value = "name"
17 | flags.Description.Value = "description"
18 |
19 | opts := create.NewCreateOptions(flags, &cmd.Dependencies{Ask: asker})
20 |
21 | err := create.PromptMissing(opts)
22 | checkRemainingPrompts()
23 | assert.NoError(t, err)
24 | }
25 |
26 | func TestPromptMissing_ShouldPrompt(t *testing.T) {
27 | pa := []*testutil.PA{
28 | testutil.NewInputPrompt("Name", "A short, memorable, unique name for this Static Worker Pool.", "name"),
29 | testutil.NewInputPrompt("Description", "A short, memorable, description for this Static Worker Pool.", "description"),
30 | }
31 |
32 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
33 | flags := create.NewCreateFlags()
34 | opts := create.NewCreateOptions(flags, &cmd.Dependencies{Ask: asker})
35 |
36 | err := create.PromptMissing(opts)
37 | checkRemainingPrompts()
38 | assert.NoError(t, err)
39 |
40 | assert.Equal(t, "name", flags.Name.Value)
41 | assert.Equal(t, "description", flags.Description.Value)
42 | }
43 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/static/static.go:
--------------------------------------------------------------------------------
1 | package static
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | cmdCreate "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/static/create"
6 | cmdView "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/static/view"
7 | "github.com/OctopusDeploy/cli/pkg/constants"
8 | "github.com/OctopusDeploy/cli/pkg/factory"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdStatic(f factory.Factory) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Use: "static ",
15 | Short: "Manage static worker pools",
16 | Long: "Manage static worker pools in Octopus Deploy",
17 | Example: heredoc.Docf("$ %s worker-pool static view", constants.ExecutableName),
18 | }
19 |
20 | cmd.AddCommand(cmdView.NewCmdView(f))
21 | cmd.AddCommand(cmdCreate.NewCmdCreate(f))
22 |
23 | return cmd
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "fmt"
5 | dynamicPool "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/dynamic/view"
6 | "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/shared"
7 | staticPool "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/static/view"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
9 |
10 | "github.com/MakeNowJust/heredoc/v2"
11 | "github.com/OctopusDeploy/cli/pkg/cmd"
12 | "github.com/OctopusDeploy/cli/pkg/constants"
13 | "github.com/OctopusDeploy/cli/pkg/factory"
14 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
15 | "github.com/OctopusDeploy/cli/pkg/usage"
16 | "github.com/spf13/cobra"
17 | )
18 |
19 | func NewCmdView(f factory.Factory) *cobra.Command {
20 | flags := shared.NewViewFlags()
21 | cmd := &cobra.Command{
22 | Args: usage.ExactArgs(1),
23 | Use: "view { | }",
24 | Short: "View a worker pool",
25 | Long: "View a worker pool in Octopus Deploy",
26 | Example: heredoc.Docf(`
27 | $ %[1]s worker-pool view WorkerPools-3
28 | $ %[1]s worker-pool view 'linux workers'
29 | `, constants.ExecutableName),
30 | RunE: func(c *cobra.Command, args []string) error {
31 | return ViewRun(shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args))
32 | },
33 | }
34 |
35 | machinescommon.RegisterWebFlag(cmd, flags.WebFlags)
36 |
37 | return cmd
38 | }
39 |
40 | func ViewRun(opts *shared.ViewOptions) error {
41 | var workerPool, err = opts.Client.WorkerPools.GetByIdentifier(opts.IdOrName)
42 | if err != nil {
43 | return err
44 | }
45 |
46 | switch workerPool.GetWorkerPoolType() {
47 | case workerpools.WorkerPoolTypeDynamic:
48 | return dynamicPool.ViewRun(opts)
49 | case workerpools.WorkerPoolTypeStatic:
50 | return staticPool.ViewRun(opts)
51 | }
52 |
53 | return fmt.Errorf("unsupported worker pool '%s'", workerPool.GetWorkerPoolType())
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/cmd/workerpool/workerpool.go:
--------------------------------------------------------------------------------
1 | package workerpool
2 |
3 | import (
4 | "github.com/MakeNowJust/heredoc/v2"
5 | deleteCmd "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/delete"
6 | dynamicCmd "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/dynamic"
7 | listCmd "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/list"
8 | staticCmd "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/static"
9 | viewCmd "github.com/OctopusDeploy/cli/pkg/cmd/workerpool/view"
10 | "github.com/OctopusDeploy/cli/pkg/constants"
11 | "github.com/OctopusDeploy/cli/pkg/constants/annotations"
12 | "github.com/OctopusDeploy/cli/pkg/factory"
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdWorkerPool(f factory.Factory) *cobra.Command {
17 | cmd := &cobra.Command{
18 | Use: "worker-pool ",
19 | Short: "Manage worker pools",
20 | Long: "Manage worker pools in Octopus Deploy",
21 | Example: heredoc.Docf(`
22 | $ %[1]s worker-pool list
23 | $ %[1]s worker-pool ls
24 | `, constants.ExecutableName),
25 | Annotations: map[string]string{
26 | annotations.IsCore: "true",
27 | },
28 | }
29 |
30 | cmd.AddCommand(deleteCmd.NewCmdDelete(f))
31 | cmd.AddCommand(listCmd.NewCmdList(f))
32 | cmd.AddCommand(viewCmd.NewCmdView(f))
33 | cmd.AddCommand(staticCmd.NewCmdStatic(f))
34 | cmd.AddCommand(dynamicCmd.NewCmdSsh(f))
35 |
36 | return cmd
37 | }
38 |
--------------------------------------------------------------------------------
/pkg/config/provider.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/spf13/viper"
8 | )
9 |
10 | type IConfigProvider interface {
11 | Get(key string) string
12 | Set(key string, value string) error
13 | }
14 |
15 | type FileConfigProvider struct {
16 | viper *viper.Viper
17 | IConfigProvider
18 | }
19 |
20 | func New(viper *viper.Viper) IConfigProvider {
21 | return &FileConfigProvider{
22 | viper: viper,
23 | }
24 | }
25 |
26 | func (accessToken *FileConfigProvider) Get(key string) string {
27 | return viper.GetString(key)
28 | }
29 |
30 | func (accessToken *FileConfigProvider) Set(key string, value string) error {
31 | // have to make new viper so it only contains file value, no ENVs or Flags
32 | configPath, err := EnsureConfigPath()
33 | if err != nil {
34 | return err
35 | }
36 |
37 | localViper := viper.New()
38 | SetupConfigFile(localViper, configPath)
39 |
40 | if err := localViper.ReadInConfig(); err != nil {
41 | if _, ok := err.(viper.ConfigFileNotFoundError); ok {
42 | // config file not found, we create it here and recover
43 | if err = localViper.SafeWriteConfig(); err != nil {
44 | return err
45 | }
46 | } else {
47 | return err // any other error is unrecoverable; abort
48 | }
49 | }
50 | if key != "" && !IsValidKey(key) {
51 | return fmt.Errorf("the key '%s' is not a valid", key)
52 | }
53 | key = strings.ToLower(key)
54 | localViper.Set(key, value)
55 | if err := localViper.WriteConfig(); err != nil {
56 | return err
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/constants/annotations/annotations.go:
--------------------------------------------------------------------------------
1 | package annotations
2 |
3 | const (
4 | IsCore = "IsCore"
5 | IsConfiguration = "IsConfiguration"
6 | IsLibrary = "IsLibrary"
7 | IsInfrastructure = "IsInfrastructure"
8 | )
9 |
--------------------------------------------------------------------------------
/pkg/errors/errors.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import "fmt"
4 |
5 | // OsEnvironmentError is raised when the CLI cannot launch because a required environment variable is not set
6 | type OsEnvironmentError struct{ EnvironmentVariable string }
7 |
8 | func (e *OsEnvironmentError) Error() string {
9 | return fmt.Sprintf("%s environment variable is missing or blank", e.EnvironmentVariable)
10 | }
11 |
12 | // PromptDisabledError is a fallback error if code attempts to prompt the user when prompting is disabled.
13 | // If you see it, it represents a bug because Commands should check IsInteractive before attempting to prompt
14 | type PromptDisabledError struct{}
15 |
16 | func (e *PromptDisabledError) Error() string {
17 | return "prompt disabled"
18 | }
19 |
20 | // ArgumentNullOrEmptyError is a utility error indicating that a required parameter was
21 | // null or blank. This is not a recoverable error; if you observe one this indicates a bug in the code.
22 | type ArgumentNullOrEmptyError struct{ ArgumentName string }
23 |
24 | func (e *ArgumentNullOrEmptyError) Error() string {
25 | return fmt.Sprintf("argument %s was nil or empty", e.ArgumentName)
26 | }
27 | func NewArgumentNullOrEmptyError(argumentName string) *ArgumentNullOrEmptyError {
28 | return &ArgumentNullOrEmptyError{ArgumentName: argumentName}
29 | }
30 |
31 | // InvalidResponseError is a utility error that means the CLI couldn't deal with a response from the server.
32 | // this may represent a bug (missing code path) in the CLI, a bug in the server (wrong response), or a change in server behaviour over time.
33 | type InvalidResponseError struct{ Message string }
34 |
35 | func (e *InvalidResponseError) Error() string { return e.Message }
36 | func NewInvalidResponseError(message string) *InvalidResponseError {
37 | return &InvalidResponseError{Message: message}
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/executor/executor.go:
--------------------------------------------------------------------------------
1 | package executor
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/spaces"
8 | )
9 |
10 | // task type definitions
11 | type TaskType string
12 |
13 | const (
14 | TaskTypeCreateAccount = TaskType("CreateAccount")
15 | TaskTypeCreateRelease = TaskType("CreateRelease")
16 | TaskTypeDeployRelease = TaskType("DeployRelease")
17 | TaskTypeRunbookRun = TaskType("RunbookRun")
18 | TaskTypeGitRunbookRun = TaskType("GitRunbookRun")
19 | )
20 |
21 | type Task struct {
22 | // which type of task this is.
23 | Type TaskType
24 |
25 | // task-specific payload (usually a struct containing the data required for this task)
26 | // rememmber pass this as a pointer
27 | Options any
28 | }
29 |
30 | func NewTask(taskType TaskType, options any) *Task {
31 | return &Task{
32 | Type: taskType,
33 | Options: options,
34 | }
35 | }
36 |
37 | // ProcessTasks iterates over the list of tasks and attempts to run them all.
38 | // If everything goes well, a nil error will be returned.
39 | // On the first failure, the error will be returned and the process will halt.
40 | // TODO some kind of progress/results callback? A Goroutine with channels?
41 | func ProcessTasks(octopus *client.Client, space *spaces.Space, tasks []*Task) error {
42 | for _, task := range tasks {
43 | switch task.Type {
44 | case TaskTypeCreateAccount:
45 | if err := accountCreate(octopus, space, task.Options); err != nil {
46 | return err
47 | }
48 | case TaskTypeCreateRelease:
49 | if err := releaseCreate(octopus, space, task.Options); err != nil {
50 | return err
51 | }
52 | case TaskTypeDeployRelease:
53 | if err := releaseDeploy(octopus, space, task.Options); err != nil {
54 | return err
55 | }
56 | case TaskTypeRunbookRun:
57 | if err := runbookRun(octopus, space, task.Options); err != nil {
58 | return err
59 | }
60 | case TaskTypeGitRunbookRun:
61 | if err := gitRunbookRun(octopus, space, task.Options); err != nil {
62 | return err
63 | }
64 | default:
65 | return fmt.Errorf("unhandled task CommandType %s", task.Type)
66 | }
67 | }
68 | return nil
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/machinescommon/comms.go:
--------------------------------------------------------------------------------
1 | package machinescommon
2 |
3 | var CommunicationStyleToDescriptionMap = map[string]string{
4 | "TentaclePassive": "Listening Tentacle",
5 | "TentacleActive": "Polling Tentacle",
6 | "Ssh": "SSH Connection",
7 | "OfflineDrop": "Offline Package Drop",
8 | "AzureWebApp": "Azure Web App",
9 | "AzureCloudService": "Azure Cloud Service",
10 | "AzureServiceFabricCluster": "Service Fabric Cluster",
11 | "Kubernetes": "Kubernetes Cluster",
12 | "None": "Cloud Region",
13 | "StepPackage": "Step Package",
14 | }
15 |
16 | var CommunicationStyleToDeploymentTargetTypeMap = map[string]string{
17 | "TentaclePassive": "TentaclePassive",
18 | "TentacleActive": "TentacleActive",
19 | "Ssh": "Ssh",
20 | "OfflineDrop": "OfflineDrop",
21 | "AzureWebApp": "AzureWebApp",
22 | "AzureCloudService": "AzureCloudService",
23 | "AzureServiceFabricCluster": "AzureServiceFabricCluster",
24 | "Kubernetes": "Kubernetes",
25 | "None": "CloudRegion",
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/machinescommon/machinepolicy_test.go:
--------------------------------------------------------------------------------
1 | package machinescommon_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/cmd"
5 | "github.com/OctopusDeploy/cli/pkg/machinescommon"
6 | "github.com/OctopusDeploy/cli/test/testutil"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
8 | "github.com/stretchr/testify/assert"
9 | "testing"
10 | )
11 |
12 | func TestMachinePolicyFlagSupplied_ShouldNotPrompt(t *testing.T) {
13 | pa := []*testutil.PA{}
14 |
15 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
16 | flags := machinescommon.NewCreateTargetMachinePolicyFlags()
17 | flags.MachinePolicy.Value = "MachinePolicy-1"
18 |
19 | opts := machinescommon.NewCreateTargetMachinePolicyOptions(&cmd.Dependencies{Ask: asker})
20 |
21 | err := machinescommon.PromptForMachinePolicy(opts, flags)
22 | checkRemainingPrompts()
23 |
24 | assert.NoError(t, err)
25 | }
26 |
27 | func TestNoFlag_ShouldPrompt(t *testing.T) {
28 | pa := []*testutil.PA{
29 | testutil.NewSelectPrompt("Select the machine policy to use", "", []string{"Policy 1", "Policy 2"}, "Policy 2"),
30 | }
31 |
32 | asker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
33 | flags := machinescommon.NewCreateTargetMachinePolicyFlags()
34 | opts := machinescommon.NewCreateTargetMachinePolicyOptions(&cmd.Dependencies{Ask: asker})
35 | opts.GetAllMachinePoliciesCallback = func() ([]*machines.MachinePolicy, error) {
36 | return []*machines.MachinePolicy{
37 | machines.NewMachinePolicy("Policy 1"),
38 | machines.NewMachinePolicy("Policy 2"),
39 | }, nil
40 | }
41 |
42 | err := machinescommon.PromptForMachinePolicy(opts, flags)
43 | checkRemainingPrompts()
44 | assert.NoError(t, err)
45 | assert.Equal(t, "Policy 2", flags.MachinePolicy.Value)
46 | }
47 |
--------------------------------------------------------------------------------
/pkg/machinescommon/web.go:
--------------------------------------------------------------------------------
1 | package machinescommon
2 |
3 | import (
4 | "fmt"
5 | "github.com/OctopusDeploy/cli/pkg/cmd"
6 | "github.com/OctopusDeploy/cli/pkg/output"
7 | "github.com/OctopusDeploy/cli/pkg/util/flag"
8 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workerpools"
10 | "github.com/pkg/browser"
11 | "github.com/spf13/cobra"
12 | "io"
13 | )
14 |
15 | const (
16 | FlagWeb = "web"
17 | )
18 |
19 | type WebFlags struct {
20 | Web *flag.Flag[bool]
21 | }
22 |
23 | func NewWebFlags() *WebFlags {
24 | return &WebFlags{
25 | Web: flag.New[bool](FlagWeb, false),
26 | }
27 | }
28 |
29 | func RegisterWebFlag(cmd *cobra.Command, flags *WebFlags) {
30 | cmd.Flags().BoolVarP(&flags.Web.Value, flags.Web.Name, "w", false, "Open in web browser")
31 | }
32 |
33 | func DoWebForTargets(target *machines.DeploymentTarget, dependencies *cmd.Dependencies, flags *WebFlags, description string) {
34 | url := fmt.Sprintf("%s/app#/%s/infrastructure/machines/%s/settings", dependencies.Host, dependencies.Space.GetID(), target.GetID())
35 | doWeb(url, description, dependencies.Out, flags)
36 | }
37 |
38 | func DoWebForWorkers(worker *machines.Worker, dependencies *cmd.Dependencies, flags *WebFlags, description string) {
39 | url := fmt.Sprintf("%s/app#/%s/infrastructure/workers/%s/settings", dependencies.Host, dependencies.Space.GetID(), worker.GetID())
40 | doWeb(url, description, dependencies.Out, flags)
41 | }
42 |
43 | func DoWebForWorkerPools(workerPool workerpools.IWorkerPool, dependencies *cmd.Dependencies, flags *WebFlags) {
44 | url := fmt.Sprintf("%s/app#/%s/infrastructure/workerpools/%s/settings", dependencies.Host, dependencies.Space.GetID(), workerPool.GetID())
45 | doWeb(url, "Worker Pool", dependencies.Out, flags)
46 | }
47 |
48 | func doWeb(url string, description string, out io.Writer, flags *WebFlags) {
49 | link := output.Bluef(url)
50 | fmt.Fprintf(out, "View this %s on Octopus Deploy: %s\n", description, link)
51 | if flags.Web.Value {
52 | browser.OpenURL(url)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/output/format.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import "strings"
4 |
5 | func FormatAsList(items []string) string {
6 | return strings.Join(items, ", ")
7 | }
8 |
--------------------------------------------------------------------------------
/pkg/output/print_array.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "strings"
8 |
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 |
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 |
13 | "github.com/spf13/cobra"
14 | "github.com/spf13/viper"
15 | )
16 |
17 | func PrintArray[T any](items []T, cmd *cobra.Command, mappers Mappers[T]) error {
18 | outputFormat, _ := cmd.Flags().GetString(constants.FlagOutputFormat)
19 | if outputFormat == "" {
20 | outputFormat = viper.GetString(constants.ConfigOutputFormat)
21 | }
22 |
23 | switch strings.ToLower(outputFormat) {
24 | case constants.OutputFormatJson:
25 | jsonMapper := mappers.Json
26 | if jsonMapper == nil {
27 | return errors.New("command does not support output in JSON format")
28 | }
29 | var outputJson []any
30 | for _, e := range items {
31 | outputJson = append(outputJson, jsonMapper(e))
32 | }
33 |
34 | data, _ := json.MarshalIndent(outputJson, "", " ")
35 | cmd.Println(string(data))
36 |
37 | case constants.OutputFormatBasic:
38 | textMapper := mappers.Basic
39 | if textMapper == nil {
40 | return errors.New("command does not support output in plain text")
41 | }
42 | for _, e := range items {
43 | cmd.Println(textMapper(e))
44 | }
45 |
46 | case constants.OutputFormatTable, "": // table is the default of unspecified
47 | tableMapper := mappers.Table
48 | if tableMapper.Row == nil {
49 | return errors.New("command does not support output in table format")
50 | }
51 |
52 | t := NewTable(cmd.OutOrStdout())
53 | if tableMapper.Header != nil {
54 | for k, v := range tableMapper.Header {
55 | tableMapper.Header[k] = Bold(v)
56 | }
57 | t.AddRow(tableMapper.Header...)
58 | }
59 |
60 | for _, item := range items {
61 | t.AddRow(tableMapper.Row(item)...)
62 | }
63 |
64 | return t.Print()
65 |
66 | default:
67 | return usage.NewUsageError(
68 | fmt.Sprintf("unsupported output format %s. Valid values are 'json', 'table', 'basic'. Defaults to table", outputFormat),
69 | cmd)
70 | }
71 | return nil
72 | }
73 |
--------------------------------------------------------------------------------
/pkg/output/print_resource.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "strings"
8 |
9 | "github.com/OctopusDeploy/cli/pkg/constants"
10 |
11 | "github.com/OctopusDeploy/cli/pkg/usage"
12 |
13 | "github.com/spf13/cobra"
14 | "github.com/spf13/viper"
15 | )
16 |
17 | func PrintResource[T any](item T, cmd *cobra.Command, mappers Mappers[T]) error {
18 | outputFormat, _ := cmd.Flags().GetString(constants.FlagOutputFormat)
19 | if outputFormat == "" {
20 | outputFormat = viper.GetString(constants.ConfigOutputFormat)
21 | }
22 |
23 | switch strings.ToLower(outputFormat) {
24 | case constants.OutputFormatJson:
25 | jsonMapper := mappers.Json
26 | if jsonMapper == nil {
27 | return errors.New("command does not support output in JSON format")
28 | }
29 | var outputJson any
30 | outputJson = jsonMapper(item)
31 |
32 | data, _ := json.MarshalIndent(outputJson, "", " ")
33 | cmd.Println(string(data))
34 |
35 | case constants.OutputFormatBasic:
36 | textMapper := mappers.Basic
37 | if textMapper == nil {
38 | return errors.New("command does not support output in plain text")
39 | }
40 | cmd.Println(textMapper(item))
41 |
42 | case constants.OutputFormatTable, "": // table is the default of unspecified
43 | tableMapper := mappers.Table
44 | if tableMapper.Row == nil {
45 | return errors.New("command does not support output in table format")
46 | }
47 |
48 | t := NewTable(cmd.OutOrStdout())
49 | if tableMapper.Header != nil {
50 | for k, v := range tableMapper.Header {
51 | tableMapper.Header[k] = Bold(v)
52 | }
53 | t.AddRow(tableMapper.Header...)
54 | }
55 |
56 | t.AddRow(tableMapper.Row(item)...)
57 |
58 | return t.Print()
59 |
60 | default:
61 | return usage.NewUsageError(
62 | fmt.Sprintf("unsupported output format %s. Valid values are 'json', 'table', 'basic'. Defaults to table", outputFormat),
63 | cmd)
64 | }
65 | return nil
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/output/shared.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | // Common struct used for rendering JSON summaries of things that just have an ID and a Name
4 | type IdAndName struct {
5 | Id string `json:"Id"`
6 | Name string `json:"Name"`
7 | }
8 |
9 | type TableDefinition[T any] struct {
10 | Header []string
11 | Row func(item T) []string
12 | }
13 |
14 | // carries conversion functions used by PrintArray and potentially other output code in future
15 | type Mappers[T any] struct {
16 | // A function which will convert T into an output structure suitable for json.Marshal (e.g. IdAndName).
17 | // If you leave this as nil, then the command will simply not support output as JSON and will
18 | // fail if someone asks for it
19 | Json func(item T) any
20 |
21 | // A function which will convert T into ?? suitable for table printing
22 | // If you leave this as nil, then the command will simply not support output as
23 | // a table and will fail if someone asks for it
24 | Table TableDefinition[T]
25 |
26 | // A function which will convert T into a string suitable for basic text display
27 | // If you leave this as nil, then the command will simply not support output as basic text and will
28 | // fail if someone asks for it
29 | Basic func(item T) string
30 |
31 | // NOTE: We might have some kinds of entities where table formatting doesn't make sense, and we want to
32 | // render those as basic text instead. This seems unlikely though, defer it until the issue comes up.
33 |
34 | // NOTE: The structure for printing tables would also work for CSV... perhaps we can have --outputFormat=csv for free?
35 | }
36 |
--------------------------------------------------------------------------------
/pkg/output/truncate.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "github.com/muesli/reflow/ansi"
5 | "github.com/muesli/reflow/truncate"
6 | )
7 |
8 | const (
9 | ellipsis = "..."
10 | minWidthForEllipsis = len(ellipsis) + 2
11 | )
12 |
13 | func Truncate(maxWidth int, s string) string {
14 | w := ansi.PrintableRuneWidth(s)
15 | if w <= maxWidth {
16 | return s
17 | }
18 |
19 | tail := ""
20 | if maxWidth >= minWidthForEllipsis {
21 | tail = ellipsis
22 | }
23 |
24 | ts := truncate.StringWithTail(s, uint(maxWidth), tail)
25 | if ansi.PrintableRuneWidth(ts) < maxWidth {
26 | ts += " "
27 | }
28 |
29 | return ts
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/question/ask.go:
--------------------------------------------------------------------------------
1 | package question
2 |
3 | import (
4 | "github.com/AlecAivazis/survey/v2"
5 | cliErrors "github.com/OctopusDeploy/cli/pkg/errors"
6 | )
7 |
8 | type Asker func(p survey.Prompt, response interface{}, opts ...survey.AskOpt) error
9 |
10 | // Both the ClientFactory and the main Factory need to reference survey to ask questions,
11 | // but we need a single place to hold the reference to survey so we can switch it off for
12 | // automation mode. This wrapper fills that gap.
13 |
14 | type AskProvider interface {
15 | IsInteractive() bool
16 | DisableInteractive()
17 | Ask(p survey.Prompt, response interface{}, opts ...survey.AskOpt) error
18 | }
19 |
20 | type askWrapper struct {
21 | asker Asker
22 | }
23 |
24 | func NewAskProvider(asker Asker) AskProvider {
25 | return &askWrapper{
26 | asker: asker,
27 | }
28 | }
29 |
30 | func (a *askWrapper) IsInteractive() bool {
31 | return a.asker != nil
32 | }
33 |
34 | func (a *askWrapper) DisableInteractive() {
35 | a.asker = nil
36 | }
37 |
38 | func (a *askWrapper) Ask(p survey.Prompt, response interface{}, opts ...survey.AskOpt) error {
39 | if a.asker != nil {
40 | return a.asker(p, response, opts...)
41 | } else {
42 | // this shouldn't happen; commands should check IsInteractive before attempting to prompt
43 | return &cliErrors.PromptDisabledError{}
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/question/helpers_test.go:
--------------------------------------------------------------------------------
1 | package question_test
2 |
3 |
--------------------------------------------------------------------------------
/pkg/question/select_test.go:
--------------------------------------------------------------------------------
1 | package question_test
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/question"
5 | "github.com/OctopusDeploy/cli/test/testutil"
6 | "github.com/stretchr/testify/assert"
7 | "testing"
8 | )
9 |
10 | func TestMultiSelectMap_NoItems(t *testing.T) {
11 | pa := []*testutil.PA{}
12 | mockAsker, _ := testutil.NewMockAsker(t, pa)
13 | selectedItem, err := question.MultiSelectMap(mockAsker, "question", []*string{}, func(item *string) string { return *item }, false)
14 | assert.Nil(t, selectedItem)
15 | assert.Error(t, err)
16 | }
17 |
18 | func TestSelectMap_NoItems(t *testing.T) {
19 | pa := []*testutil.PA{}
20 | mockAsker, _ := testutil.NewMockAsker(t, pa)
21 | selectedItem, err := question.SelectMap(mockAsker, "question", []*string{}, func(item *string) string { return *item })
22 | assert.Nil(t, selectedItem)
23 | assert.Error(t, err)
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/question/selectors/channels.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "fmt"
5 | "github.com/OctopusDeploy/cli/pkg/output"
6 | "github.com/OctopusDeploy/cli/pkg/question"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/channels"
8 | octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
10 | "io"
11 | "strings"
12 | )
13 |
14 | func Channel(octopus *octopusApiClient.Client, ask question.Asker, io io.Writer, questionText string, project *projects.Project) (*channels.Channel, error) {
15 | existingChannels, err := octopus.Projects.GetChannels(project)
16 | if len(existingChannels) == 1 {
17 | fmt.Fprintf(io, "Selecting only available channel '%s'.\n", output.Cyan(existingChannels[0].Name))
18 | return existingChannels[0], nil
19 | }
20 | if err != nil {
21 | return nil, err
22 | }
23 |
24 | return question.SelectMap(ask, questionText, existingChannels, func(p *channels.Channel) string {
25 | return p.Name
26 | })
27 | }
28 |
29 | func FindChannel(octopus *octopusApiClient.Client, project *projects.Project, channelName string) (*channels.Channel, error) {
30 | foundChannels, err := octopus.Projects.GetChannels(project) // TODO change this to channel partial name search on server; will require go client update
31 | if err != nil {
32 | return nil, err
33 | }
34 | for _, c := range foundChannels { // server doesn't support channel search by exact name so we must emulate it
35 | if strings.EqualFold(c.Name, channelName) {
36 | return c, nil
37 | }
38 | }
39 | return nil, fmt.Errorf("no channel found with name of %s", channelName)
40 | }
41 |
--------------------------------------------------------------------------------
/pkg/question/selectors/gitCredentialStorage.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/question"
5 | )
6 |
7 | func GitCredentialStorage(questionText string, ask question.Asker) (string, error) {
8 | options := []*SelectOption[string]{
9 | {Display: "Project", Value: "project"},
10 | {Display: "Library", Value: "library"},
11 | }
12 |
13 | optionsCallback := func() ([]*SelectOption[string], error) {
14 | return options, nil
15 | }
16 |
17 | selectedOption, err := Select(ask, questionText, optionsCallback, func(option *SelectOption[string]) string {
18 | return option.Display
19 | })
20 |
21 | if err != nil {
22 | return "", err
23 | }
24 |
25 | return selectedOption.Value, nil
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/question/selectors/git_references.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/OctopusDeploy/cli/pkg/output"
7 | "github.com/OctopusDeploy/cli/pkg/question"
8 | "github.com/OctopusDeploy/cli/pkg/util"
9 | octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
11 | )
12 |
13 | func GitReference(questionText string, octopus *octopusApiClient.Client, ask question.Asker, project *projects.Project) (*projects.GitReference, error) {
14 | branches, err := octopus.Projects.GetGitBranches(project)
15 | if err != nil {
16 | return nil, err
17 | }
18 |
19 | tags, err := octopus.Projects.GetGitTags(project)
20 |
21 | if err != nil {
22 | return nil, err
23 | }
24 |
25 | allRefs := append(branches, tags...)
26 |
27 | defaultBranch := project.PersistenceSettings.(projects.GitPersistenceSettings).DefaultBranch()
28 |
29 | // if the default branch is in the list, move it to the top
30 | defaultBranchInRefsList := util.SliceFilter(allRefs, func(g *projects.GitReference) bool { return g.Name == defaultBranch })
31 | if len(defaultBranchInRefsList) > 0 {
32 | defaultBranchRef := defaultBranchInRefsList[0]
33 | allRefs = util.SliceExcept(allRefs, func(g *projects.GitReference) bool { return g.Name == defaultBranch })
34 | allRefs = append([]*projects.GitReference{defaultBranchRef}, allRefs...)
35 | }
36 |
37 | return question.SelectMap(ask, questionText, allRefs, func(g *projects.GitReference) string {
38 | return fmt.Sprintf("%s %s", g.Name, output.Dimf("(%s)", g.Type.Description()))
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/pkg/question/selectors/lifecycles.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/question"
5 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
6 | octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
7 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/lifecycles"
8 | )
9 |
10 | func Lifecycle(questionText string, octopus *client.Client, ask question.Asker) (*lifecycles.Lifecycle, error) {
11 | existingLifecycles, err := octopus.Lifecycles.GetAll()
12 | if err != nil {
13 | return nil, err
14 | }
15 |
16 | return question.SelectMap(ask, questionText, existingLifecycles, func(lc *lifecycles.Lifecycle) string {
17 | return lc.Name
18 | })
19 | }
20 |
21 | func FindLifecycle(octopus *octopusApiClient.Client, lifecycleIdentifier string) (*lifecycles.Lifecycle, error) {
22 | lifecycle, err := octopus.Lifecycles.GetByIDOrName(lifecycleIdentifier)
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | return lifecycle, nil
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/question/selectors/projects.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/question"
5 | octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
6 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
7 | )
8 |
9 | func Project(questionText string, octopus *octopusApiClient.Client, ask question.Asker) (*projects.Project, error) {
10 | existingProjects, err := octopus.Projects.GetAll()
11 | if err != nil {
12 | return nil, err
13 | }
14 |
15 | return question.SelectMap(ask, questionText, existingProjects, func(p *projects.Project) string {
16 | return p.Name
17 | })
18 | }
19 |
20 | func FindProject(octopus *octopusApiClient.Client, projectIdentifier string) (*projects.Project, error) {
21 | project, err := octopus.Projects.GetByIdentifier(projectIdentifier)
22 | if err != nil {
23 | return nil, err
24 | }
25 |
26 | return project, nil
27 | }
28 |
--------------------------------------------------------------------------------
/pkg/question/selectors/runbooks.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "errors"
5 | "math"
6 |
7 | "github.com/OctopusDeploy/cli/pkg/question"
8 | octopusApiClient "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
9 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
10 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks"
11 | )
12 |
13 | func Runbook(questionText string, client *octopusApiClient.Client, ask question.Asker, projectID string) (*runbooks.Runbook, error) {
14 | existingRunbooks, err := runbooks.List(client, client.GetSpaceID(), projectID, "", math.MaxInt32)
15 | if err != nil {
16 | return nil, err
17 | }
18 |
19 | if len(existingRunbooks.Items) == 0 {
20 | return nil, errors.New("no runbooks found")
21 | }
22 |
23 | if len(existingRunbooks.Items) == 1 {
24 | return existingRunbooks.Items[0], nil
25 | }
26 |
27 | return question.SelectMap(ask, questionText, existingRunbooks.Items, func(r *runbooks.Runbook) string {
28 | return r.Name
29 | })
30 | }
31 |
32 | func FindRunbook(client *octopusApiClient.Client, project *projects.Project, runbookIdentifier string) (*runbooks.Runbook, error) {
33 | runbook, err := runbooks.GetByID(client, client.GetSpaceID(), runbookIdentifier)
34 | if err != nil {
35 | runbook, err = runbooks.GetByName(client, client.GetSpaceID(), project.GetID(), runbookIdentifier)
36 | if err != nil {
37 | return nil, err
38 | }
39 | }
40 |
41 | return runbook, nil
42 | }
43 |
44 | func FindGitRunbook(client *octopusApiClient.Client, project *projects.Project, runbookIdentifier string, gitRef string) (*runbooks.Runbook, error) {
45 | runbook, err := runbooks.GetGitRunbookByID(client, client.GetSpaceID(), project.ID, gitRef, runbookIdentifier)
46 | if err != nil {
47 | runbook, err = runbooks.GetGitRunbookByName(client, client.GetSpaceID(), project.GetID(), gitRef, runbookIdentifier)
48 | if err != nil {
49 | return nil, err
50 | }
51 | }
52 |
53 | return runbook, nil
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/question/selectors/selector_test.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "github.com/AlecAivazis/survey/v2"
5 | "github.com/OctopusDeploy/cli/test/testutil"
6 | "github.com/stretchr/testify/assert"
7 | "testing"
8 | )
9 |
10 | type Item struct {
11 | id string
12 | name string
13 | }
14 |
15 | func TestSelectForSingleItem(t *testing.T) {
16 | itemsCallback := func() ([]*Item, error) {
17 | return []*Item{
18 | {
19 | id: "1",
20 | name: "name",
21 | },
22 | }, nil
23 | }
24 |
25 | selectedItem, err := Select(nil, "question", itemsCallback, func(item *Item) string { return item.name })
26 | assert.Nil(t, err)
27 | assert.Equal(t, selectedItem.id, "1")
28 | }
29 |
30 | func TestSelectForMultipleItem(t *testing.T) {
31 | items := []*Item{
32 | {
33 | id: "1",
34 | name: "name",
35 | },
36 | {
37 | id: "2",
38 | name: "name 2",
39 | },
40 | }
41 | itemsCallback := func() ([]*Item, error) {
42 | return items, nil
43 | }
44 | pa := []*testutil.PA{
45 | {
46 | Prompt: &survey.Select{
47 | Message: "question",
48 | Options: []string{items[0].name, items[1].name},
49 | },
50 | Answer: "name 2",
51 | },
52 | }
53 | mockAsker, checkRemainingPrompts := testutil.NewMockAsker(t, pa)
54 | selectedItem, err := Select(mockAsker, "question", itemsCallback, func(item *Item) string { return item.name })
55 | checkRemainingPrompts()
56 | assert.Nil(t, err)
57 | assert.Equal(t, selectedItem.id, "2")
58 | }
59 |
--------------------------------------------------------------------------------
/pkg/question/selectors/selectors.go:
--------------------------------------------------------------------------------
1 | package selectors
2 |
3 | import (
4 | "github.com/OctopusDeploy/cli/pkg/question"
5 | )
6 |
7 | type SelectOption[T any] struct {
8 | Value T
9 | Display string
10 | }
11 |
12 | func NewSelectOption[T any](value any, display string) *SelectOption[T] {
13 | return &SelectOption[T]{
14 | Value: value.(T),
15 | Display: display,
16 | }
17 | }
18 |
19 | type Nameable interface {
20 | GetName() string
21 | }
22 |
23 | func ByName[T Nameable](ask question.Asker, list []T, message string) (T, error) {
24 | var selectedItem T
25 | selectedItem, err := question.SelectMap(ask, message, list, func(item T) string {
26 | return item.GetName()
27 | })
28 | if err != nil {
29 | return selectedItem, err
30 | }
31 | return selectedItem, nil
32 | }
33 |
34 | func SelectOptions[T any](ask question.Asker, questionText string, itemsCallback func() []*SelectOption[T]) (*SelectOption[T], error) {
35 | items := itemsCallback()
36 | callback := func() ([]*SelectOption[T], error) {
37 | return items, nil
38 | }
39 | return Select(ask, questionText, callback, func(option *SelectOption[T]) string { return option.Display })
40 | }
41 |
42 | func Select[T any](ask question.Asker, questionText string, itemsCallback func() ([]T, error), getKey func(item T) string) (T, error) {
43 | items, err := itemsCallback()
44 | if err != nil {
45 | var item T
46 | return item, err
47 | }
48 | if len(items) == 1 {
49 | return items[0], nil
50 | }
51 |
52 | return question.SelectMap(ask, questionText, items, getKey)
53 | }
54 |
55 | // SelectOrNew is the same as Select but show a create new option at the bottom of the list
56 | // When create new is selected the returned bool will be true
57 | func SelectOrNew[T any](ask question.Asker, questionText string, itemsCallback func() ([]T, error), getKey func(item T) string) (T, bool, error) {
58 | items, err := itemsCallback()
59 | if err != nil {
60 | var item T
61 | return item, false, err
62 | }
63 | return question.SelectMapWithNew(ask, questionText, items, getKey)
64 | }
65 |
--------------------------------------------------------------------------------
/pkg/surveyext/paginate.go:
--------------------------------------------------------------------------------
1 | package surveyext
2 |
3 | import "github.com/AlecAivazis/survey/v2/core"
4 |
5 | func paginate(pageSize int, choices []core.OptionAnswer, sel int) (choiceList []core.OptionAnswer, cursor int) {
6 | var start, end int
7 |
8 | if len(choices) < pageSize {
9 | // if we dont have enough options to fill a page
10 | start = 0
11 | end = len(choices)
12 | cursor = sel
13 | choiceList = choices[start:end]
14 |
15 | } else if sel < pageSize/2 {
16 | // if we are in the first half page
17 | start = 0
18 | end = pageSize - 1
19 | cursor = sel
20 | choiceList = append(choices[start:end], choices[len(choices)-1])
21 |
22 | } else if len(choices)-sel-1 < pageSize/2 {
23 | // if we are in the last half page
24 | start = len(choices) - pageSize
25 | end = len(choices)
26 | cursor = sel - start
27 | choiceList = choices[start:end]
28 | } else {
29 | // somewhere in the middle
30 | above := pageSize / 2
31 | below := pageSize - above
32 |
33 | cursor = pageSize / 2
34 | start = sel - above
35 | end = (sel + below) - 1
36 | choiceList = append(choices[start:end], choices[len(choices)-1])
37 | }
38 |
39 | // return the subset we care about and the index
40 | return choiceList, cursor
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/usage/usage.go:
--------------------------------------------------------------------------------
1 | package usage
2 |
3 | import (
4 | "fmt"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | // UsageError indicates the caller has not invoked the CLI properly
9 | // and the root error handler should print the usage/help
10 | type UsageError struct {
11 | s string
12 |
13 | // this needs to carry the command, otherwise the root cmd doesn't know how to print usage
14 | cmd *cobra.Command
15 | }
16 |
17 | func NewUsageError(s string, cmd *cobra.Command) *UsageError {
18 | return &UsageError{s: s, cmd: cmd}
19 | }
20 |
21 | func (e *UsageError) Error() string {
22 | return e.s
23 | }
24 |
25 | func (e *UsageError) Command() *cobra.Command {
26 | return e.cmd
27 | }
28 |
29 | // Argument validation helper which emits a UsageError rather than a plain string error
30 | func ExactArgs(n int) cobra.PositionalArgs {
31 | return func(cmd *cobra.Command, args []string) error {
32 | if len(args) != n {
33 | return NewUsageError(
34 | fmt.Sprintf("accepts %d arg(s), received %d", n, len(args)),
35 | cmd)
36 | }
37 | return nil
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/pkg/util/featuretoggle/feature_toggles.go:
--------------------------------------------------------------------------------
1 | package featuretoggle
2 |
3 | import (
4 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
5 | "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/configuration"
6 | )
7 |
8 | // IsToggleEnabled retrieves an Octopus feature toggle by name. If an error occurs, it returns false.
9 | func IsToggleEnabled(client *client.Client, toggleName string) (bool, error) {
10 | toggleRequest := &configuration.FeatureToggleConfigurationQuery{
11 | Name: toggleName,
12 | }
13 |
14 | returnToggleResponse, err := configuration.Get(client, toggleRequest)
15 |
16 | if err != nil {
17 | return false, err
18 | }
19 |
20 | if len(returnToggleResponse.FeatureToggles) == 0 {
21 | return false, err
22 | }
23 |
24 | var toggleValue = returnToggleResponse.FeatureToggles[0]
25 |
26 | // Verify name to avoid error in older versions of Octopus where Name param is not recognised
27 | if toggleValue.Name != toggleName {
28 | return false, err
29 | }
30 |
31 | return toggleValue.IsEnabled, err
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/util/pipe.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "bufio"
5 | "os"
6 | )
7 |
8 | func IsCalledFromPipe() bool {
9 | fileInfo, _ := os.Stdin.Stat()
10 | return fileInfo != nil && fileInfo.Mode()&os.ModeCharDevice == 0
11 | }
12 |
13 | // ReadValuesFromPipe will return an array of strings from the pipe
14 | // input separated by new line.
15 | func ReadValuesFromPipe() []string {
16 | items := []string{}
17 | if IsCalledFromPipe() {
18 | scanner := bufio.NewScanner(bufio.NewReader(os.Stdin))
19 | for scanner.Scan() {
20 | items = append(items, scanner.Text())
21 | }
22 | }
23 | return items
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/validation/validation.go:
--------------------------------------------------------------------------------
1 | package validation
2 |
3 | import (
4 | "fmt"
5 | "github.com/AlecAivazis/survey/v2"
6 | uuid "github.com/google/uuid"
7 | "os"
8 | "reflect"
9 | )
10 |
11 | // NotEquals requires that the string does not equal any of the specified values
12 | func NotEquals(stringsToCheck []string, errorMessage string) survey.Validator {
13 | // return a validator to perform the check
14 | return func(val interface{}) error {
15 | if str, ok := val.(string); ok {
16 | for _, v := range stringsToCheck {
17 | if str == v {
18 | return fmt.Errorf("%s", errorMessage)
19 | }
20 | }
21 | } else {
22 | // otherwise we cannot convert the value into a string and cannot perform check
23 | return fmt.Errorf("cannot check value on response of type %v", reflect.TypeOf(val).Name())
24 | }
25 |
26 | // the input is fine
27 | return nil
28 | }
29 | }
30 |
31 | // IsUuid requires that the string is a valid UUID
32 | func IsUuid(val interface{}) error {
33 | if str, ok := val.(string); ok {
34 | if _, err := uuid.Parse(str); err != nil {
35 | return fmt.Errorf("not a valid UUID")
36 | }
37 | } else {
38 | // otherwise we cannot convert the value into a string and cannot perform check
39 | return fmt.Errorf("cannot check value on response of type %v", reflect.TypeOf(val).Name())
40 | }
41 |
42 | // the input is fine
43 | return nil
44 | }
45 |
46 | func IsExistingFile(val interface{}) error {
47 | if str, ok := val.(string); ok {
48 | info, err := os.Stat(str)
49 | if os.IsNotExist(err) {
50 | return fmt.Errorf("\"%s\" is not a valid file path", str)
51 | }
52 | if info.IsDir() {
53 | return fmt.Errorf("\"%s\" is a directory, the path must be a file", str)
54 | }
55 | } else {
56 | return fmt.Errorf("cannot check value on response of type %v", reflect.TypeOf(val).Name())
57 | }
58 | // path is real file
59 | return nil
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/validation/validation_test.go:
--------------------------------------------------------------------------------
1 | package validation
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/google/uuid"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestNotEquals(t *testing.T) {
11 | testStrings := []string{"foo", "bar", "quxx"}
12 | errorMessage := "this is an error"
13 | notEqualsValidator := NotEquals(testStrings, errorMessage)
14 |
15 | assert.NotNil(t, notEqualsValidator)
16 |
17 | for _, v := range testStrings {
18 | err := notEqualsValidator(v)
19 | assert.Error(t, err)
20 | assert.Equal(t, err.Error(), errorMessage)
21 | }
22 |
23 | test := "xyzzy"
24 | err := notEqualsValidator(test)
25 | assert.NoError(t, err)
26 | }
27 |
28 | func TestIsUUID(t *testing.T) {
29 | testStrings := []string{"foo", "bar", "quxx"}
30 | for _, v := range testStrings {
31 | err := IsUuid(v)
32 | assert.Error(t, err)
33 | }
34 |
35 | testUUID, err := uuid.NewUUID()
36 | assert.NoError(t, err)
37 | assert.NotNil(t, testUUID)
38 |
39 | testUUIDString := testUUID.String()
40 | err = IsUuid(testUUIDString)
41 | assert.NoError(t, err)
42 | }
43 |
--------------------------------------------------------------------------------
/version.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | import _ "embed"
4 |
5 | //go:embed version.txt
6 | var Version string
7 |
--------------------------------------------------------------------------------
/version.txt:
--------------------------------------------------------------------------------
1 | 2.18.0
2 |
--------------------------------------------------------------------------------