{"id":45240827,"url":"https://github.com/hops-ops/hops-cli","last_synced_at":"2026-05-17T05:09:37.965Z","repository":{"id":339656399,"uuid":"971701202","full_name":"hops-ops/hops-cli","owner":"hops-ops","description":"hops, like in basketball, but for ops - CLI","archived":false,"fork":false,"pushed_at":"2026-04-01T22:13:19.000Z","size":150,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-02T08:12:02.560Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","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/hops-ops.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":"2025-04-23T23:42:37.000Z","updated_at":"2026-04-01T22:06:04.000Z","dependencies_parsed_at":"2026-02-21T01:00:13.642Z","dependency_job_id":null,"html_url":"https://github.com/hops-ops/hops-cli","commit_stats":null,"previous_names":["hops-ops/hops-cli"],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/hops-ops/hops-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fhops-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fhops-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fhops-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fhops-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hops-ops","download_url":"https://codeload.github.com/hops-ops/hops-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hops-ops%2Fhops-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31571601,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"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":[],"created_at":"2026-02-20T21:19:53.228Z","updated_at":"2026-04-08T20:03:59.244Z","avatar_url":"https://github.com/hops-ops.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hops-cli\n\n`hops-cli` is a Rust CLI for Crossplane development and XR lifecycle workflows.\n\n## Overview\n\nThis tool supports three related workflows:\n\n- Local cluster setup on Colima\n- Configuration package install/uninstall against the connected cluster\n- XR observe/manage/adopt/orphan workflows for existing infrastructure\n\nFor local development, it can also:\n\n- Install and manage Colima\n- Start a local k8s cluster with Crossplane installed via Helm\n- Install the Kubernetes and Helm Crossplane providers\n- Deploy an in-cluster OCI registry (`crossplane-system/registry`)\n- Build and publish Crossplane configuration packages from an XRD project\n\n## Installation\n\n### Using ubi\n\n1. **Install ubi:**  \n   Ensure you have ubi installed by running:\n   ```bash\n   curl --silent --location \\\n    https://raw.githubusercontent.com/houseabsolute/ubi/master/bootstrap/bootstrap-ubi.sh |\n    sh\n\n   mkdir -p ~/.ubi/bin\n   echo 'export PATH=\"$HOME/.ubi/bin:$PATH\"' \u003e\u003e ~/.zshrc  # or your preferred shell profile\n   ```\n2. **Install vnext with ubi:**  \n   ```bash\n   ubi --project hops-ops/hops-cli --in /usr/local/bin --rename-exe hops\n   ```\n\nInstall a specific version:\n\n```bash\nubi --project hops-ops/hops-cli --tag vx.x.x --in /usr/local/bin/ --rename-exe hops\n```\n\nSee \"Releases\" for available versions and changenotes.\n\n## Prerequisites\n\n- macOS\n- [Rust/Cargo](https://www.rust-lang.org/tools/install)\n- [Homebrew](https://brew.sh/)\n- `docker` CLI\n- `kubectl`\n- `helm`\n- `up` (Upbound CLI, used by `up project build`)\n- `aws` CLI v2 (used by `local aws` to export profile credentials)\n\nNote: `hops-cli local install` installs `colima` through Homebrew.\n\n## Build\n\n```bash\ncargo build\n```\n\nIf you want static OpenSSL vendoring:\n\n```bash\ncargo build --features vendored\n```\n\n## Usage\n\n```bash\nhops --help\nhops local --help\nhops config --help\nhops secrets --help\nhops validate --help\nhops xr --help\n```\n\n## Command Areas\n\n`hops-cli` is organized into a few command groups:\n\n- `local`\n  - Manage a local Colima-based control plane, install providers, and bootstrap AWS or GitHub provider auth.\n- `config`\n  - Build, install, reload, and uninstall Crossplane configuration packages against the connected cluster.\n- `secrets`\n  - Initialize secrets config, encrypt and decrypt local secrets, and sync repo-managed secrets to AWS Secrets Manager or GitHub repository secrets.\n- `validate`\n  - Generate configuration manifests from Upbound-format XRD projects for validation workflows.\n- `xr`\n  - Observe existing XR-backed infrastructure and render adoption, management, or orphaning manifests.\n\n## Secrets\n\n`hops secrets init` sets up local secrets directories, `.sops.yaml`, and `.hops.yaml` so plaintext secrets can be encrypted locally and synced to AWS Secrets Manager or GitHub repository secrets.\n\nTypical layout:\n\n```text\nsecrets/\n  aws/\n  github/\n    _shared/\nsecrets-encrypted/\n  aws/\n  github/\n```\n\nTypical config:\n\n```yaml\nsecrets:\n  plaintext_dir: secrets\n  encrypted_dir: secrets-encrypted\n  aws:\n    path: aws\n    region: us-east-2\n    tags:\n      hops.ops.com.ai/secret: \"true\"\n  github:\n    owner: hops-ops\n    path: github\n    shared_secrets:\n      path: _shared\n      repos:\n        - repo-a\n        - repo-b\n```\n\nEncrypt and decrypt operate from the configured roots:\n\n```bash\nhops secrets encrypt\nhops secrets decrypt\n```\n\nAWS sync reads from `\u003cplaintext_dir\u003e/\u003caws.path\u003e`:\n\n```bash\nhops secrets sync aws\n```\n\nAWS rules:\n\n- A `.json` file becomes one AWS Secrets Manager secret with the JSON object stored as-is.\n- A directory containing plain files rolls up into one AWS secret. Each filename becomes a key in the JSON object.\n- A `.env` file is parsed into key/value pairs and stored as one JSON secret.\n- A directory containing a `.env` file merges those parsed key/value pairs into that directory's rolled-up JSON secret.\n- Secret names are derived from the path relative to the AWS root.\n- `--cleanup` only works when syncing the full configured AWS root.\n- `hops.ops.com.ai/secret=true` is always applied to repo-managed AWS secrets.\n\nExamples:\n\n- `secrets/aws/app.json` -\u003e AWS secret `app`\n- `secrets/aws/github/token` and `secrets/aws/github/owner` -\u003e AWS secret `github`\n- `secrets/aws/slack/.env` with `WEBHOOK_URL=...` -\u003e AWS secret `slack`\n\nGitHub sync reads from `\u003cplaintext_dir\u003e/\u003cgithub.path\u003e`:\n\n```bash\nhops secrets sync github\n```\n\nGitHub rules:\n\n- Each GitHub secret remains a separate GitHub secret. There is no AWS-style roll-up into a single JSON secret.\n- A raw file becomes one GitHub secret.\n- A `.json` file becomes multiple GitHub secrets, one per top-level key.\n- A `.env` file becomes multiple GitHub secrets, one per `KEY=value` entry.\n- Repo-specific secrets come from repo-named paths like `secrets/github/repo-a/...` or `secrets/github/repo-a.json`.\n- Shared GitHub secrets come from `secrets/github/_shared/...` and fan out to the repos listed in `secrets.github.shared_secrets.repos` or passed with `--repo`.\n- If a shared secret and a repo-specific secret have the same final name, the repo-specific value wins for that repo.\n- GitHub secret names are normalized by the CLI to a stable format before syncing.\n\nExamples:\n\n- `secrets/github/repo-a/NPM_TOKEN` -\u003e GitHub secret `NPM_TOKEN` in `repo-a`\n- `secrets/github/repo-a/actions.json` with `{\"SLACK_WEBHOOK\":\"...\"}` -\u003e GitHub secret `SLACK_WEBHOOK` in `repo-a`\n- `secrets/github/repo-a/.env` with `NPM_TOKEN=...` -\u003e GitHub secret `NPM_TOKEN` in `repo-a`\n- `secrets/github/_shared/ORG_TOKEN` -\u003e synced to every configured shared target repo\n\n## Create a Local Control Plane\n\n```bash\n# 1) Install Colima (via Homebrew)\nhops local install\n\n# 2) Start local k8s + Crossplane + providers + local registry\nhops local start\n\n# 3) Configure AWS provider-family + ProviderConfig from your AWS profile\nhops local aws --profile \u003caws-profile\u003e\n\n# 4) Configure GitHub provider + ProviderConfig from your gh auth login\nhops local github --owner \u003corg-or-user\u003e\n\n# 5) Install a Crossplane configuration package from an Upbound-format XRD project\nhops config install --repo hops-ops/aws-auto-eks-cluster --version v0.11.0\n```\n\n### Local provider setup and auth\n\n`hops local aws` and `hops local github` install the provider package and bootstrap auth into a local control plane. The exception is `--refresh`, which updates credentials only.\n\n#### AWS auth\n\n`hops local aws` installs the AWS provider package and uses your AWS CLI configuration to generate credentials for it.\n\n```bash\n# Use an explicit AWS profile\nhops local aws --profile hops\n\n# Refresh only the Secret credentials without re-applying the Provider or ProviderConfig\nhops local aws --profile hops --refresh\n```\n\nHow it works:\n\n- Resolves the profile in this order: `--profile`, `AWS_PROFILE`, `AWS_DEFAULT_PROFILE`, then interactive prompt.\n- Runs `aws configure export-credentials --format process`.\n- If the selected profile needs AWS SSO login, it runs `aws sso login --profile \u003cprofile\u003e` and retries once.\n- Applies the AWS provider package unless `--refresh` is used.\n- Writes the generated credentials into a Kubernetes Secret, defaulting to `default/aws-creds`.\n- Applies an AWS `ProviderConfig` named `default` unless `--refresh` is used.\n- Supports overrides for namespace, Secret name, ProviderConfig name, provider name, and provider package.\n\n#### GitHub auth\n\n`hops local github` installs the GitHub provider package and uses your GitHub CLI login to generate credentials for it.\n\n```bash\n# Use an explicit owner\nhops local github --owner hops-ops\n\n# Refresh only the Secret credentials without re-applying the Provider or ProviderConfig\nhops local github --owner hops-ops --refresh\n```\n\nHow it works:\n\n- Resolves the owner in this order: `--owner`, `GH_OWNER`, `GITHUB_OWNER`, then interactive prompt.\n- Uses your current `gh auth token`.\n- If `gh` is not authenticated, it runs `gh auth login` and retries once.\n- Applies the GitHub provider package unless `--refresh` is used.\n- Writes the generated credentials into a Kubernetes Secret, defaulting to `default/github-creds`.\n- Applies a GitHub `ProviderConfig` named `default` unless `--refresh` is used.\n- Supports overrides for namespace, Secret name, ProviderConfig name, provider name, and provider package.\n\n## Config packages\n\n`config install` and `config uninstall` operate on the currently connected Kubernetes cluster.\n\nThere are two different `config install` modes:\n\n- Source-build mode via `--path` or `--repo` builds an Upbound-format XRD project locally, pushes the package through the local registry flow, and is intended for a local control plane started with `hops local start`.\n- Remote-package mode via `--repo ... --version ...` skips the build and applies a pinned package reference directly, so it can work against non-local connected clusters too.\n\nCommon install flows:\n\n```bash\n# Build from the current directory when it is an Upbound-format XRD project\nhops config install\n\n# Build from an explicit local Upbound-format XRD project path\nhops config install --path /path/to/project\n\n# Install from a GitHub repo; interactive TTY runs ask whether to build from source\n# or use a published version\nhops config install --repo hops-ops/aws-auto-eks-cluster\n\n# Force a source reload before re-applying\nhops config install --repo hops-ops/aws-auto-eks-cluster --reload\n\n# Set spec.skipDependencyResolution=true on the generated Configuration\nhops config install --path /path/to/project --skip-dependency-resolution\n\n# Apply a pinned remote package directly from ghcr.io\nhops config install --repo hops-ops/aws-auto-eks-cluster --version v0.11.0\n```\n\nCommon uninstall flows:\n\n```bash\n# Remove by explicit configuration name\nhops config uninstall --name hops-ops-aws-auto-eks-cluster\n\n# Remove by repo slug\nhops config uninstall --repo hops-ops/aws-auto-eks-cluster\n\n# Remove configurations derived from local build artifacts\nhops config uninstall --path /path/to/project\n```\n\nNotes:\n\n- `--reload` only applies to source installs: `--path` or `--repo` without `--version`.\n- `--skip-dependency-resolution` sets `spec.skipDependencyResolution=true` on the generated `Configuration`.\n- `config install --repo ...` now prompts in interactive terminals to choose between cloning/building from source or applying a published package version. Published-version prompts suggest the latest discovered tag by default and still accept arbitrary tags such as `pr-\u003cgitsha\u003e`.\n- Non-interactive `config install --repo ...` keeps the previous default behavior and builds from source.\n- `config install --repo ... --version ...` skips clone/build and applies the remote package directly.\n- `config uninstall --repo ...` derives the configuration name as `\u003corg\u003e-\u003crepo\u003e`.\n\n## Commands\n\n- `local install`\n  - Runs `brew install colima`.\n- `local reset`\n  - Runs `colima kubernetes reset`.\n- `local start`\n  - Runs `colima start --kubernetes --cpu 8 --memory 16 --disk 60`\n  - Installs Crossplane from `crossplane-stable/crossplane`\n  - Applies manifests from `bootstrap/` for runtime config, providers, provider configs, and registry (embedded in the binary at build time)\n  - Configures Docker in Colima for insecure pulls from `registry.crossplane-system.svc.cluster.local:5000`\n  - Adds host mapping in Colima VM for the registry service DNS name\n- `local stop`\n  - Runs `colima stop`.\n- `local destroy`\n  - Runs `colima delete --force`.\n- `local uninstall`\n  - Prompts for confirmation, then runs `brew uninstall colima`.\n- `config install [--path \u003cPATH\u003e] [--reload]`\n  - Targets the currently connected Kubernetes cluster\n  - Source-build mode intended for a local control plane because it depends on the local registry flow\n  - Runs `up project build` in `PATH` (defaults to current directory)\n  - Loads generated `.uppkg` artifacts from `\u003cPATH\u003e/_output`\n  - Pushes package images to the registry exposed at `localhost:30500`\n  - Applies Crossplane `Configuration` resources pointing at `registry.crossplane-system.svc.cluster.local:5000/...`\n  - Supports `--skip-dependency-resolution`\n- `config install --repo \u003corg/repo\u003e [--reload]`\n  - Interactive terminals prompt for install mode: source build or published version\n  - Published-version installs suggest the latest discovered tag by default and accept custom tags such as `pr-\u003cgitsha\u003e`\n  - Non-interactive runs and `--reload` continue to use the source-build flow\n  - Source-build mode is intended for a local control plane because it depends on the local registry flow\n  - Source builds use local repo cache at `~/.hops/local/repo-cache/\u003corg\u003e/\u003crepo\u003e`\n  - Source builds clone on first use, then fetch/pull on subsequent runs\n  - Source builds run the same build/load/push/apply flow as `--path`\n- `--reload`\n  - Forces source-based config install (`--path` or `--repo` without `--version`) to delete existing `ConfigurationRevision` resources and matching `Function`/`FunctionRevision` package resources from the same sources, then re-apply the `Configuration`\n  - Useful when re-running a config and you want Crossplane to re-create the current revision from source\n- `config install --repo \u003corg/repo\u003e --version \u003ctag\u003e`\n  - Remote-package mode that can target any connected cluster\n  - Skips clone/build and applies `Configuration` with package `ghcr.io/\u003corg\u003e/\u003crepo\u003e:\u003ctag\u003e`\n  - Uses configuration name `\u003corg\u003e-\u003crepo\u003e` (for example `hops-ops-aws-auto-eks-cluster`)\n  - Does not support `--reload`\n  - Supports `--skip-dependency-resolution`\n- `config uninstall --name \u003cconfiguration-name\u003e`\n  - Deletes the target `Configuration`\n  - Waits for package lock reconciliation\n  - Prunes orphaned `Configuration`/`Function`/`Provider` packages and revisions no longer present in lock\n  - Prunes orphaned `ImageConfig` rewrites for removed render functions\n- `config uninstall --repo \u003corg/repo\u003e`\n  - Targets configuration name `\u003corg\u003e-\u003crepo\u003e`\n  - If cached repo exists at `~/.hops/local/repo-cache/\u003corg\u003e/\u003crepo\u003e`, derives source hints from it for additional package pruning\n- `config uninstall --path \u003cPATH\u003e`\n  - Derives target configuration names from `\u003cPATH\u003e/_output/*.uppkg` image tags\n  - Also derives package sources from those artifacts and prunes matching package resources (including Functions) if they remain\n- `local aws [--profile \u003cAWS_PROFILE\u003e]`\n  - Exports temporary AWS credentials with `aws configure export-credentials --format process`\n  - Uses profile resolution order: `--profile` -\u003e `AWS_PROFILE` -\u003e `AWS_DEFAULT_PROFILE` -\u003e interactive prompt\n  - If AWS SSO token is missing/expired, runs `aws sso login --profile \u003cprofile\u003e` and retries once\n  - Applies `xpkg.crossplane.io/crossplane-contrib/provider-family-aws:v2.4.0`\n  - Waits for `providerconfigs.aws.m.upbound.io` CRD to exist\n  - Applies a Secret (`aws-creds`) and AWS `ProviderConfig` (`default`) in namespace `default`\n  - `--refresh` updates only the Secret credentials and skips Provider/ProviderConfig apply\n  - Supports overrides via `--namespace`, `--secret-name`, `--provider-config-name`, `--provider-name`, and `--provider-package`\n- `local github [--owner \u003cORG_OR_USER\u003e]`\n  - Exports your current GitHub CLI token with `gh auth token`\n  - Uses owner resolution order: `--owner` -\u003e `GH_OWNER` -\u003e `GITHUB_OWNER` -\u003e interactive prompt with your authenticated `gh` login as the default\n  - If GitHub CLI is not authenticated, runs `gh auth login` and retries once\n  - Applies `xpkg.crossplane.io/crossplane-contrib/provider-upjet-github:v0.19.0`\n  - Waits for `providerconfigs.github.m.upbound.io` CRD to exist\n  - Applies a Secret (`github-creds`) and GitHub `ProviderConfig` (`default`) in namespace `default`\n  - `--refresh` updates only the Secret credentials and skips Provider/ProviderConfig apply\n  - Supports overrides via `--namespace`, `--secret-name`, `--provider-config-name`, `--provider-name`, and `--provider-package`\n- `validate generate-configuration [--path \u003cPATH\u003e] [--api-path \u003cAPIS_PATH\u003e]`\n  - Reads `\u003cPATH\u003e/upbound.yaml` and writes `\u003cAPIS_PATH\u003e/configuration.yaml`\n  - Auto-detects `--api-path` via `apis/*/definition.yaml` when omitted\n  - Ensures `apis/**/configuration.yaml` is present in `\u003cPATH\u003e/.gitignore` (unless `--no-gitignore-update`)\n- `xr observe --kind \u003cKIND\u003e --name \u003cNAME\u003e --namespace \u003cNAMESPACE\u003e --aws-region \u003cREGION\u003e`\n  - Generates an observe-only XR manifest for an existing resource\n  - Loads the live XR from the cluster when present\n  - Enriches the manifest with live AWS discovery for supported XR kinds such as `AutoEKSCluster` and `Network`\n  - Supports `--output` and `--apply`\n- `xr adopt --kind \u003cKIND\u003e --name \u003cNAME\u003e --namespace \u003cNAMESPACE\u003e`\n  - Lists managed resources that belong to the XR and renders metadata patches needed for adoption\n  - For `AutoEKSCluster`, uses the composite-specific label `hops.ops.com.ai/autoekscluster=\u003cname\u003e`\n  - Only emits patches for resources whose external name is missing or blank and can be resolved for that kind\n  - Supports `--apply`, `--output`, and `--recursive`\n- `xr manage --kind \u003cKIND\u003e --name \u003cNAME\u003e --namespace \u003cNAMESPACE\u003e`\n  - Generates the final managed XR manifest from an observed or adopted XR already in the cluster\n  - Supports `--output` and `--apply`\n- `xr orphan --kind \u003cKIND\u003e --name \u003cNAME\u003e --namespace \u003cNAMESPACE\u003e`\n  - Renders managed-resource patches that remove `Delete` from management policies\n  - Supports `--apply` and `--output`\n\n## XR workflow\n\nTypical reclaim flow:\n\n```bash\n# 1) Observe the existing resource into an XR manifest\nhops xr observe --kind AutoEKSCluster --name pat-local --namespace default --aws-region us-east-2 --output observed.yaml\n\n# 2) Apply the observe XR if desired\nkubectl apply -f observed.yaml\n\n# 3) Render and apply adoption patches for the next set of managed resources\nhops xr adopt --kind AutoEKSCluster --name pat-local --namespace default --apply\n\n# 4) Repeat adopt until no more patches are needed, or use --recursive\nhops xr adopt --kind AutoEKSCluster --name pat-local --namespace default --recursive --apply\n\n# 5) Convert the XR into a managed manifest\nhops xr manage --kind AutoEKSCluster --name pat-local --namespace default --output managed.yaml\n```\n\nNotes:\n\n- `xr adopt` only patches resources it can identify for the selected XR kind.\n- A blank `crossplane.io/external-name` is treated as missing.\n- `AutoEKSCluster` adoption currently resolves identities for supported managed kinds such as IAM attachments and KMS keys.\n\n## Logging\n\nSet `LOG_LEVEL` to control output (default: `info`):\n\n```bash\nLOG_LEVEL=debug hops local start\n```\n\n## Development\n\n```bash\ncargo test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhops-ops%2Fhops-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhops-ops%2Fhops-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhops-ops%2Fhops-cli/lists"}