Contributing to Meshery CLI
mesheryctl is written in Golang or the Go Programming Language. For development use Go version 1.26+. mesheryctl uses the Cobra framework. A good first-step towards contributing to mesheryctl would be to familiarize yourself with the Cobra concepts. For manipulating config files, mesheryctl uses Viper.
- Meshery Command Tracker: Status of mesheryctl command implementation and platform compatibility.
- Meshery CLI Commands and Documentation: Detailed documentation of the `mesheryctl` commands.
- Meshery Test Plan: Test cases for end-to-end testing of Meshery functionality.
- mesheryctl open issues and pull requests: Matching the "component/mesheryctl" label.
Resource Access
Fill in a community member form to gain access to community resources.Designing Commands
The Meshery CLI Style Guide outlines the process by which new commands are designed and contains a collection of principles and conventions that need to be followed while designing mesheryctl commands. mesheryctl might be the interface that the users first have with Meshery. As such, mesheryctl needs to provide a great UX.
Building CLI
The /mesheryctl folder contains the complete code for mesheryctl. Fork and clone the Meshery repo. cd mesheryctl to change directory mesheryctl’s source. After making changes, run make in the mesheryctl folder to build the binary. You can then use the binary by, say, ./mesheryctl system start.
Understand Model and Configuration Data
A central struct is maintained in the mesheryctl/internal/cli/root/config/config.go file. These are updated and should be used for getting the Meshery configuration.
Updates to this central struct is made through updates in Context with setter functions. The changes made in this central struct are reflected back in the Meshery configuration file (.meshery/config.yaml).
Logging
For logs, mesheryctl uses MeshKit.
Documenting
The documentation pages for mesheryctl reference are made with the help of the Cobra Golang framework and use of GitHub Actions. The Meshery CLI Reference is autogenerated based on docs sections in each of mesheryctl’s Golang files. Meshery CLI Reference pages are updated each time a change to its respective mesheryctl Golang file is merged into the project’s master branch. This approach to documentation facilitates a single source of truth for CLI syntax and command behavior, which results in higher quality reference and a reduction in the toil involved in keeping documentation up-to-date. To contribute to the Meshery CLI Reference, follow these steps:
- Go to the required command file in which the documentation has to be created/updated (mainly under /mesheryctl/internal/cli/root/…)
- Then, edit the Cobra macro variables present in the each file. An example is given below for reference.
var startCmd = &cobra.Command{ Use: "start", Short: "Start Meshery", Long: 'Start Meshery and each of its components.', Args: cobra.NoArgs, Example: // Start meshery mesheryctl system start // To create a new context for in-cluster Kubernetes deployments and set the new context as your current-context mesheryctl system context create k8s -p kubernetes -s, Annotations: linkScreenshot, ...
The variables present in above sample will be used in creating the doc pages for the specific command
Also, if the screenshot is present in the command, an Annotation macro variable (of map[string]string type) containing the link and the caption has to be added at the bottom of the Examples field in the command file. The image file has to be included in the docs/content/en/reference/references/images folder in PNG format. The screenshot field is given for reference below
var linkDocPatternApply = map[string]string{ "link": "", "caption": "Usage of mesheryctl design apply", } ... Example: // apply a pattern file mesheryctl design apply -f [file | URL] // deploy a saved design mesheryctl design apply [design-name] Annotations: linkDocPatternApply, ...
It is advised not to modify the changes in docs folder, rather should be done in mesheryctl folder as the changes will get overwritten by the CI workflows.
Adding New/Removing Existing commands in the reference index
Though the command page is generated automatically by the Cobra CLI library, there are chances where the command does not appear in the reference index page. In such cases, the command details must be manually added to the reference index YAML file. This is generally done by editing the below two files:
- cmds.yml - The YAML file containing the data about the commands
- _index.md - The markdown page of the command reference documentation
Preserving Manually Added Documentation
mesheryctl uses Cobra CLI library and GitHub Actions to automate the generation of command documentation pages. On occasion, additional documentation beyond that included in the mesheryctl Golang files is ideal to capture and include in the CLI reference pages. Contributors are encouraged to add more usage examples, screenshots to any of the CLI reference pages. To protect any manually added content and ensure it remains intact after regeneration, create a separate Hugo shortcode file. Follow file naming scheme outlined below:
If your mesheryctl docs end like this, add a shortcode at the end of the file. An example is given below
Example: // apply a pattern file mesheryctl design apply -f [file | URL] // deploy a saved design mesheryctl design apply [design-name], Annotations: linkDocPatternApply, ... <pre class='codeblock-pre'> <div class='codeblock'> --config string path to config file (default "/home/runner/.meshery/config.yaml") -t, --token string Path to token file default from current context -v, --verbose verbose output </div> </pre> {{< permalink-of-file >}}
Quality
Linting
mesheryctl uses golangci-lint with the repo-wide configuration at .github/.golangci.yml, plus Meshery’s repo-specific Go analyzers - notably the one that fails the build when a gorm ORDER BY clause is built from an unsanitized value. Run both from mesheryctl/:
make lint
That target is the CLI-scoped equivalent of the two jobs that gate your pull request in .github/workflows/go-testing-ci.yml: golangci-lint-cli applies the same shared configuration to mesheryctl, and golangci-lint-server runs the ORDER BY analyzer over the whole root module, mesheryctl included. What each rule protects and what to do when one fires is in Go Lint Rules.
Testing
Key Test Writing Principles
The following key principles should be taken to mind when writing tests:
- Golang’s standard library will be used to write tests.
- The tests should cover all possible use cases and not just the happy paths.
- Integration tests should contain the keyword “Integration” in the title and should be marked to be skipped under unit testing. (See below)
- Fixtures are mock/raw data to use(for e.g. API response to mock an HTTP call).
- Testdata is the expected response of mesheryctl commands or functions.
- The mock data and the expected responses are stored in the golden files.
- Table formatted tests are performed on functions and commands.
- mesheryctl/pkg/utils/fixtures/validate.version.github.golden file needs to be updated regularly.
- The version in utils.NewTestHelper() should be updated regularly.
- Golden files should be updated synchronously as API responses, mesheryctl outputs are updated.
References
- jarcoal/httpmock
- Unit testing CLIs in Go
- How to test CLI commands with Go and Cobra
- Contributing to Meshery API Swagger Documentation
Unit Tests
Unit tests and integration tests are essential to make each mesheryctl release robust and of high quality. Below you will find guidelines to write unit tests and integration tests and examples of how they are implemented in mesheryctl.
Unit test code coverage reports can be found in the CodeCov logs. Note: GitHub login may be required for access.
This format should reference an external file where your manual changes are stored. These files should be present at the folder path (_includes/mesheryctl/). Any content added using this method will not be altered during the documentation generation process, but instead will be included post-auto doc generation. When making new changes or additions, understand that these additional details are positioned at the end their given CLI reference page, so bear this in mind as you organize and present your additional command details.
Integration Tests
Marking integration tests under unit tests
Since there is no straightforward way to mark unit tests and integration tests differently. Here we use the --short flag while running tests to differentiate between unit tests and integration tests.
func TestPreflightCmdIntegration(t *testing.T) { // skipping this integration test with --short flag if testing.Short() { t.Skip("skipping integration test") } }
In the above code sample, the test is marked with “Integration” in the title and if a --short flag is passed with the command, this test is skipped.
Running tests in GitHub workflows
Unit tests
go test --short ./... -race -coverprofile=coverage.txt -covermode=atomic
Integration tests
go test -run Integration ./... -race -coverprofile=coverage.txt -covermode=atomic
Updating golden files
Test packages that compare against golden files declare an update flag:
var update = flag.Bool("update", false, "update golden files")
Pass it after -args so go test hands it to the test binary rather than
interpreting it itself. Regenerate only the package you are changing:
cd mesheryctl && go test --short ./internal/cli/root/relationships/ -args -update
-update rewrites the expectation to whatever the command currently prints, so
it will just as happily record a regression. After regenerating, read the diff
and confirm each change is output you actually want users to see - correct
message text, a documentation URL that resolves, the right set of subcommands.
If the diff shows the command is wrong, fix the command and regenerate; never
let -update settle a disagreement between the test and the code.
Fixtures and testdata are not interchangeable (see “Key Test Writing
Principles” above): -update regenerates only the expected output under
testdata/. When a wire contract changes, the fixture holding the mocked
response is usually the file that has gone stale - updating only the expectation
bakes the broken behavior into the test.
The shared meshconfig fixture is read-only
utils.SetupContextEnv loads mesheryctl/pkg/utils/TestConfig.yaml, and almost
every command package reaches it through utils.InitTestEnvironment. Because
go test ./mesheryctl/... runs each package as its own process and several of
them at once, that one file is mutable state shared across concurrent
processes.
Any command that persists configuration - system channel set, system context create, system token create - calls viper.WriteConfig, which truncates the
active config file before rewriting it. Point viper at the shared fixture and a
sibling package reading it inside that window parses an empty document and gets
no error for it, leaving an empty current-context. The next request made with
that config reaches MesheryCtlConfig.GetBaseMesheryURL, which calls
Log.Fatal and exits the whole test binary, so the run reports a bare
package-level FAIL that names no test and points at no assertion.
SetupContextEnv therefore hands each test a private copy of the fixture
through utils.CopyMeshconfigFixture. A test that needs a writable meshconfig
must use that helper, or utils.SetupCustomContextEnv with a testdata file of
its own. Never point viper at pkg/utils/TestConfig.yaml directly.
system channel set stable rewrote the shared fixture with byte-identical
content, so it left the working tree clean and nothing in review suggested a
test was writing to it. The truncate is what breaks sibling packages, not the
content, and only the file’s modification time recorded that it happened.
End-to-end Tests
End-to-end testing of mesheryctl uses the Bash Automated Testing System (BATS) framework to define and execute CLI tests. See Contributing to Meshery CLI End-to-End Tests