diff --git a/README.md b/README.md index e495bf7..192fd65 100644 --- a/README.md +++ b/README.md @@ -147,44 +147,14 @@ How it works: - Applies a GitHub `ProviderConfig` named `default` unless `--refresh` is used. - Supports overrides for namespace, Secret name, ProviderConfig name, provider name, and provider package. -## Quick Start - -```bash -# Build and load a Crossplane configuration package from an Upbound-format XRD project -hops config install --path /path/to/project - -# Build from a GitHub repo containing an Upbound-format XRD project -hops config install --repo hops-ops/helm-certmanager - -# Force reload from source (deletes existing ConfigurationRevision(s) first) -hops config install --repo hops-ops/helm-certmanager --reload - -# Apply a pinned remote package version directly (no clone/build) -hops config install --repo hops-ops/helm-certmanager --version v0.1.0 - -# Remove a configuration and prune orphaned package dependencies -hops config uninstall --repo hops-ops/helm-certmanager - -# Generate apis/*/configuration.yaml from upbound.yaml for validation -hops validate generate-configuration --path /path/to/project - -# Observe an existing XR into a manifest -hops xr observe --kind AutoEKSCluster --name pat-local --namespace default --aws-region us-east-2 - -# Render adoption patches for managed resources under an existing XR -hops xr adopt --kind AutoEKSCluster --name pat-local --namespace default - -# Convert an observed/adopted XR into a managed manifest -hops xr manage --kind AutoEKSCluster --name pat-local --namespace default - -# Render patches that remove Delete from management policies -hops xr orphan --kind AutoEKSCluster --name pat-local --namespace default -``` - ## Config packages `config install` and `config uninstall` operate on the currently connected Kubernetes cluster. -`config install` expects an Upbound-format XRD project when building from source via `--path` or `--repo`. + +There are two different `config install` modes: + +- 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`. +- 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. Common install flows: @@ -196,23 +166,26 @@ hops config install hops config install --path /path/to/project # Build from a cached GitHub repo checkout containing an Upbound-format XRD project -hops config install --repo hops-ops/helm-certmanager +hops config install --repo hops-ops/aws-auto-eks-cluster # Force a source reload before re-applying -hops config install --repo hops-ops/helm-certmanager --reload +hops config install --repo hops-ops/aws-auto-eks-cluster --reload + +# Set spec.skipDependencyResolution=true on the generated Configuration +hops config install --path /path/to/project --skip-dependency-resolution # Apply a pinned remote package directly from ghcr.io -hops config install --repo hops-ops/helm-certmanager --version v0.1.0 +hops config install --repo hops-ops/aws-auto-eks-cluster --version v0.11.0 ``` Common uninstall flows: ```bash # Remove by explicit configuration name -hops config uninstall --name hops-ops-helm-certmanager +hops config uninstall --name hops-ops-aws-auto-eks-cluster # Remove by repo slug -hops config uninstall --repo hops-ops/helm-certmanager +hops config uninstall --repo hops-ops/aws-auto-eks-cluster # Remove configurations derived from local build artifacts hops config uninstall --path /path/to/project @@ -221,6 +194,7 @@ hops config uninstall --path /path/to/project Notes: - `--reload` only applies to source installs: `--path` or `--repo` without `--version`. +- `--skip-dependency-resolution` sets `spec.skipDependencyResolution=true` on the generated `Configuration`. - `config install --repo ... --version ...` skips clone/build and applies the remote package directly. - `config uninstall --repo ...` derives the configuration name as `-`. @@ -244,11 +218,14 @@ Notes: - Prompts for confirmation, then runs `brew uninstall colima`. - `config install [--path ] [--reload]` - Targets the currently connected Kubernetes cluster + - Source-build mode intended for a local control plane because it depends on the local registry flow - Runs `up project build` in `PATH` (defaults to current directory) - Loads generated `.uppkg` artifacts from `/_output` - Pushes package images to the registry exposed at `localhost:30500` - Applies Crossplane `Configuration` resources pointing at `registry.crossplane-system.svc.cluster.local:5000/...` + - Supports `--skip-dependency-resolution` - `config install --repo [--reload]` + - Source-build mode intended for a local control plane because it depends on the local registry flow - Uses local repo cache at `~/.hops/local/repo-cache//` - Clones on first use, then fetches/pulls on subsequent runs - Runs the same build/load/push/apply flow as `--path` @@ -256,9 +233,11 @@ Notes: - 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` - Useful when re-running a config and you want Crossplane to re-create the current revision from source - `config install --repo --version ` + - Remote-package mode that can target any connected cluster - Skips clone/build and applies `Configuration` with package `ghcr.io//:` - - Uses configuration name `-` (for example `hops-ops-helm-certmanager`) + - Uses configuration name `-` (for example `hops-ops-aws-auto-eks-cluster`) - Does not support `--reload` + - Supports `--skip-dependency-resolution` - `config uninstall --name ` - Deletes the target `Configuration` - Waits for package lock reconciliation diff --git a/src/commands/config/install.rs b/src/commands/config/install.rs index 01a1927..121e733 100644 --- a/src/commands/config/install.rs +++ b/src/commands/config/install.rs @@ -41,6 +41,10 @@ pub struct ConfigArgs { /// Force reload from source by recreating ConfigurationRevision(s) before apply (path/repo only) #[arg(long, conflicts_with = "version")] pub reload: bool, + + /// Set spec.skipDependencyResolution=true on the generated Configuration + #[arg(long)] + pub skip_dependency_resolution: bool, } #[derive(Clone, Debug)] @@ -127,9 +131,15 @@ pub fn run(args: &ConfigArgs) -> Result<(), Box> { validate_reload_args(args)?; match (args.repo.as_deref(), args.version.as_deref()) { - (Some(repo), Some(version)) => apply_repo_version(repo, version), - (Some(repo), None) => run_repo_clone(repo, args.reload), - (None, _) => run_local_path(args.path.as_deref().unwrap_or("."), args.reload), + (Some(repo), Some(version)) => { + apply_repo_version(repo, version, args.skip_dependency_resolution) + } + (Some(repo), None) => run_repo_clone(repo, args.reload, args.skip_dependency_resolution), + (None, _) => run_local_path( + args.path.as_deref().unwrap_or("."), + args.reload, + args.skip_dependency_resolution, + ), } } @@ -140,10 +150,18 @@ fn validate_reload_args(args: &ConfigArgs) -> Result<(), Box> { Ok(()) } -fn run_repo_clone(repo: &str, reload: bool) -> Result<(), Box> { +fn run_repo_clone( + repo: &str, + reload: bool, + skip_dependency_resolution: bool, +) -> Result<(), Box> { let spec = parse_repo_spec(repo)?; let cache_path = ensure_cached_repo_checkout(&spec)?; - run_local_path(&cache_path.to_string_lossy(), reload) + run_local_path( + &cache_path.to_string_lossy(), + reload, + skip_dependency_resolution, + ) } fn ensure_cached_repo_checkout(spec: &RepoSpec) -> Result> { @@ -202,7 +220,11 @@ fn refresh_cached_repo(cache_path: &Path) -> Result<(), Box> { Ok(()) } -fn apply_repo_version(repo: &str, version: &str) -> Result<(), Box> { +fn apply_repo_version( + repo: &str, + version: &str, + skip_dependency_resolution: bool, +) -> Result<(), Box> { let spec = parse_repo_spec(repo)?; let version = version.trim(); if version.is_empty() { @@ -215,7 +237,12 @@ fn apply_repo_version(repo: &str, version: &str) -> Result<(), Box> { sanitize_name_component(&spec.org), sanitize_name_component(&spec.repo) ); - apply_configuration(&config_name, &package_ref, false, false) + apply_configuration( + &config_name, + &package_ref, + skip_dependency_resolution, + false, + ) } fn parse_repo_spec(repo: &str) -> Result> { @@ -261,7 +288,11 @@ fn sanitize_name_component(input: &str) -> String { } } -fn run_local_path(path: &str, reload: bool) -> Result<(), Box> { +fn run_local_path( + path: &str, + reload: bool, + skip_dependency_resolution: bool, +) -> Result<(), Box> { let dir = Path::new(path); if !dir.is_dir() { return Err(format!("{} is not a directory", path).into()); @@ -452,7 +483,7 @@ spec: for pull_ref in &config_pull_refs { let (img_path, _) = split_ref(pull_ref); let name = img_path.rsplit('/').next().unwrap_or(img_path); - apply_configuration(name, pull_ref, false, reload)?; + apply_configuration(name, pull_ref, skip_dependency_resolution, reload)?; } Ok(()) @@ -1121,6 +1152,7 @@ spec: repo: Some("hops-ops/helm-certmanager".to_string()), version: Some("v0.1.0".to_string()), reload: true, + skip_dependency_resolution: false, }; assert!(validate_reload_args(&args).is_err()); } @@ -1132,6 +1164,7 @@ spec: repo: None, version: None, reload: true, + skip_dependency_resolution: false, }; assert!(validate_reload_args(&args).is_ok()); }