Skip to content

Releases: hops-ops/hops-cli

v0.30.0

29 May 23:42

Choose a tag to compare

What's changed in v0.30.0

  • feat(local): label hops provider install resources so doctor knows they're intentional (#54) (by @patrickleet)

    hops provider install now stamps app.kubernetes.io/managed-by: hops-provider-install
    on the Provider, DeploymentRuntimeConfig, and ClusterRoleBinding it manages. A
    custom install (e.g. a forked provider built from source) uses the
    <provider>-runtime DRC convention rather than the bootstrap local-dev-<provider>
    DRC, which hops local doctor previously reported as drift.

    hops local doctor now reads that label: a labeled provider is checked against the
    install convention (<provider>-runtime DRC, <provider>-cluster-admin binding,
    SA <provider>) and reported as a "custom install" with its package, instead of
    false drift — while still verifying cluster-admin and still flagging real drift
    (runtimeConfigRef=default / missing cluster-admin) in both modes.

    Co-authored-by: Claude Opus 4.8 (1M context) noreply@anthropic.com

See full diff: v0.29.0...v0.30.0

v0.29.0

29 May 23:10

Choose a tag to compare

What's changed in v0.29.0

  • feat(local): per-provider DRCs, hops local doctor, and global --context (#53) (by @patrickleet)

    Split the shared local-dev DeploymentRuntimeConfig into per-provider DRCs
    (local-dev-kubernetes, local-dev-helm), each with its own cluster-admin
    ServiceAccount + ClusterRoleBinding, and point each provider's runtimeConfigRef
    at its own DRC. A shared DRC let one provider's runtime image/SA silently
    clobber the other's pod.

    Add hops local doctor: verifies what hops local start set up — crossplane,
    both providers (installed / healthy / runtimeConfigRef pinned to its own DRC /
    DRC present / cluster-admin binding / ProviderConfig) and the registry — and
    reports drift with a non-zero exit + remediation. Catches a provider whose
    runtimeConfigRef reverted to default, dropping its cluster-admin SA (which
    breaks observing XRs through the in-cluster ProviderConfig).

    Add a global --context flag to hops local so every subcommand can target a
    context (e.g. hops local aws --refresh --profile hops --context colima), given
    before or after the subcommand. Plumbs through HOPS_KUBE_CONTEXT_ENV like
    config/provider install.

    Co-authored-by: Claude Opus 4.8 (1M context) noreply@anthropic.com

See full diff: v0.28.0...v0.29.0

v0.28.0

25 May 08:56

Choose a tag to compare

What's changed in v0.28.0

  • feat(local): add hops local listmonk + make --source-context required (#51) (by @patrickleet)

    Mirrors hops local zitadel / hops local github. Bootstraps the
    hops-ops/provider-listmonk Crossplane provider + a cluster-scoped
    ProviderConfig pointing at a Listmonk instance via Basic-Auth
    credentials (JSON Secret).

    Credential resolution waterfall:

    1. Explicit --endpoint / --username / --token flags
    2. LISTMONK_{ENDPOINT,USERNAME,TOKEN} env vars
    3. Read from the chart-bootstrapped Secret on a source cluster
      (with keys username + token — the shape produced by
      listmonk-chart v0.2.0's post-install api-user-bootstrap hook)

    Endpoint is derived from the source Secret name when not explicitly
    set: <release>-provider-creds → in-cluster service
    http://<release>.<source-namespace>.svc.cluster.local:9000.

    Default upjet provider package: ghcr.io/hops-ops/provider-listmonk:v0.0.3.

    Also drops the pat-local default from --source-context on BOTH
    this command and hops local zitadel — that hardcoded value bakes
    the implementer's personal cluster name into a tool meant for
    multiple users. Required positional flag now; users explicitly pass
    their own source context.

    Verified end-to-end on pat-local 2026-05-25:

    • Provider install + Healthy
    • ProviderConfig applied
    • UserRole MR reconciled (Crossplane → upjet → TF provider →
      Listmonk REST API → users / roles table)
    • User MR reconciled with cross-resource userRoleIdRef → numeric
      userRoleId (typed-reference resolution works end-to-end)
    • AppSettings MR reconciled (no-op write of current values; round-
      trip lossless)

See full diff: v0.27.0...v0.28.0

v0.27.0

25 May 08:00

Choose a tag to compare

What's changed in v0.27.0

  • feat: add local zitadel provider bootstrap (#49) (by @patrickleet)

    • feat: add local zitadel provider bootstrap

    • fix: preserve zitadel domain ports

See full diff: v0.26.1...v0.27.0

v0.26.1

22 May 16:50

Choose a tag to compare

What's changed in v0.26.1

  • chore(deps): update rust crate openssl-sys to v0.9.116 (#43) (by @renovate[bot])

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

  • chore(deps): update rust crate tokio to v1.52.3 (#44) (by @renovate[bot])

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

  • chore(deps): update unbounded-tech/workflow-vnext-tag action to v1.21.3 (#45) (by @renovate[bot])

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

  • chore(deps): update unbounded-tech/workflows-rust action to v2.3.0 (#46) (by @renovate[bot])

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

  • chore(deps): update rust crate tar to v0.4.46 (#47) (by @renovate[bot])

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

  • fix(deps): update rust crate serde_json to v1.0.150 (#48) (by @renovate[bot])

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

See full diff: v0.26.0...v0.26.1

v0.26.0

17 May 03:51

Choose a tag to compare

What's changed in v0.26.0

  • feat(secrets): three-bucket list with platform-pushed section, symmetric managed-by tagging (by @patrickleet)

    hops secrets sync aws now writes hops.ops.com.ai/managed-by=sync
    alongside the existing hops.ops.com.ai/secret=true, mirroring the
    managed-by=pushsecret tag that AuthStack-style Crossplane stacks
    attach to ESO-pushed AWS SM entries. Bucketing in secrets list
    becomes a positive match instead of "secret=true minus everything
    else."

    secrets list gains a "Platform-pushed secrets" section between the
    existing "Managed secrets" and "Other AWS secrets" sections. Routing
    by hops.ops.com.ai/managed-by:

    • sync (or absent + secret=true for legacy entries) → Managed
    • pushsecret → Platform-pushed
    • everything else → Other

    Platform-pushed columns: Name | Owner | Cluster | Namespace | KMS Key |
    Status. Owner is derived from the explicit hops.ops.com.ai/kind +
    hops.ops.com.ai/name tags (renders e.g. AuthStack/pat-local); falls
    back to scanning for the older hops.ops.com.ai/<kind.lower>=<name>
    label for entries pushed before the explicit tags were added.

    has_managed_secret_tag() (used by sync --cleanup to decide which
    remote secrets to consider for deletion) was tightened to require
    either managed-by=sync OR no managed-by tag at all. Previously it
    matched any secret=true entry, which would have swept up PushSecret-
    managed entries the moment we added secret=true to that flow.

    Existing managed secrets show remote tags differ in the list output
    until the next hops secrets sync aws writes the new managed-by tag.
    No data is at risk — the migration is just additive.

    Pairs with the AuthStack PushSecret commit + the SecretStack
    push/* IAM grant.

See full diff: v0.25.1...v0.26.0

v0.25.1

15 May 19:45

Choose a tag to compare

What's changed in v0.25.1

  • fix(provider install): never reuse a Provider's existing DRC ref (by @patrickleet)

    apply_provider_resources now always names the DRC -runtime
    instead of inheriting the existing Provider's spec.runtimeConfigRef.name.
    A previous "avoid orphaning the DRC" heuristic blindly reused whatever
    DRC the upstream Provider already referenced, then overwrote that DRC
    with the new install's image — silently corrupting any other Provider
    that happened to reference the same DRC.

    Repro (the bug that surfaced this): provider-helm and provider-kubernetes
    both referenced DRC "local-dev"; a later 'hops provider install helm'
    wrote local-dev with the helm dev image, leaving provider-kubernetes
    pinned to that DRC and therefore running the helm binary in a pod
    labeled as kubernetes.

    After the fix:

    • Each install creates an owned DRC named <existing_provider>-runtime
    • If the existing Provider already pointed at a differently-named DRC,
      a log::warn surfaces the migration ("switching to owned DRC ...; the
      old DRC is not deleted") so leftovers are discoverable
    • The orphaned DRC is left in place; it may still be referenced by
      another Provider, and deletion is the operator's call

    Validated end-to-end on colima: 'hops provider install --repo
    jonasz-lasut/provider-helm' migrated provider-helm from local-dev to
    crossplane-contrib-provider-helm-runtime, helm pod healthy on
    v1.999.2, provider-kubernetes (after clearing its stale local-dev ref)
    healthy on upstream provider-kubernetes:v1.2.0.

    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

See full diff: v0.25.0...v0.25.1

v0.25.0

14 May 19:18

Choose a tag to compare

What's changed in v0.25.0

  • feat(secrets): --secret-path scope on encrypt/decrypt (by @patrickleet)

    SOPS encryption is non-deterministic — re-encrypting unchanged
    plaintexts produces different ciphertext on every run, polluting the
    git diff with files the operator didn't actually touch. hops secrets encrypt and decrypt now accept an optional --secret-path that
    scopes the traversal to a single file or subdirectory inside the
    configured source root.

    encrypt only the AuthStack durable secrets, no spurious diffs

    hops secrets encrypt --secret-path secrets/aws/pat-local/zitadel

    symmetric — decrypt one subtree for inspection

    hops secrets decrypt --secret-path secrets-encrypted/aws/pat-local

    Validates the scope resolves inside the source root before running.

    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

  • refactor(auth): bootstrap writes flat files (one shared AWS SM secret) (by @patrickleet)

    Both durable AuthStack values now live as JSON properties under a
    single AWS SM secret. Bootstrap writes flat plaintext files instead
    of single-file directories so hops secrets sync aws groups them
    into one AWS SM blob:

    secrets/aws//zitadel/masterkey
    secrets/aws//zitadel/admin-password

    → AWS SM <cluster>/zitadel:
    { "masterkey": "...", "admin-password": "..." }

    Drops the masterkey/masterkey and admin-password/password directory
    shapes — the property-name=directory-name redundancy was awkward and
    the values are seeded together / rotate together, so grouping them as
    one secret is the natural fit.

    Pairs with the AuthStack composition's collapsed externalSecrets.secretPath.

    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

See full diff: v0.24.2...v0.25.0

v0.24.2

14 May 19:01

Choose a tag to compare

What's changed in v0.24.2

  • refactor(auth): bootstrap writes plaintext, not AWS SM (by @patrickleet)

    hops auth bootstrap <cluster> now generates the durable AuthStack
    secret plaintexts into the repo's secrets/ tree, matching the AWS
    secret-path conventions the AuthStack composition's ExternalSecrets
    expect:

    secrets/aws//zitadel/masterkey/masterkey
    secrets/aws//zitadel/admin-password/password

    The platform's existing pipeline takes it from there:

    hops secrets encrypt # SOPS-encrypts into secrets-encrypted/
    hops secrets sync aws # pushes to AWS Secrets Manager

    This collapses two bootstrap pathways into one, makes the durable
    secrets reviewable in git (as SOPS-encrypted), and removes the
    direct AWS SDK dependency from this command.

    Idempotency unchanged: existing plaintexts are left alone unless
    --force.

    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

See full diff: v0.24.1...v0.24.2

v0.24.1

14 May 18:52

Choose a tag to compare

What's changed in v0.24.1

  • fix(auth): SSO-aware AWS creds + Zitadel-compliant password generator

    Two follow-ups from end-to-end testing of hops auth bootstrap:

    • Switch from a raw rusoto ChainProvider to reusing secrets::aws_clients
      so bootstrap resolves SSO profiles the same way hops secrets does
      (AWS_PROFILE → aws configure export-credentials → StaticProvider).
      Makes bootstrap work cleanly under AWS_PROFILE=hops against the
      SSO-only hops account.
    • Add a generate_complex_password that satisfies Zitadel's default
      policy (HasUppercase + HasLowercase + HasNumber + HasSymbol). The
      prior generator emitted hex (digits + lowercase only); Zitadel's
      Human user creation rejected those.
    • Pass a UUID client_request_token to both CreateSecret and
      PutSecretValue (AWS SM requires it on PutSecretValue too).
    • Unit tests cover all four character classes across 200 rolls.

    Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

See full diff: v0.24.0...v0.24.1