Commit Graph
237 Commits
Author SHA1 Message Date
vulragrag-starandyxxhero d4a4135f9b fix: cancel kubedog-tracked helm subprocesses on SIGINT/SIGTERM (#2791)
* fix: cancel kubedog-tracked helm subprocesses on SIGINT/SIGTERM

Kubedog tracking rooted helm and tracker contexts at Background (via
traceOnlyContext), so App.Cancel never reached those subprocesses and
Ctrl+C blocked in CleanWaitGroup until helm exited on its own.

Thread the app cancel context into HelmState (SetCancelContext) and use
it for the three kubedog tracking call sites, keeping the per-release
WithCancel safety valve. Hooks stay on the non-canceling trace bridge.

Fixes #2770

Signed-off-by: Jason Wang <vulragrag@gmail.com>

* docs/test: align cancel-context docs with #2791 and pin the kubedog wiring

- traceOnlyContext comment no longer lists kubedog tracking among the
  detached paths; only hooks (#2771) remain, with a pointer to #2791.
- docs/proposals/otel-tracing.md sections 4.3/4.4/7/9 now mark the
  kubedog cancellation gap as fixed by #2791 (SetCancelContext), so the
  design doc stops misdescribing main.
- TestBufferHelmOutputRootsAtCancelContext pins the actual #2770 wiring:
  the context bufferHelmOutput hands to ContextSwapper.WithContext must
  become Done when the app cancel context is canceled, and stay
  non-cancelable when unset.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: Jason Wang <vulragrag@gmail.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: yxxhero <aiopsclub@163.com>
2026-09-15 07:35:09 +08:00
jimmyRandyxxhero bc0c103d79 feat: add HELMFILE_DISABLE_VALS env var to skip vals processing (#2471)
Signed-off-by: Jim Robinson <1643772+jimmyR@users.noreply.github.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: yxxhero <aiopsclub@163.com>
2026-09-13 15:40:12 +08:00
hoppla20andClaude Opus 5 daa43e1081 feat(remote): support wildcards in remote values/secrets file selectors (#2787)
Extend git-getter style remote references (git::, s3::, https://, ...) used
in release and environment values/secrets to support glob patterns in the
file selector, e.g.:

  git::https://github.com/org/repo.git@config/*.yaml?ref=main

Remote.Fetch already downloads the whole repository/directory and joins the
"@<file>" selector onto it verbatim, so a wildcard selector already survives
untouched; the only missing piece was that Storage.resolveFile checked the
result with FileExistsAt instead of expanding it as a glob.

- pkg/remote/remote.go: add HasGlobPattern to detect a wildcard in the file
  selector (checking only the selector, not the raw URL, so "?ref=main" and
  IPv6/placeholder brackets elsewhere are not mistaken for wildcards). Reject
  wildcards in Fetch for getter shapes that can never expand one: plain
  http(s)/s3 (single object), non-archive forced s3:: (single object), and
  any getter used without an explicit "@" selector (Dir/File cannot be
  reliably split from the pattern otherwise).
- pkg/state/storage.go: resolveFile now globs the fetched cache path with the
  same st.fs.Glob/sort.Strings used for local values-file globs when the
  selector is a pattern, filtering out directory matches. A literal, existing
  path is still resolved directly. Fixed an existing err-shadowing hazard in
  the same code path while restructuring it.
- docs/environments.md: document the new wildcard support, its syntax
  (filepath.Match, no recursive **), and its getter/selector requirements.
- Tests: new cases in pkg/remote/remote_test.go (glob detection, Fetch
  wildcard expansion and cache-key sharing, rejected getter shapes) and
  pkg/state/storage_test.go (a real end-to-end wildcard fetch against a
  pinned upstream tag, plus a hermetic fan-out/sorting/missing-file test with
  no network access).

Release values/secrets keep their existing "glob patterns ... not supported
yet" restriction for multi-file matches (pkg/state/state.go), unchanged by
this commit and applying equally to local and remote globs. helmfiles: entries
are out of scope.

Signed-off-by: Vincent Cui <privat@vincentcui.de>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-13 09:16:26 +08:00
dependabot[bot] 822fa3afef build(deps): bump gitpython from 3.1.58 to 3.1.59 in /docs (#2785)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.58 to 3.1.59.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.58...3.1.59)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.59
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-10 08:12:46 +08:00
yxxhero 16259008d5 feat: opt-in OpenTelemetry tracing and metrics (experimental) (#2769)
* feat(telemetry): add opt-in OpenTelemetry tracing (PR 1: lifecycle + root span)

Implements the first increment of docs/proposals/otel-tracing.md (#2767):

- pkg/telemetry: SDK setup from standard OTEL_* env vars (autoexport for
  exporter selection, env-driven sampler/propagators, OTEL_SDK_DISABLED),
  command-span lifecycle, no-op-by-default accessors
- --otel-tracing flag / HELMFILE_OTEL_TRACING env switch
- root span "helmfile <command>" with file/environment/selectors/exit_code
  attributes; TRACEPARENT-based remote-parent extraction for CI correlation
- shutdown flush on both normal-exit and signal paths (nil-safe, 5s bound)
- app.New derives its context from telemetry.CommandContext()
  (Background-identical when tracing is disabled)
- docs: otel.md user guide, experimental-features entry, design proposal
- tests: hermetic unit tests, app context-contract pinning, flag registration

Telemetry problems never fail a run: exporter misconfiguration and export
errors degrade to disabled with a warning. When disabled, behavior and
performance are identical to before (no-op tracer, no goroutines, no
network).

Refs: #2767, #2758
Signed-off-by: yxxhero <aiopsclub@163.com>

* feat(telemetry): trace every external process + trace-context bridges (PR 2)

Implements the second increment of docs/proposals/otel-tracing.md (#2767):

- pkg/helmexec/span.go: one span per external process started by helmfile
  (helm invocations, hooks, plugin execs) at the ShellRunner choke point —
  helm.exec (with helm.subcommand) vs os.exec, with redacted exec.args,
  exec.exit_code, and error status on failure
- pkg/helmexec/redact.go: shared argument redaction with two profiles;
  legacy is byte-identical to the historical exit-error behavior (existing
  goldens unchanged), strict (spans) additionally covers --set=k=v and
  credential flags; exit_error.go now uses the shared helper
- orphan-trace bridges with bit-identical cancellation semantics
  (context.WithoutCancel of the command context): both kubedog call sites
  (state.go) and hook execution (event.Bus gains an optional Ctx consumed
  by its default runner; state.go sets it, nil falls back to TODO as before)
- OTLP end-to-end test (in-process httptest receiver, no external
  collector): span export, error status/exit code, redaction, and
  parent-linkage to the command span
- docs/otel.md updated to the now-traced surface

Verified end-to-end with the console exporter: helmfile template on a
local chart yields the command span plus helm.exec spans for helm
version/dependency/template, all nested under it.

Refs: #2767
Signed-off-by: yxxhero <aiopsclub@163.com>

* feat(telemetry): state-loading and hook spans (PR 3a)

Implements the third increment of docs/proposals/otel-tracing.md (#2767):

- helmfile.discover_states around findDesiredStateFiles and helmfile.load
  around loadDesiredStateFromYamlWithBaseDir; both cover all callers
  (incl. nested helmfiles) with no signature changes
- helmfile.render / helmfile.parse children per document part, parented
  through a traceCtx field on the unexported desiredStateLoader struct
  (set once at its single construction site)
- helmfile.hook span per hook execution: Trigger's per-hook body extracted
  into runHook (readability win on its own), the hook's subprocess span
  nests under it via a per-hook ctx-swapped ShellRunner clone
  (cancellation unchanged — Bus.Ctx never carries cancellation by contract)
- pkg/telemetry/otlptest: shared in-process OTLP/HTTP receiver harness,
  now used by helmexec, event, and app span tests
- golden span-tree test at the app layer (root -> discover -> load ->
  render/parse, via the exectest fake helm) and a hook-span nesting test
- nil-ctx guard for App literals built directly by tests (App.spanParentCtx)

Verified end-to-end with the console exporter: a template run over a
gotmpl state file with a prepare hook yields the full tree with the hook's
os.exec nested under helmfile.hook.

Refs: #2767
Signed-off-by: yxxhero <aiopsclub@163.com>

* feat(telemetry): per-release spans nested under the load span (PR 3b)

Implements the per-release increment of docs/proposals/otel-tracing.md
(#2767) — spans nest command -> load -> release -> helm exec:

- helmexec.HelmContext gains an optional Ctx carrying the per-release span
  context; the execer's new execWithContext funnel consumes it via a
  per-call runner clone (runnerWithCtx) so the shared, cached execer is
  never mutated across concurrent workers. The seven Interface methods
  that take a HelmContext (Sync/Diff/ReleaseStatus/List/DecryptSecret/
  Delete/Test) route through it; nil Ctx behaves exactly as before.
  exec() lost its always-nil override parameter on the way (unparam).
- pkg/state/span.go: SetTraceContext + startReleaseSpan/endReleaseSpan
  helpers (release/namespace/chart/labels attributes, sorted for stable
  output); a typed-nil guard (releaseErrAsError) avoids the classic
  nil-pointer-in-interface trap on *ReleaseError.
- release spans in the worker loops: SyncReleases, DiffReleases,
  DeleteReleasesForSync, PrepareCharts, and iterateOnReleases (status/
  delete/test via a new verb parameter); their HelmContext is stamped with
  the release span context where one is built.
- pkg/app sets st.SetTraceContext(loadCtx) right after loading a state
  file, rooting all per-release spans under helmfile.load.
- bridged one more detached tracking call found on the way
  (trackReleaseIfEnabled's context.Background in the sync worker).
- golden test: release span present, nested under load, correct
  attributes; unit test for the runnerWithCtx clone semantics.

Verified with the console exporter: helmfile template yields
release.prepare(demo) under load with full attributes.

Refs: #2767
Signed-off-by: yxxhero <aiopsclub@163.com>

* feat(telemetry): nest status/delete/test execs under their release spans

Completes the per-release exec nesting for the iterateOnReleases-based
loops (docs/proposals/otel-tracing.md §4.4 phase 2): the do closures now
receive the release span context and stamp it into their HelmContext, so
helm status/delete/test subprocess spans nest under
helmfile.release.<verb> like sync/diff already did.

- scatterGatherReleases/iterateOnReleases/doWithReleaseSpan: do gains a
  context parameter (the release span context)
- ReleaseStatuses/DeleteReleases/TestReleases closures stamp
  HelmContext.Ctx from it
- integration test with a real execer (version-probe shim binary): the
  release's status subprocess nests under helmfile.release.status, same
  trace, with helm.subcommand=status

This also makes the otel.md claim ("upgrade, diff, delete, status, test
nested under the release span") fully accurate.

Refs: #2767
Signed-off-by: yxxhero <aiopsclub@163.com>

* feat(telemetry): OTel metrics — helm exec duration and release results (PR 4)

Implements the metrics increment of docs/proposals/otel-tracing.md
(#2767) on the same provider, switch, and resource as traces:

- pkg/telemetry/metrics.go: helmfile.helm.exec.duration histogram
  (subcommand, success) and helmfile.release.count counter (verb,
  result). Instruments come from the otel global meter, so recording at
  call sites is branch-free no-op when telemetry is disabled.
- Setup builds the resource once and installs both providers; reader
  selection delegates to autoexport (OTEL_METRICS_EXPORTER: otlp |
  console | prometheus | none), the OTLP reader's interval honors
  OTEL_METRIC_EXPORT_INTERVAL (read by the SDK). Shutdown flushes both
  providers (errors.Join). StartCommandSpan now carries the meter
  provider across state transitions (fixes a nil-shutdown panic).
- helmexec: finishExecSpan records exec duration for helm binaries;
  state: endReleaseSpan counts release outcomes for sync/diff/delete/
  status/test/prepare (diff counted as success when no hard error).
- otlptest: recorder routes by OTLP path (/v1/traces vs /v1/metrics)
  and decodes metrics; new FindMetric helper.
- tests: metrics recorded as no-op when disabled, provider enabled with
  the none exporter, degradation on an invalid metrics exporter, and an
  integration assertion (status exec duration datapoint + one successful
  release.count) in the shim-based state test.

Verified with the console exporter: helmfile template emits
helmfile.helm.exec.duration per subcommand (version/dependency/
template) and helmfile.release.count{verb=prepare,result=success}=1.

Refs: #2767
Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: complete OTel documentation coverage

- docs/cli.md: --otel-tracing in the CLI reference help block (verbatim
  from the cobra output)
- CHANGELOG.md: [Unreleased] Added entry for tracing + metrics
- docs/index.md: Observability highlight linking docs/otel.md
- docs/proposals/otel-tracing.md: add OTEL_METRICS_EXPORTER /
  OTEL_METRIC_EXPORT_INTERVAL rows to the env-var table and note the
  periodic reader + bounded metric cardinality in §7

Refs: #2767
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: drop unused id parameter from parsePart (unparam)

The id parameter was never used inside the span wrapper; the caller's id
variable is still used for the render calls and error messages.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix(telemetry): address review — redaction gaps, kubedog valve, phantom metrics

Addresses all Copilot review comments on #2769:

Security (span payloads):
- exec.args: positional arguments are additionally passed through
  helmexec.RedactedURL, so credentials embedded in chart/repository URLs
  (AddRepo, RegistryLogin, OCI refs) are masked exactly like log output
- release spans sanitize helmfile.chart the same way
- error statuses no longer embed raw errors (which contain rendered
  commands, arguments, and subprocess output): the command span, release
  spans, hook spans, and exec spans now use generic descriptions; the
  concrete exit code remains an attribute, and RecordError on the root
  span is dropped

Correctness:
- kubedog safety valve restored: execWithContext now attaches the
  per-release span into the runner's own context instead of replacing it,
  so trackHandle.Cancel() can interrupt a wedged helm again and app
  cancellation semantics stay exactly as before the PR
- diff release spans/metrics: real failures are recorded (exit code 2
  "changes detected" still counts as success); previously every diff was
  exported as successful
- skipped releases no longer emit phantom spans and inflate
  helmfile.release.count: iterateOnReleases callers pass a skip predicate
  (skipUndesired for status/test; delete deletes undesired releases and
  passes nil)
- Setup shuts down the already-constructed tracer provider (bounded) when
  the metrics provider fails, instead of abandoning its batch goroutine

Tests: URL redaction cases (masked/untouched), spanAttachedContext
preserves the runner cancellation chain while attaching the caller's
span, skipUndesired, and a failing-hook span asserting the generic
message.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: lint — restore nolint placement and avoid nil context literal

- the skipUndesired insertion had displaced the // nolint: unparam
  directive off iterateOnReleases (helm param is intentionally unused
  there); also fixes a skipDesired/skipUndesired comment typo
- use a typed nil in TestSpanAttachedContext (staticcheck SA1012)

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix(telemetry): address review round 2 — remote-ref redaction, wrapper helm binaries, hook release attribution

Addresses all 6 new review comments on #2769:

Security (remote references):
- new helmexec.RedactedRef sanitizes go-getter style references for
  telemetry: forced-form prefixes (git::, s3::) preserved, whole URL
  userinfo masked (usernames carry tokens too), credential-bearing query
  parameters masked using pkg/remote's heuristic (token/password/secret/
  key/signature). Applied to helmfile.file (command span), helmfile.path
  (discover_states), helmfile.chart (release spans), and exec.args —
  log-time RedactedURL is untouched so log output is unchanged

Correctness:
- wrapper helm binaries (--helm-binary custom names) are now classified
  as helm operations by an explicit context marker stamped in the execer
  funnel, instead of the executable-basename heuristic; the same
  classification gates helmfile.helm.exec.duration, so the metric no
  longer misses wrapper invocations (classifyExec)
- release-scoped hooks (presync/postsync/preuninstall/postuninstall/
  cleanup in the sync/delete/diff workers) now attach their helmfile.hook
  spans to the active helmfile.release.* span via a variadic parent on
  the trigger functions; global hooks keep the command context and all
  29 existing call sites compile unchanged; hook cancellation stays
  detached (WithoutCancel) as before
- signal-terminated runs (Shutdown with exitCode 130/143 and nil error)
  now mark the command span with error status, consistent with their
  nonzero exit code

Tests: RedactedRef table (forced forms, userinfo, s3/token query params,
untouched cases), classifyExec marker case, hookTraceContext parent
attribution + non-cancellability + fallback.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix(telemetry): address review round 3 — redaction corner cases, value runners

Addresses 5 of the 6 new review comments on #2769 (the sixth — an
unused strings import in exit_error.go — is a false positive: Indent
still uses strings.Split/Builder and the package compiles):

- RedactArgs read the previous token from the progressively redacted
  output, so {--set, --set-string, secret} leaked the secret
  (the masked value hid the following flag). Read the previous token
  from the original input, restoring the legacy contract for adjacent
  secret flags
- RedactedRef fails closed for malformed references: URL-like refs with
  invalid percent escapes export a fully redacted value, and an
  unparseable query is dropped entirely instead of exported verbatim
- ShellRunner has value receivers, so a ShellRunner VALUE satisfies the
  Runner API; the helm marker stamping and the per-release span
  attachment now handle both value and pointer forms (matching
  WithContext), so value-runner callers keep release nesting and the
  helm.exec classification/metric

Regression tests: adjacent secret flags (legacy + strict), malformed
URL-like ref, malformed query, value-runner marker + span attachment.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix(telemetry): stamp the helm marker on the stdin funnel too

execStdIn (registry login, repo add) called the runner directly, so
wrapper --helm-binary names were misclassified as os.exec and omitted
from helmfile.helm.exec.duration on that path. The marking now goes
through a shared markHelmRunner helper (value and pointer ShellRunner
forms) used by both execution funnels.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix(telemetry): redact helm's --kube-token in strict profile

Helm's global --kube-token carries a bearer token; both the
two-argument and inline forms are now masked in span exec.args
(legacy exit-error output is untouched, matching its historical
behavior).

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* fix(telemetry): OTel metrics best-practice alignment

- helmfile.helm.exec.duration now declares explicit bucket boundaries
  tuned for seconds-scale helm invocations (5ms…600s); the SDK defaults
  are millisecond-oriented and lumped every sub-5s invocation — the
  common case — into the first bucket, defeating the histogram
- instruments are re-created under the installed provider with the
  instrumentation scope version stamped (Setup-time, race-free)
- helmfile.release.count declares the {release} curly-annotation unit
  per the metrics naming conventions

Tested end-to-end via the OTLP integration test: exported bounds are
the tuned set, units are asserted, and the scope carries the version.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* feat(telemetry): per-release duration metrics behind an opt-in switch

New helmfile.release.duration histogram (seconds, same tuned buckets)
with bounded dimensions by default (verb, result). Setting
HELMFILE_OTEL_METRICS_PER_RELEASE=true adds helmfile.release and
helmfile.namespace, answering "which release is slow" from dashboards:

- well-suited to bounded CI runs; long-lived centralized collection
  needs a backend capacity/TTL story (documented in docs/otel.md)
- per-release timing remains available in traces without the flag
- env read per call (release operations are low-frequency, and tests
  toggle it)

endReleaseSpan now takes the release and the operation start time; the
five worker-loop call sites pass them (doWithReleaseSpan, SyncReleases,
DeleteReleasesForSync, PrepareCharts, DiffReleases).

Verified end-to-end with the console exporter (default dims vs
per-release) and OTLP integration tests pinning both modes.

Refs: #2767, #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor(telemetry): maintainability pass over the runner/metric plumbing

- StartCommandSpan copies the tracingState struct instead of enumerating
  fields by hand — that pattern dropped the meter provider once already
- the two value/pointer ShellRunner switches (helm marker, span
  attachment) are unified into one withRunnerCtx helper; the duplication
  caused two review rounds of value-form misses
- classifyExec derives the helm classification from the span name
  (helmExecSpanName constant) instead of returning a third parallel bool
- metrics: shared outcomeAttrs for the verb/result dimensions, and the
  bucket slice renamed to durationBuckets with a comment covering both
  histograms that use it

No behavior change; full -race suite green, lint clean.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor(telemetry): consolidate test env lists, trace bridges, and hook prep; sync the design doc

Maintainability:
- HermeticEnvVars is now exported from pkg/telemetry (the owner of the
  env surface) and used by both telemetry tests and otlptest — the two
  copies had already drifted once (HELMFILE_OTEL_METRICS_PER_RELEASE
  needed updating in both)
- kubedogTraceContext and hookTraceContext were the same concept written
  twice; unified into traceOnlyContext(parent...) in span.go

Readability:
- runHook's nested kubectl rewrite extracted into prepareKubectlHook
  with guard-clause structure

Accuracy (docs ↔ code, drifted over five review rounds):
- §4.4 now describes the implemented mechanism: the release span is
  INJECTED into the runner's own context (preserving the kubedog safety
  valve) rather than the runner context being replaced, and helm
  classification is marker-based for wrapper binaries
- §5 exec span rows list the actual attributes incl. URL/query masking
- §6 strict profile documents RedactedRef, --kube-token, and the
  adjacent-token guarantee

No behavior change; full -race suite green, lint clean.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor(telemetry): drop the dead noop state, relocate skipUndesired, sync user-facing accuracy

- tracingState.noop was dead weight in the enabled state and a
  copy-surface in every transition; a single package-level
  noopTracerProvider now backs Tracer while disabled
- skipUndesired moved next to doWithReleaseSpan in span.go, its only
  conceptual home (span/metric suppression, not run plumbing)
- accuracy: the package doc, --otel-tracing flag help,
  experimental-features entry, and CHANGELOG now all say tracing AND
  metrics and list the third instrument (helmfile.release.duration with
  the HELMFILE_OTEL_METRICS_PER_RELEASE opt-in) — these had drifted
  when the metric was added; the PR description's metric table is
  updated to match as well

No behavior change; full -race suite green (except the pre-existing
network-dependent TestStorage_resolveFile flake), lint clean.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor(telemetry): flatten Setup, name the prefix bound, dedupe test fake; fix instrument-count drift

Readability/maintainability:
- Setup drops from 56 to 39 lines: provider construction (including the
  shutdown-tracer-on-meter-failure recovery) moves to newProviders in
  exporter.go next to the constructors it composes
- refredact's magic 16 becomes maxForcedFormPrefix with a comment
- span_test's hand-rolled fakeRunner removed in favor of the existing
  mockRunner (same package)

Accuracy:
- "Two instruments" wording survived in docs/otel.md and the design
  proposal §7 after helmfile.release.duration was added; both now say
  three and mention the per-release opt-in

No behavior change; full -race suite green (except the pre-existing
network flake), lint clean.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor(telemetry): co-locate span machinery, drop a dead export, fix docs nits

- the span plumbing helpers (markHelmExec, withRunnerCtx,
  markHelmRunner, spanAttachedContext) move from exec.go to span.go,
  next to the marker type and classifiers they serve — exec.go keeps
  only the funnel call sites
- otlptest.SpanNames was never used outside the package; unexported
- isHelmBinary's comment now states it is the FALLBACK classifier
  (funnel invocations are marker-classified), replacing the outdated
  "cosmetic distinction" framing from before the marker existed
- docs/otel.md: release-scoped hooks nest under their release span
  (added in review round 2, never documented)

No behavior change; full -race suite green, lint clean.

Refs: #2769
Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-09-07 20:34:10 +08:00
c36dbfd417 fix: resolve OCI version constraints before deriving the shared chart cache path (#2768)
* fix: resolve OCI version constraints before deriving the shared chart cache path

When an OCI release uses a semver constraint (e.g. `~1`, `^2.0.0`, `*`),
`getOCIChartPath` currently derives the on-disk cache directory from the
raw constraint string via `safeVersionPath`, which substitutes constraint
characters (`~`, `^`, `>`, `<`, `!`, `|`, `=`, ` `, `,`, `*`) with `_`.
So `version: ~1` becomes `.../mychart/_1/` on disk. `acquireChartLock`
then refuses to refresh anything under the shared cache dir to avoid
race conditions between concurrent processes, so once the constraint is
first resolved and written to `_1/`, every subsequent render on that
machine (or that container replica) returns the pinned tarball
regardless of newer matching tags being published.

In multi-pod deployments like ArgoCD's argocd-repo-server this shows up
as intermittent stale renders: different pods populate their caches at
different moments and serve different snapshots of the same `~1`
release forever.

Fix: for OCI releases whose `version` looks like a constraint, run
`helm show chart <ref> --version <constraint> [flags]` and use the
returned metadata.Version as the effective version for all downstream
cache-key and path derivation. Helm already resolves the constraint
against the registry and returns the concrete matching Chart.yaml.
Callers get a content-addressable cache path (`.../mychart/1.0.1/`)
that naturally invalidates when the constraint resolves to a new
version. Exact-version releases and non-OCI releases skip the extra
call.

Adds an opt-out `resolveOCIVersions` field on `helmDefaults` and
`ReleaseSpec` (both default true). If the resolution call fails
transiently, the resolver logs a warning and falls back to the
pre-fix behavior so a network hiccup doesn't break rendering.

Adds `ShowChartWithFlags` to helmexec.Interface so the existing
`ShowChart` API stays backwards compatible.

Resolves #2766

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* refactor: move ShowChartWithFlags to a ChartInspector capability interface

Address Copilot review feedback on PR #2768: adding a method to the
exported helmexec.Interface is a source-breaking change for every
third-party implementation and mock of that interface, even though
ShowChart itself stayed backward-compatible.

Move ShowChartWithFlags off Interface and onto a new capability
interface, helmexec.ChartInspector, following the same pattern used
by the existing DependencyUpdater capability interface. The concrete
execer and the exectest.Helm test stub still satisfy it (they
already have the method); the OCI resolver in state.HelmState now
type-asserts and falls back to the pre-fix caching behavior when the
capability is absent, so downstream callers with their own
helmexec.Interface implementations keep compiling untouched.

Adds TestResolveOCIConstraintVersion_ChartInspectorFallback that
exercises the type-assertion path with a helm value that satisfies
Interface but deliberately does not satisfy ChartInspector.

Reverts ShowChartWithFlags additions from testutil.noCallHelmExec
and app_test.mockHelmExec since Interface no longer requires them.

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* fix: detect wildcard-segment semver constraints (1.x, 1.X) as constraints

Address Copilot review feedback on PR #2768: the previous
isVersionConstraint implementation scanned the input for operator
characters (~, ^, >, <, !, |, =, space, comma, *). Masterminds/semver
also accepts wildcard-segment constraints like "1.x", "1.X", "1.x.x",
and "1.2.X" that contain no operator characters. Those would slip past
the classifier, bypass OCI constraint resolution, and remain cached
forever under the raw ".../mychart/1.x/" path — the same stale-cache
bug the PR is meant to fix.

Replace the character scan with a semver-parser-based check: a value
is a constraint iff Masterminds/semver rejects it as a NewVersion but
accepts it as a NewConstraint. This correctly:

  - Recognizes wildcard forms (1.x, 1.X, 1.x.x, 1.2.x, v1.x).
  - Preserves exact versions where "x" appears in prerelease metadata
    ("1.0.0-alpha.x") or build metadata ("1.0.0+x", "1.0.0+build.x.1")
    without misclassifying them, which a naive "add x to the scanned
    charset" fix would have gotten wrong.
  - Continues to classify values that are neither a version nor a
    constraint (empty string, "latest", junk) as non-constraints; helm
    handles those elsewhere.

Removes the now-unused versionConstraintChars string constant.

Expands TestIsVersionConstraint with 8 wildcard cases and 3 prerelease
/build metadata cases containing "x", plus 2 non-parseable inputs.
Adds a "wildcard segment constraint resolves to concrete version"
subtest to TestResolveOCIConstraintVersion so the end-to-end pipeline
is exercised for a version string that has no operator characters.

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* test: add getOCIChart integration test proving cache-path/pull-flag wiring

Address Copilot review feedback on PR #2768. The existing unit test
exercised resolveOCIConstraintVersion in isolation but did not prove
that its output was propagated into the downstream cache key, cache
path, and `helm chart pull --version` flag. Add a targeted
integration test that:

  1. Calls getOCIChart with a constraint release (`~1`) and a helm
     mock whose ShowChartWithFlags returns Chart.yaml version 1.0.1.
  2. Asserts helm chart pull receives `--version 1.0.1`, not `~1`.
  3. Asserts the destination path passed to helm chart pull contains
     the resolved-version segment (`/1.0.1/`) and does NOT contain
     the raw-constraint segment (`/_1/`).
  4. Reads back the on-disk Chart.yaml under the cache path to
     confirm resolved version, path, and flag agree end to end.

Add a second test that runs the same release twice with different
resolver outputs (1.0.1, then 1.0.2 — simulating a newly published
matching tag) and asserts the two resolutions land in distinct cache
directories. This is the promise of the fix: once the raw constraint
is out of the path, a new matching tag stops silently reusing the
previously-resolved cache entry.

The integration test flushed out a real correctness gap in the
initial fix: getOCIChart resolved release.Version and chartVersion
but did NOT recompute the qualified OCI ref that
getOCIQualifiedChartName built pre-resolution. Helm was therefore
receiving `oci://<repo>/<chart>:<constraint>` alongside a
`--version <resolved>` flag — at best redundant, at worst rejected
by future Helm versions. Fixed by re-invoking
getOCIQualifiedChartName on the mutated release copy so the
embedded tag also carries the resolved value.

Isolates the shared helmfile cache via `t.Setenv(HELMFILE_CACHE_HOME,
t.TempDir())` so the OutputDirTemplate == "" code path (which writes
into remote.CacheDir) does not touch the user's real
`~/.cache/helmfile` during test runs.

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>

* refactor: flatten OCI constraint-resolution wiring in getOCIChart

Address review feedback on PR #2768:

- Extract the inline resolve/requalify block from getOCIChart into
  applyOCIConstraintResolution, keeping getOCIChart flat (guard-clause
  style) and making the resolution wiring independently testable. The
  helper returns the (possibly updated) release, qualified chart name,
  and chart version; every failure mode returns its inputs unchanged.

- On a re-qualify failure after a successful resolution, fall back to
  the pre-fix behavior entirely (raw constraint in cache key, ref, AND
  --version flag) instead of the previous half-resolved mix (resolved
  version in the cache key, raw constraint in the path and flag), which
  could desynchronize the in-process cache key from the on-disk path.

- Build the 'helm show chart' ref by reusing parseOCIChartRef instead of
  re-implementing its last-slash/last-colon tag-splitting inline. Same
  behavior for all realistic refs (registry ports preserved), and it
  also handles the digest suffix should one ever reach this point.

- Drop --devel from the resolver flags: helm documents --devel as
  ignored whenever --version is set, and --version is always passed on
  this path.

No behavior change intended beyond the requalify-failure fallback
(which cannot realistically trigger) and the removal of the inert
--devel flag.

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: classify partial semver versions (1, 1.2) as OCI constraints

Address review feedback on PR #2768: Masterminds' lenient parser accepts
partial versions like "1" or "1.2" as versions, so the previous
classifier (NewVersion fails && NewConstraint succeeds) treated them as
exact pins. But helm's OCI resolution — registry.GetTagMatchingVersionOrConstraint
— honors a version string as an exact pin ONLY when a registry tag
literally equals it; otherwise it parses the string as a constraint, and
"1"/"1.2" float across 1.x.y/1.2.y tags. Caching those under their raw
spelling reproduces the stale-cache bug of issue #2766, just with a
narrower trigger.

Replace the NewVersion probe with isFullSemver, which additionally
requires the whole major.minor.patch triple to be spelled out
(optional v prefix, prerelease, and build metadata all still count as
exact when the core is fully qualified). When a registry does carry a
literal tag equal to the version string, the resolver's
metadata.Version == chartVersion path reports no change, so literal-tag
pins keep today's behavior.

TestIsVersionConstraint: "1"/"1.0" flip to constraints, joined by new
v1.2/0/v1 cases and a 1.2.3 exact case. TestResolveOCIConstraintVersion
gains a "partial version resolves" subtest.

Docs updated to describe the parser-based classification instead of
"constraint characters".

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: skip OCI constraint resolution under skipRefresh

Address review feedback on PR #2768: the resolver ran even under
--skip-refresh, so offline and cache-only workflows gained a
'helm show chart' registry attempt per constraint-versioned OCI release.
It degraded gracefully (warn + fallback), but added registry-timeout
latency and warning noise per release.

skipOCIConstraintResolution now suppresses resolution when any of the
skipRefresh levels is set — CLI --skip-refresh (forced), per-release
skipRefresh, or helmDefaults.skipRefresh — with the same precedence the
other skipRefresh consumers in prepareChartForRelease use. Skipped runs
fall back to the constraint-keyed cache path, i.e. they reuse whatever a
previous non-skipped run resolved, which is what 'skip checking for
updates to cached charts' means for constraint versions.

The existing issue #2766 integration tests flip their opts to
SkipRefresh: false since they assert resolution happens. New coverage:
TestSkipOCIConstraintResolution (tri-state precedence table) and
TestGetOCIChart_SkipRefreshSkipsConstraintResolution (no inspector call,
raw constraint in --version and cache path).

Signed-off-by: yxxhero <aiopsclub@163.com>

* perf: memoize OCI constraint resolution per chart+constraint

Address review feedback on PR #2768: resolution ran before the
in-process chart-cache fast path and was not memoized, so every
constraint-versioned OCI release paid its own 'helm show chart' registry
round-trip on every render — including N releases sharing the same
chart+constraint, whose parallel workers could even resolve to different
versions if the registry changed between their lookups.

Memoize successful resolutions in resolvedOCIConstraints keyed by
(chart ref, constraint), mirroring the downloadedCharts pattern:

- Releases sharing a chart+constraint cost one round-trip per process
  and consistently use one resolved version per run.
- Only successful resolutions are memoized; failures may be transient.
- Flags are not part of the key: they govern TLS/verification/registry
  credentials, not which tag a constraint matches (--devel is already
  omitted as it is ignored whenever --version is set).
- Concurrent misses may both hit the registry; last write wins,
  harmlessly.

resetResolvedOCIConstraintsForTest is added alongside the existing
resetChartCacheForTest and wired into the issue #2766 tests — notably
ResolvesToDifferentVersionsPicksSeparateCachePaths, which reuses the
same chart+constraint across its two runs and would otherwise be served
the first resolution from the memo (which is exactly the intended
per-process semantics).

New coverage: TestResolveOCIConstraintVersion_Memoized (memo hit skips
the registry, different constraint is a different key) and
TestGetOCIChart_SharedConstraintResolvedOncePerProcess (two releases,
one inspector call, one pull, same path).

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: cover URL-embedded OCI constraint resolution

Address review feedback on PR #2768: the existing integration tests only
exercised the repo-aliased spelling (chart: myrepo/mychart, version:
'~1') and the version-field spelling. The chart-URL spelling
(chart: oci://<registry>/<chart>:~1) takes a different branch in
getOCIQualifiedChartName — the URL version is deliberately NOT embedded
into the qualified ref and flows through --version only — so its
re-qualification after constraint resolution (release.Version mutated to
the resolved value, versionInURL still the constraint) was untested.

TestGetOCIChart_URLEmbeddedConstraintResolves asserts the resolver
receives the URL-embedded constraint, helm chart pull receives the
resolved version via --version with a tag-less ref, and the cache path
carries the resolved version segment instead of the raw constraint.

Also gofmt-aligns the test tables added in earlier commits and drops a
redundant 1.2.3 test case that tripped goconst.

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: note empty-version OCI releases are unaffected by resolveOCIVersions

Releases with no version: at all keep their pre-existing semantics: helm
picks the latest tag at pull time and helmfile caches it under a
version-less shared-cache path. Document the limitation alongside the
other resolveOCIVersions scope notes.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: Samuel Archambault <samuel.archambault@getmaintainx.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: Samuel Archambault <samuel.archambault@getmaintainx.com>
Co-authored-by: yxxhero <aiopsclub@163.com>
2026-09-07 16:50:25 +08:00
34ead21107 feat: allow helmfile to continue on failed releases (#2616)
* feat: allow helmfile to continue on failed releases

Co-authored-by: Peter Honeder <peter.honeder@unwired.at>
Signed-off-by: Niklas Ott <niklas.ott@unwired.at>

* fix: skip failed-prep releases, complete flag wiring, add tests and docs (#64)

Review follow-ups for --allow-failed-releases (#2616):

- Track per-release chart preparation failures in PrepareCharts (returned
  as a map keyed by release) and remove those releases from the state in
  Run.WithPreparedCharts when --allow-failed-releases is set, so a failed
  release is never executed against its original, un-prepared chart
  reference (which could either fail again with a duplicate error or, for
  charts requiring chartify, bypass patches/dependency modifications and
  produce an unintended result). All failures are still reported at the
  end via the aggregated MultiError.
- Complete the release identity on error results from
  prepareChartForRelease so failures are attributed to the correct
  release.
- With --allow-failed-releases, continue building dependencies of the
  remaining charts when 'helm dep build' fails for one release, and skip
  the affected releases during execution.
- Wire --allow-failed-releases into 'helmfile unittest' and 'helmfile
  status'; remove the dead flag wiring for write-values and list (both
  never prepare charts, see commandsSkipChartPrep).
- Simplify control flow (guard clauses, errors.As, drop dead code and
  redundant else branches).
- Add end-to-end coverage in pkg/app/issue_2616_test.go and extend the
  state-level tests; document the flag in docs/cli.md and CHANGELOG.md.

Signed-off-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

---------

Signed-off-by: Niklas Ott <niklas.ott@unwired.at>
Signed-off-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
Co-authored-by: Peter Honeder <peter.honeder@unwired.at>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2026-09-03 06:26:46 +08:00
dependabot[bot] c36a9667d1 build(deps): bump gitpython from 3.1.57 to 3.1.58 in /docs (#2737)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.57 to 3.1.58.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.57...3.1.58)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.58
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-10 07:30:34 +08:00
dependabot[bot] d131ba5c33 build(deps): bump gitpython from 3.1.54 to 3.1.57 in /docs (#2730)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.54 to 3.1.57.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.54...3.1.57)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.57
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 07:05:22 +08:00
henrichter-sap 7cf4ff9a25 feat: add --skip-diff-validation-on-install CLI flag (#2728)
Signed-off-by: Richter <h.richter@sap.com>
2026-08-03 21:11:45 +08:00
yxxhero 7f748fec1b feat: support Helm 4 --rollback-on-failure alongside deprecated --atomic (#2722)
* feat: support Helm 4 --rollback-on-failure alongside deprecated --atomic (#2712)

Helm 4 renamed the `--atomic` flag to `--rollback-on-failure`
(helm/helm#13629). The old flag still works under Helm 4 but is
deprecated (prints a warning) and slated for removal in Helm 5.

Add a `rollbackOnFailure` key to both `helmDefaults` (HelmSpec) and
`releases[]` (ReleaseSpec) that emits `--rollback-on-failure`. It
requires Helm 4+ (errors otherwise) and is mutually exclusive with
`atomic`.

Additionally, when the resolved Helm binary is v4+, an existing
`atomic: true` now emits `--rollback-on-failure` instead of `--atomic`,
so users are migrated off the deprecated flag automatically without any
config change. On older Helm, `atomic: true` continues to emit
`--atomic`.

Updated the spew-based values-ID hashes in temp_test.go that change
whenever ReleaseSpec gains a field (same approach as the --force-conflicts
change in #2480).

Closes #2712.

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: add integration test for rollback-on-failure / atomic migration (#2712)

Covers the end-to-end plumbing that unit tests cannot (real helm version
detection + cluster deploy) via test/integration/run.sh:

  1. atomic: true parses, deploys a ConfigMap, and emits the version-correct
     flag: --rollback-on-failure on Helm 4 (auto-migration of the deprecated
     --atomic) and --atomic on Helm 3.
  2. rollbackOnFailure: true emits --rollback-on-failure on Helm 4 and is
     rejected with a clear Helm-4-required error on Helm 3.

Flag assertions grep the `exec: helm upgrade --install` lines logged under
--debug, matching flags as standalone tokens so the release name
"issue-2712-atomic" cannot be confused with the "--atomic" flag.

Verified locally against Helm 4.2.3: both atomic:true and
rollbackOnFailure:true emit --rollback-on-failure with no --atomic.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-07-31 22:22:31 +08:00
dependabot[bot] 48c46eda3b build(deps): bump gitpython from 3.1.52 to 3.1.54 in /docs (#2715)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.52 to 3.1.54.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.52...3.1.54)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.54
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 06:35:24 +08:00
yxxhero 7bebfab71a feat: add --repo-retries for helm repo and registry login commands (#2683)
* feat: add --repo-retries for retrying helm repo and registry login commands

Add a configurable retry mechanism for chart repository operations to
handle unstable networks (corporate proxies, slow internal registries).

Closes #1894

- New --repo-retries N flag and HELMFILE_REPO_RETRIES env var (default 0
  = opt-in, backward compatible)
- Retry applies to helm repo add, helm repo update (incl. ACR), and
  helm registry login with exponential backoff (1s, 2s, 4s, ..., capped 30s)
- Single retryRepoOp helper; per-attempt args/buffer are local to avoid
  state leaking across retries
- Tests cover succeed-after-retry, exhausted-retries, disabled-by-default,
  and regression guards for password-buffer and args-accumulation

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address PR review (overflow guard, cancellable sleep, flag-override, docs)

Address Copilot review feedback on #2683:

- Cap backoff shift exponent at 5 to prevent time.Duration overflow on
  large --repo-retries values
- Make retry sleep context-aware (sleepCtx) so Ctrl+C aborts the retry
  loop promptly via the ShellRunner context
- Log a concise exit status instead of the verbose ExitError dump, and
  clarify the retry-counter wording ('retry N/M')
- Use -1 sentinel as the CLI default so --repo-retries=0 can explicitly
  disable retries even when HELMFILE_REPO_RETRIES is set
- Align help text and docs: retry applies 'on failure' (not just
  transient errors), document the 0-disables behavior
- Add tests for overflow guard, cancellable sleep, and flag-zero-disables

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: abort retries on canceled context, hide sentinel default, align comment

Address follow-up Copilot review on #2683:

- Fix tight-loop bug: sleepCtx now returns whether it completed vs was
  interrupted by context cancellation, and retryRepoOp aborts the retry
  loop on interruption so Ctrl+C no longer spins into rapid helm calls
- Hide the -1 sentinel from --help by overriding the displayed default
  to 0 (pflag DefValue), matching the documented default while keeping
  the flag-override semantics
- Correct HelmExecOptions.RepoRetry comment: 'on failure' not 'transient
  network errors', matching the actual retry behavior
- Add Test_Retry_AbortsOnCanceledContext covering the no-tight-loop path

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: copy args per retry in RegistryLogin, make cancel test deterministic

Address follow-up Copilot review on #2683:

- RegistryLogin: pass a per-attempt copy of args to execStdIn so its
  internal append (for helm.extra) can't alias the shared slice across
  retries
- Test_Retry_AbortsOnCanceledContext: cancel the context deterministically
  inside the op closure after the first attempt, replacing the flaky
  time.Sleep(20ms) goroutine

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: return error on unknown managed repo type instead of silent skip

Address Copilot review on #2683: AddRepo logged an error for an unknown
managed type but returned nil, silently succeeding while skipping the
repo add. Now returns an error so misconfigurations fail loudly.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-07-23 18:10:04 +08:00
dependabot[bot] f64f3ec197 build(deps): bump gitpython from 3.1.50 to 3.1.52 in /docs (#2710)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.50 to 3.1.52.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.50...3.1.52)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.52
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-23 18:06:09 +08:00
Kral 09afa425d7 docs: add OCI auth troubleshooting (#2679)
Signed-off-by: yxxhero <aiopsclub@163.com>
2026-07-11 14:33:29 +08:00
Arthur Garreauandyxxhero cc85562625 feat: Add ConditionTemplate support in releaseSpec (#2669)
* feat: Add ConditionTemplate support in releaseSpec

Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

* feat: improve testing, clarify doc

Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

* feat: update CHANGELOG for ConditionTemplate support and fix test cases for ID generation

Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

* Update pkg/state/state_exec_tmpl.go

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

* Update pkg/state/state_exec_tmpl.go

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

* Update docs/configuration.md

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

* refactor: improve comments for condition checks and clean up whitespace

Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>

---------

Signed-off-by: Arthur Garreau <arthur.garreau98@gmail.com>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2026-07-01 18:44:43 +08:00
yxxhero afdb2487a6 feat: add inherits: for sub-helmfile config inheritance (#2680)
* feat: add `inherits:` for sub-helmfile config inheritance

Add an opt-in `inherits:` field to `helmfiles:` entries so a sub-helmfile
can inherit specific configuration categories from its parent:

  helmfiles:
  - path: myapp.yaml
    inherits: [repositories, environments]

Allowed values: repositories, helmDefaults, commonLabels, apiVersions,
kubeVersion, templates, environments. Child values win; parent fills gaps
(consistent with `bases:`). This directly fixes #1495, where a repository
declared in the parent was unavailable to sub-helmfiles, producing a
confusing "repo not found" error.

Implementation notes:
- The 6 pure fields (repositories, helmDefaults, commonLabels, apiVersions,
  kubeVersion, templates) are merged post-load via MergeInherited; verified
  all are consumed post-load (ExecuteTemplates/converge), never at parse.
- environments is injected pre-load as ctxEnv, because RenderedValues is
  baked at load time; the parent's resolved values become the base and the
  child's own environments: block overrides per key.
- helmDefaults uses a *HelmSpec pointer (value type is non-comparable) with
  a no-override mergo merge, so a child that omits helmDefaults inherits the
  parent's fully.
- A footgun warning (WarnUninheritedRepos) suggests
  `inherits: [repositories]` when a release references a repo the parent
  declares but the child lacks.
- Unknown inherits keys are rejected at parse time with the allowed set.

Inheritance is opt-in and fully backward compatible: empty (the default)
preserves the historical independent-sub-helmfile behavior.

Fixes #1495

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address review — deep-copy inherited config and fix bases: doc link

- BuildInheritedConfig now deep-copies the pure fields via a YAML round-trip
  (Env via environment.DeepCopy) so the returned config never aliases the
  parent state's slices/maps, matching its doc comment. Now returns an error
  to surface round-trip failures; the call site in processNestedHelmfiles is
  updated. Added TestBuildInheritedConfig_PureFieldsAreDeepCopied to lock
  in the no-aliasing guarantee.
- Fix the broken `bases:` anchor (#) in shared-configuration-across-teams.md
  to point to writing-helmfile.md#layering-state-files.

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address review — reject inherits without path and document helmDefaults caveat

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address review — make AllowedInherits immutable and clarify effective-repo wording

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-07-01 14:44:16 +08:00
yxxhero e3f757d5ed feat: add --template-args flag to template/apply/sync for helm lookup() support (#2666)
* feat: add --template-args to enable helm lookup() during template/apply/sync (#1833)

Add a --template-args flag to the template, apply, and sync subcommands so
extra args (most notably --dry-run=server) can be passed to the helm template
invocation, enabling Helm's lookup() function to resolve live cluster values.

- template: --template-args reaches both chartify's pre-render helm template
  and the final helm template output (flagsForTemplate).
- apply/sync: --template-args reaches chartify's pre-render helm template.
  apply/sync already inject --dry-run=server automatically for cluster
  operations; the flag is an explicit opt-in for the template subcommand or
  for passing additional flags.
- When --dry-run is present in template args, kube-context/kubeconfig are also
  injected into chartify so lookup() can actually reach the cluster.
- Resolves the long-stale PR #1833 rebased onto current main, which already
  contains the cluster-connectivity infrastructure (issues #2271, #2309,
  #2355, #2444).
- Includes integration test (lookup.sh) covering both chartify and
  non-chartify scenarios.

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: make lookup template nil-safe to fix integration CI

The lookup() function returns an empty map when the chart is rendered
without a cluster connection (notably the helm-diff phase of `helmfile
apply`). The original fixture chained `index` over the lookup result,
panicking with "index of untyped nil" during apply's diff rendering.

Guard every index with `default dict` so the template falls back to
"overwritten" when lookup is empty, while still resolving to the live
value ("init") when cluster access is available (--dry-run=server via
--template-args, or a real helm upgrade).

Signed-off-by: yxxhero <aiopsclub@163.com>

* feat: enable lookup() during apply/diff via --template-args in helm-diff

Thread --template-args into the helm-diff rendering path so that
`helmfile apply`/`diff --template-args="--dry-run=server"` resolves
Helm's lookup() function during the diff phase too. helm-diff supports
`--dry-run=server`, which explicitly "enables the cluster access ...
and the lookup template function".

Previously --template-args only reached chartify's pre-render (which is a
no-op for plain charts due to chartify's early-return when there is no
forceNamespace/patches/injections) and the final `helm template` of the
`template` subcommand. As a result `helmfile apply` on a lookup chart
rendered client-side during the diff phase.

Changes:
- pkg/state: add TemplateArgs to DiffOpts; append it in appendExtraDiffFlags
  (reaches every helm-diff invocation: apply, standalone diff, interactive
  sync), mirroring the existing flagsForTemplate handling.
- pkg/config + cmd: add --template-args to the diff/doctor commands and to
  DiffConfigProvider, so lookup works for `helmfile diff` as well.
- pkg/app: populate DiffOpts.TemplateArgs from apply/diff/sync-interactive.
- docs/cli.md: correct the previous overpromising wording and document diff
  support plus the nil-safe lookup guidance.
- tests: unit-test the TemplateArgs handling in appendExtraDiffFlags and
  flagsForTemplate; integration lookup.sh now exercises apply with
  --template-args="--dry-run=server".

Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor: de-duplicate chartify template-args logic, add helmDefaults.templateArgs

Address review feedback on #2666:

1. Eliminate stale duplicated test helpers (issue_2444_test.go, issue_2355_test.go).
   Both files intentionally copied the processChartification flag-building logic
   with explicit SYNC WARNING comments, then drifted out of sync when #2666
   refactored the production code (needsKubeConnection gate, user-args merge).
   Extract the real logic into pure, unit-tested helpers
   (buildChartifyTemplateArgs, commandRequiresCluster) and delete the copies.

2. Add unit coverage for the new chartify merge path: template +
   --template-args=--dry-run=server now triggers kubeconfig/kube-context
   injection (TestTemplateArgsDryRunTriggersKubeInjection,
   TestTemplateArgsMergedBeforeInjection) — previously only covered by the
   cluster-dependent integration test.

3. Add a negative integration case (lookup.sh assert_template_fallback)
   verifying lookup() falls back to the default value WITHOUT --template-args,
   guarding against a regression that silently always connects to the cluster.

4. Add helmDefaults.templateArgs for parity with diffArgs/syncArgs, so users
   can enable lookup() support permanently instead of passing the flag on every
   invocation. CLI --template-args overrides (does not merge with) the default.
   Resolved via effectiveTemplateArgs, wired into the chartify, flagsForTemplate,
   and appendExtraDiffFlags paths.

5. Minor: capitalize --template-args help text to match surrounding flags;
   document helmDefaults.templateArgs precedence in docs/cli.md.

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: cover helmDefaults->chartify composition; fix helm helm-diff typo

Address remaining review nits on #2666:

- Add TestHelmDefaultsTemplateArgsReachesChartify, a belt-and-suspenders test
  for the processChartification composition (effectiveTemplateArgs ->
  buildChartifyTemplateArgs), closing the last unit-level coverage gap for
  helmDefaults.templateArgs reaching the chartify path.

- Fix pre-existing typo in cmd/bind_diff_flags.go: 'pass args to helm helm-diff'
  -> 'Pass args to helm-diff' (doubled 'helm', lowercase).

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: correct 'helm helm-diff' typo in apply --diff-args help text

Sibling of the bind_diff_flags.go fix; the same doubled-'helm' typo and
lowercase help existed in cmd/apply.go's --diff-args registration, leaving
the apply and diff/doctor help strings inconsistent.

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: add helmDefaults.templateArgs to configuration reference

The complete helmfile.yaml schema in docs/configuration.md documents
diffArgs and syncArgs under helmDefaults but was missing the new
templateArgs field added in #2666. Add it beside syncArgs for
discoverability, noting the --template-args CLI override.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-06-28 12:19:33 +08:00
yxxhero 9b943adc9e feat: add helmfile doctor command for AI-assisted diff analysis (#2660)
* feat: add `helmfile doctor` command for AI-assisted diff analysis

`helmfile doctor` runs `helmfile diff` and asks an OpenAI-compatible LLM to
summarize the changes and flag risks (data loss, security exposure, breaking
changes, downtime, performance, best-practice issues).

Key design decisions:
- When no LLM is configured, doctor is equivalent to `helmfile diff` with
  one exception: --show-secrets is always forced off (secrets never reach
  stdout, even without an LLM).
- Secrets are ALWAYS redacted via two layers: (1) ShowSecrets() forced to
  false so helm-diff emits <REDACTED> placeholders; (2) a defense-in-depth
  text redactor strips residual secret-looking content (Secret YAML blocks,
  sensitive key/value lines, base64 blobs, JWT tokens) before LLM transmission.
- LLM configuration precedence: env (HELMFILE_LLM_*) < helmfile.yaml (llm:)
  < CLI flags (--llm-*).
- Supports any OpenAI-compatible backend (OpenAI, Azure, One-API, LiteLLM,
  Ollama, etc.) with automatic response_format fallback for backends that
  don't support JSON mode.
- Prompt injection defense: release names and environment values are
  JSON-encoded before insertion into the LLM prompt.
- Exit codes: 0 (success/low-risk), 2 (high-risk gate, bypass with --force),
  1 (other errors). Helm-diff's 'detected changes' exit-2 is swallowed.

New packages:
- pkg/agent/llm: OpenAI-compatible client with JSON response parsing, mock
  client for testing, prompt builder with injection defense.
- pkg/agent/doctor: secret redactor (state machine + regex), report renderer
  (markdown + JSON), config resolver (env < yaml < flag merge).

Testing: 70+ unit tests covering redaction patterns, prompt injection,
response_format fallback, JSON parsing, yaml roundtrip, concurrency safety,
panic recovery, and error propagation. go test -race passes.

Documentation: full doctor section in docs/cli.md, llm: block reference in
docs/configuration.md, updated skills/helmfile for AI agents.

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: fix doctor equivalence wording per PR review

Per review feedback (PR #2660): the docs claimed doctor is 'equivalent to
helmfile diff — same flags, same output, same exit codes' in the unconfigured
path, but this over-promises because:

  1. doctor --output is the report format (not helm-diff's output format)
  2. helm-diff's --output is exposed as --diff-output in doctor
  3. --show-secrets is silently ignored

Updated all three locations (cli.md, cmd/doctor.go Long + godoc, pkg/app/doctor.go
godoc) to say 'falls back to helmfile diff with --show-secrets forced off' and
explicitly note the --output / --diff-output flag difference.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-06-22 16:52:35 +08:00
Fritz Reichwald 61a534c990 Small documentation indentation fixes (#2655)
* fix(indentation): don't indent codeblocks as readthedocs fails in rendering indented codeblocks

Signed-off-by: Fritz Reichwald <reichwald@b1-systems.de>

* fix(indentation): fix alignment in flow chart

Signed-off-by: Fritz Reichwald <reichwald@b1-systems.de>

---------

Signed-off-by: Fritz Reichwald <reichwald@b1-systems.de>
2026-06-19 17:02:30 +08:00
SEONGHYUN HONG 32492beb18 docs: fix duplicated word in hcl_funcs log description (#2647)
Signed-off-by: s3onghyun <s3onghyun.hong@gmail.com>
2026-06-18 15:16:05 +08:00
yxxhero a94fdf4194 feat: add support for helm 4 --server-side upgrade flag (#2641)
* feat: add support for helm 4 --server-side upgrade flag

Add support for the helm 4 upgrade flag --server-side which accepts
"true", "false", or "auto" (default "auto"). This allows users to
explicitly control server-side apply behavior, which is needed for
releases originally installed with Helm 3 and being managed with Helm 4.

The flag can be configured via:
- CLI: --server-side flag on sync, apply, and diff commands
- helmDefaults.serverSide in helmfile.yaml
- releases[].serverSide per-release override

Precedence: release-level > CLI flag > helmDefaults.
Errors are returned when serverSide is set but running Helm 3, or when
an invalid value is provided.

Closes #2640

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: update TestGenerateID expected hashes for new ServerSide field

Adding ServerSide *string to ReleaseSpec changes spew's %#v output and
shifts the FNV hash used by generateValuesID. Update the hard-coded want
values to the new deterministic hashes.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-06-16 08:24:47 +08:00
Dominik Schmidt 33eadc993e feat: support HELMFILE_* env vars for more global flags (#2606)
* feat: support more HELMFILE_* env vars as flag fallbacks

Adds env-var fallbacks for global flags, mirroring the existing
HELMFILE_ENVIRONMENT / HELMFILE_KUBE_CONTEXT pattern:

* --helm-binary       -> HELMFILE_HELM_BINARY
* --kustomize-binary  -> HELMFILE_KUSTOMIZE_BINARY
* --log-level         -> HELMFILE_LOG_LEVEL
* --debug             -> HELMFILE_DEBUG       (expecting "true" lower case)
* --quiet             -> HELMFILE_QUIET       (expecting "true" lower case)
* --no-color          -> HELMFILE_NO_COLOR    (expecting "true" lower case),
                         additionally honors NO_COLOR per no-color.org
                         (any non-empty value disables color)

Flag values still take precedence; env vars are consulted only when the
flag is unset. The string-flag default values ("helm", "kustomize",
"info") move into the accessor methods so the env-var fallback can
actually trigger when no flag is passed.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* docs: mention new HELMFILE_* env vars in cli.md and templating.md

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* fix: make Color/NoColor/env interaction consistent

Two issues with the env-aware NoColor() introduced together with
HELMFILE_NO_COLOR / NO_COLOR support:

1. Color() consulted the raw GlobalOptions.NoColor field instead of
   NoColor(), so in a TTY with only the env set, Color() fell through
   to terminal autodetect and ValidateConfig() spuriously errored with
   "--color and --no-color cannot be specified at the same time".

2. NoColor() returned true via env even when --color was explicitly
   passed, so `helmfile --color` with NO_COLOR (or HELMFILE_NO_COLOR=true)
   in the environment hit the same ValidateConfig() error. A flag should
   always win over an env var.

Fix both by routing Color() through NoColor() and giving NoColor() an
explicit --color short-circuit. Regression tests added for both paths.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

---------

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>
2026-05-22 09:16:52 +08:00
yxxheroandcopilot-swe-agent[bot] 781d28a47a feat: add defaultInherit for automatic release template inheritance (#2600)
* feat: add defaultInherit for automatic release template inheritance

Add a top-level defaultInherit field to helmfile.yaml that automatically
applies template inheritance to all releases without requiring explicit
inherit on each release.

The field accepts a single template name as a string or a list of
template names. Releases that already explicitly inherit from the same
template are not duplicated.

Fixes #2599

Signed-off-by: yxxhero <aiopsclub@163.com>

* style: fix gci formatting in app_template_test.go

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: correct relative chart path in integration test

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: use absolute chart path in bad-helmfile test

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: use clean chart path in bad-helmfile test

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: use dir variable for chart path

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: fix flaky defaultInherit integration assertions

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d0884e8e-8b1b-456d-8250-dec1566b8a37

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: tighten defaultInherit integration assertions

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d0884e8e-8b1b-456d-8250-dec1566b8a37

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: harden release block parsing in issue-2599 case

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d0884e8e-8b1b-456d-8250-dec1566b8a37

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: make issue-2599 assertions format-tolerant

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d0884e8e-8b1b-456d-8250-dec1566b8a37

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: fix section extraction and regex matching in issue-2599 case

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/d0884e8e-8b1b-456d-8250-dec1566b8a37

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: sanitize defaultInherit values and dedupe applied templates

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/85a8e815-3701-4b48-a28d-6bb2d50a3b40

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* chore: address validation feedback on defaultInherit fixes

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/85a8e815-3701-4b48-a28d-6bb2d50a3b40

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: sanitize releaseInherit entries in applyDefaultInherit; add cleanup trap and quote vars in integration test

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/1fbf62d5-7ce2-42e5-898b-30151c0c1ef9

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* refactor: combine releaseInherit loops in applyDefaultInherit to avoid double TrimSpace; clarify test comment

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/1fbf62d5-7ce2-42e5-898b-30151c0c1ef9

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: align default inherit tests with yaml wrapper and assertions

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/3ea9b8e4-633f-43c4-899f-e063ec576486

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: address review feedback on defaultInherit tests

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/3ea9b8e4-633f-43c4-899f-e063ec576486

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* test: fix issue-2599 integration script helmfile invocation

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/9452bb65-7086-459f-b5ae-0b00c1e021eb

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-20 18:21:03 +08:00
Dominik Schmidt 31ac918512 feat: support HELMFILE_NAMESPACE env var for default namespace (#2592)
* feat: support HELMFILE_NAMESPACE env var for default namespace

Mirrors the existing HELMFILE_ENVIRONMENT pattern: the --namespace
CLI flag takes precedence, falling back to HELMFILE_NAMESPACE when
unset.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* docs: mention HELMFILE_NAMESPACE in cli.md and templating.md

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

---------

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>
2026-05-19 21:43:11 +08:00
Dominik Schmidt c15cbb096a feat: support HELMFILE_KUBE_CONTEXT env var for default kube context (#2593)
* feat: support HELMFILE_KUBE_CONTEXT env var for default kube context

Mirrors the existing HELMFILE_ENVIRONMENT pattern: the --kube-context
CLI flag takes precedence, falling back to HELMFILE_KUBE_CONTEXT when
unset.

Refs #1213.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* docs: mention HELMFILE_KUBE_CONTEXT in cli.md and templating.md

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

---------

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>
2026-05-19 20:43:28 +08:00
dependabot[bot] 9e6ed57015 build(deps): bump gitpython from 3.1.49 to 3.1.50 in /docs (#2585)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.49 to 3.1.50.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.49...3.1.50)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.50
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 20:46:32 +08:00
dependabot[bot] 86066cbf98 build(deps): bump gitpython from 3.1.47 to 3.1.49 in /docs (#2582)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.47 to 3.1.49.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.47...3.1.49)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.49
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-08 08:11:41 +08:00
Dominik Schmidt 0139304d97 feat(state): add mergeStrategy: fallback for first-file-wins env values (#2578)
* feat(state): add mergeStrategy field to EnvironmentSpec

Introduces a per-environment mergeStrategy with valid values "override"
(default, current behavior) and "fallback". This commit only adds the
field, the constants, and a parse-time validator; the loader still
ignores the value, so behavior is unchanged.

Subsequent commits thread the value through the values loader and
implement the fallback semantics.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* refactor(state): thread mergeStrategy through values loader

Adds a mergeStrategy string parameter to LoadEnvironmentValues,
loadValuesEntries, and mapMerge so the value can flow from
EnvironmentSpec down to the merge call site. Behavior is unchanged in
this commit; mapMerge ignores the strategy and the next commit
implements the fallback semantics.

Top-level state.DefaultValues and the --state-values-file/-set loaders
are passed an empty strategy ("") since they have no per-environment
spec to consult and stay on the default override behavior.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* feat(state): implement fallback merge strategy

Adds a hand-rolled fallbackDeepMerge that, unlike mergo, preserves
keys present in the destination even when their value is the zero
value (false, 0, "", nil, empty list/map). mapMerge dispatches to it
when mergeStrategy == "fallback"; "override" and the empty default
keep using mergo with WithOverride so existing behaviour is unchanged.

Validation lives at the entry of LoadEnvironmentValues so a single
chokepoint guards the field. Invalid values produce an error naming
both the offending value and the valid options.

Tests cover: first-file-wins precedence, gap filling, deep nested
merge, three-file chains, explicit zero-value preservation (the case
naïve mergo gets wrong), explicit nil preservation, inline map
entries, override regression, default-equals-override equivalence,
and invalid-strategy errors.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* feat(state): expose prior-file values in fallback template context

Under mergeStrategy: fallback, .gotmpl values files can now reference
values from earlier files in the same `values:` list via .Values
(e.g. `service.domain: "service.{{ .Values.cluster.domain }}"`).

The accumulated result is layered under env.GetMergedValues so env
defaults, env values, and CLI overrides still win on overlap. Override
mode keeps the historical template context — unchanged — so this is
strictly opt-in via the mergeStrategy field.

Together with the precedence flip from the previous commit, this lets
users replace the brittle two-stage `merged-values.yaml.gotmpl`
workaround with native helmfile syntax.

Tests cover the headline cross-file template reference case and pin
the override-mode contract that prior-file values stay invisible.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* docs: document mergeStrategy and fallback semantics

Adds a new section to values-and-merging.md describing the override vs
fallback strategies, the explicit-zero-value preservation guarantee,
and the cross-file template reference behavior. Adds a brief pointer
to environments.md so users land on the new field from the
environment values discussion.

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

* refactor(state): reuse maputil.MergeMaps for fallback merge

Replaces the hand-rolled fallbackDeepMerge with a single call to
maputil.MergeMaps, swapping its arguments so the accumulated dest wins
over the new src file. Same first-file-wins semantic, fewer lines, and
the fallback path now inherits the same slice merge strategies the
rest of helmfile already uses.

The one observable behavior shift is for explicit nil values: under
fallback, nil in an earlier file no longer 'wins' over a non-nil value
in a later file — instead it falls through (matching MergeMaps' rule
that nil from the override side only fills missing keys). This is
internally consistent: nil-overwrites is an mergo.WithOverride quirk
that lives only in the override path. The renamed test
NilFallsThroughToFallback pins the new behavior with a comment
referencing the contrast with override mode (Issue1154).

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>

---------

Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>
2026-05-07 21:50:05 +08:00
yxxhero 7cc5fe0358 docs: deduplicate Technical Details sections in values-and-merging.md (#2575)
Signed-off-by: yxxhero <aiopsclub@163.com>
2026-05-03 20:59:03 +08:00
yxxhero e703b15075 docs: restructure documentation and improve newcomer experience (#2573)
* feat: add --write-output flag to helmfile fetch for air-gapped environments

Add --write-output flag to helmfile fetch that outputs a modified
helmfile.yaml with chart references updated to point to downloaded
local chart paths. Combined with --output-dir, this enables preparing
all charts for deployment in air-gapped environments.

Usage:
  helmfile fetch --output-dir ./charts --write-output > helmfile-airgapped.yaml

Fixes #2571

Signed-off-by: yxxhero <yxxhero@users.noreply.github.com>
Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: restructure documentation and improve newcomer experience

Split the monolithic index.md (1990 lines) into focused topic pages,
update mkdocs.yml navigation, and add missing documentation for
undocumented code features.

Structure changes:
- Extract configuration.md (helmfile.yaml reference)
- Extract cli.md (CLI commands and flags)
- Extract templating.md (template syntax and env vars)
- Extract environments.md (environment configuration)
- Extract releases.md (DAG, needs, selectors)
- Extract hooks.md (lifecycle hooks)
- Extract integrations.md (ArgoCD, Azure ACR, OCI)
- Slim index.md to ~270 line landing page with step-by-step tutorial

Newcomer improvements:
- Add 5-step Getting Started tutorial with explanations
- Reorganize nav: Getting Started now shows core learning path
  (Writing Helmfile → Values → Environments → Releases)
- Add Quick Reference table to configuration.md
- Simplify writing-helmfile.md title

Code-vs-docs gap fixes:
- Document 23 undocumented release fields (valuesTemplate,
  setTemplate, forceNamespace, adopt, trackMode, etc.)
- Document 6 undocumented helmDefaults fields (enableDNS,
  forceConflicts, skipRefresh, takeOwnership, etc.)
- Document print-env command and missing CLI flags
- Document kubectlApply hook field
- Document environment defaults field and merge order
- Document kubedogQPS/kubedogBurst advanced settings
- Document template partials (_*.tpl) auto-loading

Cleanup:
- Fix Docker image version from v0.156.0 to v1.1.0
- Fix heading nesting in advanced-features.md
- Update experimental-features.md with current features
- Fix broken cross-references and anchor links

Signed-off-by: yxxhero <aiopsclub@163.com>

* Revert changes to pkg/app from docs/restructure-and-improve branch

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: add create subcommand to README and CLI reference

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <yxxhero@users.noreply.github.com>
Signed-off-by: yxxhero <aiopsclub@163.com>
2026-05-03 19:33:33 +08:00
dependabot[bot] b50c9d6256 build(deps): bump gitpython from 3.1.41 to 3.1.47 in /docs (#2555)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.41 to 3.1.47.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.41...3.1.47)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.47
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-26 08:58:12 +08:00
yxxheroandcopilot-swe-agent[bot] 5368ab8d95 fix: skip subhelmfiles when selectors conflict with CLI selectors (#2545)
* fix: skip subhelmfiles when selectors conflict with CLI selectors (#2544)

When CLI selectors are provided (e.g. -l name=b), subhelmfiles whose
explicit selectors are provably incompatible are now skipped entirely,
avoiding unnecessary YAML loading and template rendering.

Two selector sets are incompatible when every pair has a positive label
conflict: same key with different values (e.g. name=b vs name=a).
Negative labels are not compared.

Fixes #2544

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address PR review comments - use CLI selectors, fix doc comment, add malformed selector test

Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/1f1c33ce-e50d-4781-85b8-d606b5d4ca54

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* fix: add debug logging, unit tests, docs, and fix integration test for subhelmfile selector skip

- Add debug log when skipping subhelmfile due to selector conflict
- Add TestSubhelmfileSelectorsConflict with 11 cases for direct unit coverage
- Document the selector-based subhelmfile skip optimization in docs/index.md
- Fix integration test: use 'app' label key instead of reserved 'name' key
  (GetReleasesWithLabels overwrites labels["name"] with the release name)

Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor: avoid map allocation in positiveLabelsCompatibleWith

Compare positive label slices directly instead of allocating a map per
comparison, as label counts are typically small (1-3 entries).

Addresses Copilot review comment on PR #2545.

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: clarify subhelmfile selector docs per Copilot review feedback

Reword the first two bullets to avoid the contradiction between
'CLI selectors are ignored' and the new skip optimization.

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address Copilot review comments round 3

- Log parse errors from SelectorsAreCompatible at debug level instead of
  silently discarding them
- Hoist regex compilation to package-level vars in ParseLabels to avoid
  repeated compilation per selector
- Replace EXIT traps with explicit cleanup calls in integration test to
  avoid interfering with the parent runner's trap

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-04-25 17:18:39 +08:00
yxxhero 472e8c7a2d fix: error on missing secret key when using vals (#2496)
* fix: error on missing secret key when using vals

Add HELMFILE_VALS_FAIL_ON_MISSING_KEY_IN_MAP environment variable
to control whether vals should fail when a referenced key does not
exist in the secret map.

Previously, when a secret reference like ref+vault://path#/nonexistent-key
pointed to a non-existent key, vals would silently return an empty string
without error. This could lead to deployments with missing configuration.

Default behavior remains backward compatible (returns empty string).
Set HELMFILE_VALS_FAIL_ON_MISSING_KEY_IN_MAP=true to enable strict mode.

Fixes #1563

Signed-off-by: yxxhero <aiopsclub@163.com>

* refactor: extract buildValsOptions helper and improve tests

- Extract buildValsOptions() to make vals configuration testable
- Use t.Setenv instead of manual env save/restore in tests
- Test actual vals.Options output including FailOnMissingKeyInMap

Addresses PR review comments on #2496

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: use strconv.ParseBool and make tests hermetic

- Use strconv.ParseBool for FailOnMissingKeyInMap parsing to support
  common boolean values like 'TRUE', '1', '0', etc.
- Always set env vars explicitly in tests (even to empty string) to
  prevent flaky tests when env vars are set externally
- Add test cases for various boolean formats

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: add documentation for vals-related environment variables

Add documentation for:
- HELMFILE_AWS_SDK_LOG_LEVEL: configure AWS SDK logging for vals
- HELMFILE_VALS_FAIL_ON_MISSING_KEY_IN_MAP: enable strict mode for secret refs

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: improve error handling and case-insensitive comparison

- buildValsOptions now returns error for invalid boolean values
  instead of silently defaulting to false
- Use strings.EqualFold for case-insensitive 'off' comparison
  to handle OFF, Off, etc.
- Add test cases for invalid boolean and uppercase OFF
- Update docs to mention case-insensitive and error behavior

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: normalize log level and improve singleton initialization

- Normalize AWS log level 'off' to lowercase for true case-insensitivity
- Replace sync.Once with mutex to allow recovery from config errors
- Update tests to expect normalized 'off' value
- Update docs to clarify when error is raised

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-03-24 09:42:54 +08:00
Thomas Arrowandyxxhero 06d0994b84 Fix interactive apply asks in no change situation (#945)
This commit makes the apply logic exit early in the event there are no
changes to releases. I believe this effectively reverts helmfile#522.

Updates relevant snapshots

Clarify conditions under which preapply hooks are triggered to include that they will no longer fire if there is a no-op.
Docs as requested by the maintainer from a copilot request made by them.

Fixes: helmfile#679

Signed-off-by: Thomas Arrow <thomas.arrow@wikimedia.de>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2026-03-19 08:45:42 +08:00
yxxhero 26646ebd31 docs: add comprehensive values merging and data flow guide (#2461)
Add new documentation explaining how Helmfile merges values from various sources:
- Core architecture and data flow
- Values sources and precedence order
- Deep merge behavior for maps and arrays
- Environment-specific value handling
- Secret management and priorities
- Common patterns and troubleshooting

This guide helps users understand the foundational concepts needed for
writing effective helmfiles, especially regarding value overrides and
merge strategies.

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-03-09 07:34:05 +08:00
yxxheroandCopilot c6e7249eb9 feat: add helm-legacy track mode for Helm v4 compatibility (#2466)
Add support for trackMode: helm-legacy to use Helm v4's --wait=legacy flag,
which maintains compatibility with Helm v3's wait behavior during migration.

Helm v4 changed the default --wait behavior from polling to a watcher-based
approach. This can cause issues with charts that have broken livenessProbe
configurations without startupProbe. The --wait=legacy flag preserves the
Helm v3 polling behavior for smoother migration.

Changes:
- Add TrackModeHelmLegacy constant in pkg/kubedog/options.go
- Use kubedog.TrackMode constants instead of raw strings in helmx.go
- Enhance appendWaitFlags to use --wait=legacy for Helm v4 when trackMode
  is helm-legacy
- Add nil check for logger before logging warning
- Add version check with warning when helm-legacy is used with Helm v3
- Update validation in pkg/config to accept helm-legacy track mode
- Update command-line flags in cmd/apply.go and cmd/sync.go
- Add comprehensive documentation in docs/advanced-features.md
- Add thorough test coverage including warning message verification

Behavior:
- Helm v4 + helm-legacy: Uses --wait=legacy
- Helm v3 + helm-legacy: Falls back to --wait with warning
- Helm v4 + helm: Uses --wait (watcher mode)
- Any + kubedog: Skips --wait flag

Fixes #2464

Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: Copilot <copilot@github.com>
2026-03-08 11:51:14 +08:00
dependabot[bot] 6a4c7fae87 build(deps): bump markdown from 3.6 to 3.8.1 in /docs (#2463)
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.6 to 3.8.1.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.6...3.8.1)

---
updated-dependencies:
- dependency-name: markdown
  dependency-version: 3.8.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-06 11:24:32 +08:00
yxxhero 6e21671228 feat: kubedog integration with unified resource handling (#2383)
* feat: add kubedog-based resource tracking integration

Add kubedog tracking as an alternative to Helm's --wait flag with:
- Real-time deployment progress tracking
- Container log streaming
- Fine-grained resource filtering (trackKinds/skipKinds/trackResources)

Features:
- New pkg/resource package for unified manifest parsing and filtering
- New pkg/kubedog package wrapping kubedog library
- CLI flags: --track-mode, --track-timeout, --track-logs
- Helmfile YAML support for trackMode, trackTimeout, trackLogs, trackKinds, skipKinds, trackResources
- Case-insensitive kind matching for filtering
- Multi-context support with proper kubeconfig/kubeContext handling

Tracking supports: Deployment, StatefulSet, DaemonSet, Job

Resource filtering priority (highest to lowest):
1. trackResources - explicit resource whitelist
2. skipKinds - blacklist specific kinds
3. trackKinds - whitelist specific kinds

Integration:
- Disable Helm --wait when using kubedog tracking
- Track after successful Helm sync/apply
- Respect release.Namespace as fallback for resources without namespace
- Use getKubeContext() for correct cluster targeting

Tests:
- Unit tests for resource filtering and kubedog options
- Integration test with httpbin chart
- E2E snapshot tests for YAML serialization
- Documentation in docs/advanced-features.md

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address PR #2383 review comments (round 4)

1. resource/filter.go: Skip empty whitelist entries in matchWhitelist
   - At least one field (kind/name/namespace) must be specified
   - Prevents matching all resources with empty TrackResources entries

2. config/apply.go: Add ValidateConfig for track-mode validation
   - Validate --track-mode must be 'helm' or 'kubedog'
   - Reject invalid values like --track-mode foo

3. config/sync.go: Add ValidateConfig for track-mode validation
   - Same validation as apply command
   - Ensures consistent behavior across commands

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-03-02 17:15:12 +08:00
Oleh Neichev 3537f5f5c4 feat: Add IP Network to supported HCL Functions (#2426)
* Add IP Network to supported HCL Functions

This patch adds CIDR functions from the `go-cty-funcs` package to
supported HCL functions

Signed-off-by: Oleh Neichev <oleg.neichev@gmail.com>

* Test HCL CIDR Functions

Signed-off-by: Oleh Neichev <oleg.neichev@gmail.com>

---------

Signed-off-by: Oleh Neichev <oleg.neichev@gmail.com>
2026-02-26 17:20:48 +08:00
Benjamin Zoresandyxxhero fad470f38a feat: allow for HCL values override (#2402)
* feat: allow for HCL values override

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* fix: ensure overriden HCL expression uses range from latest defined block vars

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: implement HCL cty values override tests

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* docs: better describe new behavior

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* fix: add extra parenthesis for better readability

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: implement variable override in decodeGraph() function, AFTER interpolation, providing back access to hv.* and local.* accessors

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: implement better HCL test to override values using local.* and hv.* accessors and pre-processing function calls

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: remove deprecated hclParseError() function (and test)

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: don't let HCL override with null value win

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: proper test condition on HCL map type merge (and tests)

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: more accurate HCL test error statement

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: ensure HCL DAG graph collects dependencies from ALL definitions to ensure proper evaluation order even if only earlier definitions have dependencies

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: verify HCL mixed-types merges are correctly supported

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* docs: improved environment values precedence section with HCL override support

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: HCL test spell-check, linter failure

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: implement HCL override e2e tests

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* fix: correct hcl_loader test error message

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* fix: ensure correct cty type is returned in case of object/map hcl merge

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* fix: ensure hcl locals from a previous definition/file do not leak into this evaluation when merging

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* fix: correct e2e hcl_override test; missing line in output string comparison

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* docs: spell-check on HCL doc

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

* chore: update comment for accuracy in HCL read routine

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>

---------

Signed-off-by: Benjamin Zores <benjamin.zores@gmail.com>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2026-02-24 18:02:20 +08:00
Aditya Menon c63947483c fix: eliminate os.Chdir in sequential helmfiles to fix relative path resolution (#2410)
* fix: eliminate os.Chdir in sequential helmfiles to fix relative path resolution

The sequential code path used within() → os.Chdir() to change the
process-wide working directory when processing helmfile.d files.
This broke relative environment variable paths (e.g. KUBECONFIG=kubeconfig.yaml)
because they resolved from the wrong directory after chdir.

Replace the chdir-based approach with the same baseDir parameter pattern
used by the parallel code path, passing explicit directory context through
loadDesiredStateFromYamlWithBaseDir() instead of mutating global process state.

Closes #2409

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* fix: restore within() for single-file sequential to preserve chart path format

The previous approach used baseDir for all sequential processing, which
changed chart path format in output (e.g. from "../../../../charts/raw"
to "test/integration/charts/raw"). This broke integration tests that
compare chart paths in expected output.

Now the sequential branch uses two strategies:
- Single file: use os.Chdir via within() to preserve backward-compatible
  relative chart paths in output
- Multiple files with --sequential-helmfiles: use baseDir parameter to
  avoid os.Chdir, fixing relative env var paths like KUBECONFIG (#2409)

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* fix: revert e2e snapshot outputs to match within() behavior

The previous commit restored within() for single-file sequential
processing, which produces relative chart paths (e.g. ../../charts/raw)
and filename-only FilePath. Revert the e2e snapshot expected outputs
to match main branch since single-file behavior is now identical.

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* fix: restructure integration test for multi-file sequential processing

- Point -f at helmfile.d/ directly (not parent dir) so findDesiredStateFiles
  discovers the yaml files
- Add second helmfile to trigger baseDir path (len > 1)
- Inline environment config to avoid base file relative path issues
- Verify both releases appear in output instead of comparing with parallel
  (which may differ in ordering)

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* fix: reduce cognitive complexity and improve accuracy of sequential helmfiles

Replace inline visitSubHelmfiles closure with calls to the existing
processNestedHelmfiles() method, matching the parallel path. This
eliminates duplicated nested logic and reduces gocognit complexity
below the CI threshold of 110. Also fixes help text and docs to
accurately describe that single-file processing still uses within(),
and adds kubeContext verification to the integration test.

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

* test: validate kubeContext resolution in sequential helmfiles integration test

Restructure the integration test to replicate the exact user scenario
from issue #2409:
  - Multiple files in helmfile.d/ using bases: with relative paths
    (../bases/) for environments and defaults
  - Environment values set kubeContext via .Environment.Values
  - helmDefaults.kubeContext rendered from gotmpl
  - Local chart references (../../../../charts/raw) from helmfile.d/
  - Run diff against the minikube cluster to exercise kubeContext
    resolution, which would fail with "context does not exist" if
    os.Chdir() broke relative path resolution
  - Also verify template output for both releases and relative values
    file (values/common.yaml) resolution

Fix normalizeChart() in util.go to be idempotent — skip re-prefixing
when the chart path already starts with basePath. This prevents
double-prefixing of local chart paths (e.g. helmfile.d/test/.../raw)
when normalizeChart is called multiple times (once during chart
preparation and again during diff/sync).

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>

---------

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
2026-02-22 09:21:46 +08:00
Aditya Menon 0129681222 feat: add helmfile unittest command for helm-unittest integration (#2400)
Adds a new `helmfile unittest` command that integrates the helm-unittest
plugin, allowing users to define unit test paths per release and run them
via helmfile.

Closes #2376

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
2026-02-16 09:45:10 +08:00
yxxhero 58df057dcc fix: skip cache refresh for shared cache paths to prevent race conditions (#2396)
* fix: skip cache refresh for shared cache paths to prevent race conditions

When multiple helmfile processes run in parallel (e.g., as ArgoCD plugin),
they share the same OCI chart cache in ~/.cache/helmfile. One process could
delete and re-download (refresh) a cached chart while another process was
still using it, causing "path not found" errors.

This fix:
- Adds isSharedCachePath() helper to detect shared cache paths
- Skips chart deletion/refresh for paths in the shared cache directory
- Users can force refresh by running `helmfile cache cleanup` first

Fixes #2387

Signed-off-by: yxxhero <aiopsclub@163.com>

* docs: document OCI chart caching behavior and multi-process safety

Add documentation for:
- OCI chart cache location and behavior
- How to force cache refresh with `helmfile cache cleanup`
- Multi-process safety when using shared cache
- Cache management commands (info, cleanup)

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address review comments for shared cache handling

- Return error instead of chartActionDownload for corrupted shared cache
- Change refresh skip log from Debugf to Infof for user visibility
- Add t.Helper() to createTestLogger test helper

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: handle symlinks and add debug logging in isSharedCachePath

- Use filepath.EvalSymlinks to resolve symlinks before path comparison
- Add debug logging when filepath.Abs fails
- Add test case for symlink to shared cache directory

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: address copilot review comments

- Include underlying error in corrupted cache error message
- Add cleanup for test directories created in shared cache
- Clarify --skip-refresh flag documentation

Signed-off-by: yxxhero <aiopsclub@163.com>

* fix: handle edge case when chartPath equals sharedCacheDir

- isSharedCachePath now returns true for exact match with cache dir
- Add test case for exact match with shared cache directory

Signed-off-by: yxxhero <aiopsclub@163.com>

* test: add integration test for acquireChartLock shared cache behavior

Add TestAcquireChartLockSharedCacheSkipRefresh to verify that
acquireChartLock returns chartActionUseCached instead of
chartActionRefresh when the chart exists in the shared cache,
even when refresh is requested. This tests the core fix for
the race condition issue #2387.

Signed-off-by: yxxhero <aiopsclub@163.com>

---------

Signed-off-by: yxxhero <aiopsclub@163.com>
2026-02-14 10:46:05 +08:00
Copilotandyxxhero 5b7cbe54e9 Add --sequential-helmfiles flag for optional sequential processing (#2350)
* Initial plan

* Fix helmfile.d parallel processing to respect alphabetical order

Changed multiple helmfile.d file processing from parallel to sequential
to match the documented behavior. Files are now processed in alphabetical
order as stated in the documentation, ensuring predictable deployment order.

This fixes issues where deployments would break because parallel processing
did not respect the intended ordering (e.g., databases before applications).

- Modified visitStatesWithContext to process files sequentially
- Removed unused processStateFileParallel function
- Updated test snapshots to reflect deterministic ordering

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* Add --sequential-helmfiles flag for opt-in sequential processing

Based on feedback, added a flag to control processing mode instead of
forcing sequential behavior. Parallel processing is now the default for
performance, with an opt-in --sequential-helmfiles flag for when order matters.

Changes:
- Added SequentialHelmfiles field to GlobalOptions and App
- Added --sequential-helmfiles CLI flag
- Modified visitStatesWithContext to check flag and choose parallel vs sequential
- Updated documentation to clarify parallel is default, sequential is opt-in
- Restored original parallel processing as default behavior

This gives users control over the behavior based on their needs.

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* Add documentation and improve code readability

- Added documentation to processStateFileParallel function
- Extracted shouldProcessInParallel variable for clarity
- All tests pass, security scan clean

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* Fix lint issue: remove trailing whitespace

Removed trailing whitespace from line 983 in pkg/app/app.go to fix
formatting lint error.

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2026-01-08 14:28:25 -05:00
f3b19fd81e Add parameter to render helmfile as go template without .gotmpl extension (#2312)
* Add parameter to render helmfile as go template without gotmpl extension

Signed-off-by: Ronaldo <ronaldo.ur@gmail.com>

* Update pkg/envvar/const.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Signed-off-by: Ronaldo <ronaldo.ur@gmail.com>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-09 14:41:47 +09:00
Aditya Menon 4f275b3667 feat: add Helm 4 support while maintaining Helm 3 compatibility (#2262)
This commit adds comprehensive support for Helm 4 while maintaining
full backward compatibility with Helm 3. The implementation includes:

- Updated helm version detection to support both Helm 3 and Helm 4
- Added HELMFILE_HELM4 environment variable to control Helm version
- Modified helm execution paths to handle version-specific binaries
- Updated helm plugin installation to support split architecture

- Helm 4: Uses split plugin architecture (3 separate .tgz files)
  - helm-secrets.tgz
  - helm-secrets-getter.tgz
  - helm-secrets-post-renderer.tgz
- Helm 3: Continues using single plugin installation
- Updated Dockerfiles, CI workflows, and core installation code

- Helm 4 requires post-renderers to be plugins, not executable scripts
- Created Helm plugin structure for integration tests
- Updated helmfile.yaml templates to dynamically select renderer type
- Added test plugins: add-cm, add-cm1, add-cm2

- Updated integration tests for Helm 3/4 compatibility
- Created Helm 4 variant expected output files
- Fixed test determinism issues (repo cleanup between iterations)
- Added version-specific output filtering for warnings/messages

- Updated workflows to test both Helm 3 and Helm 4
- Matrix testing across Helm versions
- Updated helm-diff to v3.14.0 for compatibility

- Updated README and docs with Helm 4 information
- Added migration guidance
- Updated version requirements

All changes are backward compatible - existing Helm 3 users will
see no behavior changes.



fix: update Helm 4 lint expected output to match filtered output

The grep filter removes the semver warning, so the expected output
should not include it. Updated lint-helm4 files to match the filtered
output (warning removed, no extra blank line).

Signed-off-by: Aditya Menon <amenon@canarytechnologies.com>
2025-11-19 07:49:30 +08:00
Simon Bouchard a6fab4dc75 feat: update strategy for reinstall (#2019)
* feat: Add updateStrategy option in the state file with 'reinstall'/'reinstallIfForbidden' choices to uninstall and apply the specific release(s) (if forbidden to update)

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Fix unit tests related to the new updateStrategy feature

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Fix unit tests related to the new updateStrategy feature

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Resolve linter issue due to cognitive complexity

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Updated index.md to describe the possible values of updateStrategy

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Add validation of updateStrategy parameter and unit test

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Updated unit test

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Removed 'reinstall' update strategy option to only have reinstallIfForbidden, cleanup of pre-sync changes, adapted unit tests

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Display affected releases that were reinstalled

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Make sure to add --wait when deleting a release to be reinstalled due to reinstallIfForbidden

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

* Apply suggestions from Copilot code review

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>

---------

Signed-off-by: Simon Bouchard <sbouchard@rbbn.com>
2025-10-29 08:47:46 +08:00
Copilotandyxxhero 3728b6f647 Remove deprecated --wait-retries flag support to fix Helm compatibility error (#2179)
* Remove --wait-retries flag support and update documentation

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* Fix unused helm parameter in appendWaitFlags function

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2025-09-11 13:57:28 +08:00
Copilotandyxxhero 135ff63aa3 Add missing --timeout flag to helmfile sync command with documentation (#2148)
* Initial plan

* Implement --timeout flag for helmfile sync command

- Add Timeout field to SyncOptions struct in pkg/config/sync.go
- Add --timeout flag to sync command in cmd/sync.go
- Add Timeout field to SyncOpts struct in pkg/state/state.go
- Modify timeoutFlags() function to prioritize CLI timeout over release and default configs
- Add test case to verify CLI timeout overrides other timeout settings
- Follow same pattern as existing --wait and --wait-for-jobs flags

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* Fix lint issues: format test struct fields properly

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

* Update docs: Add --timeout flag documentation for helmfile sync command

Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
2025-08-27 14:32:02 +08:00