Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nelm for openSUSE:Factory checked in at 2025-05-14 17:01:59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nelm (Old) and /work/SRC/openSUSE:Factory/.nelm.new.30101 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nelm" Wed May 14 17:01:59 2025 rev:7 rq:1277395 version:1.4.0 Changes: -------- --- /work/SRC/openSUSE:Factory/nelm/nelm.changes 2025-05-07 19:21:52.332723273 +0200 +++ /work/SRC/openSUSE:Factory/.nelm.new.30101/nelm.changes 2025-05-14 17:02:34.046655715 +0200 @@ -1,0 +2,15 @@ +Wed May 14 10:43:53 UTC 2025 - Johannes Kastl <[email protected]> + +- Update to version 1.4.0: + * Features + - --no-install-crds for release install/plan (efc22bc) + - --print-values option for release get (f41f615) + - --release-labels option for release install (9b20bc0) + - --timeout option for release install/rollback/uninstall/plan + (d563296) + * Bug Fixes + - disallow unknown NELM_FEAT_.* env vars (7e25a16) + * Dependencies + - chore: update 3p-helm module + +------------------------------------------------------------------- Old: ---- nelm-1.3.0.obscpio New: ---- nelm-1.4.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nelm.spec ++++++ --- /var/tmp/diff_new_pack.Hz7Vjg/_old 2025-05-14 17:02:35.286707789 +0200 +++ /var/tmp/diff_new_pack.Hz7Vjg/_new 2025-05-14 17:02:35.286707789 +0200 @@ -17,7 +17,7 @@ Name: nelm -Version: 1.3.0 +Version: 1.4.0 Release: 0 Summary: Helm 3 alternative License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.Hz7Vjg/_old 2025-05-14 17:02:35.330709638 +0200 +++ /var/tmp/diff_new_pack.Hz7Vjg/_new 2025-05-14 17:02:35.334709806 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/werf/nelm</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v1.3.0</param> + <param name="revision">v1.4.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.Hz7Vjg/_old 2025-05-14 17:02:35.358710814 +0200 +++ /var/tmp/diff_new_pack.Hz7Vjg/_new 2025-05-14 17:02:35.362710982 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/werf/nelm</param> - <param name="changesrevision">d2d74179b19089543e60d302f74e969f6cbe8d6c</param></service></servicedata> + <param name="changesrevision">bbc21f6c6e7bc0b7b7791fd575a3899a17dda2b5</param></service></servicedata> (No newline at EOF) ++++++ nelm-1.3.0.obscpio -> nelm-1.4.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/CHANGELOG.md new/nelm-1.4.0/CHANGELOG.md --- old/nelm-1.3.0/CHANGELOG.md 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/CHANGELOG.md 2025-05-14 11:22:12.000000000 +0200 @@ -1,5 +1,20 @@ # Changelog +## [1.4.0](https://www.github.com/werf/nelm/compare/v1.3.0...v1.4.0) (2025-05-14) + + +### Features + +* `--no-install-crds` for `release install/plan` ([efc22bc](https://www.github.com/werf/nelm/commit/efc22bca73d07af229c351ca74f3e29ebb571f44)) +* `--print-values` option for `release get` ([f41f615](https://www.github.com/werf/nelm/commit/f41f615b3917da22d333d6d588b7b7adf2f9505e)) +* `--release-labels` option for `release install` ([9b20bc0](https://www.github.com/werf/nelm/commit/9b20bc0c63e8651ce3cd728fcf9d8d471da12652)) +* `--timeout` option for `release install/rollback/uninstall/plan` ([d563296](https://www.github.com/werf/nelm/commit/d563296ac4866f3d9ec0030308acb7f9ef20211a)) + + +### Bug Fixes + +* disallow unknown NELM_FEAT_.* env vars ([7e25a16](https://www.github.com/werf/nelm/commit/7e25a16f5f0c40d94308c77d18cad5cee31d5194)) + ## [1.3.0](https://www.github.com/werf/nelm/compare/v1.2.2...v1.3.0) (2025-05-07) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/README.md new/nelm-1.4.0/README.md --- old/nelm-1.3.0/README.md 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/README.md 2025-05-14 11:22:12.000000000 +0200 @@ -9,7 +9,7 @@ * lots of fixes for Helm 3 bugs, e.g. ["no matches for kind Deployment in version apps/v1beta1"](https://github.com/helm/helm/issues/7219); * ... and more. -**Note on the project status and production readiness**. Prior to a formal public announcement, thousands of projects actively used Nelm via werf. Being the default and only deployment engine since werf v2.0 ([released](https://github.com/werf/werf/discussions/6100) in April 2024), Nelm and all its main features were battle-tested in production before a separate tool with user-facing CLI emerged. However, the Nelm *CLI* [went public](https://blog.werf.io/nelm-cli-helm-compatible-alternative-5648b191f0af) with the Nelm v1 release only (in April 2025) and hasn’t been tested that much yet. +We consider Nelm production-ready, since 95% of the Nelm codebase basically is the werf deployment engine, which was battle-tested in production across thousands of projects for years. ## Table of Contents @@ -27,6 +27,7 @@ - [Printing logs and events during deploy](#printing-logs-and-events-during-deploy) - [Release planning](#release-planning) - [Encrypted values and encrypted files](#encrypted-values-and-encrypted-files) + - [Improved CRD management](#improved-crd-management) - [Documentation](#documentation) - [Usage](#usage) - [Encrypted values files](#encrypted-values-files) @@ -48,8 +49,10 @@ - [Annotation `werf.io/show-logs-only-for-containers`](#annotation-werfioshow-logs-only-for-containers) - [Annotation `werf.io/show-service-messages`](#annotation-werfioshow-service-messages) - [Function `werf_secret_file`](#function-werf_secret_file) + - [Feature gates](#feature-gates) + - [Env variable `NELM_FEAT_REMOTE_CHARTS`](#env-variable-nelm_feat_remote_charts) - [More information](#more-information) -- [Known issues](#known-issues) +- [Limitations](#limitations) - [Future plans](#future-plans) <!-- END doctoc generated TOC please keep comment here to allow auto update --> @@ -296,6 +299,10 @@ `nelm chart secret` commands manage encrypted values files such as `secret-values.yaml` or encrypted arbitrary files like `secret/mysecret.txt`. These files are decrypted in-memory during templating and can be used in templates as `.Values.my.secret.value` and `{{ werf_secret_file "mysecret.txt" }}`, respectively. +### Improved CRD management + +CRDs from the `crds/` directory of the chart deployed not only on the very first release install, but also on release upgrades. Also, CRDs not only can be created, but can be updated as well. + ## Documentation Nelm-specific features are described below. For general documentation, see [Helm docs](https://helm.sh/docs/) and [werf docs](https://werf.io/docs/v2/usage/deploy/overview.html). @@ -499,21 +506,35 @@ Read the specified secret file from the `secret/` directory of the Helm chart. +### Feature gates + +#### Env variable `NELM_FEAT_REMOTE_CHARTS` + +Example: +```shell +export NELM_FEAT_REMOTE_CHARTS=true +nelm release install -n myproject -r myproject --chart-version 19.1.1 bitnami/nginx +``` + +Allows specifying not only local, but also remote charts as a command-line argument to commands such as `nelm release install`. Adds the `--chart-version` option as well. + ### More information For more information, see [Helm docs](https://helm.sh/docs/) and [werf docs](https://werf.io/docs/v2/usage/deploy/overview.html). -## Known issues +## Limitations -- Nelm won't work with Kubernetes versions earlier than v1.14. The `ServerSideApply` feature gate should be enabled (it's enabled by default starting from Kubernetes v1.16). This requirement is caused by leveraging the Server-Side Apply (instead of 3-Way Merge in Helm). +* Nelm requires Server-Side Apply enabled in Kubernetes. It is enabled by default since Kubernetes 1.16. In Kubernetes 1.14-1.15 it can be enabled, but disabled by default. Kubernetes 1.13 and older doesn't have Server-Side Apply, thus Nelm won't work with it. +* Helm *sometimes* uses Values from the previous Helm release to deploy a new release. This "feature" meant to make using Helm without a proper CI/CD process easier. This is dangerous, goes against IaC and this is not what most users expect. Nelm will *never* use Values from previous Helm releases for any purposes. What you explicitly pass via `--values` and `--set` options will be merged with builtin chart Values, then applied to the cluster. Thus, options `--reuse-values`, `--reset-values`, `--reset-then-reuse-values` will not be implemented. ## Future plans -Here are some of the big things we plan to implement — feel free to upvote or comment on the relevant issues and/or raise new ones to show your interest: +Here are some of the big things we plan to implement — upvote or comment relevant issues or create a new one to help us prioritize: -- An alternative to Helm templating ([#54](https://github.com/werf/nelm/issues/54)); -- An option to pull charts directly from Git; -- A public Go API for embedding Nelm into third-party software; -- Enhance the CLI experience with new commands and improve the consistency between the reimplemented commands and original Helm commands; -- Overhaul the chart dependency management ([#61](https://github.com/werf/nelm/issues/61)); -- Migrate the built-in secret management to Mozilla SOPS ([#62](https://github.com/werf/nelm/issues/62)). +* The Nelm operator, interoperable with ArgoCD/Flux. +* An alternative to Helm templating ([#54](https://github.com/werf/nelm/issues/54)). +* An option to pull charts directly from Git. +* A public Go API for embedding Nelm into third-party software. +* Enhance the CLI experience with new commands and improve the consistency between the reimplemented commands and original Helm commands. +* Overhaul the chart dependency management ([#61](https://github.com/werf/nelm/issues/61)). +* Migrate the built-in secret management to Mozilla SOPS ([#62](https://github.com/werf/nelm/issues/62)). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/chart_lint.go new/nelm-1.4.0/cmd/nelm/chart_lint.go --- old/nelm-1.3.0/cmd/nelm/chart_lint.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/chart_lint.go 2025-05-14 11:22:12.000000000 +0200 @@ -23,7 +23,7 @@ cfg := &chartLintConfig{} use := "lint [options...]" - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { use += " [chart-dir|chart-repo-name/chart-name|chart-archive|chart-archive-url]" } else { use += " [chart-dir]" @@ -48,7 +48,7 @@ }) if len(args) > 0 { - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { cfg.Chart = args[0] } else { cfg.ChartDirPath = args[0] @@ -92,7 +92,7 @@ return fmt.Errorf("add flag: %w", err) } - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { if err := cli.AddFlag(cmd, &cfg.ChartVersion, "chart-version", "", "Choose a remote chart version, otherwise the latest version is used", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: mainFlagGroup, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/chart_render.go new/nelm-1.4.0/cmd/nelm/chart_render.go --- old/nelm-1.3.0/cmd/nelm/chart_render.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/chart_render.go 2025-05-14 11:22:12.000000000 +0200 @@ -23,7 +23,7 @@ cfg := &chartRenderConfig{} use := "render [options...]" - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { use += " [chart-dir|chart-repo-name/chart-name|chart-archive|chart-archive-url]" } else { use += " [chart-dir]" @@ -49,14 +49,14 @@ }) if len(args) > 0 { - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { cfg.Chart = args[0] } else { cfg.ChartDirPath = args[0] } } - if err := action.ChartRender(ctx, cfg.ChartRenderOptions); err != nil { + if _, err := action.ChartRender(ctx, cfg.ChartRenderOptions); err != nil { return fmt.Errorf("chart render: %w", err) } @@ -93,7 +93,7 @@ return fmt.Errorf("add flag: %w", err) } - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { if err := cli.AddFlag(cmd, &cfg.ChartVersion, "chart-version", "", "Choose a remote chart version, otherwise the latest version is used", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: mainFlagGroup, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/main.go new/nelm-1.4.0/cmd/nelm/main.go --- old/nelm-1.3.0/cmd/nelm/main.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/main.go 2025-05-14 11:22:12.000000000 +0200 @@ -41,12 +41,11 @@ } } - unsupportedEnvVars := cli.FindUndefinedFlagEnvVarsInEnviron() - unsupportedEnvVars = lo.Filter(unsupportedEnvVars, func(env string, _ int) bool { - return !strings.HasPrefix(env, featgate.FeatGateEnvVarsPrefix) + featGatesEnvVars := lo.Map(featgate.FeatGates, func(fg *featgate.FeatGate, index int) string { + return fg.EnvVarName() }) - if len(unsupportedEnvVars) > 0 { + if unsupportedEnvVars := lo.Without(cli.FindUndefinedFlagEnvVarsInEnviron(), featGatesEnvVars...); len(unsupportedEnvVars) > 0 { abort(ctx, fmt.Errorf("unsupported environment variable(s): %s", strings.Join(unsupportedEnvVars, ",")), 1) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/release_get.go new/nelm-1.4.0/cmd/nelm/release_get.go --- old/nelm-1.3.0/cmd/nelm/release_get.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/release_get.go 2025-05-14 11:22:12.000000000 +0200 @@ -168,6 +168,13 @@ return fmt.Errorf("add flag: %w", err) } + if err := cli.AddFlag(cmd, &cfg.PrintValues, "print-values", false, "Print Values of the last Helm release", cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } + if err := cli.AddFlag(cmd, &cfg.ReleaseName, "release", "", "The release name. Must be unique within the release namespace", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: mainFlagGroup, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/release_install.go new/nelm-1.4.0/cmd/nelm/release_install.go --- old/nelm-1.3.0/cmd/nelm/release_install.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/release_install.go 2025-05-14 11:22:12.000000000 +0200 @@ -25,7 +25,7 @@ cfg := &releaseInstallConfig{} use := "install [options...] -n namespace -r release" - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { use += " [chart-dir|chart-repo-name/chart-name|chart-archive|chart-archive-url]" } else { use += " [chart-dir]" @@ -50,7 +50,7 @@ }) if len(args) > 0 { - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { cfg.Chart = args[0] } else { cfg.ChartDirPath = args[0] @@ -100,7 +100,7 @@ return fmt.Errorf("add flag: %w", err) } - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { if err := cli.AddFlag(cmd, &cfg.ChartVersion, "chart-version", "", "Choose a remote chart version, otherwise the latest version is used", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: mainFlagGroup, @@ -261,6 +261,13 @@ return fmt.Errorf("add flag: %w", err) } + if err := cli.AddFlag(cmd, &cfg.NoInstallCRDs, "no-install-crds", false, `Don't install CRDs from "crds/" directories of installed charts`, cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } + if err := cli.AddFlag(cmd, &cfg.NoProgressTablePrint, "no-show-progress", false, "Don't show logs, events and real-time info about release resources", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: progressFlagGroup, @@ -296,6 +303,13 @@ return fmt.Errorf("add flag: %w", err) } + if err := cli.AddFlag(cmd, &cfg.ReleaseLabels, "release-labels", map[string]string{}, "Add labels to the release. What kind of labels depends on the storage driver", cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagLocalMultiEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } + if err := cli.AddFlag(cmd, &cfg.ReleaseName, "release", "", "The release name. Must be unique within the release namespace", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: mainFlagGroup, @@ -364,6 +378,13 @@ }); err != nil { return fmt.Errorf("add flag: %w", err) } + + if err := cli.AddFlag(cmd, &cfg.Timeout, "timeout", 0, "Fail if not finished in time", cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } if err := cli.AddFlag(cmd, &cfg.TrackCreationTimeout, "resource-creation-timeout", 0, "Fail if resource creation tracking did not finish in time", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/release_plan_install.go new/nelm-1.4.0/cmd/nelm/release_plan_install.go --- old/nelm-1.3.0/cmd/nelm/release_plan_install.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/release_plan_install.go 2025-05-14 11:22:12.000000000 +0200 @@ -25,7 +25,7 @@ cfg := &releasePlanInstallConfig{} use := "install [options...] -n namespace -r release" - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { use += " [chart-dir|chart-repo-name/chart-name|chart-archive|chart-archive-url]" } else { use += " [chart-dir]" @@ -50,7 +50,7 @@ }) if len(args) > 0 { - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { cfg.Chart = args[0] } else { cfg.ChartDirPath = args[0] @@ -94,7 +94,7 @@ return fmt.Errorf("add flag: %w", err) } - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) { + if featgate.FeatGateRemoteCharts.Enabled() { if err := cli.AddFlag(cmd, &cfg.ChartVersion, "chart-version", "", "Choose a remote chart version, otherwise the latest version is used", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: mainFlagGroup, @@ -247,6 +247,13 @@ return fmt.Errorf("add flag: %w", err) } + if err := cli.AddFlag(cmd, &cfg.NoInstallCRDs, "no-install-crds", false, `Don't install CRDs from "crds/" directories of installed charts`, cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } + if err := cli.AddFlag(cmd, &cfg.RegistryCredentialsPath, "oci-chart-repos-creds", action.DefaultRegistryCredentialsPath, "Credentials to access OCI chart repositories", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: chartRepoFlagGroup, @@ -309,6 +316,13 @@ }); err != nil { return fmt.Errorf("add flag: %w", err) } + + if err := cli.AddFlag(cmd, &cfg.Timeout, "timeout", 0, "Fail if not finished in time", cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } if err := cli.AddFlag(cmd, &cfg.ValuesFileSets, "set-file", []string{}, "Set new values, where the key is the value path and the value is the path to the file with the value content", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/release_rollback.go new/nelm-1.4.0/cmd/nelm/release_rollback.go --- old/nelm-1.3.0/cmd/nelm/release_rollback.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/release_rollback.go 2025-05-14 11:22:12.000000000 +0200 @@ -242,6 +242,13 @@ return fmt.Errorf("add flag: %w", err) } + if err := cli.AddFlag(cmd, &cfg.Timeout, "timeout", 0, "Fail if not finished in time", cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } + if err := cli.AddFlag(cmd, &cfg.TrackCreationTimeout, "resource-creation-timeout", 0, "Fail if resource creation tracking did not finish in time", cli.AddFlagOptions{ GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, Group: progressFlagGroup, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/release_uninstall.go new/nelm-1.4.0/cmd/nelm/release_uninstall.go --- old/nelm-1.3.0/cmd/nelm/release_uninstall.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/release_uninstall.go 2025-05-14 11:22:12.000000000 +0200 @@ -208,6 +208,13 @@ return fmt.Errorf("add flag: %w", err) } + if err := cli.AddFlag(cmd, &cfg.Timeout, "timeout", 0, "Fail if not finished in time", cli.AddFlagOptions{ + GetEnvVarRegexesFunc: cli.GetFlagGlobalAndLocalEnvVarRegexes, + Group: mainFlagGroup, + }); err != nil { + return fmt.Errorf("add flag: %w", err) + } + return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/cmd/nelm/usage.go new/nelm-1.4.0/cmd/nelm/usage.go --- old/nelm-1.3.0/cmd/nelm/usage.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/cmd/nelm/usage.go 2025-05-14 11:22:12.000000000 +0200 @@ -19,6 +19,13 @@ "github.com/werf/logboek" "github.com/werf/logboek/pkg/types" "github.com/werf/nelm/internal/common" + "github.com/werf/nelm/pkg/featgate" +) + +const ( + flagsHelpIndent = 10 + featGatesHelpIndent = 10 + minUsageWrapWidth = 40 ) const helpTemplate = ` @@ -60,6 +67,10 @@ {{- if .HasAvailableLocalFlags}} {{ flagsUsage .LocalFlags | trimTrailingWhitespaces }} {{- end }} + +{{- if not .HasAvailableSubCommands}} +{{ featGatesUsage | trimTrailingWhitespaces }} +{{- end }} ` var templateFuncs = template.FuncMap{ @@ -67,8 +78,9 @@ "trimTrailingWhitespaces": func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) }, - "flagsUsage": flagsUsage, - "commandsUsage": commandsUsage, + "flagsUsage": flagsUsage, + "commandsUsage": commandsUsage, + "featGatesUsage": featGatesUsage, } func usageFunc(c *cobra.Command) error { @@ -89,9 +101,6 @@ } func flagsUsage(fset *pflag.FlagSet) string { - const helpIndent = 10 - const minHelpWidthToWrap = 40 - terminalWidth := logboek.Streams().Width() groupsByPriority, groupedFlags := groupFlags(fset) @@ -126,16 +135,14 @@ header += fmt.Sprintf("=%s", flag.DefValue) } - helpWrapWidth := terminalWidth - helpIndent - var help string - if helpWrapWidth > minHelpWidthToWrap { + if terminalWidth > minUsageWrapWidth { help = logboek.FitText(flag.Usage, types.FitTextOptions{ - ExtraIndentWidth: helpIndent, - Width: helpWrapWidth + helpIndent, + ExtraIndentWidth: flagsHelpIndent, + Width: terminalWidth, }) } else { - help = fmt.Sprintf("%s%s", strings.Repeat(" ", helpIndent), flag.Usage) + help = fmt.Sprintf("%s%s", strings.Repeat(" ", flagsHelpIndent), flag.Usage) } line := fmt.Sprintf("%s\n%s", header, help) @@ -282,3 +289,37 @@ priority int short string } + +func featGatesUsage() string { + terminalWidth := logboek.Streams().Width() + + buf := new(bytes.Buffer) + lines := []string{} + + for i, featGate := range featgate.FeatGates { + if i == 0 { + lines = append(lines, "\nFeature gates:\n") + } + + header := fmt.Sprintf(" $%s=%v", featGate.EnvVarName(), featGate.Default()) + + var help string + if terminalWidth > minUsageWrapWidth { + help = logboek.FitText(featGate.Help, types.FitTextOptions{ + ExtraIndentWidth: featGatesHelpIndent, + Width: terminalWidth, + }) + } else { + help = fmt.Sprintf("%s%s", strings.Repeat(" ", featGatesHelpIndent), featGate.Help) + } + + line := fmt.Sprintf("%s\n%s", header, help) + lines = append(lines, line) + } + + for _, line := range lines { + fmt.Fprintln(buf, line) + } + + return buf.String() +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/go.mod new/nelm-1.4.0/go.mod --- old/nelm-1.3.0/go.mod 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/go.mod 2025-05-14 11:22:12.000000000 +0200 @@ -34,7 +34,7 @@ github.com/spf13/pflag v1.0.5 github.com/tidwall/sjson v1.2.5 github.com/wI2L/jsondiff v0.5.0 - github.com/werf/3p-helm v0.0.0-20250423070931-cbf97ffb83ad + github.com/werf/3p-helm v0.0.0-20250513175502-6861d09b2363 github.com/werf/common-go v0.0.0-20250417171011-97dbede6f27c github.com/werf/kubedog v0.13.1-0.20250411133038-3d8084fab0ec github.com/werf/lockgate v0.1.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/go.sum new/nelm-1.4.0/go.sum --- old/nelm-1.3.0/go.sum 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/go.sum 2025-05-14 11:22:12.000000000 +0200 @@ -413,8 +413,8 @@ github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/wI2L/jsondiff v0.5.0 h1:RRMTi/mH+R2aXcPe1VYyvGINJqQfC3R+KSEakuU1Ikw= github.com/wI2L/jsondiff v0.5.0/go.mod h1:qqG6hnK0Lsrz2BpIVCxWiK9ItsBCpIZQiv0izJjOZ9s= -github.com/werf/3p-helm v0.0.0-20250423070931-cbf97ffb83ad h1:UNOFSDUUCBNX0HSQPzVPySs7BP7KoqofdgszTOFIrrA= -github.com/werf/3p-helm v0.0.0-20250423070931-cbf97ffb83ad/go.mod h1:bwpkc66otpnI2/K8fteIF/IkrHrq6jrAFW5ETHPNa00= +github.com/werf/3p-helm v0.0.0-20250513175502-6861d09b2363 h1:71f/6hHWMGpm4+nKCKGbpkVMOd2ePzY//qgSBkEQXsY= +github.com/werf/3p-helm v0.0.0-20250513175502-6861d09b2363/go.mod h1:bwpkc66otpnI2/K8fteIF/IkrHrq6jrAFW5ETHPNa00= github.com/werf/common-go v0.0.0-20250417171011-97dbede6f27c h1:DOXbzVhCnkn9znHNgiD1UahLu59Dv48iFO/L8i4Z/bI= github.com/werf/common-go v0.0.0-20250417171011-97dbede6f27c/go.mod h1:taKDUxKmGfqNOlVx1O0ad5vdV4duKexTLO7Rch9HfeA= github.com/werf/kubedog v0.13.1-0.20250411133038-3d8084fab0ec h1:tyfkagRcJVtfBF4aoxmnE6/idE7YIPo4RqdJQXNoJRg= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/internal/chart/chart_tree.go new/nelm-1.4.0/internal/chart/chart_tree.go --- old/nelm-1.3.0/internal/chart/chart_tree.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/internal/chart/chart_tree.go 2025-05-14 11:22:12.000000000 +0200 @@ -37,7 +37,7 @@ ) func NewChartTree(ctx context.Context, chartPath, releaseName, releaseNamespace string, revision int, deployType common.DeployType, opts ChartTreeOptions) (*ChartTree, error) { - if featgate.FeatGateEnabled(featgate.FeatGateRemoteCharts) && !IsLocalChart(chartPath) { + if featgate.FeatGateRemoteCharts.Enabled() && !IsLocalChart(chartPath) { chartDownloader, chartRef, err := NewChartDownloader(ctx, chartPath, opts.RegistryClient, ChartDownloaderOptions{ CaFile: opts.KubeCAPath, SkipTLSVerify: opts.ChartRepoSkipTLSVerify, @@ -162,16 +162,19 @@ log.Default.Debug(ctx, "Rendering resources for chart at %q", chartPath) var standaloneCRDs []*resource.StandaloneCRD - for _, crd := range legacyChart.CRDObjects() { - for _, manifest := range releaseutil.SplitManifests(string(crd.File.Data)) { - if res, err := resource.NewStandaloneCRDFromManifest(manifest, resource.StandaloneCRDFromManifestOptions{ - FilePath: crd.Filename, - DefaultNamespace: releaseNamespace, - Mapper: opts.Mapper, - }); err != nil { - return nil, fmt.Errorf("error constructing standalone CRD for chart at %q: %w", chartPath, err) - } else { - standaloneCRDs = append(standaloneCRDs, res) + + if !opts.NoStandaloneCRDs { + for _, crd := range legacyChart.CRDObjects() { + for _, manifest := range releaseutil.SplitManifests(string(crd.File.Data)) { + if res, err := resource.NewStandaloneCRDFromManifest(manifest, resource.StandaloneCRDFromManifestOptions{ + FilePath: crd.Filename, + DefaultNamespace: releaseNamespace, + Mapper: opts.Mapper, + }); err != nil { + return nil, fmt.Errorf("error constructing standalone CRD for chart at %q: %w", chartPath, err) + } else { + standaloneCRDs = append(standaloneCRDs, res) + } } } } @@ -270,6 +273,7 @@ KubeConfig *kube.KubeConfig KubeVersion *chartutil.KubeVersion Mapper meta.ResettableRESTMapper + NoStandaloneCRDs bool RegistryClient *registry.Client SetValues []string StringSetValues []string diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/internal/release/legacy_release.go new/nelm-1.4.0/internal/release/legacy_release.go --- old/nelm-1.3.0/internal/release/legacy_release.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/internal/release/legacy_release.go 2025-05-14 11:22:12.000000000 +0200 @@ -48,6 +48,7 @@ Manifest: strings.Join(generalResourcesManifests, "\n---\n"), Config: rel.Values(), Chart: rel.LegacyChart(), + Labels: rel.Labels(), } return legacyRel, nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/internal/release/release.go new/nelm-1.4.0/internal/release/release.go --- old/nelm-1.3.0/internal/release/release.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/internal/release/release.go 2025-05-14 11:22:12.000000000 +0200 @@ -44,32 +44,38 @@ opts.InfoAnnotations = map[string]string{} } + if opts.Labels == nil { + opts.Labels = map[string]string{} + } + return &Release{ - name: name, - namespace: namespace, - revision: revision, - values: values, - legacyChart: legacyChart, - mapper: opts.Mapper, - status: status, - firstDeployed: opts.FirstDeployed, - lastDeployed: opts.LastDeployed, appVersion: legacyChart.Metadata.AppVersion, chartName: legacyChart.Metadata.Name, chartVersion: legacyChart.Metadata.Version, - infoAnnotations: opts.InfoAnnotations, - hookResources: hookResources, + firstDeployed: opts.FirstDeployed, generalResources: generalResources, + hookResources: hookResources, + infoAnnotations: opts.InfoAnnotations, + labels: opts.Labels, + lastDeployed: opts.LastDeployed, + legacyChart: legacyChart, + mapper: opts.Mapper, + name: name, + namespace: namespace, notes: notes, + revision: revision, + status: status, + values: values, }, nil } type ReleaseOptions struct { - InfoAnnotations map[string]string - Status helmrelease.Status FirstDeployed time.Time + InfoAnnotations map[string]string + Labels map[string]string LastDeployed time.Time Mapper meta.ResettableRESTMapper + Status helmrelease.Status } func NewReleaseFromLegacyRelease(legacyRelease *helmrelease.Release, opts ReleaseFromLegacyReleaseOptions) (*Release, error) { @@ -100,12 +106,18 @@ } } - rel, err := NewRelease(legacyRelease.Name, legacyRelease.Namespace, legacyRelease.Version, legacyRelease.Config, legacyRelease.Chart, hookResources, generalResources, legacyRelease.Info.Notes, ReleaseOptions{ - InfoAnnotations: legacyRelease.Info.Annotations, - Status: legacyRelease.Info.Status, + vals, err := chartutil.CoalesceValues(legacyRelease.Chart, legacyRelease.Config) + if err != nil { + return nil, fmt.Errorf("coalesce values for legacy release %q (namespace: %q, revision: %d): %w", legacyRelease.Name, legacyRelease.Namespace, legacyRelease.Version, err) + } + + rel, err := NewRelease(legacyRelease.Name, legacyRelease.Namespace, legacyRelease.Version, vals, legacyRelease.Chart, hookResources, generalResources, legacyRelease.Info.Notes, ReleaseOptions{ FirstDeployed: legacyRelease.Info.FirstDeployed.Time, + InfoAnnotations: legacyRelease.Info.Annotations, + Labels: legacyRelease.Labels, LastDeployed: legacyRelease.Info.LastDeployed.Time, Mapper: opts.Mapper, + Status: legacyRelease.Info.Status, }) if err != nil { return nil, fmt.Errorf("error building release %q (namespace: %q, revision: %d): %w", legacyRelease.Name, legacyRelease.Namespace, legacyRelease.Version, err) @@ -120,24 +132,23 @@ } type Release struct { - name string - namespace string - revision int - values map[string]interface{} - legacyChart *chart.Chart - mapper meta.ResettableRESTMapper - - status helmrelease.Status - firstDeployed time.Time - lastDeployed time.Time - appVersion string - chartName string - chartVersion string - infoAnnotations map[string]string - - hookResources []*resource.HookResource + appVersion string + chartName string + chartVersion string + firstDeployed time.Time generalResources []*resource.GeneralResource + hookResources []*resource.HookResource + infoAnnotations map[string]string + labels map[string]string + lastDeployed time.Time + legacyChart *chart.Chart + mapper meta.ResettableRESTMapper + name string + namespace string notes string + revision int + status helmrelease.Status + values map[string]interface{} } func (r *Release) Name() string { @@ -200,6 +211,10 @@ return r.infoAnnotations } +func (r *Release) Labels() map[string]string { + return r.labels +} + func (r *Release) ID() string { return fmt.Sprintf("%s:%s:%d", r.namespace, r.name, r.revision) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/chart_render.go new/nelm-1.4.0/pkg/action/chart_render.go --- old/nelm-1.3.0/pkg/action/chart_render.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/action/chart_render.go 2025-05-14 11:22:12.000000000 +0200 @@ -66,6 +66,7 @@ LogRegistryStreamOut io.Writer NetworkParallelism int OutputFilePath string + OutputNoPrint bool RegistryCredentialsPath string ReleaseName string ReleaseNamespace string @@ -84,23 +85,23 @@ ValuesStringSets []string } -func ChartRender(ctx context.Context, opts ChartRenderOptions) error { +func ChartRender(ctx context.Context, opts ChartRenderOptions) (*ChartRenderResultV1, error) { actionLock.Lock() defer actionLock.Unlock() currentDir, err := os.Getwd() if err != nil { - return fmt.Errorf("get current working directory: %w", err) + return nil, fmt.Errorf("get current working directory: %w", err) } homeDir, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("get home directory: %w", err) + return nil, fmt.Errorf("get home directory: %w", err) } opts, err = applyChartRenderOptionsDefaults(opts, currentDir, homeDir) if err != nil { - return fmt.Errorf("build chart render options: %w", err) + return nil, fmt.Errorf("build chart render options: %w", err) } if opts.SecretKey != "" { @@ -136,12 +137,12 @@ Token: opts.KubeToken, }) if err != nil { - return fmt.Errorf("construct kube config: %w", err) + return nil, fmt.Errorf("construct kube config: %w", err) } clientFactory, err = kube.NewClientFactory(ctx, kubeConfig) if err != nil { - return fmt.Errorf("construct kube client factory: %w", err) + return nil, fmt.Errorf("construct kube client factory: %w", err) } } @@ -160,7 +161,7 @@ helmRegistryClient, err := registry.NewClient(helmRegistryClientOpts...) if err != nil { - return fmt.Errorf("construct registry client: %w", err) + return nil, fmt.Errorf("construct registry client: %w", err) } releaseStorageOptions := release.ReleaseStorageOptions{ @@ -173,7 +174,7 @@ releaseStorage, err := release.NewReleaseStorage(ctx, opts.ReleaseNamespace, opts.ReleaseStorageDriver, releaseStorageOptions) if err != nil { - return fmt.Errorf("construct release storage: %w", err) + return nil, fmt.Errorf("construct release storage: %w", err) } chartextender.DefaultChartAPIVersion = opts.DefaultChartAPIVersion @@ -200,17 +201,17 @@ historyOptions, ) if err != nil { - return fmt.Errorf("construct release history: %w", err) + return nil, fmt.Errorf("construct release history: %w", err) } prevRelease, prevReleaseFound, err := history.LastRelease() if err != nil { - return fmt.Errorf("get last release: %w", err) + return nil, fmt.Errorf("get last release: %w", err) } _, prevDeployedReleaseFound, err := history.LastDeployedRelease() if err != nil { - return fmt.Errorf("get last deployed release: %w", err) + return nil, fmt.Errorf("get last deployed release: %w", err) } var newRevision int @@ -248,7 +249,7 @@ } else { ver, err := chartutil.ParseKubeVersion(opts.LocalKubeVersion) if err != nil { - return fmt.Errorf("parse local kube version %q: %w", opts.LocalKubeVersion, err) + return nil, fmt.Errorf("parse local kube version %q: %w", opts.LocalKubeVersion, err) } chartTreeOptions.KubeVersion = ver @@ -264,7 +265,7 @@ chartTreeOptions, ) if err != nil { - return fmt.Errorf("construct chart tree: %w", err) + return nil, fmt.Errorf("construct chart tree: %w", err) } var prevRelGeneralResources []*resource.GeneralResource @@ -315,20 +316,20 @@ ) if err := resProcessor.Process(ctx); err != nil { - return fmt.Errorf("process resources: %w", err) + return nil, fmt.Errorf("process resources: %w", err) } var showFiles []string for _, file := range opts.ShowOnlyFiles { absFile, err := filepath.Abs(file) if err != nil { - return fmt.Errorf("get absolute path for %q: %w", file, err) + return nil, fmt.Errorf("get absolute path for %q: %w", file, err) } if strings.HasPrefix(absFile, opts.Chart) { f, err := filepath.Rel(opts.Chart, absFile) if err != nil { - return fmt.Errorf("get relative path for %q: %w", absFile, err) + return nil, fmt.Errorf("get relative path for %q: %w", absFile, err) } if !strings.HasPrefix(f, chartTree.Name()) { @@ -352,7 +353,7 @@ if opts.OutputFilePath != "" { file, err := os.Create(opts.OutputFilePath) if err != nil { - return fmt.Errorf("create chart render output file %q: %w", opts.OutputFilePath, err) + return nil, fmt.Errorf("create chart render output file %q: %w", opts.OutputFilePath, err) } defer file.Close() @@ -365,16 +366,22 @@ } } - if opts.ShowCRDs { - for _, resource := range resProcessor.DeployableStandaloneCRDs() { - if len(showFiles) > 0 && !lo.Contains(showFiles, resource.FilePath()) { - continue - } + result := &ChartRenderResultV1{ + APIVersion: ChartRenderResultApiVersionV1, + } + for _, resource := range resProcessor.DeployableStandaloneCRDs() { + if len(showFiles) > 0 && !lo.Contains(showFiles, resource.FilePath()) { + continue + } + + if opts.ShowCRDs { if err := renderResource(resource.Unstructured(), resource.FilePath(), renderOutStream, renderColorLevel); err != nil { - return fmt.Errorf("render CRD %q: %w", resource.HumanID(), err) + return nil, fmt.Errorf("render CRD %q: %w", resource.HumanID(), err) } } + + result.CRDs = append(result.CRDs, resource.Unstructured().Object) } for _, resource := range resProcessor.DeployableHookResources() { @@ -383,8 +390,10 @@ } if err := renderResource(resource.Unstructured(), resource.FilePath(), renderOutStream, renderColorLevel); err != nil { - return fmt.Errorf("render hook resource %q: %w", resource.HumanID(), err) + return nil, fmt.Errorf("render hook resource %q: %w", resource.HumanID(), err) } + + result.Hooks = append(result.Hooks, resource.Unstructured().Object) } for _, resource := range resProcessor.DeployableGeneralResources() { @@ -393,11 +402,13 @@ } if err := renderResource(resource.Unstructured(), resource.FilePath(), renderOutStream, renderColorLevel); err != nil { - return fmt.Errorf("render general resource %q: %w", resource.HumanID(), err) + return nil, fmt.Errorf("render general resource %q: %w", resource.HumanID(), err) } + + result.Resources = append(result.Resources, resource.Unstructured().Object) } - return nil + return result, nil } func applyChartRenderOptionsDefaults(opts ChartRenderOptions, currentDir, homeDir string) (ChartRenderOptions, error) { @@ -490,3 +501,12 @@ return nil } + +const ChartRenderResultApiVersionV1 = "v1" + +type ChartRenderResultV1 struct { + APIVersion string `json:"apiVersion"` + CRDs []map[string]interface{} `json:"crds"` + Hooks []map[string]interface{} `json:"hooks"` + Resources []map[string]interface{} `json:"resources"` +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/errors.go new/nelm-1.4.0/pkg/action/errors.go --- old/nelm-1.3.0/pkg/action/errors.go 1970-01-01 01:00:00.000000000 +0100 +++ new/nelm-1.4.0/pkg/action/errors.go 2025-05-14 11:22:12.000000000 +0200 @@ -0,0 +1,22 @@ +package action + +import "fmt" + +type ReleaseNotFoundError struct { + ReleaseName string + ReleaseNamespace string +} + +func (e *ReleaseNotFoundError) Error() string { + return fmt.Sprintf("release %q (namespace %q) not found", e.ReleaseName, e.ReleaseNamespace) +} + +type ReleaseRevisionNotFoundError struct { + ReleaseName string + ReleaseNamespace string + Revision int +} + +func (e *ReleaseRevisionNotFoundError) Error() string { + return fmt.Sprintf("revision %d of release %q (namespace %q) not found", e.Revision, e.ReleaseName, e.ReleaseNamespace) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/release_get.go new/nelm-1.4.0/pkg/action/release_get.go --- old/nelm-1.3.0/pkg/action/release_get.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/action/release_get.go 2025-05-14 11:22:12.000000000 +0200 @@ -39,6 +39,7 @@ NetworkParallelism int OutputFormat string OutputNoPrint bool + PrintValues bool ReleaseStorageDriver string Revision int SQLConnectionString string @@ -119,27 +120,30 @@ return nil, fmt.Errorf("construct release history: %w", err) } - var ( - release *release.Release - releaseFound bool - ) + if history.Empty() { + return nil, &ReleaseNotFoundError{ + ReleaseName: releaseName, + ReleaseNamespace: releaseNamespace, + } + } + + var release *release.Release if opts.Revision == 0 { - release, releaseFound, err = history.LastRelease() + release, _, err = history.LastRelease() if err != nil { return nil, fmt.Errorf("get last release: %w", err) } } else { - release, releaseFound, err = history.Release(opts.Revision) + var revisionFound bool + release, revisionFound, err = history.Release(opts.Revision) if err != nil { return nil, fmt.Errorf("get release revision %d: %w", opts.Revision, err) - } - } - - if !releaseFound { - if opts.Revision == 0 { - return nil, fmt.Errorf("release %q (namespace %q) not found", releaseName, releaseNamespace) - } else { - return nil, fmt.Errorf("revision %d of release %q (namespace %q) not found", opts.Revision, releaseName, releaseNamespace) + } else if !revisionFound { + return nil, &ReleaseRevisionNotFoundError{ + ReleaseName: releaseName, + ReleaseNamespace: releaseNamespace, + Revision: opts.Revision, + } } } @@ -161,7 +165,8 @@ Version: release.ChartVersion(), AppVersion: release.AppVersion(), }, - Notes: release.Notes(), + Notes: release.Notes(), + Values: release.Values(), } for _, hook := range release.HookResources() { @@ -175,6 +180,11 @@ if !opts.OutputNoPrint { var resultMessage string + savedValues := result.Values + if !opts.PrintValues { + result.Values = nil + } + switch opts.OutputFormat { case JsonOutputFormat: b, err := json.MarshalIndent(result, "", strings.Repeat(" ", 2)) @@ -194,6 +204,10 @@ return nil, fmt.Errorf("unknown output format %q", opts.OutputFormat) } + if !opts.PrintValues { + result.Values = savedValues + } + var colorLevel color.Level if color.Enable { colorLevel = color.TermColorLevel() @@ -250,6 +264,7 @@ Release *ReleaseGetResultRelease `json:"release"` Chart *ReleaseGetResultChart `json:"chart"` Notes string `json:"notes"` + Values map[string]interface{} `json:"values"` Hooks []map[string]interface{} `json:"hooks"` Resources []map[string]interface{} `json:"resources"` } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/release_install.go new/nelm-1.4.0/pkg/action/release_install.go --- old/nelm-1.3.0/pkg/action/release_install.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/action/release_install.go 2025-05-14 11:22:12.000000000 +0200 @@ -76,11 +76,13 @@ KubeToken string LogRegistryStreamOut io.Writer NetworkParallelism int + NoInstallCRDs bool NoProgressTablePrint bool ProgressTablePrintInterval time.Duration RegistryCredentialsPath string ReleaseHistoryLimit int ReleaseInfoAnnotations map[string]string + ReleaseLabels map[string]string ReleaseStorageDriver string RollbackGraphPath string SQLConnectionString string @@ -90,6 +92,7 @@ SecretWorkDir string SubNotes bool TempDirPath string + Timeout time.Duration TrackCreationTimeout time.Duration TrackDeletionTimeout time.Duration TrackReadinessTimeout time.Duration @@ -103,6 +106,29 @@ actionLock.Lock() defer actionLock.Unlock() + if opts.Timeout == 0 { + return releaseInstall(ctx, releaseName, releaseNamespace, opts) + } + + ctx, ctxCancelFn := context.WithTimeoutCause(ctx, opts.Timeout, fmt.Errorf("action timed out after %s", opts.Timeout.String())) + defer ctxCancelFn() + + actionCh := make(chan error, 1) + go func() { + actionCh <- releaseInstall(ctx, releaseName, releaseNamespace, opts) + }() + + for { + select { + case err := <-actionCh: + return err + case <-ctx.Done(): + return context.Cause(ctx) + } + } +} + +func releaseInstall(ctx context.Context, releaseName, releaseNamespace string, opts ReleaseInstallOptions) error { currentDir, err := os.Getwd() if err != nil { return fmt.Errorf("get current working directory: %w", err) @@ -280,6 +306,7 @@ KubeCAPath: opts.KubeCAPath, KubeConfig: clientFactory.KubeConfig(), Mapper: clientFactory.Mapper(), + NoStandaloneCRDs: opts.NoInstallCRDs, RegistryClient: helmRegistryClient, SetValues: opts.ValuesSets, StringSetValues: opts.ValuesStringSets, @@ -355,6 +382,7 @@ InfoAnnotations: opts.ReleaseInfoAnnotations, FirstDeployed: firstDeployed, Mapper: clientFactory.Mapper(), + Labels: opts.ReleaseLabels, }, ) if err != nil { @@ -946,8 +974,10 @@ resProcessor.ReleasableGeneralResources(), prevDeployedRelease.Notes(), release.ReleaseOptions{ - FirstDeployed: prevDeployedRelease.FirstDeployed(), - Mapper: clientFactory.Mapper(), + InfoAnnotations: prevDeployedRelease.InfoAnnotations(), + FirstDeployed: prevDeployedRelease.FirstDeployed(), + Mapper: clientFactory.Mapper(), + Labels: prevDeployedRelease.Labels(), }, ) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/release_plan_install.go new/nelm-1.4.0/pkg/action/release_plan_install.go --- old/nelm-1.3.0/pkg/action/release_plan_install.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/action/release_plan_install.go 2025-05-14 11:22:12.000000000 +0200 @@ -65,6 +65,7 @@ KubeToken string LogRegistryStreamOut io.Writer NetworkParallelism int + NoInstallCRDs bool RegistryCredentialsPath string ReleaseStorageDriver string SQLConnectionString string @@ -73,6 +74,7 @@ SecretValuesPaths []string SecretWorkDir string TempDirPath string + Timeout time.Duration ValuesFileSets []string ValuesFilesPaths []string ValuesSets []string @@ -83,6 +85,29 @@ actionLock.Lock() defer actionLock.Unlock() + if opts.Timeout == 0 { + return releasePlanInstall(ctx, releaseName, releaseNamespace, opts) + } + + ctx, ctxCancelFn := context.WithTimeoutCause(ctx, opts.Timeout, fmt.Errorf("action timed out after %s", opts.Timeout.String())) + defer ctxCancelFn() + + actionCh := make(chan error, 1) + go func() { + actionCh <- releasePlanInstall(ctx, releaseName, releaseNamespace, opts) + }() + + for { + select { + case err := <-actionCh: + return err + case <-ctx.Done(): + return context.Cause(ctx) + } + } +} + +func releasePlanInstall(ctx context.Context, releaseName, releaseNamespace string, opts ReleasePlanInstallOptions) error { currentDir, err := os.Getwd() if err != nil { return fmt.Errorf("get current working directory: %w", err) @@ -237,6 +262,7 @@ KubeCAPath: opts.KubeCAPath, KubeConfig: clientFactory.KubeConfig(), Mapper: clientFactory.Mapper(), + NoStandaloneCRDs: opts.NoInstallCRDs, RegistryClient: helmRegistryClient, SetValues: opts.ValuesSets, StringSetValues: opts.ValuesStringSets, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/release_rollback.go new/nelm-1.4.0/pkg/action/release_rollback.go --- old/nelm-1.3.0/pkg/action/release_rollback.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/action/release_rollback.go 2025-05-14 11:22:12.000000000 +0200 @@ -54,6 +54,7 @@ RollbackReportPath string SQLConnectionString string TempDirPath string + Timeout time.Duration TrackCreationTimeout time.Duration TrackDeletionTimeout time.Duration TrackReadinessTimeout time.Duration @@ -63,6 +64,29 @@ actionLock.Lock() defer actionLock.Unlock() + if opts.Timeout == 0 { + return releaseRollback(ctx, releaseName, releaseNamespace, opts) + } + + ctx, ctxCancelFn := context.WithTimeoutCause(ctx, opts.Timeout, fmt.Errorf("action timed out after %s", opts.Timeout.String())) + defer ctxCancelFn() + + actionCh := make(chan error, 1) + go func() { + actionCh <- releaseRollback(ctx, releaseName, releaseNamespace, opts) + }() + + for { + select { + case err := <-actionCh: + return err + case <-ctx.Done(): + return context.Cause(ctx) + } + } +} + +func releaseRollback(ctx context.Context, releaseName, releaseNamespace string, opts ReleaseRollbackOptions) error { homeDir, err := os.UserHomeDir() if err != nil { return fmt.Errorf("get home directory: %w", err) @@ -241,8 +265,10 @@ resProcessor.ReleasableGeneralResources(), notes, release.ReleaseOptions{ - FirstDeployed: firstDeployed, - Mapper: clientFactory.Mapper(), + InfoAnnotations: releaseToRollback.InfoAnnotations(), + FirstDeployed: firstDeployed, + Mapper: clientFactory.Mapper(), + Labels: releaseToRollback.Labels(), }, ) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/action/release_uninstall.go new/nelm-1.4.0/pkg/action/release_uninstall.go --- old/nelm-1.3.0/pkg/action/release_uninstall.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/action/release_uninstall.go 2025-05-14 11:22:12.000000000 +0200 @@ -49,12 +49,36 @@ ReleaseHistoryLimit int ReleaseStorageDriver string TempDirPath string + Timeout time.Duration } func ReleaseUninstall(ctx context.Context, releaseName, releaseNamespace string, opts ReleaseUninstallOptions) error { actionLock.Lock() defer actionLock.Unlock() + if opts.Timeout == 0 { + return releaseUninstall(ctx, releaseName, releaseNamespace, opts) + } + + ctx, ctxCancelFn := context.WithTimeoutCause(ctx, opts.Timeout, fmt.Errorf("action timed out after %s", opts.Timeout.String())) + defer ctxCancelFn() + + actionCh := make(chan error, 1) + go func() { + actionCh <- releaseUninstall(ctx, releaseName, releaseNamespace, opts) + }() + + for { + select { + case err := <-actionCh: + return err + case <-ctx.Done(): + return context.Cause(ctx) + } + } +} + +func releaseUninstall(ctx context.Context, releaseName, releaseNamespace string, opts ReleaseUninstallOptions) error { currentDir, err := os.Getwd() if err != nil { return fmt.Errorf("get current working directory: %w", err) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/pkg/featgate/feat.go new/nelm-1.4.0/pkg/featgate/feat.go --- old/nelm-1.3.0/pkg/featgate/feat.go 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/pkg/featgate/feat.go 2025-05-14 11:22:12.000000000 +0200 @@ -8,13 +8,41 @@ "github.com/werf/nelm/internal/common" ) -const ( +var ( + FeatGateEnvVarsPrefix = caps.ToScreamingSnake(common.Brand) + "_FEAT_" + FeatGates = []*FeatGate{} + // TODO(v2): always enable - FeatGateRemoteCharts = "remote-charts" + FeatGateRemoteCharts = NewFeatGate( + "remote-charts", + `Allow not only local, but also remote charts as an argument to cli commands. Also adds the "--chart-version" option`, + ) ) -var FeatGateEnvVarsPrefix = caps.ToScreamingSnake(common.Brand) + "_FEAT_" +func NewFeatGate(name, help string) *FeatGate { + fg := &FeatGate{ + Name: name, + Help: help, + } + + FeatGates = append(FeatGates, fg) + + return fg +} + +type FeatGate struct { + Name string + Help string +} + +func (g *FeatGate) EnvVarName() string { + return FeatGateEnvVarsPrefix + caps.ToScreamingSnake(g.Name) +} + +func (g *FeatGate) Default() bool { + return false +} -func FeatGateEnabled(featGate string) bool { - return os.Getenv(FeatGateEnvVarsPrefix+caps.ToScreamingSnake(featGate)) == "true" +func (g *FeatGate) Enabled() bool { + return os.Getenv(g.EnvVarName()) == "true" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nelm-1.3.0/trdl_channels.yaml new/nelm-1.4.0/trdl_channels.yaml --- old/nelm-1.3.0/trdl_channels.yaml 2025-05-07 12:46:15.000000000 +0200 +++ new/nelm-1.4.0/trdl_channels.yaml 2025-05-14 11:22:12.000000000 +0200 @@ -2,12 +2,12 @@ - name: "1" channels: - name: alpha - version: 1.2.2 + version: 1.3.0 - name: beta version: 1.2.2 - name: ea - version: 1.1.5 + version: 1.2.2 - name: stable - version: 1.1.1 + version: 1.1.5 - name: rock-solid version: 1.1.1 ++++++ nelm.obsinfo ++++++ --- /var/tmp/diff_new_pack.Hz7Vjg/_old 2025-05-14 17:02:35.646722908 +0200 +++ /var/tmp/diff_new_pack.Hz7Vjg/_new 2025-05-14 17:02:35.650723076 +0200 @@ -1,5 +1,5 @@ name: nelm -version: 1.3.0 -mtime: 1746614775 -commit: d2d74179b19089543e60d302f74e969f6cbe8d6c +version: 1.4.0 +mtime: 1747214532 +commit: bbc21f6c6e7bc0b7b7791fd575a3899a17dda2b5 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/nelm/vendor.tar.gz /work/SRC/openSUSE:Factory/.nelm.new.30101/vendor.tar.gz differ: char 130, line 4
