Go CF CLI plugin that exposes MTA deployment commands (cf deploy, cf undeploy, etc.) to
the user. It has no business logic of its own — it translates CLI arguments into REST API calls
against the multiapps-controller backend and streams the results back to the terminal.
This is an OPEN SOURCE repository. Never introduce proprietary logic, credentials, or internal company context into this codebase.
- Every commit and PR must reference a JIRA item. No commit or PR should be merged without a backlog reference in its description.
- At least one commit in the change must contain a real JIRA key in the form
LMCROSSITXSADEPLOY-1234(theLMCROSSITXSADEPLOY-<number>format), pointing to an actual Jira item — not a placeholder.
Claude Code must NOT perform any of the following actions unless the user has explicitly confirmed that specific action in the current conversation. Do not infer permission from an earlier, unrelated approval — ask, then wait.
- Do NOT push to any remote (no
git push, no branch/tag pushes, no opening PRs) without explicit confirmation. - Do NOT cut, tag, or trigger a release — releases are handled by the maintainers' dedicated process.
- Do NOT update version files —
cfg/VERSIONis injected at build time via-ldflags; leave it (and any other version markers) untouched unless explicitly told to change it. - Do NOT bump, publish, or otherwise mutate the project version or any similar project-state-changing action (release automation, changelog/version tagging, dependency version bumps) without explicit confirmation.
- Do NOT hand-edit generated clients under
clients/mtaclient/,clients/mtaclient_v2/, or the specs inclients/swagger/— regenerate them instead.
When in doubt about whether an action falls into the above, treat it as requiring confirmation and ask first.
- Go / go modules — repo lives at
github.com/cloudfoundry/multiapps-cli-plugin(thego.modmodule path is still the legacygithub.com/cloudfoundry-incubator/multiapps-cli-plugin) - CF CLI plugin SDK (
code.cloudfoundry.org/cli/v8/plugin) - go-openapi generated REST clients — do not hand-edit files under
clients/mtaclient/orclients/mtaclient_v2/ - Ginkgo v1 + Gomega for tests — suites bootstrap via
go test(RunSpecsin*_suite_test.go)
| Package | Purpose |
|---|---|
multiapps_plugin.go |
Plugin entry point — Commands slice, Run(), GetMetadata() |
commands/ |
One file per CF command; all embed *BaseCommand |
commands/fakes/ |
Fakes for command-level interfaces |
clients/baseclient/ |
Shared client plumbing — token factory, user-agent transport, client errors |
clients/mtaclient/ |
go-openapi generated MTA REST client (API v1) |
clients/mtaclient_v2/ |
go-openapi generated MTA REST client (API v2) |
clients/mtaclient/fakes/ |
Fake v1 client builder for tests |
clients/mtaclient_v2/fakes/ |
Fake v2 client builder for tests |
clients/restclient/ |
Lower-level REST client for purge-configuration; also defines the CSRF base-path constants |
clients/cfrestclient/ |
CF-specific REST client (space/org resolution) |
clients/csrf/ |
CSRF token handling |
clients/models/ |
Shared model types |
clients/swagger/ |
OpenAPI/Swagger specs (mta_rest.yaml, rest.yaml) the clients are generated from |
configuration/ |
Snapshot accessors that read the env-var config (GetUploadChunkSizeInMB, GetBackendURL, …) |
configuration/properties/ |
One file per env-var-backed config property |
secure_parameters/ |
Handling of sensitive deploy parameters |
log/ |
Debug logging + Exiter abstraction (Debug, Fatal, Exit) |
util/ |
CF target resolution, URL calculator, file splitter, user-agent |
ui/ |
Terminal output helpers |
testutil/ |
Shared test helpers (output capturer, table formatter, fake transport) |
cfg/VERSION |
Version string — injected via -ldflags at build time, do not edit manually |
-
Create
commands/<name>_command.go:- Define a struct embedding
*BaseCommand - Implement
GetPluginCommand()— setsName,HelpText,UsageDetails.Usage, andUsageDetails.Options - Implement
defineCommandOptions(flags *flag.FlagSet)— declare flags - Implement
executeInternal(positionalArgs []string, dsHost string, flags *flag.FlagSet, cfTarget util.CloudFoundryTarget) ExecutionStatus - Use
c.NewMtaClient()orc.NewMtaV2Client()to get the REST client - Use
ui.Say(),ui.Ok(),ui.Failed()for terminal output - See
commands/mtas_command.goas the canonical simple example
- Define a struct embedding
-
Register the command in
multiapps_plugin.goCommandsslice — without this the command is invisible to CF CLI:var Commands = []commands.Command{ ... commands.NewYourCommand(), }
-
Write tests in
commands/<name>_command_test.go:- Package
commands_test, GinkgoDescribe/Context/Itstructure - Use
cli_fakes.NewFakeCliConnectionBuilder()for the CF connection - Use fake client builders from
clients/mtaclient/fakes/orclients/mtaclient_v2/fakes/ - Use
testutil.NewUIOutputCapturer()to capture and assert terminal output - Use
util_fakes.NewDeployServiceURLFakeCalculator()for the URL calculator - Initialize with
command.InitializeAll(...)— notcommand.Initialize(...) - See
commands/mtas_command_test.goas the canonical simple example
- Package
Then build, test, and verify as described below.
These steps apply to any change, not just new commands:
-
Run tests:
go test ./commands/... # or all packages: go test ./...
-
Format:
gofmt -w cli clients commands testutil ui util
-
Build:
go build -ldflags "-X main.Version=$(cat cfg/VERSION)" -o multiapps-plugin .
-
Install into CF CLI:
cf install-plugin ./multiapps-plugin -f
-
Verify manually against a CF environment with a running multiapps-controller (e.g. run the affected
cf <command>).
./build.shProduces static and non-static binaries for all platforms + checksums.txt in build/.
A few behaviors are non-obvious from the package layout — check the referenced files before touching them:
- File upload —
commands/file_uploader.go(FileUploader) performs the actual upload to the controller, delegating chunking toutil/file_splitter.go: MTAR archives are split into at most 50 chunks (MaxFileChunkCount); default chunk size 45 MB (MULTIAPPS_UPLOAD_CHUNK_SIZE), uploaded in parallel unlessMULTIAPPS_UPLOAD_CHUNKS_SEQUENTIALLY=true. - Controller URL resolution (
util/deploy_service_url_calculator.go) — resolved in order:-uflag →MULTIAPPS_CONTROLLER_URL→ auto-derived from the CF API host (https://api.cf.example.com→deploy-service.cf.example.com). - REST API contract — client targets
/api/v1/spaces/{spaceGuid}/(full operation set) and/api/v2/...(MTA listing with namespace filtering). REST model or endpoint changes inmultiapps-controllerrequire regenerating the Go clients; breaking changes need a new version path (/api/v3/) coordinated across both repos.
| Variable | Default | Purpose |
|---|---|---|
MULTIAPPS_CONTROLLER_URL |
(auto-derived) | Override backend URL |
MULTIAPPS_UPLOAD_CHUNK_SIZE |
45 (MB) |
Size of each upload chunk |
MULTIAPPS_UPLOAD_CHUNKS_SEQUENTIALLY |
false |
Upload chunks one at a time |
MULTIAPPS_DISABLE_UPLOAD_PROGRESS_BAR |
false |
Suppress progress bar |
MULTIAPPS_USER_AGENT_SUFFIX |
(empty) | Append string to HTTP User-Agent |
DEBUG |
false |
Set to 1 to enable HTTP request logging |
| CF command | Go file | Alias |
|---|---|---|
cf deploy |
deploy_command.go |
|
cf bg-deploy |
blue_green_deploy_command.go |
|
cf undeploy |
undeploy_command.go |
|
cf mtas |
mtas_command.go |
|
cf mta |
mta_command.go |
|
cf mta-ops |
mta_operations_command.go |
|
cf download-mta-op-logs |
download_mta_op_logs_command.go |
dmol |
cf purge-mta-config |
purge_config_command.go |
|
cf rollback-mta |
rollback_mta_command.go |