👋 Welcome, and thank you for your interest in contributing to opslevel-cli! This guide will help you ramp up, propose changes, develop locally, and contribute code effectively.
- About the CLI
- Getting Started
- Development Workflow
- Proposing and Submitting Changes
- Command Architecture & Style
- Testing & Tooling
- Release Process
The opslevel-cli is a command-line interface for interacting with the OpsLevel API. It helps engineers automate, inspect, and manage their service catalog, ownership, checks, and more.
Cobra is the library we use to define our CLI. Commands are defined as Go structs with handlers, descriptions, and flags.
- Viper handles flag parsing, environment variables, and configuration files. Examples of our usage of it are here and here
- Modular command files live under
/cmd, grouped by functionality (e.g., services, checks, etc.). - Commands are registered to
rootCmdviainit()functions. - 80% of our functionality is provided by opslevel-go and the purpose of this CLI is just to marshal data between the user and opslevel-go in a UX friendly way.
- Most commands follow the standard CRUD pattern
opslevel create ...,opslevel get ...,opslevel list ...,opslevel update ...,opslevel delete ..., etc. - We have an
opslevel betasubcommand for experimental commands that are subject to change.
If you're an external contributor fork the repo, then:
git clone https://github.com/YOUR_USERNAME/cli.git
cd cli
git checkout -b my-featureIf you're part of the OpsLevel org:
git clone git@github.com:OpsLevel/cli.git
cd cli
git checkout -b my-featureYou might need to be given permissions to the repo, please reach out to team platform.
You can use task setup to run an idempotent one-time setup.
Set your API token:
export OPSLEVEL_API_TOKEN=your_token_hereIf you need to target a different environment, set the OPSLEVEL_API_URL environment variable:
export OPSLEVEL_API_URL=https://self-hosted.opslevel.dev/cd ./src
go run main.go --helpThis is the easiest way to test your changes live.
Sometimes you will need to make CLI changes in tandem with changes to opslevel-go.
To test changes from a local branch of opslevel-go:
# Set up workspace using task
task workspace
# Switch to your feature branch in the submodule
git -C ./src/submodules/opslevel-go checkout --track origin/my-feature-branchAll CLI calls will now use your local opslevel-go code checked out into the submodule at ./src/submodules/opslevel-go.
This way you can effectively work on both the CLI and opslevel-go in parallel if needed.
- Use
task testto run tests locally - Use
task lintto check for code quality issues locally - Use
task fixto fix formatting, linting, go.mod, and update submodule all in one go
Our CI pipeline will run task ci which can also be run locally to debug any issue that might only arise in CI.
If you're fixing a bug or adding a feature, please open an issue first. This helps us track discussions and ensures your work aligns with project goals.
Note: All PRs must be tied to a GitHub issue.
Look for issues marked good first issue if you're new.
- Push your changes to your fork or feature branch
- Run
changie newto create a changelog entry - Open a PR to
mainor the relevant feature branch - Our GitHub Actions pipeline will run tests and lint checks
- A maintainer will review your PR and request changes if needed
Once approved and merged, your change will be included in the next release.
- Each CLI command lives in its own file under
/cmd - Commands should:
- Register themselves via
init() - Use
RunEinstead ofRunfor error handling
- Register themselves via
- Flags and environment variables should be registered with Viper for consistency
- Prefer readable, minimalistic command logic — delegate heavy logic to opslevel-go unless it's not possible
The following shows the minimum amount of code needed to create a command. There are a plethora of commands already registered to the root command, so this is just an example of how to create a new command. Please take a look at the existing commands to get a feel for how they work and whats possible.
var exampleCmd = &cobra.Command{
Use: "example",
Short: "Hello World Command",
Long: "Hello World Command to show how an example command works",
RunE: func(cmd *cobra.Command, args []string) error {
log.Info().Msg("Hello World!")
return nil
},
}
func init() {
rootCmd.AddCommand(exampleCmd)
}-
All customer facing changes must have a Changie changelog entry
- Run:
changie new - Follow prompts to categorize your change
- This generates a YAML file in
.changes/that must be committed with your PR
- Run:
-
CI/CD (GitHub Actions) runs lint and tests automatically on pull requests and the main branch
-
Maintainers will merge once approved
-
Your contribution will be included in the next versioned release (triggered by a maintainer)
Happy hacking 🎉 and thank you for helping improve the opslevel-cli!