mirror of
https://github.com/helmfile/helmfile.git
synced 2026-09-30 14:47:44 +02:00
ad81e4231e40fea9798ccc6dc93e2c0363e95e7d
515
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ad81e4231e |
build: update Go to 1.27.1 and modernize the codebase (#2798)
Toolchain and CI:
- Bump go directive from 1.26.8 to 1.27.1 (go.mod)
- Use golang:1.27-alpine builder images in all Dockerfiles
- Bump golangci-lint to v2.13.2 (first release with go1.27 support)
- Add CI gate: `go fix -diff` fails when outdated Go patterns are
detected (locally: `make check-modernize`)
Note: darwin binaries now require macOS 13 or later.
Lint fixes required by golangci-lint v2.13.2:
- goconst: ignore tests (all 436 findings were test-only; goconst
got stricter since v2.12 and this option was added for it)
- openai.go: keep deprecated MaxTokens deliberately with a nolint
rationale (max_tokens is the only form universally supported by
OpenAI-compatible backends like One-API, LiteLLM, Ollama shim)
- state.go: drop always-nil flags param from appendChartVersionFlags
(renamed to chartVersionFlags, unparam)
Modernization (go fix ./..., 62 files):
- interface{} -> any, maps.Copy, strings.SplitSeq, range-over-int,
builtin min/max, slices.Contains/ContainsFunc/Sort, WaitGroup.Go,
reflect.Type.Fields(), new(expr)
- exit_error.go: strings.Builder + fmt.Fprintf instead of string
concatenation and WriteString(fmt.Sprintf(...)) (QF1012)
- chart_dependency.go: strings.CutLast for OCI dependency helpers
Signed-off-by: yxxhero <aiopsclub@163.com>
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
6034a54e0d |
refactor: drop empty-render workaround now that chartify handles it natively (#2747)
Bump github.com/helmfile/chartify to v0.28.2, which fixes the empty-render crash upstream (helmfile/chartify#206, fixed in helmfile/chartify#207): when a chart renders zero resources, chartify now treats it as a no-op success itself - removing the chart's content dirs, cleaning Chart.yaml dependencies and the lock file, and skipping the kustomize step - instead of failing with: assertion failed: unexpected dir entry "" it must be the abs path to the output directory That makes the helmfile-side string-matching workaround from #2724 dead code: the error it matched can no longer be produced. Remove isChartifyEmptyRenderOutputError and the special-cased no-op branch in processChartification, so the empty-render case simply flows through the regular chartify path. Also bump github.com/helmfile/vals to v0.46.0 and refresh transitive dependencies. The regression test for #1757 is updated to assert the new direct behavior: processChartification succeeds, returns a chartified chart whose Chart.yaml no longer declares dependencies (so a subsequent "helm dep build"/"helm template" cannot fail with "found in Chart.yaml, but missing in charts/ directory"), renders empty output, and is cleaned up by CleanupChartifyTempDirs. Fixes #1757 (follow-up to #2724) Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
2cb878d5a0 |
fix(#2741): prefetch shared remote charts instead of serializing sync (#2743)
* fix(state): prefetch shared remote charts instead of serializing sync/diff (#2741) PR #2662 fixed a Windows chart-download race (#768) by wrapping the entire helm upgrade/diff operation in a per-chart+version lock, not just the download. Since sync/apply/diff never set ForceDownload, releases sharing a remote chart end up fully serialized even at high --concurrency. Add ChartPrepareOptions.PrefetchSharedRemoteCharts: PrepareCharts groups selected releases by chart+version, and force-downloads (once) any chart used by 2+ releases that also resolve to identical acquisition flags (--verify/--keyring/--plain-http/--insecure-skip-tls-verify/--devel) and to a configured repository (or OCI ref) - not a bare \"dir/chart\"-shaped local path. That materializes release.ChartPath, which lets withChartOperationLock's existing ChartPath != \"\" guard skip the lock, restoring concurrency without touching the #768 protection for charts that aren't prefetched. Also add chartFetchFlags to give forcedDownloadChart's \`helm fetch\` the same verify/keyring/TLS flags flagsForUpgrade already applies, closing a parity gap that predates this change (affects lint/unittest/pull too). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Thomas Hanser <gh@toms.place> * fix(state): exclude verify-enabled charts from shared-chart prefetch, use NUL-delimited flag signature Copilot review on #2741's PR flagged two issues in the shared-chart prefetch added there: 1. forcedDownloadChart untars a shared chart into a local directory, but flagsForUpgrade unconditionally re-adds --verify for the later `helm upgrade` regardless of ChartPath. Helm's VerifyChart only accepts a packaged .tgz/provenance pair, not an unpacked directory, so upgrading a prefetched chart with verify enabled would fail. Exclude --verify from prefetch eligibility entirely rather than trying to suppress the later flag - those releases just keep the pre-existing serialized-lock behavior, unaffected by this feature. 2. The per-key flag-agreement signature joined flags with a space, which isn't injective: a keyring path containing a space and a flag-like token could collide with a different keyring plus a real flag, silently deduplicating releases with different acquisition settings. Join with NUL instead, which can't appear in an OS argument. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Thomas Hanser <gh@toms.place> * fix(state): apply -chart override before shared-chart grouping Copilot flagged that PrepareCharts grouped releases by release.Chart before prepareChartForRelease applied st.OverrideChart (the -chart CLI flag), so distinct original charts that all resolve to the same overridden chart were never recognized as shared and missed the prefetch. Apply the override once upfront, before the grouping loop reads release.Chart. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Thomas Hanser <gh@toms.place> --------- Signed-off-by: Thomas Hanser <gh@toms.place> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7cf4ff9a25 |
feat: add --skip-diff-validation-on-install CLI flag (#2728)
Signed-off-by: Richter <h.richter@sap.com> |
||
|
|
1341fd8659 |
fix: skip chartification when chart renders zero resources (#2724)
* fix: skip chartification when chart renders zero resources
When a chart's templates render zero resources (e.g. everything is
gated behind a falsy `{{- if .Values.enabled }}`) and the release has
transformers, jsonPatches, or strategicMergePatches configured,
helmfile crashed with:
assertion failed: unexpected dir entry "" it must be the abs path
to the output directory
Root cause: chartify's replace.go runs `helm template --output-dir`
and expects exactly one directory entry under that output dir (the
rendered chart). When helm renders no resources, the output dir is
empty, so chartOutputDir stays "" and chartify's own assertion on it
being an absolute path fails. chartify (v0.28.0) doesn't expose a
typed/sentinel error for this, only the assertion text.
Since there's nothing to chartify when a release has no rendered
resources, treat this specific chartify failure as a no-op: keep
using the chart as-is and let helm template it normally (producing
the same empty output helm would have produced without chartify).
Any other chartify error is still surfaced unchanged.
Verified manually end-to-end with a real helm+kustomize:
- a chart with `enabled: false` + a transformer now runs without
error instead of crashing
- a chart with `enabled: true` + the same transformer still gets
transformed correctly (annotations applied), confirming the normal
chartify path is untouched
Added unit tests for the new error-matching helper in
pkg/state/issue_1757_test.go.
Fixes #1757
Signed-off-by: ankit090701 <ankitanku090701@gmail.com>
* fix: return original chart path from empty-render no-op, not the deps-rewrite temp copy
Review feedback on the original fix (#2724) found a real bug: the
empty-render no-op path returned chartPath after it may have already
been reassigned to the temp copy created by rewriteChartDependencies
(for charts with relative file:// deps). That temp dir is removed by
a deferred cleanupTempChart() as soon as processChartification
returns, so the caller was handed a path to a directory that no
longer existed, breaking any subsequent helm command with "chart not
found" - narrow (only local charts with relative file:// deps that
also render zero resources) but real and reproducible.
Capture originalChartPath before the rewrite and return that instead.
Also flatten the nested `if err != nil { if isChartifyEmptyRenderOutputError...`
into two sequential checks per review, and link the upstream tracking
issue (helmfile/chartify#206, opened by a maintainer during review) in
the error-matching constant's doc comment.
Added TestProcessChartification_EmptyRenderReturnsSurvivingPath, an
end-to-end test exercising the real processChartification ->
chartify.Chartify wiring (not just the isChartifyEmptyRenderOutputError
helper) with a chart that has a relative file:// dependency and renders
zero resources - the exact conditions that trigger the bug. Verified
passing against real helm+kustomize in a Linux container; skipped on
Windows due to an unrelated, pre-existing Windows path-handling issue
in chartify's dependency resolution (a drive letter embedded in a
file:// URL gets mis-joined), independent of the code path under test.
Signed-off-by: ankit090701 <ankitanku090701@gmail.com>
* fix: narrow chartify empty-render error match to avoid false positives
Per Copilot's automated review on this PR: the previous substring,
"it must be the abs path to the output directory", matches chartify's
assertion regardless of what chartOutputDir actually is. In the real
empty-render case chartOutputDir is "" (formatted via %q as `""`), but
the same assertion (chartify replace.go:151) would also fire if
chartOutputDir were ever a non-empty-but-still-relative path - a
different, genuine bug that should be surfaced as an error, not
silently treated as an empty-render no-op.
Narrow the match to include the `unexpected dir entry ""` prefix, so
it can only match the exact empty-string case. Verified against the
actual chartify v0.28.0 source (fmt.Errorf with %q on chartOutputDir)
that this is precisely what the empty-render case produces.
Added a test case asserting the same assertion text with a non-empty
dir entry is correctly NOT treated as the empty-render case. Re-ran
the full test suite (including the real helm+kustomize end-to-end
integration test) in a Linux container to confirm the narrower match
still catches the actual reported bug.
Signed-off-by: ankit090701 <ankitanku090701@gmail.com>
---------
Signed-off-by: ankit090701 <ankitanku090701@gmail.com>
|
||
|
|
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> |
||
|
|
43aafeaad1 |
fix: ensure OCI registry login when SkipRepos is set (#2701)
* fix: ensure OCI registry login when SkipRepos is set (#1847) Commands like build, status, list, and show-dag set SkipRepos: true to avoid slow helm repo add/update for classic repos. However, this also skipped helm registry login for OCI registries, causing 401 Unauthorized errors when pulling OCI charts. Add a variadic SyncOption parameter (backward compatible) with WithOCIOnly() that limits repo processing to OCI registries only. When skipRepos is true, callers now pass WithOCIOnly() so that OCI authentication still happens before chart pulls. Signed-off-by: yxxhero <aiopsclub@163.com> * fix: reword OCI login comments per review feedback RegistryLogin is a no-op when credentials are not configured, so the word 'always' was misleading. Clarify that login is only needed when credentials are present. Signed-off-by: yxxhero <aiopsclub@163.com> * fix: skip OCI login for commands that don't pull charts Commands like 'list' and 'write-values' skip chart preparation entirely, so OCI registry login is unnecessary for them. Extract the skip-command list into a shared variable and use it to gate OCI-only login in WithPreparedCharts. Signed-off-by: yxxhero <aiopsclub@163.com> * docs: clarify commandsSkipChartPrep comment per review feedback Clarify that these commands only skip OCI login when skipRepos is true; when skipRepos is false, SyncReposOnce still runs normally for all repos. Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
a9e4a8518a |
feat: center section divider to match table width (#2698)
The '========== Updated Releases ==========' header was a fixed 38 chars regardless of the table width below it. Now the divider line is extended to match the table's visual width, with the title text centered within the '=' borders. - Add TableVisualWidth() to measure table width via runewidth - Add HeaderDividerCentered() and HeaderDividerCenteredStyled() for centered dividers with optional bold+blue ANSI styling - Refactor DisplayAffectedReleases to build the table first, then compute its width before logging the header - Update all test snapshots and integration test output files Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
bbf925f154 |
test: add integration test for issue #1880 transformers with file:// deps (#2673)
* test: add integration test for issue #1880 transformers with file:// deps Add an integration test reproducing the exact scenario from issue #1880: a local chart with a relative file:// dependency (file://../library) used together with kustomize transformers. Before the fix (rewriteChartDependencies in PR #2334), chartify copied the chart to a temp directory, breaking the relative file:// path and causing helm dependency up to fail with: Error: directory /tmp/chartify.../monitoring/library not found Also fix test artifact leak in issue923_test.go where OCI chart downloads wrote to CWD because OutputDirTemplate lacked {{ .OutputDir }}. Closes #1880 Signed-off-by: yxxhero <aiopsclub@163.com> * fix(test): guard exit-code captures against set -e in integration tests Under set -e (enabled in run.sh), a failing command exits the shell before 'var=$?' can execute, defeating diagnostic cat+fail blocks and breaking helm diff tests that expect exit code 2. Replaced 'cmd; var=$?' with 'var=0; cmd || var=$?' across 12 test files (29 sites), matching the pattern already used in oci-parallel-pull.sh. Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
8f1bb404d6 |
fix: support go-getter URLs in ad-hoc dependencies to fix #821 (#2670)
* fix: support go-getter URLs in ad-hoc dependencies to fix #821 Ad-hoc release dependencies (release.dependencies[].chart) that used go-getter URLs like "git::https://host/repo.git@path?ref=tag" were passed to chartify as-is. chartify then tried to resolve them via `helm repo list`, which fails with "no helm list entry found for repository \"git::https:\". please `helm repo add` it!". The primary chart already fetched such URLs via downloadChartWithGoGetter, but the ad-hoc dependency path in PrepareChartify only handled local directories and OCI rewrites, so go-getter URLs fell through. This adds a branch that detects remote go-getter URLs (remote.IsRemote) and fetches them to a local cache directory via a new downloadAdhocDepChartWithGoGetter helper, mirroring the primary-chart fetch path. chartify then sees a local chart and takes its file:// branch. Fixes #821 Signed-off-by: yxxhero <aiopsclub@163.com> * test: add integration test for go-getter ad-hoc dependencies (#821) Adds an integration test that commits a chart to a throwaway local git repo and references it via a "git::file://..." go-getter URL as a release ad-hoc dependency, then asserts `helmfile template` renders both the main chart and the fetched dependency. Using file:// (rather than https://) keeps the test deterministic and network-free while exercising the exact fix path (remote.IsRemote + downloadAdhocDepChartWithGoGetter in PrepareChartify). Verified to fail on the unfixed code with the original "no helm list entry found for repository \"git::file:\"" error and pass on the fixed code. Signed-off-by: yxxhero <aiopsclub@163.com> * test: surface helmfile error in issue #821 integration test The integration runner (run.sh) enables `set -e`, so a non-zero helmfile exit aborted the script before the captured output could be printed, hiding the real failure in CI. Disable `set -e` around the helmfile invocation and report the exit code plus full output on failure. Signed-off-by: yxxhero <aiopsclub@163.com> * test: fix issue-821 integration test chart path and errexit handling Two issues caused the integration test to fail in CI (while passing in my local smoke test, which used an absolute path): 1. The main chart path was relative to the integration CWD, but the helmfile.yaml is generated in a temp directory and helmfile resolves `chart:` relative to that directory (its basePath). The relative path was interpreted as a named-repo chart and failed instantly with `Error: repo test not found`. Resolve the case dir to an absolute path with `$(cd ... && pwd)`. 2. run.sh runs under `set -e`, so the unguarded helmfile invocation exited the whole script on failure before the captured output could be printed, hiding the real error. Capture the exit code via `cmd || rc=$?` so failures are reported. Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
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> |
||
|
|
0cf330611e |
fix: clean up chartify temp directories after helm operations (#2668)
fix: clean up chartify temp directories after helm operations (#1799) Chartify creates temporary output directories (e.g. /tmp/chartify<random>/) during chart preparation for commands like build, template, and diff. These directories were never tracked for cleanup, causing disk space to accumulate over time with thousands of orphaned chartify* folders. The existing clean() closure in PrepareChartify only removed generated values files, not the chartify output directory itself. The chartified chart must survive until all helm operations complete, so it could not be removed during chart preparation. This change: - Adds chartifyTempDirTracker to HelmState (pointer-based to avoid copy-lock issues from HelmState being copied in several places) - Tracks chartify output dirs via addChartifyTempDir() after c.Chartify() succeeds in processChartification - Cleans them up via CleanupChartifyTempDirs() in WithPreparedCharts after all helm operations complete - Also removes empty parent temp directories (e.g. /tmp/chartify<random>/) Fixes #1799 Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
76613bd0bb |
fix: serialize concurrent helm operations for same chart to fix #768 (#2662)
When multiple releases in a helmfile use the same remote chart, concurrent helm upgrade/diff calls race on helm's internal repository cache file rename, causing intermittent 'cannot rename: Access is denied' errors on Windows. This fix introduces two layers of serialization: 1. withChartOperationLock (operation-level): wraps SyncRelease/DiffRelease calls with a per-chart+version mutex. Only applies to remote charts (release.ChartPath is empty); local/pre-fetched/OCI charts bypass the lock entirely. Different charts remain fully parallel. 2. Per-chart+version download mutex in forcedDownloadChart/getOCIChart (download-level): uses double-check locking to ensure only one helm fetch runs per unique chart+version within a process. The fix does NOT change chart paths passed to helm, preserving backward compatibility with all existing behavior and tests. Trade-off: same-chart releases are fully serialized (the entire helm operation including deployment, not just download). This is unavoidable without pre-fetching because helm downloads and deploys atomically. Releases with different charts are unaffected. Fixes #768 Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
08d70bc9a7 |
fix: ensure OCI charts are prepared for needed releases with --include-needs (#2663)
fix: ensure OCI charts are prepared for needed releases with --include-needs (#923) When using --include-needs with a selector, releases included via needs must have their charts prepared (pulled/exported) before diff/sync/apply can process them. The core fix (ChartPrepareOptions.IncludeTransitiveNeeds = c.IncludeNeeds()) was already in place, but ForEachState calls in Diff/Template/Lint/Unittest/Sync/Apply still passed c.IncludeTransitiveNeeds() instead of c.IncludeNeeds(), creating an inconsistency that would resurface if SetFilter(true) were ever added. Changes: - Use c.IncludeNeeds() in ForEachState for all commands supporting --include-needs (Diff, Template, Lint, Unittest, Sync, Apply, Doctor) - Doctor is the only command with SetFilter(true), so this fixes a real bug: helmfile doctor --include-needs was silently ignored for filtering - Add explanatory doc comment on ForEachState parameter semantics - Enhance exectest.Helm.ChartPull to create minimal chart files and track pulls, enabling OCI chart testing - Add resetChartCacheForTest() for test isolation from global chart cache - Add regression tests for issue #923 Signed-off-by: yxxhero <11087727+yxxhero@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
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> |
||
|
|
9d3a9ad6c7 |
feat: parallel kubedog tracking with progress printer and safety valves (#2654)
* feat: parallel kubedog tracking with progress printer and safety valves Rework kubedog integration so resource tracking runs in parallel with helm upgrade/install, giving live progress output and recovering from known helm/kubedog wedge conditions. Core: - kubedogTrackingHandle runs tracking in a background goroutine alongside the helm subprocess (startBackgroundKubedogTracking); helm output is buffered and replayed as a single block so it no longer interleaves with progress ticks. - Capture UID+generation baselines before handing off to helm so each tracker waits until the resource actually changes (freshness gate). Progress printer (pkg/kubedog/printer.go): - Styled, auto-sized progress table with a heartbeat flusher, child (pod) status roll-up, pre-ready pod-phase handling, multi-namespace support, and optional color. PreviewBreakdown summarizes kept/filtered resources. Safety valves (verify cluster state via the live API): - Tracker-race valve (always on): when helm succeeds but a dyntracker goroutine is wedged, poll the API and cancel the tracker so wait() returns success instead of blocking until --track-timeout. - Helm-stuck killer (opt-in via helmStuckGrace): if the cluster stays converged while helm v4's hook waiter is wedged, SIGINT the helm subprocess to recover. - Failure watchdog (pkg/kubedog/watchdog.go): surface failing pods that never made it into dyntracker's resource graph. Options: trackFailedLogs, helmStuckGrace, trackTimeout, color (Color/NoColor), and resource filtering (trackKinds/skipKinds/ trackResources). PersistentVolumeClaim support in resource classification. Signed-off-by: Roman Mykhailiuk <romanm@cybellum.com> * test: add unit tests for kubedog tracking Cover the progress printer, resource classification, the failure watchdog, helm-output trimming/dedup, the release hard-timeout helper, and the color/track option plumbing. Signed-off-by: Roman Mykhailiuk <romanm@cybellum.com> * test: update golden logs, e2e snapshots, and values-id fixtures Refresh pkg/app testapply/testdestroy golden logs, e2e template snapshots, and the TestGenerateID values-id golden hashes for the new kubedog progress output and the merged release struct layout. Signed-off-by: Roman Mykhailiuk <romanm@cybellum.com> * refactor: remove dead kubedog display code and dedupe tracker setup Deep-review pass on the parallel kubedog tracking changes: - Remove pkg/kubedog/display.go (308 lines) and display_test.go (453 lines). These rendered progress for the legacy per-kind trackers that this PR replaces; in the merged tree every function is unreferenced outside its own tests. The new progressPrinter (printer.go) supersedes them. TestMain (color.ForceColor for deterministic ANSI in tests) is preserved in a new main_test.go. - Dedupe trackWithKubedog: the post-helm fallback rebuilt the exact same tracker options as buildReleaseTracker. Reuse buildReleaseTracker instead, dropping ~40 lines of duplicated timeout/log/filter/tracker construction. No behavior change; build, go vet, golangci-lint, and the kubedog/state unit tests all pass. Signed-off-by: yxxhero <aiopsclub@163.com> * fix: stop waitForFreshness busy-looping the API after helm finishes Once upstreamDoneCh closes it is always ready, so the select in waitForFreshness stopped blocking on the ticker and re-ran probe() (a live GET) as fast as the round-trip allowed for the whole 3s grace window — hammering the API server once per tracked resource. Track a local view of the channel and nil it out on first delivery so the first hit records the timestamp (one fast retry, as intended) and all subsequent polls are ticker-throttled. Functional behavior is unchanged: return nil when fresh, errUpstreamDoneNoChange after grace. Signed-off-by: yxxhero <aiopsclub@163.com> * test: drop trailing blank line from helm4 OCI pull snapshots The trailing-newline trim in helmexec.info() removes the blank line helm prints after the OCI chart "Digest:" line. The helm3 (output.yaml) snapshots never captured that line, but the helm4 (output-helm4.yaml) snapshots for oci_chart_pull{,_direct,_once,_once2} and issue_473_oci_chart_url_fetch still expected it, so they failed under helm 4. Remove the blank line so the snapshots match the trimmed output. Signed-off-by: yxxhero <aiopsclub@163.com> * test: refresh diff-args integration goldens for styled release headers DisplayAffectedReleases now emits a styled "========== Updated Releases ==========" header (matching the app/e2e goldens already updated by this PR) and helmexec.info() trims the trailing blank after helm's install status. Update the diff-args apply-stderr{,-helm4} and apply-live- stderr{,-helm4} goldens accordingly so they match the actual stderr. Signed-off-by: yxxhero <aiopsclub@163.com> * test: drop trimmed blank line from v1-subhelmfile template golden The trailing-newline trim in helmexec.info() removes the blank line helm prints after '"incubator" has been added to your repositories'. Update the v1-subhelmfile-multi-bases-with-array-values result and result-live goldens so the template stdout comparison matches. Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: Roman Mykhailiuk <romanm@cybellum.com> Signed-off-by: yxxhero <aiopsclub@163.com> Co-authored-by: Roman Mykhailiuk <romanm@cybellum.com> |
||
|
|
c5eebc24bd |
fix: helmfile deps broken for OCI charts with underscores in path (#2648)
fix: helmfile deps broken for OCI charts with underscores in path (#954) For OCI charts with multi-segment paths (e.g., myrepo/path_with_underscores/example), helmfile was putting the full path as the dependency name in the generated Chart.yaml. Helm then reconstructed the OCI reference using this name, and underscores in the path caused issues with helm's OCI reference handling during dependency update. Fix: move the chart path prefix into the repository URL and use only the chart basename as the dependency name, matching Helm's recommended Chart.yaml format for OCI dependencies: # Before (broken): dependencies: - name: path_with_underscores/example repository: oci://harbor.custom.com # After (fixed): dependencies: - name: example repository: oci://harbor.custom.com/path_with_underscores The resulting OCI reference is identical, but the dependency name is now clean. Includes backward-compatibility fallback for old lock files that used the full path as the dependency name. Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
bbceb05b31 |
feat: add helm 4 --server-side flag support for diff and bump helm-diff to v3.15.10 (#2645)
Add appendServerSideFlagsForDiff to validate helm-diff plugin version (v3.15.10+) before passing the --server-side flag to helm diff upgrade. Extract resolveServerSideValue helper to share precedence logic between upgrade and diff paths. Bump helm-diff recommended version to v3.15.10 across Dockerfiles, CI, and integration scripts. Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
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> |
||
|
|
38e27ee439 |
fix: include release values in .Values for patch template rendering (#2556)
* fix: include release values in .Values for jsonPatches/strategicMergePatches/transformers gotmpl rendering
Before this fix, .Values in patch template files only contained environment
values, not the release's own values. This meant references like
{{ .Values.ingress.enabled }} would fail when ingress.enabled was set in
the release's values: file rather than environment values.
Now patch gotmpl files see .Values as merged(environment values, release values),
matching user expectations that values defined in the release should be
accessible in conditional patches.
Fixes #1904
Signed-off-by: yxxhero <aiopsclub@163.com>
* test: add more tests for resolveReleaseValues, renderValuesFileToBytesWithData, and generateTemporaryReleaseValuesFilesWithData
Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/5da5c9d8-7464-4146-84b5-1433ed6193f3
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
* test: simplify newTestHelmStateWithFiles by removing empty cleanup func
Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/5da5c9d8-7464-4146-84b5-1433ed6193f3
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
* fix: remove always-constant basePath param from newTestHelmStateWithFiles to fix unparam lint error
Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/b4a669cb-692c-4ca6-a68b-1b04a062b989
Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com>
* fix: address
|
||
|
|
7391453cbe |
fix: support array of maps in set/setTemplate values (#2615)
Changed SetValue.Values type from []string to []any to allow passing
maps (not just strings) in the values field of set/setTemplate.
Previously, YAML like:
setTemplate:
- name: source.helm.parameters
values:
- name: demo
- version: v2
would fail with 'cannot unmarshal !!map into string'. Map values are
now serialized to JSON when generating --set flags.
Fixes #1021
Signed-off-by: yxxhero <aiopsclub@163.com>
|
||
|
|
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> |
||
|
|
81ab674017 |
fix: normalize dependency chart path before DirectoryExistsAt check (#2598)
When helmfile.d contains multiple release files and one release has a local chart dependency (e.g. chart: ../chart), the dependency path was passed to DirectoryExistsAt without normalizing against basePath. This caused the path to be resolved against CWD instead of the helmfile directory, so the local chart was not detected and helmfile tried to resolve it as a remote repo, failing with: 'failed reading adhoc dependencies: no helm list entry found for repository' Fixes #2596 Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
23802e7a95 |
fix: refresh Chart.lock after rewriting file:// dependencies (#2587)
* fix: refresh Chart.lock after rewriting file:// dependencies
`rewriteChartDependencies` rewrites relative `file://` repository URLs in
Chart.yaml to absolute paths so chartify can resolve them from a temp
directory. That mutates the Chart.yaml dependencies block, which
invalidates the Chart.lock digest (helm computes it as
`sha256(json.Marshal([2][]Dependency{req, lock}))` over the dependencies).
Once the lock is out of sync, downstream `helm dependency build` errors
with "the lock file (Chart.lock) is out of sync with the dependencies
file (Chart.yaml)" and chartify falls back to `helm dependency update`.
`dep update` then re-resolves Chart.yaml's version constraints against
the chart repo, so any constraint that admits newer versions
(e.g. `version: "*"`, `~1.0`) silently picks up a newer dependency on
every render — even though Chart.lock pins a specific version.
Repro:
- Chart.yaml has `version: "*"` for some-dep, Chart.lock pins 4.1.0,
upstream now publishes 4.2.0.
- `helm template .` honors the cached `charts/some-dep-4.1.0.tgz`.
- `helmfile template` produces 4.2.0, because it triggered chartify
(via jsonPatches/strategic-merge/kustomize/etc), which copied the
chart, ran `dep build` against an out-of-sync lock, fell back to
`dep up`, and re-resolved the wildcard.
This commit refreshes Chart.lock alongside Chart.yaml in the temp copy:
- Mirror the rewritten file:// repository URLs onto matching entries in
Chart.lock's dependencies. Without this, `helm dep build` would resolve
the lock's relative `file://` paths against the temp chart directory
and fail with "directory ... not found".
- Recompute the digest using helm's resolver.HashReq algorithm
(`sha256(json.Marshal([2][]chart.Dependency{req, lock}))`). The
algorithm is small and stable; resolver.HashReq itself lives in an
internal package, so it's inlined here.
- Locked versions are preserved verbatim — only the repository URL is
updated and the digest recomputed. Chart.lock remains the source of
truth for which versions get installed.
- The original Chart.lock on disk is never modified; only the temp copy
is rewritten.
Adds TestRewriteChartDependencies_RefreshesChartLock covering digest
recomputation, file:// URL mirroring, version preservation, untouched
non-file:// deps, and original-on-disk integrity.
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
* fix: address Copilot review issues for Chart.lock refresh
- Map all helm Dependency fields (alias, condition, tags, import-values,
enabled) when building the request slice for digest computation, not
just name/version/repository. This ensures the recomputed digest
matches Helm's resolver.HashReq for all dependency shapes.
- Match lock entries by Name + Alias (not Name alone) to correctly
handle charts with duplicate dependency names distinguished by alias.
- Log a warning when reading Chart.lock fails with a non-NotExist error,
while still treating a missing Chart.lock as expected.
- Add test case exercising dependencies with alias, condition, tags, and
import-values fields, including same-name deps disambiguated by alias.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
* build(deps): bump github.com/helmfile/chartify to v0.26.4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
* fix: normalize import-values for JSON marshaling and improve test coverage
- Normalize import-values using maputil.RecursivelyStringifyMapKey before
assigning to helmchart.Dependency.ImportValues. When go-yaml v2 decodes
nested maps (e.g. import-values entries with child/parent keys), they
become map[interface{}]interface{} which json.Marshal cannot encode.
This would silently prevent Chart.lock rewriting. The normalization
converts all map keys to strings, making the value JSON-safe.
- Improve TestRewriteChartDependencies_RefreshesChartLockWithExtraFields
to prove that extra fields (condition, tags, import-values) actually
affect the computed digest by comparing digests with and without those
fields and asserting they differ.
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: normalize lock ImportValues and fix digest test isolation
- Normalize lock.Dependencies ImportValues via RecursivelyStringifyMapKey
before json.Marshal, preventing failures when go-yaml v2 decodes nested
maps as map[interface{}]interface{}.
- Fix TestRewriteChartDependencies_RefreshesChartLockWithExtraFields to use
a shared root directory so both chart variants resolve file:// paths to
the same absolute location, isolating digest differences to field content.
- Add TestRewriteChartDependencies_GoYamlV2ImportValues exercising the
HELMFILE_GO_YAML_V3=false path with import-values containing nested maps.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
* fix: add exact digest verification test against Helm's HashReq
Add TestRewriteChartDependencies_DigestMatchesHelmHashReq which computes
the expected digest independently using the same algorithm as Helm's
resolver.HashReq and asserts the rewritten Chart.lock matches exactly.
This guards against producing a digest that is "different" yet still
rejected by `helm dependency build`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
---------
Signed-off-by: Shane Starcher <shane.starcher@gmail.com>
Co-authored-by: Shane Starcher <shane.starcher@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
c82c61e061 |
fix: template helmDefaults.postRendererArgs with release data (#2583)
PR #1839 introduced template rendering for postRendererArgs, but PR #2510 reverted it while fixing a separate regression. This left helmDefaults-level postRendererArgs containing template expressions (e.g. {{ .Release.Name }}) passed to helm as literal strings instead of being resolved per-release. Add renderPostRendererArgs() that templates helmDefaults.postRendererArgs at flag-generation time using the release's template data, reusing the existing createReleaseTemplateData() helper. Release-level args are already templated by ExecuteTemplateExpressions and CLI args are static, so only the helmDefaults path needs rendering. Fixes #2580 Signed-off-by: opencode <opencode@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
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> |
||
|
|
c6d0310029 |
fix(state): resolve OCI repo prefix in ad-hoc release dependencies (#2579)
When a release `dependencies[].chart` is given as `<repoName>/<chart>` and the matching `repositories:` entry has `oci: true`, helmfile now rewrites it to `oci://<repoURL>/<chart>` before passing it to chartify. Without this, chartify's lookup falls into its `helm repo list` branch, which never finds OCI repos because helm 3+ does not register OCI registries as named repos (they live in the `helm registry login` state instead). The user-visible failure was: failed reading adhoc dependencies: no helm list entry found for repository "<name>". please `helm repo add` it! Explicit `oci://` URLs already worked through chartify's OCI branch; this change makes the `<repoName>/<chart>` form behave the same way. Non-OCI repo prefixes, unknown prefixes, single-segment names, and explicit `oci://` URLs all pass through unchanged. A debug log records each rewrite at the call site for easier troubleshooting. Fixes #1756. Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de> |
||
|
|
420cc3ba9c |
fix: add trackFailOnError option to control kubedog exit code (#2576)
* fix: add trackFailOnError option to control kubedog exit code behavior When kubedog release tracking fails (e.g. pod ImagePullBackOff), helmfile exits with code 0 instead of a non-zero exit code. Add a trackFailOnError configuration option (default: false) that when set to true, propagates kubedog tracking failures to the exit code. The option is available as: - Per-release YAML: trackFailOnError: true - CLI flag: --track-fail-on-error (sync and apply commands) Extract trackReleaseIfEnabled helper to consolidate kubedog tracking logic from two duplicated call sites into a single maintainable method. Fixes #2507 Signed-off-by: yxxhero <aiopsclub@163.com> * fix: add //go:build ignore to server.go to fix go test CI failure The test/integration/test-cases/issue-2103/input/server.go is a package main helper binary used by the issue-2103 integration test. When go test -coverprofile runs on this package, it fails with "go: no such tool covdata" in the CI environment. Adding //go:build ignore excludes the file from go list ./... (and therefore from PKGS in the Makefile), while still allowing the integration test to build it explicitly via file path: go build -o server ./path/to/server.go Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/8a7000af-72b7-48f8-8a82-24813b5df341 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: update TestGenerateID expected hashes after adding TrackFailOnError field Adding TrackFailOnError *bool to ReleaseSpec changed the spew serialization of the struct, which changed the FNV-32a hash values produced by generateValuesID. Update temp_test.go with the new expected hash strings. Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/caa86cd9-73d1-4894-b745-fd70c0811fd6 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> |
||
|
|
a8e8b67086 |
fix: use --post-renderer-args=VALUE format to prevent Helm flag parsing failure (#2570)
* fix: use --post-renderer-args=VALUE format to prevent Helm flag parsing failure When postRendererArgs contains values like short flags (e.g. -v), passing --post-renderer-args and the value as separate arguments causes Helm to interpret the value as its own flag. Using the --post-renderer-args=VALUE format unambiguously binds the value to the flag. Fixes #2563 Signed-off-by: yxxhero <aiopsclub@163.com> * fix: update hasFlagWithValue doc/errors and add -v short-flag test cases - Update hasFlagWithValue doc comment to describe both '--flag value' and '--flag=value' forms - Update t.Errorf messages in app_test.go to reflect both accepted formats - Add 'post-renderer-args-short-flag-value' test case (-v) to both TestHelmState_flagsForUpgrade and TestHelmState_flagsForTemplate to verify --post-renderer-args=-v emission (core regression from #2563) Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/dd95f046-358b-4867-9069-9432c1b5318e 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> |
||
|
|
dac42105dd |
fix: deduplicate chart dependencies in helmfile.lock (#2567)
When multiple releases reference the same chart with the same name, repository, and version, helmfile deps would write duplicate entries to helmfile.lock. This adds deduplication of resolved dependencies after sorting and before writing the lock file. Fixes #2562 Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
10deabb142 |
Honor skipSchemaValidation during chartification when forceNamespace is set (#2550)
* fix(state): honor skipSchemaValidation in chartify template args Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/a695cbff-c37a-403a-9658-09f4fdaa65d0 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * test(state): harden chartify skip-schema flag detection Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/a695cbff-c37a-403a-9658-09f4fdaa65d0 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix(state): propagate cli skip-schema-validation to chartify Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/70ebf027-0ab5-4bdb-a4b4-5a77c822ee95 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> |
||
|
|
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> |
||
|
|
fc31dbfc5e |
fix: eliminate race condition in rewriteChartDependencies (#2541)
* fix: eliminate race condition in rewriteChartDependencies by copying chart before modifying Instead of modifying the original Chart.yaml in-place (which causes race conditions when multiple releases reference the same local chart), copy the chart to a temporary directory and rewrite the copy's dependencies. This eliminates the need for per-chart mutex locks and prevents file corruption when concurrent goroutines process releases sharing the same local chart. Fixes #2502 Signed-off-by: yxxhero <aiopsclub@163.com> * fix: address PR review comments for rewriteChartDependencies - Handle non-NotExist errors from st.fs.Stat to surface permission/IO failures - Reword function doc to clarify temp copy is conditional on rewrite being needed - Assert rewrittenPath vs tempDir based on expectModified in test table Signed-off-by: yxxhero <aiopsclub@163.com> * test: add integration test for issue #2502 race condition with shared local chart Signed-off-by: yxxhero <aiopsclub@163.com> * fix: separate environments and releases with --- in helmfile.yaml Signed-off-by: yxxhero <aiopsclub@163.com> * fix: correct file:// path and remove --skip-deps for dependency build Signed-off-by: yxxhero <aiopsclub@163.com> * fix: correct file:// dependency path (5 levels up to test/integration/) Signed-off-by: yxxhero <aiopsclub@163.com> * fix: remove output validation from race condition test Signed-off-by: yxxhero <aiopsclub@163.com> * fix: assert WriteFile/MkdirTemp/RemoveAll/CopyDir in DefaultFileSystem test Signed-off-by: yxxhero <aiopsclub@163.com> * fix: add strategicMergePatches to trigger chartify in race condition test Signed-off-by: yxxhero <aiopsclub@163.com> * fix: scope test values under raw subchart and align ConfigMap name with strategic merge patches The race condition test values.yaml had templates at the top level instead of scoped under the raw subchart key, causing helm template to produce no output and chartify's ReplaceWithRendered to fail with an empty helmx.1.rendered directory. Also align the ConfigMap name to match the strategicMergePatches target. Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
7a60255a9b |
enabledns flags available on template command (#2511)
* enabledns flags available on template command Enable dns flag was not available in helmfile template command Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
c584c0e07f |
fix: helmDefaults.postRendererArgs not passed to helm commands (#2510)
* fix: helmDefaults.postRendererArgs not passed to helm commands (#2508)
The commit
|
||
|
|
6faa477290 |
fix: update state values files handling to replace arrays instead of merging (#2537)
* feat: update state values handling to replace arrays instead of merging Signed-off-by: Vojta Polak <vojta.polak@gmail.com> * test: non-shallow copy of OverrideCLISetValues in DeepCopy + test Signed-off-by: Vojta Polak <vojta.polak@gmail.com> * fix: update error message for empty CalleePath in Load function Signed-off-by: Vojta Polak <vojta.polak@gmail.com> --------- Signed-off-by: Vojta Polak <vojta.polak@gmail.com> |
||
|
|
acb7ce36fc |
fix: add mutex lock for concurrent rewriteChartDependencies access (#2509)
* fix: add mutex lock for concurrent rewriteChartDependencies access Prevent race conditions when multiple goroutines concurrently access the same Chart.yaml by introducing a per-chart-path mutex via sync.Map. Signed-off-by: yxxhero <yxxhero@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> * fix: unlock mutex on all error paths and improve race condition test validation Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/135e06b8-99e8-42cb-859d-524b2d2b1907 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: remove duplicate mutex, reuse getNamedRWMutex, restore on write failure, add test barrier Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/f93746bf-82fa-46b1-b8f0-b34bc9aa749c Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: early unlock when unmodified, assert exact restore in race test Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/5981aea4-2799-4560-9272-315d28d4b7d7 Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * fix: improve comment wording and strengthen race test start barrier Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/9e4d6f4f-cdc1-4e9e-bdc6-81061ebc1dcc Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> --------- Signed-off-by: yxxhero <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> |
||
|
|
b9378b17f6 |
fix: boolean false overrides dropped in multi-document helmfiles (#2527) (#2532)
* fix: environment values pollution causing boolean false overrides to be dropped (#2527) PR #2367 introduced envCopy.Values = values in NewEnvironmentTemplateData, where values = GetMergedValues() = Defaults + Values + CLIOverrides. This caused .Environment.Values to include Defaults, so when multi-part helmfiles re-assigned environment values via {{ toYaml .Environment.Values }}, Defaults values (e.g. helmDefaults.atomic: true) were written into the environment Values field. Later, GetMergedValues() applied Values over Defaults, causing the stale atomic: true to win over the correct atomic: false override. Fix: set .Environment.Values to Values + CLIOverrides only (excluding Defaults), so re-assignment patterns don't pollute the Values layer with Defaults. Signed-off-by: yxx <yxx@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> * fix: rename test to correctly reflect Values override Defaults precedence Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/1b251877-7050-404b-8cc7-abd6aa3ec36b Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> * test: flip regression test fixture to exercise false override (issue #2527) Agent-Logs-Url: https://github.com/helmfile/helmfile/sessions/c428fd46-b698-4e88-bff2-4c9ac72d2deb Co-authored-by: yxxhero <11087727+yxxhero@users.noreply.github.com> --------- Signed-off-by: yxx <yxx@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> |
||
|
|
4bdb6f097c |
fix: keep all chart dependencies key / values (#2501)
* feat: Refactor TestRewriteChartDependencies Signed-off-by: Etienne Champetier <e.champetier@ateme.com> * fix: keep all chart dependencies key / values In rewriteChartDependencies we were only parsing name / repository / version, thus dropping keys like condition / import-values. This at least fixes the use of condition. Signed-off-by: Etienne Champetier <e.champetier@ateme.com> --------- Signed-off-by: Etienne Champetier <e.champetier@ateme.com> |
||
|
|
732e4ad913 |
fix: helmfile fetch fails for kustomization directories (#2504)
* fix: helmfile fetch fails for kustomization directories
Fixes #2503
When running `helmfile fetch` on a release that points to a local
kustomization directory (without Chart.yaml), the command failed with
"Chart.yaml is missing".
The issue was that the condition `helmfileCommand != "pull"` in
prepareChartForRelease skipped chartification for ALL cases during
fetch, including local kustomization directories that NEED chartify
to convert them to Helm charts.
Solution:
- Added `NeedsChartifyForLocalDir` field to the Chartify struct to
track when chartification is needed because the local directory
is not a Helm chart (no Chart.yaml)
- Modified the condition to skip chartification for "pull" ONLY when
it's not a local directory without Chart.yaml
This preserves the original fix (commit
|
||
|
|
c70b20ad7a |
feat: add an arg that passing description to helm upgrade command (#2497)
* feat: add an arg that passing description to `helm upgrade` command fix: github actions Signed-off-by: swimablefish <swimablefish@gmail.com> * fix: lint and test failed Signed-off-by: swimablefish <swimablefish@gmail.com> * feat: encapsulation Signed-off-by: swimablefish <swimablefish@gmail.com> * feat: add version gate Signed-off-by: swimablefish <swimablefish@gmail.com> * feat: rephrase Signed-off-by: swimablefish <swimablefish@gmail.com> --------- Signed-off-by: swimablefish <swimablefish@gmail.com> |
||
|
|
43b426892e |
fix: cleanup hooks not receiving error signal (#2475)
* fix: cleanup hooks not receiving error signal Closes #1041 Signed-off-by: yxxhero <11087727+yxxhero@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> * Add tests for cleanup hooks error propagation Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <11087727+yxxhero@users.noreply.github.com> Signed-off-by: yxxhero <aiopsclub@163.com> |
||
|
|
df01afbbeb |
fix: helmfile list now reflects version from helmfile.lock (#2486)
* fix: helmfile list now reflects version from helmfile.lock The list command now resolves locked dependencies before returning release information, ensuring the version field reflects the pinned version from helmfile.lock when present. Fixes #1953 Signed-off-by: yxxhero <aiopsclub@163.com> * fix: address PR review comments - Remove redundant maps.Copy in list() - labels already merged by GetReleasesWithLabels() - Fix default lockfile path to use basePath for multi-file mode - Update test to expect basePath-joined lockfile path - Add multi-file test for lockfile resolution in helmfile.d directory Signed-off-by: yxxhero <aiopsclub@163.com> * fix more test Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix: propagate errors instead of panic in list() When skipCharts=false, errors from list() now properly propagate instead of causing a crash. Uses a closure variable to capture the error and propagates it after withPreparedCharts completes. Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> * fix tests Signed-off-by: yxxhero <aiopsclub@163.com> --------- Signed-off-by: yxxhero <aiopsclub@163.com> |