{"id":36800055,"url":"https://github.com/mxcd/gitops-cli","last_synced_at":"2026-02-15T23:10:09.140Z","repository":{"id":117737363,"uuid":"461470584","full_name":"mxcd/gitops-cli","owner":"mxcd","description":"CLI for performing GitOps tasks","archived":false,"fork":false,"pushed_at":"2025-10-30T22:46:05.000Z","size":244,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-12T19:23:42.235Z","etag":null,"topics":["cli","gitops","hashicorp-vault","kubernetes","secrets","sops"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mxcd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2022-02-20T11:46:07.000Z","updated_at":"2025-10-30T22:45:53.000Z","dependencies_parsed_at":"2024-06-21T19:15:14.616Z","dependency_job_id":"103fd16f-9a03-4c41-bf64-b26dff3712b4","html_url":"https://github.com/mxcd/gitops-cli","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/mxcd/gitops-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxcd%2Fgitops-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxcd%2Fgitops-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxcd%2Fgitops-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxcd%2Fgitops-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mxcd","download_url":"https://codeload.github.com/mxcd/gitops-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mxcd%2Fgitops-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29492005,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T19:29:10.908Z","status":"ssl_error","status_checked_at":"2026-02-15T19:29:10.419Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","gitops","hashicorp-vault","kubernetes","secrets","sops"],"created_at":"2026-01-12T13:33:22.002Z","updated_at":"2026-02-15T23:10:09.117Z","avatar_url":"https://github.com/mxcd.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"```\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n```\n\n[//]: # \"https://patorjk.com/software/taag/#p=display\u0026f=Slant\u0026t=%3E%20gitops\"\n\n## GitOps CLI\n\nCLI tool for performing GitOps operations\n\n## Usage\n\n```\nNAME:\n   gitpos - GitOps CLI\n\nUSAGE:\n   gitpos [global options] command [command options] [arguments...]\n\nCOMMANDS:\n   secrets, s  GitOps managed secrets\n   help, h     Shows a list of commands or help for one command\n\nGLOBAL OPTIONS:\n   --root-dir value              root directory of the git repository [$GITOPS_ROOT_DIR]\n   --kubeconfig value, -k value  kubeconfig file to use for connecting to the Kubernetes cluster [$KUBECONFIG, $GITOPS_KUBECONFIG]\n   --verbose, -v                 debug output (default: false) [$GITOPS_VERBOSE]\n   --very-verbose, --vv          trace output (default: false) [$GITOPS_VERY_VERBOSE]\n   --cleartext                   print secrets in cleartext to the console (default: false) [$GITOPS_CLEARTEXT]\n   --print                       print secrets to the console (default: false) [$GITOPS_PRINT]\n   --help, -h                    show help\n```\n\n### Planning secret application to a cluster\n\n**NOTE:** It is expected, that the cluster's KUBECONFIG is already set up. Alternatively, the `--kubeconfig` flag can be used.\n\n```bash\ngitops secrets plan kubernetes\n```\n\nOr in short\n\n```bash\ngitops s p k8s\n```\n\nExample output:\n\n```\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n\nGitOps CLI computed the following changes for your cluster:\n-------------------------------------------------------\n\ndefault/my-config-map :  add\n  + data.loremIpsum: **************************************************\n  + data.someConfigMapKey: **************\n---\ndefault/implicit-name :  unchanged\n---\ndefault/my-secret-name :  unchanged\n---\ndefault/database-credentials :  change\n  ~ data.bar: ** =\u003e **\n\n-------------------------------------------------------\n\nuse gitops secrets apply kubernetes to apply these changes to your cluster\n```\n\n### Applying secrets to a cluster\n\n**NOTE:** It is expected, that the cluster's KUBECONFIG is already set up. Alternatively, the `--kubeconfig` flag can be used.\n\n```bash\ngitops secrets apply kubernetes\n```\n\nOr in short\n\n```bash\ngitops s a k8s\n```\n\nThe user will be prompted to confirm the changes before they are applied to the cluster. The prompt can be bypassed by using the `--auto-approve` flag.  \nExample output:\n\n```\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n\nGitOps CLI computed the following changes for your cluster:\n-------------------------------------------------------\n\ndefault/my-config-map :  add\n  + data.someConfigMapKey: **************\n  + data.loremIpsum: **************************************************\n---\ndefault/implicit-name :  unchanged\n---\ndefault/my-secret-name :  unchanged\n---\ndefault/database-credentials :  change\n  ~ data.bar: ** =\u003e *****\n\n-------------------------------------------------------\n\nGitOps CLI will apply these changes to your Kubernetes cluster.\nOnly 'yes' will be accepted to approve.\nApply changes above: yes\nGitOps CLI will now execute the changes for your cluster:\n-------------------------------------------------------\n\ndefault / my-config-map  created\ndefault / database-credentials  updated\n\n-------------------------------------------------------\n\nAll changes applied.\n```\n\nRedacted secrets (`*********`) can be displayed in cleartext by using the `--cleartext` flag.  \nTo print all loaded secrets to the console, use the `--print` flag.\n\n## Installation\n\n### MacOS\n\nInstall using homebrew:\n\n```bash\nbrew tap mxcd/gitops\nbrew install gitops\n```\n\n## Features\n\n### GitOps secret management\n\nThe GitOps CLI can handle secrets in a GitOps way. Either by injecting them directly\nas K8s secrets or by sending them to a vault instance for safekeeping. Either way,\nthe secrets are stored in a Git repository and secured using SOPS.\n\n#### Secret storage\n\nSecrets are stored in any directory of your git repository. The GitOps CLI will pick\nup any file that ends with `*.gitops.secret.enc.y[a]ml` except for `values.gitops.secret.enc.y[a]ml` (see [Secrets Templating](#secrets-templating))\nThe secret files must be encrypted using SOPS.\n\n**NOTE:** Secrets MUST NEVER be committed into version control unencrypted.\nTherefore, it is very much encouraged to add the following lines to your `.gitignore` file:\n\n```gitignore\n*.secret.yaml\n*.secret.yml\n*.secret.env\n```\n\nMake sure to follow a strict naming convention for your secret files, in order to keep them matching those patterns.\n\n#### Secrets file format\n\nThe secrets files must follow the following format:\n\n```yaml\n# target of the secret\ntargetType: \u003c k8s | vault \u003e\n\n# name of the secret\nname: \u003cmy-secret-name\u003e\n# optional namespace of the secret (default: default)\nnamespace: \u003cmy-namespace\u003e\n# type of the secret (default: Opaque)\n# only for k8s secrets: ConfigMap or any of the following: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types\ntype: \u003c ConfigMap | Opaque | ... \u003e\n# data of the secret as kv pairs\ndata:\n  \u003ckey1\u003e: \u003cvalue1\u003e\n  \u003ckey2\u003e: \u003cvalue2\u003e\n```\n\n##### Case 1: Secret for K8s\n\n```yaml\n# targetType of the secret\ntargetType: k8s\n# name of the secret\nname: my-secret-name\n# optional namespace of the secret (default: default)\nnamespace: my-namespace\n# type of the secret (default: Opaque)\ntype: Opaque\n# data of the secret as kv pairs\ndata:\n  key: value\n```\n\nIf the name is not given in the file, the name will be inferred from the filename. The file extension `.gitops.secret.enc.y[a]ml` will be removed.\n\n```yaml\nmy-secret-name.gitops.secret.enc.yaml\n# will be applied as\nname: my-secret-name\n```\n\nThis implies, that the filename must be a valid K8s secret name.\n\n##### Case 2: Secret for Vault\n\n**NOTE:** Vault secrets are still WIP\n\n```yaml\n# target of the secret\ntargetType: vault\n# name of the secret - will be used as path in vault\nname: /my/secret/name\n# data of the secret as kv pairs\ndata:\n  key: value\n```\n\n#### Secrets Templating\n\nIt is possible to use Go templates in the secret files. The values will originate from sops-encrypted `values.gitops.secret.enc.y[a]ml` files.  \nValues files can be located anywhere in the repository. The GitOps CLI will pick up all files that are located on the direct path towards the respective secret file.  \nValues files closer to the secret file will have higher precedence. Any object structure is allowed to be used in a values file.\n\nExample: \n\n```yaml\n# /foo/bar/dev/values.gitops.secret.enc.yaml\nenvironment: dev\ndatabase:\n  host: localhost\n  port: 5432\n  user: postgres\n  password: postgres\n```\n\n```yaml\n# /foo/bar/dev/values.gitops.secret.enc.yaml\ntargetType: k8s\n# name of the secret - will be used as path in vault\nname: my-service\nnamespace: \"{{ .Values.environment }}\"\ndata:\n  application.properties: |-\n    service.environment={{ .Values.environment }}\n    spring.datasource.url=jdbc:postgresql://{{ .Values.database.host }}:{{ .Values.database.port }}/mydb\n    spring.datasource.username={{ .Values.database.user }}\n    spring.datasource.password={{ .Values.database.password }}\n```\n**NOTE** that the template string (`{{ .Values.someValue }}`) must be enclosed in quotes for sops to work properly. In the above example, the entire `application.properties` data value is considered as a string and thus does not need further quoting.\n\n#### Multi-cluster support\nIt is possible to address multiple clusters with a single GitOps repository.  \nTo add a new cluster to the GitOps state use\n```\ngitops clusters add \u003ccluster-name\u003e \u003ckubeconfig-path\u003e\n```\nThe kubeconfig file can either be a plain text file or a sops-encrypted file. If the file is encrypted, it must adhere to the following naming convention to be decrypted properly: \n```\n*.kubeconfig.secret.enc.ya?ml\n```\n  \nTo inspect the currently configured clusters use\n```\ngitops clusters list\n```\n```\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n\ndev  =\u003e  kubeconfigs/dev.kubeconfig.secret.enc.yml\nint  =\u003e  kubeconfigs/int.kubeconfig.secret.enc.yml\nprod  =\u003e  kubeconfigs/prod.kubeconfig.secret.enc.yml\n```\n  \nTo remove a cluster from the GitOps state use\n```\ngitops clusters remove \u003ccluster-name\u003e\n```\n\nTo check connectivity with the configured clusters use\n```\ngitops clusters test\n```\n```\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n\nCluster: __default (v1.25.3)    Connected: true\nCluster: dev (v1.24.8)          Connected: true\nCluster: int (v1.24.8)          Connected: true\nCluster: prod (v1.24.8)         Connected: true\n```\n  \nA secret can be configured to be applied to a specific cluster using the `target` attribute in the secret file. The default value is the `__default` cluster which is inferred from the `KUBECONFIG` environment variable or the default kubeconfig file. The `target` attribute can also be set using a templating variable so that all secrets under a certain directory will be applied to a specific cluster.\n```yaml\n# /foo/bar/dev/values.gitops.secret.enc.yaml\ntarget: dev\n```\n```yaml\n# /foo/bar/dev/myservice/service.gitops.secret.enc.yaml\ntargetType: k8s\ntarget: \"{{ .Values.target }}\" # will be replaced with \"dev\"\nname: my-service\ndata:\n  key: value\n```\n  \nBy default, secrets will be applied to all configured clusters. This can be limited by giving the cluster as an argument:\n```\ngitops secrets plan kubernetes dev\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n\nLimiting to cluster dev\n\n[Loading local secrets] 100% |██████████████████████████████████████████████████| (63/63) \n\n\nNo changes to apply.\n```\n\n#### Directory limiter\nIt is possible to restrict the secrets input to a specific directory to speed up loading and decryption of secrets. This can be done by providing the `--dir` flag:\n```\ngitops secrets --dir application/dev plan kubernetes\n\n__             _ __\n\\ \\     ____ _(_) /_____  ____  _____\n \\ \\   / __ `/ / __/ __ \\/ __ \\/ ___/\n / /  / /_/ / / /_/ /_/ / /_/ (__  )\n/_/   \\__, /_/\\__/\\____/ .___/____/\n     /____/           /_/\n\nLimiting to directory applications/dev\n\n[Loading local secrets] 100% |██████████████████████████████████████████████████| (1/1) \n\nNo changes to apply.\n```\n**NOTE** that the directory path must be relative to the repository root and that only forward slashes (`/`) are supported.\n## Repository\n\n### After the first clone\n\n#### Pre-commit\n\nPlease make sure to follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) when committing to this repository.  \nTo make one's life easier, a pre-commit config is provided that can be installed with the following command:\n\n```bash\npre-commit install --hook-type commit-msg\n```\n\n## Repo Server\nThe repo server allows to run the GitOps CLI in a server mode. \nThis is especially useful if many concurrent patches are to be expected (e.g. due to matrix builds of multiple services). \nThe server will clone the defined GitOps cluster repository and apply patches when instructed via the REST API.\n\n### Running the server\nThe most basic configuration uses https and basic auth:\n```bash\ndocker run --platform linux/amd64 --rm -it -p 8080:8080 \\\n  -e GITOPS_REPOSITORY=\"https://github.com/\u003cowner\u003e/\u003crepo\u003e\" \\\n  -e GITOPS_REPOSITORY_BASICAUTH=\"\u003cusername\u003e:\u003cpersonal-access-token\u003e\" \\\n  -e API_KEYS=\"\u003csuper-secret-api-key\u003e\" \\\n  ghcr.io/mxcd/gitops/repo-server:2.2.3\n```\n\n`API_KEYS` is a comma separated list of API keys that are used to authenticate the requests.\n\n### Using the server\nThe server exposes a REST API that can be used to apply patches to the GitOps repository.\nThe API is available at `PUT \u003cprotocol\u003e://\u003chost\u003e:\u003cport\u003e/api/v1/patch`  \n\n```bash\n# env vars used for example curl BUT also valid for CLI usage\nexport GITOPS_REPOSITORY_SERVER=\"\u003cprotocol\u003e://\u003chost\u003e[:\u003cport\u003e]/api/v1\"\nexport GITOPS_REPOSITORY_SERVER_API_KEY=\"\u003csuper-secret-api-key\u003e\"\n```\n\n```bash\ncurl --request PUT \\\n  --url $GITOPS_REPOSITORY_SERVER/patch \\\n  --header 'content-type: application/json' \\\n  --header 'X-API-Key: $GITOPS_REPOSITORY_SERVER_API_KEY'\n  --data '{\n  \"filePath\": \"applications/dev/service-foo/values.yaml\",\n  \"patches\": [\n    {\n      \"selector\": \".service.image.tag\",\n      \"value\": \"v42.0.1\"\n    }\n  ]\n}'\n```\n\n```bash\n# Equivalent CLI command\n# Evaluates the previously exported env vars GITOPS_REPOSITORY_SERVER and GITOPS_REPOSITORY_SERVER_API_KEY\ngitops patch applications/dev/service-foo/values.yaml .service.image.tag v42.0.1\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmxcd%2Fgitops-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmxcd%2Fgitops-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmxcd%2Fgitops-cli/lists"}