{"id":22970327,"url":"https://github.com/lispyclouds/climate","last_synced_at":"2025-10-14T14:42:39.179Z","repository":{"id":267958380,"uuid":"902885018","full_name":"lispyclouds/climate","owner":"lispyclouds","description":"The sidekick for your CLIs powered by OpenAPI","archived":false,"fork":false,"pushed_at":"2025-09-23T06:38:28.000Z","size":66,"stargazers_count":28,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-23T08:29:58.754Z","etag":null,"topics":["cli","golang","openapi"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/lispyclouds/climate","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lispyclouds.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-12-13T13:21:10.000Z","updated_at":"2025-09-23T06:38:32.000Z","dependencies_parsed_at":"2025-02-05T22:19:44.330Z","dependency_job_id":"13c68824-1a75-4b5b-b8cc-b4698b2f5e1d","html_url":"https://github.com/lispyclouds/climate","commit_stats":null,"previous_names":["lispyclouds/cli-mate","lispyclouds/climate"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lispyclouds/climate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fclimate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fclimate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fclimate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fclimate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lispyclouds","download_url":"https://codeload.github.com/lispyclouds/climate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lispyclouds%2Fclimate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279019154,"owners_count":26086682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","golang","openapi"],"created_at":"2024-12-14T22:12:15.429Z","updated_at":"2025-10-14T14:42:39.161Z","avatar_url":"https://github.com/lispyclouds.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# climate\n\n[![Go Report Card](https://goreportcard.com/badge/github.com/lispyclouds/climate)](https://goreportcard.com/report/github.com/lispyclouds/climate)\n[![CI Status](https://github.com/lispyclouds/climate/workflows/Test/badge.svg)](https://github.com/lispyclouds/climate/actions?query=workflow%3ATest)\n\nRead the detailed [blogpost](https://zuplo.com/blog/2025/02/02/generate-cli-from-api-with-climate)!\n\nGo is a fantastic language to build CLI tooling, specially the ones for interacting with an API server. `\u003cyour tool\u003ectl` anyone?\nBut if you're tired of building bespoke CLIs everytime or think that the swagger codegen isn't just good enough or don't quite subscribe to the idea of codegen in general (like me!), look no further.\n\nWhat if you can influence the CLI behaviour from the server? This enables you to bootstrap your [cobra](https://cobra.dev/) CLI tooling from an [OpenAPI](https://swagger.io/specification/) spec. Checkout [Wendy](https://bob-cd.github.io/cli/#wendy) as an example of a full CLI project made using climate.\n\n## Getting started\n\n### Rationale\n\nclimate allows the server to influence the CLI behaviour by using OpenAPI's [extensions](https://swagger.io/docs/specification/v3_0/openapi-extensions/). It encourages [spec-first](https://www.atlassian.com/blog/technology/spec-first-api-development) practices thereby keeping both users and maintenance manageable. It does just enough to handle the spec and nothing more.\n\nOverall, the way it works:\n\n- Each operation is converted to a Cobra command\n- Each parameter is converted to a flag with its corresponding type\n- As of now, request bodies are a flag and treated as a string regardless of MIME type. Name defaults to `climate-data` unless specified via `x-cli-name`. All subject to change\n- The provided handlers are attached to each command, grouped and attached to the rootCmd\n\nInfluenced by some of the ideas behind [restish](https://rest.sh/) it uses the following extensions as of now:\n\n- `x-cli-aliases`: A list of strings which would be used as the alternate names for an operation\n- `x-cli-group`: A string to allow grouping subcommands together. All operations in the same group would become subcommands in that group name\n- `x-cli-hidden`: A boolean to hide the operation from the CLI menu. Same behaviour as a cobra command hide: it's present and expects a handler\n- `x-cli-ignored`: A boolean to tell climate to omit the operation completely\n- `x-cli-name`: A string to specify a different name. Applies to operations and request bodies as of now\n\n### Ideally support:\n\n- more of the OpenAPI types and their checks. eg arrays, enums, objects, multi types etc\n- type checking request bodies of certain MIME types eg, `application/json`\n- better handling of request bodies eg, providing a stdin or a curl like notation for a file `@payload.json` etc.\n\n### Installation\n\n```bash\ngo get github.com/lispyclouds/climate\n```\n\n### Usage\n\nGiven an OpenAPI spec like [api.yaml](/api.yaml)\n\nLoad the spec:\n\n```go\nmodel, err := climate.LoadFileV3(\"api.yaml\") // or climate.LoadV3 with []byte\n```\n\nDefine a cobra root command:\n\n```go\nrootCmd := \u0026cobra.Command{\n\tUse:   \"calc\",\n\tShort: \"My Calc\",\n\tLong:  \"My Calc powered by OpenAPI\",\n}\n```\n\nDefine one or more handler functions of the following signature:\n\n```go\nfunc handler(opts *cobra.Command, args []string, data climate.HandlerData) error {\n\tslog.Info(\"called!\", \"data\", fmt.Sprintf(\"%+v\", data))\n\terr := doSomethingUseful(data)\n\n\treturn err\n}\n```\n\n#### Handler Data\n\n(Feedback welcome to make this better!)\n\nAs of now, each handler is called with the cobra command it was invoked with, the args and an extra `climate.HandlerData`, more info [here](https://pkg.go.dev/github.com/lispyclouds/climate#pkg-types)\n\nThis can be used to query the params from the command mostly in a type safe manner:\n\n```go\n// to get all the int path params\nfor _, param := range data.PathParams {\n\tif param.Type == climate.Integer {\n\t\tvalue, _ := opts.Flags().GetInt(param.Name)\n\t}\n}\n```\n\nDefine the handlers for the necessary operations. These map to the `operationId` field of each operation:\n\n```go\nhandlers := map[string]Handler{\n\t\"AddGet\":      handler,\n\t\"AddPost\":     handler,\n\t\"HealthCheck\": handler,\n\t\"GetInfo\":     handler,\n}\n```\n\nBootstrap the root command:\n\n```go\nerr := climate.BootstrapV3(rootCmd, *model, handlers)\n```\n\nContinue adding more commands and/or execute:\n\n```go\n// add more commands not from the spec\n\nrootCmd.Execute()\n```\n\nSample output:\n\n```\n$ go run main.go --help\nMy Calc powered by OpenAPI\n\nUsage:\n  calc [command]\n\nAvailable Commands:\n  completion  Generate the autocompletion script for the specified shell\n  help        Help about any command\n  info        Operations on info\n  ops         Operations on ops\n  ping        Returns Ok if all is well\n\nFlags:\n  -h, --help   help for calc\n\nUse \"calc [command] --help\" for more information about a command.\n\n$ go run main.go ops --help\nOperations on ops\n\nUsage:\n  calc ops [command]\n\nAvailable Commands:\n  add-get     Adds two numbers\n  add-post    Adds two numbers via POST\n\nFlags:\n  -h, --help   help for ops\n\nUse \"calc ops [command] --help\" for more information about a command.\n\n$ go run main.go ops add-get --help\nAdds two numbers\n\nUsage:\n  calc ops add-get [flags]\n\nAliases:\n  add-get, ag\n\nFlags:\n  -h, --help     help for add-get\n      --n1 int   The first number\n      --n2 int   The second number\n\n$ go run main.go ops add-get --n1 1 --n2 foo\nError: invalid argument \"foo\" for \"--n2\" flag: strconv.ParseInt: parsing \"foo\": invalid syntax\nUsage:\n  calc ops add-get [flags]\n\nAliases:\n  add-get, ag\n\nFlags:\n  -h, --help     help for add-get\n      --n1 int   The first number\n      --n2 int   The second number\n\n$ go run main.go ops add-get --n1 1 --n2 2\n2024/12/14 12:53:32 INFO called! data=\"{Method:get Path:/add/{n1}/{n2}}\"\n```\n\n## License\n\nCopyright © 2024- Rahul De\n\nDistributed under the MIT License. See LICENSE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flispyclouds%2Fclimate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flispyclouds%2Fclimate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flispyclouds%2Fclimate/lists"}