Merge branch 'main' of https://github.com/helm/helm into filter-dup-repos

pull/11112/head
Felipe Santos 7 months ago
commit f692751a46

@ -1,14 +0,0 @@
---
# This file can be removed when Helm no longer uses CircleCI on any release
# branches. Once CircleCI is turned off this file can be removed.
version: 2
jobs:
build:
docker:
- image: cimg/go:1.18
steps:
- checkout

@ -1,7 +1,24 @@
version: 2
updates:
- # Keep dev-v3 branch dependencies up to date, while Helm v3 is within support
package-ecosystem: "gomod"
target-branch: "dev-v3"
directory: "/"
schedule:
interval: "daily"
groups:
k8s.io:
patterns:
- "k8s.io/api"
- "k8s.io/apiextensions-apiserver"
- "k8s.io/apimachinery"
- "k8s.io/apiserver"
- "k8s.io/cli-runtime"
- "k8s.io/client-go"
- "k8s.io/kubectl"
- package-ecosystem: "gomod"
target-branch: "main"
directory: "/"
schedule:
interval: "daily"
@ -16,6 +33,7 @@ updates:
- "k8s.io/client-go"
- "k8s.io/kubectl"
- package-ecosystem: "github-actions"
target-branch: "main"
directory: "/"
schedule:
interval: "daily"

@ -7,6 +7,6 @@
**Special notes for your reviewer**:
**If applicable**:
- [ ] this PR contains documentation
- [ ] this PR contains user facing changes (the `docs needed` label should be applied if so)
- [ ] this PR contains unit tests
- [ ] this PR has been tested for backwards compatibility

@ -1,18 +0,0 @@
name: Publish Release Assets to Asset Transparency Log
on:
release:
types: [published, created, edited, released]
jobs:
github_release_asset_transparency_log_publish_job:
runs-on: ubuntu-latest
name: Publish GitHub release asset digests to https://beta-asset.transparencylog.net
steps:
- name: Gather URLs from GitHub release and publish
id: asset-transparency
uses: transparencylog/github-releases-asset-transparency-verify-action@c77874b4514ae4003994ece9582675195fe012e2 # v11
- name: List verified and published URLs
run: echo "Verified URLs ${{ steps.asset-transparency.outputs.verified }}"
- name: List failed URLs
run: echo "Failed URLs ${{ steps.asset-transparency.outputs.failed }}"

@ -2,34 +2,30 @@ name: build-test
on:
push:
branches:
- 'main'
- 'release-**'
- "main"
- "dev-v3"
- "release-**"
pull_request:
branches:
- main
- "main"
- "dev-v3"
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@v3.6.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Setup Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # pin@4.1.0
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # pin@5.3.0
with:
go-version: '1.20'
- name: Install golangci-lint
run: |
curl -sSLO https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz
shasum -a 256 golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz | grep "^$GOLANGCI_LINT_SHA256 " > /dev/null
tar -xf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz
sudo mv golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64/golangci-lint /usr/local/bin/golangci-lint
rm -rf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64*
env:
GOLANGCI_LINT_VERSION: '1.51.2'
GOLANGCI_LINT_SHA256: '4de479eb9d9bc29da51aec1834e7c255b333723d38dbd56781c68e5dddc6a90b'
- name: Test style
run: make test-style
go-version: '1.23'
check-latest: true
- name: Test source headers are present
run: make test-source-headers
- name: Run unit tests
run: make test-coverage
- name: Test build

@ -13,13 +13,21 @@ name: "CodeQL"
on:
push:
branches: [ main ]
branches:
- main
- dev-v3
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
branches:
- main
- dev-v3
schedule:
- cron: '29 6 * * 6'
permissions:
contents: read
security-events: write
jobs:
analyze:
name: Analyze
@ -35,11 +43,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@v3.6.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 # pinv2.21.8
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # pinv3.26.6
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -50,7 +58,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 # pinv2.21.8
uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # pinv3.26.6
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -64,4 +72,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 # pinv2.21.8
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # pinv3.26.6

@ -0,0 +1,26 @@
name: golangci-lint
on:
push:
pull_request:
permissions:
contents: read
jobs:
golangci:
name: golangci-lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Setup Go
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # pin@5.3.0
with:
go-version: '1.23'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@051d91933864810ecd5e2ea2cfd98f6a5bca5347 #pin@6.3.2
with:
version: v1.62

@ -0,0 +1,24 @@
name: govulncheck
on:
push:
paths:
- go.sum
schedule:
- cron: "0 0 * * *"
permissions: read-all
jobs:
govulncheck:
name: govulncheck
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # pin@5.3.0
with:
go-version: '1.23'
check-latest: true
- name: govulncheck
uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # pin@1.0.4
with:
go-package: ./...

@ -7,6 +7,8 @@ on:
branches:
- main
permissions: read-all
# Note the only differences between release and canary-release jobs are:
# - only canary passes --overwrite flag
# - the VERSION make variable passed to 'make dist checksum' is expected to
@ -15,28 +17,39 @@ on:
jobs:
release:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
runs-on: ubuntu-latest-16-cores
steps:
- name: Checkout source code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@v3.6.0
fetch-depth: 0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # pin@4.1.0
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # pin@5.3.0
with:
go-version: '1.20'
go-version: '1.23'
- name: Run unit tests
run: make test-coverage
- name: Build Helm Binaries
run: |
make build-cross
set -eu -o pipefail
make build-cross VERSION="${{ github.ref_name }}"
make dist checksum VERSION="${{ github.ref_name }}"
- name: Set latest version
run: |
set -eu -o pipefail
mkdir -p _dist_versions
# Push the latest semver tag, excluding prerelease tags
git tag | sort -r --version-sort | grep '^v[0-9]' | grep -v '-' | head -n1 > _dist/helm-latest-version
LATEST_VERSION="$(git tag | sort -r --version-sort | grep '^v[0-9]' | grep -v '-' | head -n1)"
echo "LATEST_VERSION=${LATEST_VERSION}"
echo "${LATEST_VERSION}" > _dist_versions/helm-latest-version
echo "${LATEST_VERSION}" > _dist_versions/helm3-latest-version
- name: Upload Binaries
uses: bacongobbler/azure-blob-storage-upload@50f7d898b7697e864130ea04c303ca38b5751c50 # pin@3.0.0
@ -49,17 +62,29 @@ jobs:
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
extra_args: '--pattern helm-*'
- name: Upload Version tag files
uses: bacongobbler/azure-blob-storage-upload@50f7d898b7697e864130ea04c303ca38b5751c50 # pin@3.0.0
env:
AZURE_STORAGE_CONNECTION_STRING: "${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}"
AZURE_STORAGE_CONTAINER_NAME: "${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}"
with:
overwrite: 'true'
source_dir: _dist_versions
container_name: ${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
canary-release:
runs-on: ubuntu-latest
runs-on: ubuntu-latest-16-cores
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout source code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # pin@v3.6.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Setup Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # pin@4.1.0
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # pin@5.3.0
with:
go-version: '1.20'
go-version: '1.23'
check-latest: true
- name: Run unit tests
run: make test-coverage

@ -0,0 +1,69 @@
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '25 7 * * 0'
push:
branches: [ "main" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

@ -2,11 +2,14 @@ name: "Close stale issues"
on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3.0.14
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.'

3
.gitignore vendored

@ -5,8 +5,11 @@
.idea/
.vimrc
.vscode/
.devcontainer/
_dist/
_dist_versions/
bin/
vendor/
# Ignores charts pulled for dependency build tests
cmd/helm/testdata/testcharts/issue-7233/charts/*
.pre-commit-config.yaml

@ -20,6 +20,26 @@ linters-settings:
gofmt:
simplify: true
goimports:
local-prefixes: helm.sh/helm/v3
local-prefixes: helm.sh/helm/v4
dupl:
threshold: 400
issues:
exclude-rules:
# Helm, and the Go source code itself, sometimes uses these names outside their built-in
# functions. As the Go source code has re-used these names it's ok for Helm to do the same.
# Linting will look for redefinition of built-in id's but we opt-in to the ones we choose to use.
- linters:
- revive
text: "redefines-builtin-id: redefinition of the built-in function append"
- linters:
- revive
text: "redefines-builtin-id: redefinition of the built-in function clear"
- linters:
- revive
text: "redefines-builtin-id: redefinition of the built-in function max"
- linters:
- revive
text: "redefines-builtin-id: redefinition of the built-in function min"
- linters:
- revive
text: "redefines-builtin-id: redefinition of the built-in function new"

@ -5,12 +5,21 @@
# Organizations Using Helm
- [Blood Orange](https://bloodorange.io)
- [IBM](https://www.ibm.com)
- [Microsoft](https://microsoft.com)
- [Qovery](https://www.qovery.com/)
- [Samsung SDS](https://www.samsungsds.com/)
- [Softonic](https://hello.softonic.com/)
- [Ville de Montreal](https://montreal.ca)
- [IBM](https://www.ibm.com)
- [InfoCert](https://www.infocert.it/)
- [Intercept](https://Intercept.cloud)
- [Microsoft](https://microsoft.com)
- [New Relic](https://www.newrelic.com)
- [Octopus Deploy](https://octopus.com/)
- [Omnistrate](https://omnistrate.com)
- [Oracle](www.oracle.com)
- [Percona](https://www.percona.com)
- [Qovery](https://www.qovery.com/)
- [Samsung SDS](https://www.samsungsds.com/)
- [Softonic](https://hello.softonic.com/)
- [SyncTune](https://mb-consulting.dev)
- [Syself](https://syself.com)
- [Ville de Montreal](https://montreal.ca)
_This file is part of the CNCF official documentation for projects._

@ -11,9 +11,13 @@ vulnerability_, please email a report to
[cncf-helm-security@lists.cncf.io](mailto:cncf-helm-security@lists.cncf.io). This will give us a
chance to try to fix the issue before it is exploited in the wild.
## Helm v3 and v4
Helm v4 is currently under development on the `main` branch. During the development of Helm v4 and for some time after its released, Helm v3 will continue to be supported and developed on the `dev-v3` branch. Helm v3 will continue to get bug fixes and updates for new Kubernetes releases. Helm v4 is where new features and major changes will happen. For features to be backported to Helm v3, an exception will be needed. Bugs should first be fixed on Helm v4 and then backported to Helm v3.
## Sign Your Work
The sign-off is a simple line at the end of the explanation for a commit. All commits needs to be
The sign-off is a simple line at the end of the explanation for a commit. All commits need to be
signed. Your signature certifies that you wrote the patch or otherwise have the right to contribute
the material. The rules are pretty simple, if you can certify the below (from
[developercertificate.org](https://developercertificate.org/)):
@ -66,6 +70,18 @@ Use your real name (sorry, no pseudonyms or anonymous contributions.)
If you set your `user.name` and `user.email` git configs, you can sign your commit automatically
with `git commit -s`.
The following command will update your git config with `user.email`:
``` bash
git config --global user.email joe.smith@example.com
```
This command will update your git config with `user.name`:
``` bash
git config --global user.name "Joe Smith"
```
Note: If your git config information is set properly then viewing the `git log` information for your
commit will look something like this:
@ -115,8 +131,9 @@ Helm maintains a strong commitment to backward compatibility. All of our changes
formats are backward compatible from one major release to the next. No features, flags, or commands
are removed or substantially modified (unless we need to fix a security issue).
We also try very hard to not change publicly accessible Go library definitions inside of the `pkg/`
directory of our source code.
We also remain committed to not changing publicly accessible Go library definitions inside of the `pkg/` directory of our source code in a non-backwards-compatible way.
For more details on Helms minor and patch release backwards-compatibility rules, please read [HIP-0004](https://github.com/helm/community/blob/main/hips/hip-0004.md)
For a quick summary of our backward compatibility guidelines for releases between 3.0 and 4.0:
@ -126,30 +143,9 @@ For a quick summary of our backward compatibility guidelines for releases betwee
(barring the cases where (a) Kubernetes itself changed, and (b) the chart worked because it
exploited a bug)
- Chart repository functionality MUST be backward compatible
- Go libraries inside of `pkg/` SHOULD remain backward compatible, though code inside of `cmd/` and
- Go libraries inside of `pkg/` MUST remain backward compatible, though code inside of `cmd/` and
`internal/` may be changed from release to release without notice.
## Support Contract for Helm 2
With Helm 2's current release schedule, we want to take into account any migration issues for users
due to the upcoming holiday shopping season and tax season. We also want to clarify what actions may
occur after the support contract ends for Helm 2, so that users will not be surprised or caught off
guard.
After Helm 2.15.0 is released, Helm 2 will go into "maintenance mode". We will continue to accept
bug fixes and fix any security issues that arise, but no new features will be accepted for Helm 2.
All feature development will be moved over to Helm 3.
6 months after Helm 3.0.0's public release, Helm 2 will stop accepting bug fixes. Only security
issues will be accepted.
12 months after Helm 3.0.0's public release, support for Helm 2 will formally end. Download links
for the Helm 2 client through Google Cloud Storage, the Docker image for Tiller stored in Google
Container Registry, and the Google Cloud buckets for the stable and incubator chart repositories may
no longer work at any point. Client downloads through `get.helm.sh` will continue to work, and we
will distribute a Tiller image that will be made available at an alternative location which can be
updated with `helm init --tiller-image`.
## Issues
Issues are used as the primary method for tracking anything to do with the Helm project.
@ -195,7 +191,7 @@ below.
See [Proposing an Idea](#proposing-an-idea). Smaller quality-of-life enhancements are exempt.
- Issues that are labeled as `feature` or `bug` should be connected to the PR that resolves it.
- Whoever is working on a `feature` or `bug` issue (whether a maintainer or someone from the
community), should either assign the issue to themself or make a comment in the issue saying
community), should either assign the issue to themselves or make a comment in the issue saying
that they are taking it.
- `proposal` and `support/question` issues should stay open until resolved or if they have not
been active for more than 30 days. This will help keep the issue queue to a manageable size
@ -282,9 +278,9 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
#### Documentation PRs
Documentation PRs will follow the same lifecycle as other PRs. They will also be labeled with the
`docs` label. For documentation, special attention will be paid to spelling, grammar, and clarity
(whereas those things don't matter *as* much for comments in code).
Documentation PRs should be made on the docs repo: <https://github.com/helm/helm-www>. Keeping Helm's documentation up to date is highly desirable, and is recommended for all user facing changes. Accurate and helpful documentation is critical for effectively communicating Helm's behavior to a wide audience.
Small, ad-hoc changes/PRs to Helm which introduce user facing changes, which would benefit from documentation changes, should apply the `docs needed` label. Larger changes associated with a HIP should track docs via that HIP. The `docs needed` label doesn't block PRs, and maintainers/PR reviewers should apply discretion judging in whether the `docs needed` label should be applied.
## The Triager
@ -327,6 +323,7 @@ The following tables define all label types used for Helm. It is split up by cat
| `needs rebase` | Indicates a PR needs to be rebased before it can be merged |
| `needs pick` | Indicates a PR needs to be cherry-picked into a feature branch (generally bugfix branches). Once it has been, the `picked` label should be applied and this one removed |
| `picked` | This PR has been cherry-picked into a feature branch |
| `docs needed` | Tracks PRs that introduces a feature/change for which documentation update would be desirable (non-blocking). Once a suitable documentation PR has been created, then this label should be removed |
#### Size labels

118
KEYS

@ -940,3 +940,121 @@ AirPev6SluPhLJ2mswaK3THlhOZulKO/VIEJ6g50m5Vj3hdYf6sR603yK9rP+3iu
IagTQt2SGfW3Ap0RO3Yt+w29BpZ1CZ5Ml4gAYkXz0hiiMnVRhlcLIOHoFw==
=h3+3
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2018-12-08 [SC]
208DD36ED5BB3745A16743A4C7C6FBB5B91C1155
uid [ultimate] Scott Rigby <scott@r6by.com>
sig 3 C7C6FBB5B91C1155 2018-12-08 [self-signature]
sig 134FC1555856DA4F 2018-12-13 [User ID not found]
sig 62F49E747D911B60 2018-12-17 [User ID not found]
sig F54982D216088EE1 2019-01-05 [User ID not found]
sub rsa4096 2018-12-08 [E]
sig C7C6FBB5B91C1155 2018-12-08 [self-signature]
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFwMUAcBEADplQ+msULZ4kt01bXDvZ66MSVe5Fi1cPqAa/5/ZtaHZWSKrcN6
K0cadpozJp74HSZzORLYV/50EGwXU+OG1dFe73FbsTCgQyLCbh/OjT+Exq553g2D
/IB4/6/vCs9XXiYdKot3P2NsHI6RqeGqgW2IkFVsMXO2Lq1XKFTWQniO7PhHW8nG
Trub7HrxR6i9KHtVtxLs+XoXY7Jj0gB7WyRkYjHLXti4VtvcBq0WK3pSgEIy5MwR
WDepmle8n8EJZrh3T323YM41MXKGT00wCSKMbSHJO7QssiOda9XluC175HDfihm3
q5OKV2ZYIbChsQxuJz1Y97hwZ5KkLn//W2pxTdOElOcynFpQNx7D4b6UTP2DCCRc
n41SiDIyHg25cUXXAkJWlYRD1koGfLBipJA0DcKqlh3W+8zNfngZ0PSxwFtJwSre
Zx5I5uHAgKO5nS4hLxGYUMv+MsSKHMYR6qkqFg1Eal6tTa68bPFTbzypDmMUKXZT
sZtZ79WoIUU9D3O+F+Z9rxwaQ3Dv7J49FdbLPB3zqENqX7OWHZ38m5dsweTFhQi+
4AaDLEMiqMi27SfPkF1/+JDc1SOoLVo9QgukqhFlz6qEIbud7LUfpeKBRNJsfdr6
HE3cH8MWHInnlJ49De1oLl5bwAwScQig5jmv5DZxN5qdTg64vgoreBLgsQARAQAB
tBxTY290dCBSaWdieSA8c2NvdHRAcjZieS5jb20+iQJOBBMBCAA4FiEEII3TbtW7
N0WhZ0Okx8b7tbkcEVUFAlwMUAcCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA
CgkQx8b7tbkcEVUIPg//cK8zaAOUIFClQ31TeQnwHsJwcBMeRRegUjt4cIYSjv3F
oNe5EQKxgpwT/1iegvRURjxHbhSE0paQ9wsi8Tr5Ygs0DJLDp4PJVZtVHJ5qB3XT
VuCiqyb90kIG+ok1m8FHBwpeU7o2a05InYwJLWowBQTulhrS4VKzrPs2kMu4z5Am
5sbs70f0hRLUXGmEAFUnZm+lpF48/PCMNSPrxgZ6rtNqtXq8oCyPNvyO8Ou130tv
TSoHx5Tobz3RnSeDXpidC/Z1rQGq4Spi+a0WwC9BCArDvtOrQBoeQFgdpy5OsE/t
QfQNEffyLRLlAZgXewOC+PeF+xrz+ku/rMBlt/h0h5UcMS3joUHTRZyQoA5dzWZh
K8pMXYppHXJtxl0fNSG1rH1vsTu+Mjxmd9eBwaDxnBFwzBOukiwSUgW1r2DyZ7S+
25ZW62lcz/E9+sgWV/PGR+YKGbsDdnraPdQEc95k8NQnYsX/7YO/WXJfP7Cdqd8X
dAAVW6/dGUp2i4QogEYk+GeJz3rJJK24Wzgf2FJ32FuSH8uOQUH93h5QZbJAD+pD
R2qZAZjCHYAgvSuqnmDlefvG95TwIVzy3OFWbLwp6YyBdyZrdpTafER4zk17f0HF
xS3z3LG0LsEWHShxccjzjoAUeppAU0Qojag3kKLoCwveambIdjXIxliBr/S2VSWJ
AjMEEAEKAB0WIQTlYbNM52c/3RO87D71SYLSFgiO4QUCXC/89gAKCRD1SYLSFgiO
4YfMEACS7/90VvyhbcQYB/2N6dYYuVd4tMRpM7jgWO6LqDDh3C9S2NI2bzwxGFBR
nqV9N6fD+yLug/dtAPq5D4i7AXzRqPA8XQ2ky/1EJOv5EmOl6NYnUZafEBMDMai6
F6XOji2JR7b6xlRC7GwzUdMR1rn8eyCxuJobgB+wMzfcSAnaDsH1qU7a5ohEewtQ
IgEcLLmiLapCqNXm0l5oIYQQypbRrogw4ePw8KcLDreRtPCpPdLIThqdfkXLQ69Z
ZwGd1+Pg6xgu7MJggnNq2RYortW/Jx9WUFs0Jj9c+Yz5pPeQmc1jjF4uHvxfi0gt
wFqL+bu+HfxI/Hiudkzzp3v+Llmjk6RypJowLLk5cxMxv7XMfK3cLLDO5uw0pPiq
l1f4u4P3YeRLv5YFh8SRrk/PadEqFr10mIOmreASq9LivJkhGK9eqQ0X7zQfHmDq
S3nw62ousqlmEld39MIAMn02Ak9i9CD2M3G2O5gCAcvnFlWblx5CN9Pjc6kOb52W
eDMYisUmKnIkChzvAlfh8PhvQfLUpKN1AmzUOcXJokpu1Yx7OGaoDnfpXSHS8fe8
pu0jMsEhlqsNxNS6y5N0tWjxjYg7D1Qpeq3O4oft4HiO3ZfMPI4V1tatfeohnjic
UJkpVsS4RZybu8aqNGc8i/ggagiWc50oydK8Lp906XfOcEert4kCMwQQAQoAHRYh
BKuiUpWY9mJsQg0zW2L0nnR9kRtgBQJcF85IAAoJEGL0nnR9kRtg0OMQAKqpxGtA
uaMknrZxnxu+y4FXXrX/W2TLlF7Ns71upXhitwSWk0pVJi+OZUvIGj+8yCj2MEg6
o5qBJPyo8TuwIh1YfxBYigY5Hmt/uVVbKBM/VXyKDxzGrSts+r73cxf3BpPfyANB
a90LjKHvFv0czu5sfiRMHU9GCOehnBukdZ9PhOOcRuUlHoHlSf21x9kxa1tUFPVJ
eeoVOOnONDK6Dzmi6GoGRTq3X/HZ2JjhcdYSn37z/KxmZ/SNiwat60gw5zJHTh0a
dM54hwsdsp48/avpF8BlTgXsoH5dVdbaOTyNGXBbQaoL09FY2x2eXaFCP3RbMWK8
TpWh0Ijs/3JLFJ20jrvZqsmxmwIX25TmBb4UoR3HSEHFasYoIxv/me6V4oB7D1SJ
F3q5scPnRzV3I5wCRljKJcNHQbNb/c9Xnt5UVnLHRtkZW/NUw93H5Bq15dkq4U6i
prC0EgjfiWy2Esd9TiGb5kRuN0/duUY/d7dewp1tJGDRZACwWwHyYkPiPA6gzQfN
83yk29evZeX1rSBslnXSLzuwhVJc28KNZCeEcC2o1JninqfqoYnypgSFOS5BK6ZG
5YJD0gkKFX+ImC8OSKsIJ6QyrzyOBb7UwRcK5qlvYZGYgrt+B+mFDpxWPkzgpfMe
CvE0nUCgKDNg0Yvhr5w9JhK+49Qn6TuTADMIiQIzBBABCAAdFiEE2o9xo8issez5
FC4eE0/BVVhW2k8FAlwSzMYACgkQE0/BVVhW2k8jEQ//cV4+ke8eHhwwxCPZd+lA
mvgzalwSiZZ8H0EgAB2cK0LXEFe07XGKxe2tDkf5FDIQcNm7sIk0OcLhJzYX0p9P
A7ZzO2tZu8QuZlUqt4VnDL7B3xeW2Sh3STEmw80wubkgauRRysflAHIw3edchnIX
9Hq55MLBBAplQFkpA/Y7arg3Bn8v/8YQlULc30xRO8EoyxD+zyl+Ic+xFtFUxNc/
2dkqkdjq0Ohq89wTGTy0jaSI8INhZTGqR0cEYQPKZD+PXUUym/TaPKJKXagqxmu2
XXBv6QPp46a58viBxMj6+fl1JJH3DxNF9YY++7Xp8CckA3TKDA9hxOJK6wbrTzDB
DB+tjcwR4ff8QLv/CV3psyk2fX9CGCBdr0k0SCMQSFcHM8pKagkySjG+EJ1Tcflf
UDY2LD33n2BBIdCaQTu6u+Zeqq2e6R3UXm/raXuGrxUxzvOQBIhb7XaC6nhfDu8k
07yN/Tjwp+rgHt9ouH4pfFbGpvaIomBJq6pkTOk9ywDtHlSatqoVkbrbKpNzmwf8
z7pt+ICtKqAAWQTPFPD83h6elP26GKlsyXyhT7HNmKUHaXInEbaD+IoCJ+wY/O3i
gHV2Dn4QllSBSBhYlhl6utmP1zqwJJ0rI39mPS+nMXOhGB+bJ8EzAF/3N5J6y3TG
FnMEJD8qgdpDEgztjHUSAy25Ag0EXAxQBwEQALqYikkk0Ur4gn9PjxtjW4OxS5J4
e/u0UyOsv8znFM7CG9Ndha9rQs/7c7NEf3e+K6a7XqhzDKtyGAFVFlZArxbe6X8e
UV1OidOaH46z2vmtWOJYHIupXHlXS9LeXNO+pJjCNEAzmHbGjpkjGtNz6Opl4Uae
LoMFubRViXhvD8pBF72dGUlp8m+U4yeXJ03/q0sR94AdTA+1OzGd2+1s7PvL5XAx
BwXqx9gccMYhrNRPyBo/yRA+Wf4ewwluIVBMi9cpR1sNF4ITIYCH4i3mf4NJvg7P
0sPBY0s9k2jvHGLpINbFk6PkMtaRpqmgw695szTz16Gp0j41hRnEh7KnGneEp+SU
6A8A9UGnjM9upR/d1591I5gT2U+6Q05B8RtJQUmd3HBeHEBgftjgBR0tstH8qeac
Xc4V81OGlf3tdYP/LVggIlv8V5cdSZ4Bd3BXYWj2TIc2RmwWA2LWf4SA6JYvhEfp
OxOzzphlgPtZF0kneEgV/b/D/KQqEk7MyZl3gN+LNk+zX7VJ2RDeUiUnoxZDFJGi
jsbZd7yoDLkYvGiFkcQXORs2zbucweVXXK1Gyskj0c3Ih4syYYmKS0WJHMEozJUl
b81oa7kSc2XFArcAnPz2c1yErfzcCAlg/HImkZmAgVqAfuRyxZ426F7CYucHAOcE
N7bpIrOkqFp3uUb3ABEBAAGJAjYEGAEIACAWIQQgjdNu1bs3RaFnQ6THxvu1uRwR
VQUCXAxQBwIbDAAKCRDHxvu1uRwRVbFaD/0e57rP3H+1rUoGhRO0oeIveQqIdd9V
LKXUYuwzoK3HLg3BYUDEN03RS0KyNMYlHpnjyFl5L2JuXqGiJd/eu2iRXCwUMRb7
SPvH7gypa1NUK5te85+Y8JhXOMjwZkly3zS2nRTyvHxMn9EV7NPlT/oEVu/woPrM
o7XzmPChuvnk8pLWBW04wg5G5atDbu5+QVZlecNCrtRYJg/Cd8alKpJSeZX7y3cy
fe2P20Gv0UOipKWaAFL55zFLbmu7HWVumYAKs6T+X/pZqmcfMaVwodIBeRJxRIvl
PkrBxljahaFGOdgJ6FVnmO34uoYcpd019NEr9gbPoaFWmw37h3Tnc6U5sLAouaV4
AERWmwBPIVTizYt1h8Qj4qyBhJ+QgZMjPlRqHWPZogHfMXDQV4gw3jgvVWTMVp1Z
gDQgrFNbw02CqPwgtFn15VNwAv/4vbyToRhc3pG54e3xwdAFM8R2uM9lHJKuHafW
7aFUk7aA20k8SG2BsZalb6tZLGxgcZOwMdO3lnLMPu1I5oOLl4cVoUIRZxtgmrbQ
ROaGdXGIgO7fJBXXogMxjUGhMola+v6ioFQpbOnJRAr2AUVBCrrEgHoodAufGTDu
nk38BkgHg3LHjCbCNEVkSK2TMT69A58iwpY9WUQlphsiz4WBpafSPbv/jSlsm7uK
TNWtbFGBRpJyEg==
=w141
-----END PGP PUBLIC KEY BLOCK-----
pub ed25519 2024-07-09 [SC]
7FEC81FACC7FFB2A010ADD13C2D40F4D8196E874
uid [ultimate] Robert Sirchia (I like turtles.) <rsirchia@outlook.com>
sig 3 C2D40F4D8196E874 2024-07-09 [self-signature]
sub cv25519 2024-07-09 [E]
sig C2D40F4D8196E874 2024-07-09 [self-signature]
-----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEZo2C6xYJKwYBBAHaRw8BAQdA8kCWaI+FlCabcTw8EVeiMkokyWDalgl/Inbn
ACcGN1e0N1JvYmVydCBTaXJjaGlhIChJIGxpa2UgdHVydGxlcy4pIDxyc2lyY2hp
YUBvdXRsb29rLmNvbT6IkwQTFgoAOxYhBH/sgfrMf/sqAQrdE8LUD02Bluh0BQJm
jYLrAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEMLUD02Bluh0dyYA
/i7RB6m3MXNA8ei7GD8uQVpLfCRgEFsqSS/AzAOu8NGhAQCbw1kWL3AUll7KKtiQ
UE96nhCk+HnkQeVkWYS+MZ1tALg4BGaNgusSCisGAQQBl1UBBQEBB0CCA6Au4krL
YinQq9aAs29fFeRu/ye3PqQuz5jZ2r1ScAMBCAeIeAQYFgoAIBYhBH/sgfrMf/sq
AQrdE8LUD02Bluh0BQJmjYLrAhsMAAoJEMLUD02Bluh0KH4BAMSwEIGkoQl10LN3
K6V08VpFmniENmCDHshXYq0gGiTDAP9FsXl2UtmFU5xuYxH4fRKIxgmxJRAFMWI8
u3Rdu/s+DQ==
=smBO
-----END PGP PUBLIC KEY BLOCK-----

@ -1,8 +1,8 @@
BINDIR := $(CURDIR)/bin
INSTALL_PATH ?= /usr/local/bin
DIST_DIRS := find * -type d -exec
TARGETS := darwin/amd64 darwin/arm64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum darwin-arm64.tar.gz darwin-arm64.tar.gz.sha256 darwin-arm64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum
TARGETS := darwin/amd64 darwin/arm64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x linux/riscv64 windows/amd64 windows/arm64
TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum darwin-arm64.tar.gz darwin-arm64.tar.gz.sha256 darwin-arm64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum linux-riscv64.tar.gz linux-riscv64.tar.gz.sha256 linux-riscv64.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum windows-arm64.zip windows-arm64.zip.sha256 windows-arm64.zip.sha256sum
BINNAME ?= helm
GOBIN = $(shell go env GOBIN)
@ -11,7 +11,7 @@ GOBIN = $(shell go env GOPATH)/bin
endif
GOX = $(GOBIN)/gox
GOIMPORTS = $(GOBIN)/goimports
ARCH = $(shell uname -p)
ARCH = $(shell go env GOARCH)
ACCEPTANCE_DIR:=../acceptance-testing
# To specify the subset of acceptance tests to run. '.' means all tests
@ -44,7 +44,7 @@ BINARY_VERSION ?= ${GIT_TAG}
# Only set Version if building a tag or VERSION is set
ifneq ($(BINARY_VERSION),)
LDFLAGS += -X helm.sh/helm/v3/internal/version.version=${BINARY_VERSION}
LDFLAGS += -X helm.sh/helm/v4/internal/version.version=${BINARY_VERSION}
endif
VERSION_METADATA = unreleased
@ -53,9 +53,9 @@ ifneq ($(GIT_TAG),)
VERSION_METADATA =
endif
LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=${VERSION_METADATA}
LDFLAGS += -X helm.sh/helm/v3/internal/version.gitCommit=${GIT_COMMIT}
LDFLAGS += -X helm.sh/helm/v3/internal/version.gitTreeState=${GIT_DIRTY}
LDFLAGS += -X helm.sh/helm/v4/internal/version.metadata=${VERSION_METADATA}
LDFLAGS += -X helm.sh/helm/v4/internal/version.gitCommit=${GIT_COMMIT}
LDFLAGS += -X helm.sh/helm/v4/internal/version.gitTreeState=${GIT_DIRTY}
LDFLAGS += $(EXT_LDFLAGS)
# Define constants based on the client-go version
@ -63,10 +63,10 @@ K8S_MODULES_VER=$(subst ., ,$(subst v,,$(shell go list -f '{{.Version}}' -m k8s.
K8S_MODULES_MAJOR_VER=$(shell echo $$(($(firstword $(K8S_MODULES_VER)) + 1)))
K8S_MODULES_MINOR_VER=$(word 2,$(K8S_MODULES_VER))
LDFLAGS += -X helm.sh/helm/v3/pkg/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v3/pkg/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
LDFLAGS += -X helm.sh/helm/v3/pkg/chartutil.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v3/pkg/chartutil.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/lint/rules.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/chartutil.k8sVersionMajor=$(K8S_MODULES_MAJOR_VER)
LDFLAGS += -X helm.sh/helm/v4/pkg/chartutil.k8sVersionMinor=$(K8S_MODULES_MINOR_VER)
.PHONY: all
all: build
@ -78,7 +78,7 @@ all: build
build: $(BINDIR)/$(BINNAME)
$(BINDIR)/$(BINNAME): $(SRC)
GO111MODULE=on CGO_ENABLED=$(CGO_ENABLED) go build $(GOFLAGS) -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm
CGO_ENABLED=$(CGO_ENABLED) go build $(GOFLAGS) -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm
# ------------------------------------------------------------------------------
# install
@ -104,7 +104,16 @@ test: test-unit
test-unit:
@echo
@echo "==> Running unit tests <=="
GO111MODULE=on go test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS)
go test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS)
@echo
@echo "==> Running unit test(s) with ldflags <=="
# Test to check the deprecation warnings on Kubernetes templates created by `helm create` against the current Kubernetes
# version. Note: The version details are set in var LDFLAGS. To avoid the ldflags impact on other unit tests that are
# based on older versions, this is run separately. When run without the ldflags in the unit test (above) or coverage
# test, it still passes with a false-positive result as the resources shouldnt be deprecated in the older Kubernetes
# version if it only starts failing with the latest.
go test $(GOFLAGS) -run ^TestHelmCreateChart_CheckDeprecatedWarnings$$ ./pkg/lint/ $(TESTFLAGS) -ldflags '$(LDFLAGS)'
.PHONY: test-coverage
test-coverage:
@ -114,7 +123,11 @@ test-coverage:
.PHONY: test-style
test-style:
GO111MODULE=on golangci-lint run
golangci-lint run ./...
@scripts/validate-license.sh
.PHONY: test-source-headers
test-source-headers:
@scripts/validate-license.sh
.PHONY: test-acceptance
@ -138,7 +151,7 @@ coverage:
.PHONY: format
format: $(GOIMPORTS)
GO111MODULE=on go list -f '{{.Dir}}' ./... | xargs $(GOIMPORTS) -w -local helm.sh/helm
go list -f '{{.Dir}}' ./... | xargs $(GOIMPORTS) -w -local helm.sh/helm
# Generate golden files used in unit tests
.PHONY: gen-test-golden
@ -155,10 +168,10 @@ gen-test-golden: test-unit
# without a go.mod file when downloading the following dependencies
$(GOX):
(cd /; GO111MODULE=on go install github.com/mitchellh/gox@latest)
(cd /; go install github.com/mitchellh/gox@v1.0.2-0.20220701044238-9f712387e2d2)
$(GOIMPORTS):
(cd /; GO111MODULE=on go install golang.org/x/tools/cmd/goimports@latest)
(cd /; go install golang.org/x/tools/cmd/goimports@latest)
# ------------------------------------------------------------------------------
# release
@ -166,7 +179,7 @@ $(GOIMPORTS):
.PHONY: build-cross
build-cross: LDFLAGS += -extldflags "-static"
build-cross: $(GOX)
GOFLAGS="-trimpath" GO111MODULE=on CGO_ENABLED=0 $(GOX) -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/$(BINNAME)" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/helm
GOFLAGS="-trimpath" CGO_ENABLED=0 $(GOX) -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/$(BINNAME)" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/helm
.PHONY: dist
dist:

@ -1,22 +1,24 @@
maintainers:
- hickeyma
- gjenkins8
- joejulian
- jdolitsky
- marckhouzam
- mattfarina
- robertsirc
- sabre1041
- scottrigby
- technosophos
triage:
- banjoh
- yxxhero
- zonggen
- gjenkins8
- z4ce
emeritus:
- adamreese
- bacongobbler
- fibonacci1729
- hickeyma
- jascott1
- jdolitsky
- michelleN
- migmartri
- nebril

@ -2,8 +2,9 @@
[![Build Status](https://github.com/helm/helm/workflows/release/badge.svg)](https://github.com/helm/helm/actions?workflow=release)
[![Go Report Card](https://goreportcard.com/badge/github.com/helm/helm)](https://goreportcard.com/report/github.com/helm/helm)
[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/helm.sh/helm/v3)
[![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/helm.sh/helm/v4)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3131/badge)](https://bestpractices.coreinfrastructure.org/projects/3131)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/helm/helm/badge)](https://scorecard.dev/viewer/?uri=github.com/helm/helm)
Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources.
@ -28,6 +29,11 @@ Think of it like apt/yum/homebrew for Kubernetes.
- Charts can be stored on disk, or fetched from remote chart repositories
(like Debian or RedHat packages)
## Helm Development and Stable Versions
Helm v4 is currently under development on the `main` branch. This is unstable and the APIs within the Go SDK and at the command line are changing.
Helm v3 (current stable) is maintained on the `dev-v3` branch. APIs there follow semantic versioning.
## Install
Binary downloads of the Helm client can be found on [the Releases page](https://github.com/helm/helm/releases/latest).
@ -38,8 +44,10 @@ If you want to use a package manager:
- [Homebrew](https://brew.sh/) users can use `brew install helm`.
- [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`.
- [Winget](https://learn.microsoft.com/en-us/windows/package-manager/) users can use `winget install Helm.Helm`.
- [Scoop](https://scoop.sh/) users can use `scoop install helm`.
- [Snapcraft](https://snapcraft.io/) users can use `snap install helm --classic`
- [Snapcraft](https://snapcraft.io/) users can use `snap install helm --classic`.
- [Flox](https://flox.dev) users can use `flox install kubernetes-helm`.
To rapidly get Helm up and running, start with the [Quick Start Guide](https://helm.sh/docs/intro/quickstart/).
@ -54,6 +62,8 @@ Get started with the [Quick Start guide](https://helm.sh/docs/intro/quickstart/)
The [Helm roadmap uses GitHub milestones](https://github.com/helm/helm/milestones) to track the progress of the project.
The development of Helm v4 is currently happening on the `main` branch while the development of Helm v3, the stable branch, is happening on the `dev-v3` branch. Changes should be made to the `main` branch prior to being added to the `dev-v3` branch so that all changes are carried along to Helm v4.
## Community, discussion, contribution, and support
You can reach the Helm community and developers via the following channels:

@ -23,7 +23,7 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v4/cmd/helm/require"
)
const completionDesc = `
@ -102,8 +102,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
Short: "generate autocompletion script for bash",
Long: bashCompDesc,
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(cmd *cobra.Command, _ []string) error {
return runCompletionBash(out, cmd)
},
}
@ -114,8 +114,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
Short: "generate autocompletion script for zsh",
Long: zshCompDesc,
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(cmd *cobra.Command, _ []string) error {
return runCompletionZsh(out, cmd)
},
}
@ -126,8 +126,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
Short: "generate autocompletion script for fish",
Long: fishCompDesc,
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(cmd *cobra.Command, _ []string) error {
return runCompletionFish(out, cmd)
},
}
@ -138,8 +138,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
Short: "generate autocompletion script for powershell",
Long: powershellCompDesc,
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(cmd *cobra.Command, _ []string) error {
return runCompletionPowershell(out, cmd)
},
}
@ -209,7 +209,15 @@ func runCompletionPowershell(out io.Writer, cmd *cobra.Command) error {
return cmd.Root().GenPowerShellCompletionWithDesc(out)
}
// Function to disable file completion
func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp
// noMoreArgsCompFunc deactivates file completion when doing argument shell completion.
// It also provides some ActiveHelp to indicate no more arguments are accepted.
func noMoreArgsCompFunc(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return noMoreArgsComp()
}
// noMoreArgsComp deactivates file completion when doing argument shell completion.
// It also provides some ActiveHelp to indicate no more arguments are accepted.
func noMoreArgsComp() ([]string, cobra.ShellCompDirective) {
activeHelpMsg := "This command does not take any more arguments (but may accept flags)."
return cobra.AppendActiveHelp(nil, activeHelpMsg), cobra.ShellCompDirectiveNoFileComp
}

@ -21,8 +21,8 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/release"
)
// Check if file completion should be performed according to parameter 'shouldBePerformed'

@ -23,10 +23,10 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/helmpath"
)
const createDesc = `
@ -64,16 +64,16 @@ func newCreateCmd(out io.Writer) *cobra.Command {
Short: "create a new chart with the given name",
Long: createDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// Allow file completion when completing the argument for the name
// which could be a path
return nil, cobra.ShellCompDirectiveDefault
}
// No more completions, so disable file completion
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
o.name = args[0]
o.starterDir = helmpath.DataPath("starters")
return o.run(out)

@ -22,11 +22,11 @@ import (
"path/filepath"
"testing"
"helm.sh/helm/v3/internal/test/ensure"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/chart/loader"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/helmpath"
)
func TestCreateCmd(t *testing.T) {

@ -20,9 +20,10 @@ import (
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const dependencyDesc = `
@ -93,7 +94,7 @@ func newDependencyCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
cmd.AddCommand(newDependencyListCmd(out))
cmd.AddCommand(newDependencyUpdateCmd(cfg, out))
cmd.AddCommand(newDependencyBuildCmd(cfg, out))
cmd.AddCommand(newDependencyBuildCmd(out))
return cmd
}
@ -106,7 +107,7 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
Short: "list the dependencies for the given chart",
Long: dependencyListDesc,
Args: require.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
chartpath := "."
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
@ -120,3 +121,16 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
f.UintVar(&client.ColumnWidth, "max-col-width", 80, "maximum column width for output table")
return cmd
}
func addDependencySubcommandFlags(f *pflag.FlagSet, client *action.Dependency) {
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
f.StringVar(&client.Username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&client.Password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&client.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&client.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.BoolVar(&client.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
f.StringVar(&client.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
}

@ -24,10 +24,10 @@ import (
"github.com/spf13/cobra"
"k8s.io/client-go/util/homedir"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/downloader"
"helm.sh/helm/v4/pkg/getter"
)
const dependencyBuildDesc = `
@ -41,7 +41,7 @@ If no lock file is found, 'helm dependency build' will mirror the behavior
of 'helm dependency update'.
`
func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func newDependencyBuildCmd(out io.Writer) *cobra.Command {
client := action.NewDependency()
cmd := &cobra.Command{
@ -49,18 +49,24 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
Short: "rebuild the charts/ directory based on the Chart.lock file",
Long: dependencyBuildDesc,
Args: require.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
chartpath := "."
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
man := &downloader.Manager{
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings),
RegistryClient: cfg.RegistryClient,
RegistryClient: registryClient,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
@ -68,7 +74,7 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
if client.Verify {
man.Verify = downloader.VerifyIfPossible
}
err := man.Build()
err = man.Build()
if e, ok := err.(downloader.ErrRepoNotFound); ok {
return fmt.Errorf("%s. Please add the missing repos via 'helm repo add'", e.Error())
}
@ -77,9 +83,7 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
}
f := cmd.Flags()
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
addDependencySubcommandFlags(f, client)
return cmd
}

@ -22,10 +22,10 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/provenance"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/provenance"
"helm.sh/helm/v4/pkg/repo"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestDependencyBuildCmd(t *testing.T) {
@ -58,7 +58,7 @@ func TestDependencyBuildCmd(t *testing.T) {
createTestingChart(t, rootDir, chartname, srv.URL())
repoFile := filepath.Join(rootDir, "repositories.yaml")
cmd := fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s", filepath.Join(rootDir, chartname), repoFile, rootDir)
cmd := fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s --plain-http", filepath.Join(rootDir, chartname), repoFile, rootDir)
_, out, err := executeActionCommand(cmd)
// In the first pass, we basically want the same results as an update.
@ -117,7 +117,7 @@ func TestDependencyBuildCmd(t *testing.T) {
t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v)
}
skipRefreshCmd := fmt.Sprintf("dependency build '%s' --skip-refresh --repository-config %s --repository-cache %s", filepath.Join(rootDir, chartname), repoFile, rootDir)
skipRefreshCmd := fmt.Sprintf("dependency build '%s' --skip-refresh --repository-config %s --repository-cache %s --plain-http", filepath.Join(rootDir, chartname), repoFile, rootDir)
_, out, err = executeActionCommand(skipRefreshCmd)
// In this pass, we check --skip-refresh option becomes effective.
@ -134,7 +134,7 @@ func TestDependencyBuildCmd(t *testing.T) {
if err := chartutil.SaveDir(c, dir()); err != nil {
t.Fatal(err)
}
cmd = fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json",
cmd = fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json --plain-http",
dir(ociChartName),
dir("repositories.yaml"),
dir(),

@ -16,15 +16,16 @@ limitations under the License.
package main
import (
"fmt"
"io"
"path/filepath"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/downloader"
"helm.sh/helm/v4/pkg/getter"
)
const dependencyUpDesc = `
@ -43,7 +44,7 @@ in the Chart.yaml file, but (b) at the wrong version.
`
// newDependencyUpdateCmd creates a new dependency update command.
func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func newDependencyUpdateCmd(_ *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewDependency()
cmd := &cobra.Command{
@ -52,18 +53,24 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
Short: "update charts/ based on the contents of Chart.yaml",
Long: dependencyUpDesc,
Args: require.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
chartpath := "."
if len(args) > 0 {
chartpath = filepath.Clean(args[0])
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
man := &downloader.Manager{
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings),
RegistryClient: cfg.RegistryClient,
RegistryClient: registryClient,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
@ -76,9 +83,7 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
}
f := cmd.Flags()
f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures")
f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
addDependencySubcommandFlags(f, client)
return cmd
}

@ -22,13 +22,13 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/internal/test/ensure"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/provenance"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/provenance"
"helm.sh/helm/v4/pkg/repo"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestDependencyUpdateCmd(t *testing.T) {
@ -67,7 +67,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
}
_, out, err := executeActionCommand(
fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()),
fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()),
)
if err != nil {
t.Logf("Output: %s", out)
@ -110,7 +110,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
t.Fatal(err)
}
_, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()))
_, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
if err != nil {
t.Logf("Output: %s", out)
t.Fatal(err)
@ -131,7 +131,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
if err := chartutil.SaveDir(c, dir()); err != nil {
t.Fatal(err)
}
cmd := fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json",
cmd := fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s --registry-config %s/config.json --plain-http",
dir(ociChartName),
dir("repositories.yaml"),
dir(),
@ -169,7 +169,7 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
}
createTestingChart(t, dir(), chartname, srv.URL())
_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()))
_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
if err != nil {
t.Logf("Output: %s", output)
t.Fatal(err)
@ -178,7 +178,7 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
// Chart repo is down
srv.Stop()
_, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()))
_, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s --plain-http", dir(chartname), dir("repositories.yaml"), dir()))
if err == nil {
t.Logf("Output: %s", output)
t.Fatal("Expected error, got nil")
@ -200,8 +200,9 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
}
}
// Make sure tmpcharts is deleted
if _, err := os.Stat(filepath.Join(dir(chartname), "tmpcharts")); !os.IsNotExist(err) {
// Make sure tmpcharts-x is deleted
tmpPath := filepath.Join(dir(chartname), fmt.Sprintf("tmpcharts-%d", os.Getpid()))
if _, err := os.Stat(tmpPath); !os.IsNotExist(err) {
t.Fatalf("tmpcharts dir still exists")
}
}

@ -28,7 +28,7 @@ import (
"golang.org/x/text/cases"
"golang.org/x/text/language"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v4/cmd/helm/require"
)
const docsDesc = `
@ -58,8 +58,8 @@ func newDocsCmd(out io.Writer) *cobra.Command {
Long: docsDesc,
Hidden: true,
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(cmd *cobra.Command, _ []string) error {
o.topCmd = cmd.Root()
return o.run(out)
},
@ -70,14 +70,14 @@ func newDocsCmd(out io.Writer) *cobra.Command {
f.StringVar(&o.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)")
f.BoolVar(&o.generateHeaders, "generate-headers", false, "generate standard headers for markdown files")
cmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
cmd.RegisterFlagCompletionFunc("type", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"bash", "man", "markdown"}, cobra.ShellCompDirectiveNoFileComp
})
return cmd
}
func (o *docsOptions) run(out io.Writer) error {
func (o *docsOptions) run(_ io.Writer) error {
switch o.docTypeString {
case "markdown", "mdown", "md":
if o.generateHeaders {

@ -23,7 +23,7 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v4/cmd/helm/require"
)
var envHelp = `
@ -36,15 +36,15 @@ func newEnvCmd(out io.Writer) *cobra.Command {
Short: "helm client environment information",
Long: envHelp,
Args: require.MaximumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
keys := getSortedEnvVarKeys()
return keys, cobra.ShellCompDirectiveNoFileComp
}
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
},
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, args []string) {
envVars := settings.EnvVars()
if len(args) == 0 {

@ -28,12 +28,12 @@ import (
"github.com/spf13/pflag"
"k8s.io/klog/v2"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/postrender"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/postrender"
"helm.sh/helm/v4/pkg/repo"
)
const (
@ -72,7 +72,7 @@ func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) {
cmd.Flags().VarP(newOutputValue(output.Table, varRef), outputFlag, "o",
fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", ")))
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
var formatNames []string
for format, desc := range output.FormatsWithDesc() {
formatNames = append(formatNames, fmt.Sprintf("%s\t%s", format, desc))
@ -195,7 +195,7 @@ func (p *postRendererArgsSlice) GetSlice() []string {
return p.options.args
}
func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellCompDirective) {
func compVersionFlag(chartRef string, _ string) ([]string, cobra.ShellCompDirective) {
chartInfo := strings.Split(chartRef, "/")
if len(chartInfo) != 2 {
return nil, cobra.ShellCompDirectiveNoFileComp

@ -20,9 +20,9 @@ import (
"fmt"
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/release"
helmtime "helm.sh/helm/v3/pkg/time"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/release"
helmtime "helm.sh/helm/v4/pkg/time"
)
func outputFlagCompletionTest(t *testing.T, cmdName string) {

@ -21,8 +21,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
var getHelp = `

@ -22,9 +22,9 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/output"
)
var getAllHelp = `
@ -41,13 +41,13 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "download all information for a named release",
Long: getAllHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
res, err := client.Run(args[0])
if err != nil {
return err
@ -58,20 +58,23 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
return tpl(template, data, out)
}
return output.Table.Write(out, &statusPrinter{res, true, false, false, true})
return output.Table.Write(out, &statusPrinter{
release: res,
debug: true,
showMetadata: true,
hideNotes: false,
})
},
}
f := cmd.Flags()
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}
return nil, cobra.ShellCompDirectiveNoFileComp
})
if err != nil {
log.Fatal(err)
}

@ -19,7 +19,7 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestGetCmd(t *testing.T) {

@ -23,8 +23,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const getHooksHelp = `
@ -41,13 +41,13 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "download all hooks for a named release",
Long: getHooksHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
res, err := client.Run(args[0])
if err != nil {
return err
@ -60,7 +60,7 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}

@ -19,7 +19,7 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestGetHooks(t *testing.T) {

@ -23,8 +23,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
var getManifestHelp = `
@ -43,13 +43,13 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
Short: "download the manifest for a named release",
Long: getManifestHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
res, err := client.Run(args[0])
if err != nil {
return err
@ -60,7 +60,7 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
}
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}

@ -19,7 +19,7 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestGetManifest(t *testing.T) {

@ -22,10 +22,11 @@ import (
"log"
"github.com/spf13/cobra"
k8sLabels "k8s.io/apimachinery/pkg/labels"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/output"
)
type metadataWriter struct {
@ -40,13 +41,13 @@ func newGetMetadataCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
Use: "metadata RELEASE_NAME",
Short: "This command fetches metadata for a given release",
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
releaseMetadata, err := client.Run(args[0])
if err != nil {
return err
@ -57,7 +58,7 @@ func newGetMetadataCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
f := cmd.Flags()
f.IntVar(&client.Version, "revision", 0, "specify release revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}
@ -78,10 +79,13 @@ func (w metadataWriter) WriteTable(out io.Writer) error {
_, _ = fmt.Fprintf(out, "CHART: %v\n", w.metadata.Chart)
_, _ = fmt.Fprintf(out, "VERSION: %v\n", w.metadata.Version)
_, _ = fmt.Fprintf(out, "APP_VERSION: %v\n", w.metadata.AppVersion)
_, _ = fmt.Fprintf(out, "ANNOTATIONS: %v\n", k8sLabels.Set(w.metadata.Annotations).String())
_, _ = fmt.Fprintf(out, "DEPENDENCIES: %v\n", w.metadata.FormattedDepNames())
_, _ = fmt.Fprintf(out, "NAMESPACE: %v\n", w.metadata.Namespace)
_, _ = fmt.Fprintf(out, "REVISION: %v\n", w.metadata.Revision)
_, _ = fmt.Fprintf(out, "STATUS: %v\n", w.metadata.Status)
_, _ = fmt.Fprintf(out, "DEPLOYED_AT: %v\n", w.metadata.DeployedAt)
return nil
}

@ -19,7 +19,7 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestGetMetadataCmd(t *testing.T) {

@ -23,8 +23,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
var getNotesHelp = `
@ -39,13 +39,13 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "download the notes for a named release",
Long: getNotesHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
res, err := client.Run(args[0])
if err != nil {
return err
@ -59,7 +59,7 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f := cmd.Flags()
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}

@ -19,7 +19,7 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestGetNotesCmd(t *testing.T) {

@ -23,9 +23,9 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/output"
)
var getValuesHelp = `
@ -46,13 +46,13 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "download the values file for a named release",
Long: getValuesHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
vals, err := client.Run(args[0])
if err != nil {
return err
@ -63,7 +63,7 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f := cmd.Flags()
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}

@ -19,7 +19,7 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestGetValuesCmd(t *testing.T) {

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main // import "helm.sh/helm/v3/cmd/helm"
package main // import "helm.sh/helm/v4/cmd/helm"
import (
"fmt"
@ -22,6 +22,7 @@ import (
"log"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
@ -29,12 +30,12 @@ import (
// Import to initialize client auth plugins.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/kube"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage/driver"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli"
"helm.sh/helm/v4/pkg/kube"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
"helm.sh/helm/v4/pkg/release"
"helm.sh/helm/v4/pkg/storage/driver"
)
var settings = cli.New()
@ -45,7 +46,8 @@ func init() {
func debug(format string, v ...interface{}) {
if settings.Debug {
format = fmt.Sprintf("[debug] %s\n", format)
timeNow := time.Now().String()
format = fmt.Sprintf("%s [debug] %s\n", timeNow, format)
log.Output(2, fmt.Sprintf(format, v...))
}
}

@ -28,15 +28,15 @@ import (
shellwords "github.com/mattn/go-shellwords"
"github.com/spf13/cobra"
"helm.sh/helm/v3/internal/test"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"
"helm.sh/helm/v3/pkg/time"
"helm.sh/helm/v4/internal/test"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/cli"
kubefake "helm.sh/helm/v4/pkg/kube/fake"
"helm.sh/helm/v4/pkg/release"
"helm.sh/helm/v4/pkg/storage"
"helm.sh/helm/v4/pkg/storage/driver"
"helm.sh/helm/v4/pkg/time"
)
func testTimestamper() time.Time { return time.Unix(242085845, 0).UTC() }
@ -94,7 +94,7 @@ func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string)
Releases: store,
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
Capabilities: chartutil.DefaultCapabilities,
Log: func(format string, v ...interface{}) {},
Log: func(_ string, _ ...interface{}) {},
}
root, err := newRootCmd(actionConfig, buf, args)

@ -25,13 +25,13 @@ import (
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/releaseutil"
helmtime "helm.sh/helm/v3/pkg/time"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/release"
"helm.sh/helm/v4/pkg/releaseutil"
helmtime "helm.sh/helm/v4/pkg/time"
)
var historyHelp = `
@ -60,13 +60,13 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "fetch release history",
Aliases: []string{"hist"},
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
history, err := getHistory(client, args[0])
if err != nil {
return err
@ -136,7 +136,7 @@ func getHistory(client *action.History, name string) (releaseHistory, error) {
func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
for i := len(rls) - 1; i >= 0; i-- {
r := rls[i]
c := formatChartname(r.Chart)
c := formatChartName(r.Chart)
s := r.Info.Status.String()
v := r.Version
d := r.Info.Description
@ -159,7 +159,7 @@ func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
return history
}
func formatChartname(c *chart.Chart) string {
func formatChartName(c *chart.Chart) string {
if c == nil || c.Metadata == nil {
// This is an edge case that has happened in prod, though we don't
// know how: https://github.com/helm/helm/issues/1347
@ -177,22 +177,15 @@ func formatAppVersion(c *chart.Chart) string {
return c.AppVersion()
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func compListRevisions(toComplete string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) {
func compListRevisions(_ string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) {
client := action.NewHistory(cfg)
var revisions []string
if hist, err := client.Run(releaseName); err == nil {
for _, release := range hist {
appVersion := fmt.Sprintf("App: %s", release.Chart.Metadata.AppVersion)
chartDesc := fmt.Sprintf("Chart: %s-%s", release.Chart.Metadata.Name, release.Chart.Metadata.Version)
revisions = append(revisions, fmt.Sprintf("%s\t%s, %s", strconv.Itoa(release.Version), appVersion, chartDesc))
for _, version := range hist {
appVersion := fmt.Sprintf("App: %s", version.Chart.Metadata.AppVersion)
chartDesc := fmt.Sprintf("Chart: %s-%s", version.Chart.Metadata.Name, version.Chart.Metadata.Version)
revisions = append(revisions, fmt.Sprintf("%s\t%s, %s", strconv.Itoa(version.Version), appVersion, chartDesc))
}
return revisions, cobra.ShellCompDirectiveNoFileComp
}

@ -20,7 +20,7 @@ import (
"fmt"
"testing"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestHistoryCmd(t *testing.T) {

@ -30,15 +30,15 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/chart/loader"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/downloader"
"helm.sh/helm/v4/pkg/getter"
"helm.sh/helm/v4/pkg/release"
)
const installDesc = `
@ -85,7 +85,7 @@ set for a key called 'foo', the 'newbar' value would take precedence:
$ helm install --set foo=bar --set foo=newbar myredis ./redis
Similarly, in the following example 'foo' is set to '["four"]':
Similarly, in the following example 'foo' is set to '["four"]':
$ helm install --set-json='foo=["one", "two", "three"]' --set-json='foo=["four"]' myredis ./redis
@ -94,7 +94,11 @@ And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}':
$ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis
To check the generated manifests of a release without installing the chart,
the '--debug' and '--dry-run' flags can be combined.
the --debug and --dry-run flags can be combined.
The --dry-run flag will output all generated chart manifests, including Secrets
which can contain sensitive values. To hide Kubernetes Secrets use the
--hide-secret flag. Please carefully consider how and when these flags are used.
If --verify is set, the chart MUST have a provenance file, and the provenance
file MUST pass all verification steps.
@ -132,12 +136,12 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "install a chart",
Long: installDesc,
Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstall(args, toComplete, client)
},
RunE: func(_ *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
@ -154,11 +158,20 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return errors.Wrap(err, "INSTALLATION FAILED")
}
return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false})
return outfmt.Write(out, &statusPrinter{
release: rel,
debug: settings.Debug,
showMetadata: false,
hideNotes: client.HideNotes,
})
},
}
addInstallFlags(cmd, cmd.Flags(), client, valueOpts)
// hide-secret is not available in all places the install flags are used so
// it is added separately
f := cmd.Flags()
f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag")
bindOutputFlag(cmd, &outfmt)
bindPostRenderFlag(cmd, &client.PostRenderer)
@ -176,7 +189,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.Lookup("dry-run").NoOptDefVal = "client"
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.BoolVar(&client.Replace, "replace", false, "reuse the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
@ -189,12 +202,15 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
f.StringToStringVarP(&client.Labels, "labels", "l", nil, "Labels that would be added to release metadata. Should be divided by comma.")
f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in install output. Does not affect presence in chart metadata")
f.BoolVar(&client.TakeOwnership, "take-ownership", false, "if set, install will ignore the check for helm annotations and take ownership of the existing resources")
addValueOptionsFlags(f, valueOpts)
addChartPathOptionsFlags(f, &client.ChartPathOptions)
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
requiredArgs := 2
if client.GenerateName {
requiredArgs = 1
@ -204,7 +220,6 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
}
return compVersionFlag(args[requiredArgs-1], toComplete)
})
if err != nil {
log.Fatal(err)
}

@ -23,7 +23,7 @@ import (
"path/filepath"
"testing"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestInstall(t *testing.T) {
@ -33,7 +33,7 @@ func TestInstall(t *testing.T) {
}
defer srv.Stop()
srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok || username != "username" || password != "password" {
t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)
@ -96,12 +96,18 @@ func TestInstall(t *testing.T) {
golden: "output/install-no-args.txt",
wantError: true,
},
// Install, re-use name
// Install, reuse name
{
name: "install and replace release",
cmd: "install aeneas testdata/testcharts/empty --replace",
golden: "output/install-and-replace.txt",
},
// Install, take ownership
{
name: "install and replace release",
cmd: "install aeneas-take-ownership testdata/testcharts/empty --take-ownership",
golden: "output/install-and-take-ownership.txt",
},
// Install, with timeout
{
name: "install with a timeout",
@ -225,6 +231,12 @@ func TestInstall(t *testing.T) {
wantError: true,
golden: "output/subchart-schema-cli-negative.txt",
},
// Install, values from yaml, schematized with errors but skip schema validation, expect success
{
name: "install with schema file and schematized subchart, extra values from cli, skip schema validation",
cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=-25 --skip-schema-validation",
golden: "output/schema.txt",
},
// Install deprecated chart
{
name: "install with warning about deprecated chart",
@ -252,6 +264,22 @@ func TestInstall(t *testing.T) {
cmd: fmt.Sprintf("install aeneas test/reqtest --username username --password password --repository-config %s --repository-cache %s", repoFile, srv.Root()),
golden: "output/install.txt",
},
{
name: "dry-run displaying secret",
cmd: "install secrets testdata/testcharts/chart-with-secret --dry-run",
golden: "output/install-dry-run-with-secret.txt",
},
{
name: "dry-run hiding secret",
cmd: "install secrets testdata/testcharts/chart-with-secret --dry-run --hide-secret",
golden: "output/install-dry-run-with-secret-hidden.txt",
},
{
name: "hide-secret error without dry-run",
cmd: "install secrets testdata/testcharts/chart-with-secret --hide-secret",
wantError: true,
golden: "output/install-hide-secret.txt",
},
}
runTestCmd(t, tests)

@ -26,10 +26,11 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/lint/support"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/getter"
"helm.sh/helm/v4/pkg/lint/support"
)
var longLintHelp = `
@ -44,19 +45,29 @@ or recommendation, it will emit [WARNING] messages.
func newLintCmd(out io.Writer) *cobra.Command {
client := action.NewLint()
valueOpts := &values.Options{}
var kubeVersion string
cmd := &cobra.Command{
Use: "lint PATH",
Short: "examine a chart for possible issues",
Long: longLintHelp,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
paths := []string{"."}
if len(args) > 0 {
paths = args
}
if kubeVersion != "" {
parsedKubeVersion, err := chartutil.ParseKubeVersion(kubeVersion)
if err != nil {
return fmt.Errorf("invalid kube version '%s': %s", kubeVersion, err)
}
client.KubeVersion = parsedKubeVersion
}
if client.WithSubcharts {
for _, p := range paths {
filepath.Walk(filepath.Join(p, "charts"), func(path string, info os.FileInfo, err error) error {
filepath.Walk(filepath.Join(p, "charts"), func(path string, info os.FileInfo, _ error) error {
if info != nil {
if info.Name() == "Chart.yaml" {
paths = append(paths, filepath.Dir(path))
@ -137,6 +148,8 @@ func newLintCmd(out io.Writer) *cobra.Command {
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors")
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
f.StringVar(&kubeVersion, "kube-version", "", "Kubernetes version used for capabilities and deprecation checks")
addValueOptionsFlags(f, valueOpts)
return cmd

@ -63,6 +63,34 @@ func TestLintCmdWithQuietFlag(t *testing.T) {
}
func TestLintCmdWithKubeVersionFlag(t *testing.T) {
testChart := "testdata/testcharts/chart-with-deprecated-api"
tests := []cmdTestCase{{
name: "lint chart with deprecated api version using kube version flag",
cmd: fmt.Sprintf("lint --kube-version 1.22.0 %s", testChart),
golden: "output/lint-chart-with-deprecated-api.txt",
wantError: false,
}, {
name: "lint chart with deprecated api version using kube version and strict flag",
cmd: fmt.Sprintf("lint --kube-version 1.22.0 --strict %s", testChart),
golden: "output/lint-chart-with-deprecated-api-strict.txt",
wantError: true,
}, {
// the test builds will use the default k8sVersionMinor const in deprecations.go and capabilities.go
// which is "20"
name: "lint chart with deprecated api version without kube version",
cmd: fmt.Sprintf("lint %s", testChart),
golden: "output/lint-chart-with-deprecated-api-old-k8s.txt",
wantError: false,
}, {
name: "lint chart with deprecated api version with older kube version",
cmd: fmt.Sprintf("lint --kube-version 1.21.0 --strict %s", testChart),
golden: "output/lint-chart-with-deprecated-api-old-k8s.txt",
wantError: false,
}}
runTestCmd(t, tests)
}
func TestLintFileCompletion(t *testing.T) {
checkFileCompletion(t, "lint", true)
checkFileCompletion(t, "lint mypath", true) // Multiple paths can be given

@ -25,10 +25,10 @@ import (
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/release"
)
var listHelp = `
@ -68,8 +68,8 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: listHelp,
Aliases: []string{"ls"},
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(cmd *cobra.Command, _ []string) error {
if client.AllNamespaces {
if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil {
return err
@ -156,7 +156,7 @@ func newReleaseListWriter(releases []*release.Release, timeFormat string, noHead
Namespace: r.Namespace,
Revision: strconv.Itoa(r.Version),
Status: r.Info.Status.String(),
Chart: formatChartname(r.Chart),
Chart: formatChartName(r.Chart),
AppVersion: formatAppVersion(r.Chart),
}

@ -19,9 +19,9 @@ package main
import (
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/time"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/release"
"helm.sh/helm/v4/pkg/time"
)
func TestListCmd(t *testing.T) {

@ -31,7 +31,7 @@ import (
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
"helm.sh/helm/v3/pkg/plugin"
"helm.sh/helm/v4/pkg/plugin"
)
const (
@ -154,7 +154,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io.
func manuallyProcessArgs(args []string) ([]string, []string) {
known := []string{}
unknown := []string{}
kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--kube-ca-file", "--registry-config", "--repository-cache", "--repository-config", "--insecure-skip-tls-verify", "--tls-server-name"}
kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--kube-ca-file", "--registry-config", "--repository-cache", "--repository-config", "--kube-insecure-skip-tls-verify", "--kube-tls-server-name"}
knownArg := func(a string) bool {
for _, pre := range kvargs {
if strings.HasPrefix(a, pre+"=") {
@ -286,7 +286,7 @@ func addPluginCommands(plugin *plugin.Plugin, baseCmd *cobra.Command, cmds *plug
f.BoolP(longs[i], shorts[i], false, "")
} else {
// Create a long flag with the same name as the short flag.
// Not a perfect solution, but its better than ignoring the extra short flags.
// Not a perfect solution, but it's better than ignoring the extra short flags.
f.BoolP(shorts[i], shorts[i], false, "")
}
}
@ -301,7 +301,7 @@ func addPluginCommands(plugin *plugin.Plugin, baseCmd *cobra.Command, cmds *plug
// to the dynamic completion script of the plugin.
DisableFlagParsing: true,
// A Run is required for it to be a valid command without subcommands
Run: func(cmd *cobra.Command, args []string) {},
Run: func(_ *cobra.Command, _ []string) {},
}
baseCmd.AddCommand(subCmd)
addPluginCommands(plugin, subCmd, &cmd)

@ -25,10 +25,10 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/downloader"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/downloader"
"helm.sh/helm/v4/pkg/getter"
)
const packageDesc = `
@ -47,7 +47,7 @@ If '--keyring' is not specified, Helm usually defaults to the public keyring
unless your environment is otherwise configured.
`
func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func newPackageCmd(out io.Writer) *cobra.Command {
client := action.NewPackage()
valueOpts := &values.Options{}
@ -55,7 +55,7 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Use: "package [CHART_PATH] [...]",
Short: "package a chart directory into a chart archive",
Long: packageDesc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.Errorf("need at least one argument, the path to the chart")
}
@ -75,6 +75,12 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return err
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
for i := 0; i < len(args); i++ {
path, err := filepath.Abs(args[i])
if err != nil {
@ -91,7 +97,7 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Keyring: client.Keyring,
Getters: p,
Debug: settings.Debug,
RegistryClient: cfg.RegistryClient,
RegistryClient: registryClient,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
}
@ -119,6 +125,13 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version")
f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.")
f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`)
f.StringVar(&client.Username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&client.Password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&client.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&client.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.BoolVar(&client.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
f.BoolVar(&client.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
f.StringVar(&client.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}

@ -23,8 +23,8 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/chart/loader"
)
func TestPackage(t *testing.T) {

@ -23,7 +23,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/plugin"
"helm.sh/helm/v4/pkg/plugin"
)
const pluginHelp = `
@ -47,19 +47,27 @@ func newPluginCmd(out io.Writer) *cobra.Command {
// runHook will execute a plugin hook.
func runHook(p *plugin.Plugin, event string) error {
hook := p.Metadata.Hooks[event]
if hook == "" {
plugin.SetupPluginEnv(settings, p.Metadata.Name, p.Dir)
cmds := p.Metadata.PlatformHooks[event]
expandArgs := true
if len(cmds) == 0 && len(p.Metadata.Hooks) > 0 {
cmd := p.Metadata.Hooks[event]
if len(cmd) > 0 {
cmds = []plugin.PlatformCommand{{Command: "sh", Args: []string{"-c", cmd}}}
expandArgs = false
}
}
main, argv, err := plugin.PrepareCommands(cmds, expandArgs, []string{})
if err != nil {
return nil
}
prog := exec.Command("sh", "-c", hook)
// TODO make this work on windows
// I think its ... ¯\_(ツ)_/¯
// prog := exec.Command("cmd", "/C", p.Metadata.Hooks.Install())
prog := exec.Command(main, argv...)
debug("running %s hook: %s", event, prog)
plugin.SetupPluginEnv(settings, p.Metadata.Name, p.Dir)
prog.Stdout, prog.Stderr = os.Stdout, os.Stderr
if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok {

@ -22,9 +22,9 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/plugin"
"helm.sh/helm/v3/pkg/plugin/installer"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/plugin"
"helm.sh/helm/v4/pkg/plugin/installer"
)
type pluginInstallOptions struct {
@ -39,23 +39,23 @@ This command allows you to install a plugin from a url to a VCS repo or a local
func newPluginInstallCmd(out io.Writer) *cobra.Command {
o := &pluginInstallOptions{}
cmd := &cobra.Command{
Use: "install [options] <path|url>...",
Short: "install one or more Helm plugins",
Use: "install [options] <path|url>",
Short: "install a Helm plugin",
Long: pluginInstallDesc,
Aliases: []string{"add"},
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// We do file completion, in case the plugin is local
return nil, cobra.ShellCompDirectiveDefault
}
// No more completion once the plugin path has been specified
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
},
PreRunE: func(cmd *cobra.Command, args []string) error {
PreRunE: func(_ *cobra.Command, args []string) error {
return o.complete(args)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return o.run(out)
},
}

@ -22,7 +22,7 @@ import (
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/plugin"
"helm.sh/helm/v4/pkg/plugin"
)
func newPluginListCmd(out io.Writer) *cobra.Command {
@ -30,8 +30,8 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
Use: "list",
Aliases: []string{"ls"},
Short: "list installed Helm plugins",
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(_ *cobra.Command, _ []string) error {
debug("pluginDirs: %s", settings.PluginsDirectory)
plugins, err := plugin.FindPlugins(settings.PluginsDirectory)
if err != nil {
@ -75,7 +75,7 @@ func filterPlugins(plugins []*plugin.Plugin, ignoredPluginNames []string) []*plu
}
// Provide dynamic auto-completion for plugin names
func compListPlugins(toComplete string, ignoredPluginNames []string) []string {
func compListPlugins(_ string, ignoredPluginNames []string) []string {
var pNames []string
plugins, err := plugin.FindPlugins(settings.PluginsDirectory)
if err == nil && len(plugins) > 0 {

@ -26,7 +26,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
)
func TestManuallyProcessArgs(t *testing.T) {

@ -24,7 +24,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/plugin"
"helm.sh/helm/v4/pkg/plugin"
)
type pluginUninstallOptions struct {
@ -38,13 +38,13 @@ func newPluginUninstallCmd(out io.Writer) *cobra.Command {
Use: "uninstall <plugin>...",
Aliases: []string{"rm", "remove"},
Short: "uninstall one or more Helm plugins",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compListPlugins(toComplete, args), cobra.ShellCompDirectiveNoFileComp
},
PreRunE: func(cmd *cobra.Command, args []string) error {
PreRunE: func(_ *cobra.Command, args []string) error {
return o.complete(args)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return o.run(out)
},
}
@ -78,7 +78,7 @@ func (o *pluginUninstallOptions) run(out io.Writer) error {
}
}
if len(errorPlugins) > 0 {
return errors.Errorf(strings.Join(errorPlugins, "\n"))
return errors.New(strings.Join(errorPlugins, "\n"))
}
return nil
}

@ -24,8 +24,8 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/plugin"
"helm.sh/helm/v3/pkg/plugin/installer"
"helm.sh/helm/v4/pkg/plugin"
"helm.sh/helm/v4/pkg/plugin/installer"
)
type pluginUpdateOptions struct {
@ -39,13 +39,13 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command {
Use: "update <plugin>...",
Aliases: []string{"up"},
Short: "update one or more Helm plugins",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compListPlugins(toComplete, args), cobra.ShellCompDirectiveNoFileComp
},
PreRunE: func(cmd *cobra.Command, args []string) error {
PreRunE: func(_ *cobra.Command, args []string) error {
return o.complete(args)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return o.run(out)
},
}
@ -81,7 +81,7 @@ func (o *pluginUpdateOptions) run(out io.Writer) error {
}
}
if len(errorPlugins) > 0 {
return errors.Errorf(strings.Join(errorPlugins, "\n"))
return errors.New(strings.Join(errorPlugins, "\n"))
}
return nil
}

@ -23,8 +23,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const pullDesc = `
@ -43,7 +43,7 @@ result in an error, and the chart will not be saved locally.
`
func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewPullWithOpts(action.WithConfig(cfg))
client := action.NewPull(action.WithConfig(cfg))
cmd := &cobra.Command{
Use: "pull [chart URL | repo/chartname] [...]",
@ -51,13 +51,13 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Aliases: []string{"fetch"},
Long: pullDesc,
Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
return compListCharts(toComplete, false)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.Settings = settings
if client.Version == "" && client.Devel {
debug("setting version to >0.0.0-0")
@ -65,7 +65,7 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
@ -90,7 +90,7 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and untardir are specified, untardir is appended to this")
addChartPathOptionsFlags(f, &client.ChartPathOptions)
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 1 {
return nil, cobra.ShellCompDirectiveNoFileComp
}

@ -24,7 +24,7 @@ import (
"path/filepath"
"testing"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestPullCmd(t *testing.T) {
@ -183,22 +183,27 @@ func TestPullCmd(t *testing.T) {
wantError: true,
},
{
name: "Fail fetching OCI chart without version specified",
args: fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart:0.1.0", ociSrv.RegistryURL),
wantErrorMsg: "Error: --version flag is explicitly required for OCI registries",
wantError: true,
name: "Fetching OCI chart without version option specified",
args: fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart:0.1.0", ociSrv.RegistryURL),
expectFile: "./oci-dependent-chart-0.1.0.tgz",
},
{
name: "Fail fetching OCI chart without version specified",
args: fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart:0.1.0 --version 0.1.0", ociSrv.RegistryURL),
wantError: true,
name: "Fetching OCI chart with version specified",
args: fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart:0.1.0 --version 0.1.0", ociSrv.RegistryURL),
expectFile: "./oci-dependent-chart-0.1.0.tgz",
},
{
name: "Fail fetching OCI chart with version mismatch",
args: fmt.Sprintf("oci://%s/u/ocitestuser/oci-dependent-chart:0.2.0 --version 0.1.0", ociSrv.RegistryURL),
wantErrorMsg: "Error: chart reference and version mismatch: 0.2.0 is not 0.1.0",
wantError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
outdir := srv.Root()
cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s",
cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s --registry-config %s --plain-http",
tt.args,
outdir,
filepath.Join(outdir, "repositories.yaml"),
@ -258,7 +263,7 @@ func TestPullWithCredentialsCmd(t *testing.T) {
}
defer srv.Stop()
srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok || username != "username" || password != "password" {
t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)

@ -22,9 +22,9 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/pusher"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/pusher"
)
const pushDesc = `
@ -40,6 +40,8 @@ type registryPushOptions struct {
caFile string
insecureSkipTLSverify bool
plainHTTP bool
password string
username string
}
func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
@ -50,7 +52,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "push a chart to remote",
Long: pushDesc,
Args: require.MinimumNArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// Do file completion for the chart file to push
return nil, cobra.ShellCompDirectiveDefault
@ -65,10 +67,13 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
}
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
},
RunE: func(cmd *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify, o.plainHTTP)
RunE: func(_ *cobra.Command, args []string) error {
registryClient, err := newRegistryClient(
o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify, o.plainHTTP, o.username, o.password,
)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
@ -96,6 +101,8 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart upload")
f.BoolVar(&o.plainHTTP, "plain-http", false, "use insecure HTTP connections for the chart upload")
f.StringVar(&o.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&o.password, "password", "", "chart repository password where to locate the requested chart")
return cmd
}

@ -20,7 +20,7 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/pkg/action"
)
const registryHelp = `

@ -27,8 +27,8 @@ import (
"github.com/moby/term"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const registryLoginDesc = `
@ -43,6 +43,7 @@ type registryLoginOptions struct {
keyFile string
caFile string
insecure bool
plainHTTP bool
}
func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
@ -53,8 +54,8 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
Short: "login to a registry",
Long: registryLoginDesc,
Args: require.MinimumNArgs(1),
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: cobra.NoFileCompletions,
RunE: func(_ *cobra.Command, args []string) error {
hostname := args[0]
username, password, err := getUsernamePassword(o.username, o.password, o.passwordFromStdinOpt)
@ -66,7 +67,8 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
action.WithCertFile(o.certFile),
action.WithKeyFile(o.keyFile),
action.WithCAFile(o.caFile),
action.WithInsecure(o.insecure))
action.WithInsecure(o.insecure),
action.WithPlainHTTPLogin(o.plainHTTP))
},
}
@ -78,6 +80,7 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file")
f.StringVar(&o.keyFile, "key-file", "", "identify registry client using this SSL key file")
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&o.plainHTTP, "plain-http", false, "use insecure HTTP connections for the chart upload")
return cmd
}

@ -21,8 +21,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const registryLogoutDesc = `
@ -35,8 +35,8 @@ func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma
Short: "logout from a registry",
Long: registryLogoutDesc,
Args: require.MinimumNArgs(1),
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: cobra.NoFileCompletions,
RunE: func(_ *cobra.Command, args []string) error {
hostname := args[0]
return action.NewRegistryLogout(cfg).Run(out, hostname)
},

@ -25,9 +25,9 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/cli/output"
)
const releaseTestHelp = `
@ -39,7 +39,7 @@ The tests to be run are defined in the chart that was installed.
func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewReleaseTesting(cfg)
var outfmt = output.Table
outfmt := output.Table
var outputLogs bool
var filter []string
@ -48,13 +48,13 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
Short: "run tests for a release",
Long: releaseTestHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.Namespace = settings.Namespace()
notName := regexp.MustCompile(`^!\s?name=`)
for _, f := range filter {
@ -72,7 +72,12 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
return runErr
}
if err := outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false}); err != nil {
if err := outfmt.Write(out, &statusPrinter{
release: rel,
debug: settings.Debug,
showMetadata: false,
hideNotes: client.HideNotes,
}); err != nil {
return err
}
@ -92,6 +97,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&outputLogs, "logs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)")
f.StringSliceVar(&filter, "filter", []string{}, "specify tests by attribute (currently \"name\") using attribute=value syntax or '!attribute=value' to exclude a test (can specify multiple or separate values with commas: name=test1,name=test2)")
f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in test output. Does not affect presence in chart metadata")
return cmd
}

@ -23,7 +23,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v4/cmd/helm/require"
)
var repoHelm = `

@ -31,9 +31,9 @@ import (
"golang.org/x/term"
"sigs.k8s.io/yaml"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/getter"
"helm.sh/helm/v4/pkg/repo"
)
// Repositories that have been permanently deleted and no longer work
@ -59,20 +59,22 @@ type repoAddOptions struct {
repoFile string
repoCache string
// Deprecated, but cannot be removed until Helm 4
deprecatedNoUpdate bool
}
func newRepoAddCmd(out io.Writer) *cobra.Command {
o := &repoAddOptions{}
cmd := &cobra.Command{
Use: "add [NAME] [URL]",
Short: "add a chart repository",
Args: require.ExactArgs(2),
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
Use: "add [NAME] [URL]",
Short: "add a chart repository",
Args: require.ExactArgs(2),
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) > 1 {
return noMoreArgsComp()
}
return nil, cobra.ShellCompDirectiveNoFileComp
},
RunE: func(_ *cobra.Command, args []string) error {
o.name = args[0]
o.url = args[1]
o.repoFile = settings.RepositoryConfig
@ -87,7 +89,6 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
f.StringVar(&o.password, "password", "", "chart repository password")
f.BoolVarP(&o.passwordFromStdinOpt, "password-stdin", "", false, "read chart repository password from stdin")
f.BoolVar(&o.forceUpdate, "force-update", false, "replace (overwrite) the repo if it already exists")
f.BoolVar(&o.deprecatedNoUpdate, "no-update", false, "Ignored. Formerly, it would disabled forced updates. It is deprecated by force-update.")
f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")

@ -27,10 +27,10 @@ import (
"sigs.k8s.io/yaml"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/helmpath/xdg"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/helmpath/xdg"
"helm.sh/helm/v4/pkg/repo"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestRepoAddCmd(t *testing.T) {
@ -93,11 +93,10 @@ func TestRepoAdd(t *testing.T) {
const testRepoName = "test-name"
o := &repoAddOptions{
name: testRepoName,
url: ts.URL(),
forceUpdate: false,
deprecatedNoUpdate: true,
repoFile: repoFile,
name: testRepoName,
url: ts.URL(),
forceUpdate: false,
repoFile: repoFile,
}
os.Setenv(xdg.CacheHomeEnvVar, rootDir)
@ -148,11 +147,10 @@ func TestRepoAddCheckLegalName(t *testing.T) {
repoFile := filepath.Join(t.TempDir(), "repositories.yaml")
o := &repoAddOptions{
name: testRepoName,
url: ts.URL(),
forceUpdate: false,
deprecatedNoUpdate: true,
repoFile: repoFile,
name: testRepoName,
url: ts.URL(),
forceUpdate: false,
repoFile: repoFile,
}
os.Setenv(xdg.CacheHomeEnvVar, rootDir)
@ -204,11 +202,10 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) {
go func(name string) {
defer wg.Done()
o := &repoAddOptions{
name: name,
url: ts.URL(),
deprecatedNoUpdate: true,
forceUpdate: false,
repoFile: repoFile,
name: name,
url: ts.URL(),
forceUpdate: false,
repoFile: repoFile,
}
if err := o.run(io.Discard); err != nil {
t.Error(err)

@ -24,19 +24,21 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/repo"
)
const repoIndexDesc = `
Read the current directory and generate an index file based on the charts found.
Read the current directory, generate an index file based on the charts found
and write the result to 'index.yaml' in the current directory.
This tool is used for creating an 'index.yaml' file for a chart repository. To
set an absolute URL to the charts, use '--url' flag.
To merge the generated index with an existing index file, use the '--merge'
flag. In this case, the charts found in the current directory will be merged
into the existing index, with local charts taking priority over existing charts.
into the index passed in with --merge, with local charts taking priority over
existing charts.
`
type repoIndexOptions struct {
@ -54,15 +56,15 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
Short: "generate an index file given a directory containing packaged charts",
Long: repoIndexDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
// Allow file completion when completing the argument for the directory
return nil, cobra.ShellCompDirectiveDefault
}
// No more completions, so disable file completion
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
o.dir = args[0]
return o.run(out)
},
@ -76,7 +78,7 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
return cmd
}
func (i *repoIndexOptions) run(out io.Writer) error {
func (i *repoIndexOptions) run(_ io.Writer) error {
path, err := filepath.Abs(i.dir)
if err != nil {
return err

@ -24,7 +24,7 @@ import (
"path/filepath"
"testing"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/pkg/repo"
)
func TestRepoIndexCmd(t *testing.T) {
@ -162,9 +162,9 @@ func TestRepoIndexCmd(t *testing.T) {
}
}
func linkOrCopy(old, new string) error {
if err := os.Link(old, new); err != nil {
return copyFile(old, new)
func linkOrCopy(source, target string) error {
if err := os.Link(source, target); err != nil {
return copyFile(source, target)
}
return nil

@ -24,9 +24,9 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/repo"
)
func newRepoListCmd(out io.Writer) *cobra.Command {
@ -36,8 +36,8 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
Aliases: []string{"ls"},
Short: "list chart repositories",
Args: require.NoArgs,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error {
ValidArgsFunction: noMoreArgsCompFunc,
RunE: func(_ *cobra.Command, _ []string) error {
f, _ := repo.LoadFile(settings.RepositoryConfig)
if len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML) {
return errors.New("no repositories to show")
@ -123,7 +123,7 @@ func filterRepos(repos []*repo.Entry, ignoredRepoNames []string) []*repo.Entry {
}
// Provide dynamic auto-completion for repo names
func compListRepos(prefix string, ignoredRepoNames []string) []string {
func compListRepos(_ string, ignoredRepoNames []string) []string {
var rNames []string
f, err := repo.LoadFile(settings.RepositoryConfig)

@ -25,9 +25,9 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/repo"
)
type repoRemoveOptions struct {
@ -44,10 +44,10 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command {
Aliases: []string{"rm"},
Short: "remove one or more chart repositories",
Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compListRepos(toComplete, args), cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache
o.names = args

@ -24,9 +24,9 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/repo"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestRepoRemove(t *testing.T) {

@ -19,14 +19,15 @@ package main
import (
"fmt"
"io"
"slices"
"sync"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/getter"
"helm.sh/helm/v4/pkg/repo"
)
const updateDesc = `
@ -57,10 +58,10 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
Short: "update information of available charts locally from chart repositories",
Long: updateDesc,
Args: require.MinimumNArgs(0),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compListRepos(toComplete, args), cobra.ShellCompDirectiveNoFileComp
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache
o.names = args
@ -158,10 +159,5 @@ func checkRequestedRepos(requestedRepos []string, validRepos []*repo.Entry) erro
}
func isRepoRequested(repoName string, requestedRepos []string) bool {
for _, requestedRepo := range requestedRepos {
if repoName == requestedRepo {
return true
}
}
return false
return slices.Contains(requestedRepos, repoName)
}

@ -24,17 +24,17 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/internal/test/ensure"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/getter"
"helm.sh/helm/v4/pkg/repo"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestUpdateCmd(t *testing.T) {
var out bytes.Buffer
// Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently.
updater := func(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
updater := func(repos []*repo.ChartRepository, out io.Writer, _ bool) error {
for _, re := range repos {
fmt.Fprintln(out, re.Config.Name)
}
@ -59,7 +59,7 @@ func TestUpdateCmdMultiple(t *testing.T) {
var out bytes.Buffer
// Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently.
updater := func(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
updater := func(repos []*repo.ChartRepository, out io.Writer, _ bool) error {
for _, re := range repos {
fmt.Fprintln(out, re.Config.Name)
}
@ -85,7 +85,7 @@ func TestUpdateCmdInvalid(t *testing.T) {
var out bytes.Buffer
// Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently.
updater := func(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
updater := func(repos []*repo.ChartRepository, out io.Writer, _ bool) error {
for _, re := range repos {
fmt.Fprintln(out, re.Config.Name)
}

@ -24,8 +24,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const rollbackDesc = `
@ -46,7 +46,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "roll back a release to a previous revision",
Long: rollbackDesc,
Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
return compListReleases(toComplete, args, cfg)
}
@ -55,9 +55,9 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return compListRevisions(toComplete, cfg, args[0])
}
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
if len(args) > 1 {
ver, err := strconv.Atoi(args[1])
if err != nil {

@ -17,10 +17,12 @@ limitations under the License.
package main
import (
"fmt"
"reflect"
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/release"
)
func TestRollbackCmd(t *testing.T) {
@ -121,3 +123,44 @@ func TestRollbackFileCompletion(t *testing.T) {
checkFileCompletion(t, "rollback myrelease", false)
checkFileCompletion(t, "rollback myrelease 1", false)
}
func TestRollbackWithLabels(t *testing.T) {
labels1 := map[string]string{"operation": "install", "firstLabel": "firstValue"}
labels2 := map[string]string{"operation": "upgrade", "secondLabel": "secondValue"}
releaseName := "funny-bunny-labels"
rels := []*release.Release{
{
Name: releaseName,
Info: &release.Info{Status: release.StatusSuperseded},
Chart: &chart.Chart{},
Version: 1,
Labels: labels1,
},
{
Name: releaseName,
Info: &release.Info{Status: release.StatusDeployed},
Chart: &chart.Chart{},
Version: 2,
Labels: labels2,
},
}
storage := storageFixture()
for _, rel := range rels {
if err := storage.Create(rel); err != nil {
t.Fatal(err)
}
}
_, _, err := executeActionCommandC(storage, fmt.Sprintf("rollback %s 1", releaseName))
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
updatedRel, err := storage.Get(releaseName, 3)
if err != nil {
t.Errorf("unexpected error, got '%v'", err)
}
if !reflect.DeepEqual(updatedRel.Labels, labels1) {
t.Errorf("Expected {%v}, got {%v}", labels1, updatedRel.Labels)
}
}

@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main // import "helm.sh/helm/v3/cmd/helm"
package main // import "helm.sh/helm/v4/cmd/helm"
import (
"context"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
@ -29,9 +30,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/internal/tlsutil"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/registry"
"helm.sh/helm/v4/pkg/repo"
)
var globalUsage = `The Kubernetes package manager
@ -45,31 +47,32 @@ Common actions for Helm:
Environment variables:
| Name | Description |
|------------------------------------|---------------------------------------------------------------------------------------------------|
| $HELM_CACHE_HOME | set an alternative location for storing cached files. |
| $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. |
| $HELM_DATA_HOME | set an alternative location for storing Helm data. |
| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode |
| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, sql. |
| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. |
| $HELM_MAX_HISTORY | set the maximum number of helm release history. |
| $HELM_NAMESPACE | set the namespace used for the helm operations. |
| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. |
| $HELM_PLUGINS | set the path to the plugins directory |
| $HELM_REGISTRY_CONFIG | set the path to the registry config file. |
| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory |
| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. |
| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") |
| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication |
| $HELM_KUBECAFILE | set the Kubernetes certificate authority file. |
| $HELM_KUBEASGROUPS | set the Groups to use for impersonation using a comma-separated list. |
| $HELM_KUBEASUSER | set the Username to impersonate for the operation. |
| $HELM_KUBECONTEXT | set the name of the kubeconfig context. |
| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. |
| $HELM_KUBEINSECURE_SKIP_TLS_VERIFY | indicate if the Kubernetes API server's certificate validation should be skipped (insecure) |
| $HELM_KUBETLS_SERVER_NAME | set the server name used to validate the Kubernetes API server certificate |
| $HELM_BURST_LIMIT | set the default burst limit in the case the server contains many CRDs (default 100, -1 to disable)|
| Name | Description |
|------------------------------------|------------------------------------------------------------------------------------------------------------|
| $HELM_CACHE_HOME | set an alternative location for storing cached files. |
| $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. |
| $HELM_DATA_HOME | set an alternative location for storing Helm data. |
| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode |
| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, sql. |
| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. |
| $HELM_MAX_HISTORY | set the maximum number of helm release history. |
| $HELM_NAMESPACE | set the namespace used for the helm operations. |
| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. |
| $HELM_PLUGINS | set the path to the plugins directory |
| $HELM_REGISTRY_CONFIG | set the path to the registry config file. |
| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory |
| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. |
| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") |
| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication |
| $HELM_KUBECAFILE | set the Kubernetes certificate authority file. |
| $HELM_KUBEASGROUPS | set the Groups to use for impersonation using a comma-separated list. |
| $HELM_KUBEASUSER | set the Username to impersonate for the operation. |
| $HELM_KUBECONTEXT | set the name of the kubeconfig context. |
| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. |
| $HELM_KUBEINSECURE_SKIP_TLS_VERIFY | indicate if the Kubernetes API server's certificate validation should be skipped (insecure) |
| $HELM_KUBETLS_SERVER_NAME | set the server name used to validate the Kubernetes API server certificate |
| $HELM_BURST_LIMIT | set the default burst limit in the case the server contains many CRDs (default 100, -1 to disable) |
| $HELM_QPS | set the Queries Per Second in cases where a high number of calls exceed the option for higher burst values |
Helm stores cache, configuration, and data based on the following configuration order:
@ -99,7 +102,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
addKlogFlags(flags)
// Setup shell completion for the namespace flag
err := cmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("namespace", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
if client, err := actionConfig.KubernetesClientSet(); err == nil {
// Choose a long enough timeout that the user notices something is not working
// but short enough that the user is not made to wait very long
@ -122,7 +125,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
}
// Setup shell completion for the kube-context flag
err = cmd.RegisterFlagCompletionFunc("kube-context", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err = cmd.RegisterFlagCompletionFunc("kube-context", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
cobra.CompDebugln("About to get the different kube-contexts", settings.Debug)
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
@ -152,7 +155,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
flags.ParseErrorsWhitelist.UnknownFlags = true
flags.Parse(args)
registryClient, err := newDefaultRegistryClient(false)
registryClient, err := newDefaultRegistryClient(false, "", "")
if err != nil {
return nil, err
}
@ -166,7 +169,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newPullCmd(actionConfig, out),
newShowCmd(actionConfig, out),
newLintCmd(out),
newPackageCmd(actionConfig, out),
newPackageCmd(out),
newRepoCmd(out),
newSearchCmd(out),
newVerifyCmd(out),
@ -200,9 +203,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
// Find and add plugins
loadPlugins(cmd, out)
// Check permissions on critical files
checkPerms()
// Check for expired repositories
checkForExpiredRepos(settings.RepositoryConfig)
@ -229,7 +229,7 @@ func checkForExpiredRepos(repofile string) {
}
// parse repo file.
// Ignore the error because it is okay for a repo file to be unparseable at this
// Ignore the error because it is okay for a repo file to be unparsable at this
// stage. Later checks will trap the error and respond accordingly.
repoFile, err := repo.LoadFile(repofile)
if err != nil {
@ -257,27 +257,30 @@ func checkForExpiredRepos(repofile string) {
}
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool) (*registry.Client, error) {
func newRegistryClient(
certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool, username, password string,
) (*registry.Client, error) {
if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify {
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify, username, password)
if err != nil {
return nil, err
}
return registryClient, nil
}
registryClient, err := newDefaultRegistryClient(plainHTTP)
registryClient, err := newDefaultRegistryClient(plainHTTP, username, password)
if err != nil {
return nil, err
}
return registryClient, nil
}
func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) {
func newDefaultRegistryClient(plainHTTP bool, username, password string) (*registry.Client, error) {
opts := []registry.ClientOption{
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
registry.ClientOptBasicAuth(username, password),
}
if plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
@ -291,10 +294,31 @@ func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) {
return registryClient, nil
}
func newRegistryClientWithTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
func newRegistryClientWithTLS(
certFile, keyFile, caFile string, insecureSkipTLSverify bool, username, password string,
) (*registry.Client, error) {
tlsConf, err := tlsutil.NewTLSConfig(
tlsutil.WithInsecureSkipVerify(insecureSkipTLSverify),
tlsutil.WithCertKeyPairFiles(certFile, keyFile),
tlsutil.WithCAFile(caFile),
)
if err != nil {
return nil, fmt.Errorf("can't create TLS config for client: %w", err)
}
// Create a new registry client
registryClient, err := registry.NewRegistryClientWithTLS(os.Stderr, certFile, keyFile, caFile, insecureSkipTLSverify,
settings.RegistryConfig, settings.Debug,
registryClient, err := registry.NewClient(
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stderr),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
registry.ClientOptHTTPClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConf,
},
}),
registry.ClientOptBasicAuth(username, password),
)
if err != nil {
return nil, err

@ -21,9 +21,9 @@ import (
"path/filepath"
"testing"
"helm.sh/helm/v3/internal/test/ensure"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/helmpath/xdg"
"helm.sh/helm/v4/internal/test/ensure"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/helmpath/xdg"
)
func TestRootCmd(t *testing.T) {

@ -1,58 +0,0 @@
//go:build !windows
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"os"
"os/user"
"path/filepath"
)
func checkPerms() {
// This function MUST NOT FAIL, as it is just a check for a common permissions problem.
// If for some reason the function hits a stopping condition, it may panic. But only if
// we can be sure that it is panicking because Helm cannot proceed.
kc := settings.KubeConfig
if kc == "" {
kc = os.Getenv("KUBECONFIG")
}
if kc == "" {
u, err := user.Current()
if err != nil {
// No idea where to find KubeConfig, so return silently. Many helm commands
// can proceed happily without a KUBECONFIG, so this is not a fatal error.
return
}
kc = filepath.Join(u.HomeDir, ".kube", "config")
}
fi, err := os.Stat(kc)
if err != nil {
// DO NOT error if no KubeConfig is found. Not all commands require one.
return
}
perm := fi.Mode().Perm()
if perm&0040 > 0 {
warning("Kubernetes configuration file is group-readable. This is insecure. Location: %s", kc)
}
if perm&0004 > 0 {
warning("Kubernetes configuration file is world-readable. This is insecure. Location: %s", kc)
}
}

@ -1,82 +0,0 @@
//go:build !windows
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
)
func checkPermsStderr() (string, error) {
r, w, err := os.Pipe()
if err != nil {
return "", err
}
stderr := os.Stderr
os.Stderr = w
defer func() {
os.Stderr = stderr
}()
checkPerms()
w.Close()
var text bytes.Buffer
io.Copy(&text, r)
return text.String(), nil
}
func TestCheckPerms(t *testing.T) {
tdir := t.TempDir()
tfile := filepath.Join(tdir, "testconfig")
fh, err := os.OpenFile(tfile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0440)
if err != nil {
t.Errorf("Failed to create temp file: %s", err)
}
tconfig := settings.KubeConfig
settings.KubeConfig = tfile
defer func() { settings.KubeConfig = tconfig }()
text, err := checkPermsStderr()
if err != nil {
t.Fatalf("could not read from stderr: %s", err)
}
expectPrefix := "WARNING: Kubernetes configuration file is group-readable. This is insecure. Location:"
if !strings.HasPrefix(text, expectPrefix) {
t.Errorf("Expected to get a warning for group perms. Got %q", text)
}
if err := fh.Chmod(0404); err != nil {
t.Errorf("Could not change mode on file: %s", err)
}
text, err = checkPermsStderr()
if err != nil {
t.Fatalf("could not read from stderr: %s", err)
}
expectPrefix = "WARNING: Kubernetes configuration file is world-readable. This is insecure. Location:"
if !strings.HasPrefix(text, expectPrefix) {
t.Errorf("Expected to get a warning for world perms. Got %q", text)
}
}

@ -31,7 +31,7 @@ import (
"github.com/Masterminds/semver/v3"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/pkg/repo"
)
// Result is a search result.

@ -20,8 +20,8 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/repo"
)
func TestSortScore(t *testing.T) {
@ -101,7 +101,7 @@ var indexfileEntries = map[string]repo.ChartVersions{
},
}
func loadTestIndex(t *testing.T, all bool) *Index {
func loadTestIndex(_ *testing.T, all bool) *Index {
i := NewIndex()
i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}, all)
i.AddRepo("ztesting", &repo.IndexFile{Entries: map[string]repo.ChartVersions{

@ -25,8 +25,8 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/internal/monocular"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v4/internal/monocular"
"helm.sh/helm/v4/pkg/cli/output"
)
const searchHubDesc = `
@ -64,7 +64,7 @@ func newSearchHubCmd(out io.Writer) *cobra.Command {
Use: "hub [KEYWORD]",
Short: "search for charts in the Artifact Hub or your own hub instance",
Long: searchHubDesc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
return o.run(out, args)
},
}

@ -27,7 +27,7 @@ func TestSearchHubCmd(t *testing.T) {
// Setup a mock search service
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
fmt.Fprintln(w, searchResult)
}))
defer ts.Close()
@ -57,7 +57,7 @@ func TestSearchHubListRepoCmd(t *testing.T) {
// Setup a mock search service
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
fmt.Fprintln(w, searchResult)
}))
defer ts.Close()
@ -155,7 +155,7 @@ func TestSearchHubCmd_FailOnNoResponseTests(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup a mock search service
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
fmt.Fprintln(w, tt.response)
}))
defer ts.Close()

@ -30,10 +30,10 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/search"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/helmpath"
"helm.sh/helm/v3/pkg/repo"
"helm.sh/helm/v4/cmd/helm/search"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/helmpath"
"helm.sh/helm/v4/pkg/repo"
)
const searchRepoDesc = `
@ -81,7 +81,7 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command {
Use: "repo [keyword]",
Short: "search repositories for a keyword in charts",
Long: searchRepoDesc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCacheDir = settings.RepositoryCache
return o.run(out, args)
@ -139,7 +139,7 @@ func (o *searchRepoOptions) setupSearchedVersion() {
if o.devel { // search for releases and prereleases (alpha, beta, and release candidate releases).
debug("setting version to >0.0.0-0")
o.version = ">0.0.0-0"
} else { // search only for stable releases, prerelease versions will be skip
} else { // search only for stable releases, prerelease versions will be skipped
debug("setting version to >0.0.0")
o.version = ">0.0.0"
}

@ -23,8 +23,8 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
)
const showDesc = `
@ -57,21 +57,20 @@ of the CustomResourceDefinition files
`
func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client := action.NewShowWithConfig(action.ShowAll, cfg)
client := action.NewShow(action.ShowAll, cfg)
showCommand := &cobra.Command{
Use: "show",
Short: "show information of a chart",
Aliases: []string{"inspect"},
Long: showDesc,
Args: require.NoArgs,
ValidArgsFunction: noCompletions, // Disable file completion
Use: "show",
Short: "show information of a chart",
Aliases: []string{"inspect"},
Long: showDesc,
Args: require.NoArgs,
}
// Function providing dynamic auto-completion
validArgsFunc := func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
validArgsFunc := func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListCharts(toComplete, true)
}
@ -82,7 +81,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: showAllDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowAll
err := addRegistryClient(client)
if err != nil {
@ -103,7 +102,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: showValuesDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowValues
err := addRegistryClient(client)
if err != nil {
@ -124,7 +123,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: showChartDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowChart
err := addRegistryClient(client)
if err != nil {
@ -145,7 +144,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: readmeChartDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowReadme
err := addRegistryClient(client)
if err != nil {
@ -166,7 +165,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: showCRDsDesc,
Args: require.ExactArgs(1),
ValidArgsFunction: validArgsFunc,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
client.OutputFormat = action.ShowCRDs
err := addRegistryClient(client)
if err != nil {
@ -199,7 +198,7 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) {
}
addChartPathOptionsFlags(f, &client.ChartPathOptions)
err := subCmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := subCmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 1 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
@ -227,7 +226,7 @@ func runShow(args []string, client *action.Show) (string, error) {
func addRegistryClient(client *action.Show) error {
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}

@ -22,7 +22,7 @@ import (
"strings"
"testing"
"helm.sh/helm/v3/pkg/repo/repotest"
"helm.sh/helm/v4/pkg/repo/repotest"
)
func TestShowPreReleaseChart(t *testing.T) {

@ -28,11 +28,11 @@ import (
"k8s.io/kubectl/pkg/cmd/get"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli/output"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/cli/output"
"helm.sh/helm/v4/pkg/release"
)
// NOTE: Keep the list of statuses up-to-date with pkg/release/status.go.
@ -43,8 +43,8 @@ The status consists of:
- k8s namespace in which the release lives
- state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback)
- revision of the release
- description of the release (can be completion message or error message, need to enable --show-desc)
- list of resources that this release consists of (need to enable --show-resources)
- description of the release (can be completion message or error message)
- list of resources that this release consists of
- details on last test suite run, if applicable
- additional notes provided by the chart
`
@ -58,14 +58,13 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "display the status of the named release",
Long: statusHelp,
Args: require.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
return noMoreArgsComp()
}
return compListReleases(toComplete, args, cfg)
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
// When the output format is a table the resources should be fetched
// and displayed as a table. When YAML or JSON the resources will be
// returned. This mirrors the handling in kubectl.
@ -80,7 +79,12 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
// strip chart metadata from the output
rel.Chart = nil
return outfmt.Write(out, &statusPrinter{rel, false, client.ShowDescription, client.ShowResources, false})
return outfmt.Write(out, &statusPrinter{
release: rel,
debug: false,
showMetadata: false,
hideNotes: false,
})
},
}
@ -88,31 +92,26 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision")
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 1 {
return compListRevisions(toComplete, cfg, args[0])
}
return nil, cobra.ShellCompDirectiveNoFileComp
})
if err != nil {
log.Fatal(err)
}
bindOutputFlag(cmd, &outfmt)
f.BoolVar(&client.ShowDescription, "show-desc", false, "if set, display the description message of the named release")
f.BoolVar(&client.ShowResources, "show-resources", false, "if set, display the resources of the named release")
return cmd
}
type statusPrinter struct {
release *release.Release
debug bool
showDescription bool
showResources bool
showMetadata bool
release *release.Release
debug bool
showMetadata bool
hideNotes bool
}
func (s statusPrinter) WriteJSON(out io.Writer) error {
@ -139,11 +138,9 @@ func (s statusPrinter) WriteTable(out io.Writer) error {
_, _ = fmt.Fprintf(out, "VERSION: %s\n", s.release.Chart.Metadata.Version)
_, _ = fmt.Fprintf(out, "APP_VERSION: %s\n", s.release.Chart.Metadata.AppVersion)
}
if s.showDescription {
_, _ = fmt.Fprintf(out, "DESCRIPTION: %s\n", s.release.Info.Description)
}
_, _ = fmt.Fprintf(out, "DESCRIPTION: %s\n", s.release.Info.Description)
if s.showResources && s.release.Info.Resources != nil && len(s.release.Info.Resources) > 0 {
if len(s.release.Info.Resources) > 0 {
buf := new(bytes.Buffer)
printFlags := get.NewHumanPrintFlags()
typePrinter, _ := printFlags.ToPrinter("")
@ -219,8 +216,9 @@ func (s statusPrinter) WriteTable(out io.Writer) error {
_, _ = fmt.Fprintf(out, "MANIFEST:\n%s\n", s.release.Manifest)
}
if len(s.release.Info.Notes) > 0 {
_, _ = fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(s.release.Info.Notes))
// Hide notes from output - option in install and upgrades
if !s.hideNotes && len(s.release.Info.Notes) > 0 {
fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(s.release.Info.Notes))
}
return nil
}

@ -20,9 +20,9 @@ import (
"testing"
"time"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/release"
helmtime "helm.sh/helm/v3/pkg/time"
"helm.sh/helm/v4/pkg/chart"
"helm.sh/helm/v4/pkg/release"
helmtime "helm.sh/helm/v4/pkg/time"
)
func TestStatusCmd(t *testing.T) {
@ -46,7 +46,7 @@ func TestStatusCmd(t *testing.T) {
}),
}, {
name: "get status of a deployed release, with desc",
cmd: "status --show-desc flummoxed-chickadee",
cmd: "status flummoxed-chickadee",
golden: "output/status-with-desc.txt",
rels: releasesMockWithStatus(&release.Info{
Status: release.StatusDeployed,
@ -70,7 +70,7 @@ func TestStatusCmd(t *testing.T) {
}),
}, {
name: "get status of a deployed release with resources",
cmd: "status --show-resources flummoxed-chickadee",
cmd: "status flummoxed-chickadee",
golden: "output/status-with-resources.txt",
rels: releasesMockWithStatus(
&release.Info{
@ -79,7 +79,7 @@ func TestStatusCmd(t *testing.T) {
),
}, {
name: "get status of a deployed release with resources in json",
cmd: "status --show-resources flummoxed-chickadee -o json",
cmd: "status flummoxed-chickadee -o json",
golden: "output/status-with-resources.json",
rels: releasesMockWithStatus(
&release.Info{

@ -24,18 +24,19 @@ import (
"path"
"path/filepath"
"regexp"
"slices"
"sort"
"strings"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v4/pkg/release"
"github.com/spf13/cobra"
"helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/releaseutil"
"helm.sh/helm/v4/cmd/helm/require"
"helm.sh/helm/v4/pkg/action"
"helm.sh/helm/v4/pkg/chartutil"
"helm.sh/helm/v4/pkg/cli/values"
"helm.sh/helm/v4/pkg/releaseutil"
)
const templateDesc = `
@ -61,7 +62,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Short: "locally render templates",
Long: templateDesc,
Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return compInstall(args, toComplete, client)
},
RunE: func(_ *cobra.Command, args []string) error {
@ -74,7 +75,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
client.InsecureSkipTLSverify, client.PlainHTTP)
client.InsecureSkipTLSverify, client.PlainHTTP, client.Username, client.Password)
if err != nil {
return fmt.Errorf("missing registry client: %w", err)
}
@ -206,12 +207,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
}
func isTestHook(h *release.Hook) bool {
for _, e := range h.Events {
if e == release.HookTest {
return true
}
}
return false
return slices.Contains(h.Events, release.HookTest)
}
// The following functions (writeToFile, createOrOpenFile, and ensureDirectoryForFile)
@ -219,7 +215,7 @@ func isTestHook(h *release.Hook) bool {
// bug introduced by #8156. As part of the todo to refactor renderResources
// this duplicate code should be removed. It is added here so that the API
// surface area is as minimally impacted as possible in fixing the issue.
func writeToFile(outputDir string, name string, data string, append bool) error {
func writeToFile(outputDir string, name string, data string, appendData bool) error {
outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
err := ensureDirectoryForFile(outfileName)
@ -227,7 +223,7 @@ func writeToFile(outputDir string, name string, data string, append bool) error
return err
}
f, err := createOrOpenFile(outfileName, append)
f, err := createOrOpenFile(outfileName, appendData)
if err != nil {
return err
}
@ -244,8 +240,8 @@ func writeToFile(outputDir string, name string, data string, append bool) error
return nil
}
func createOrOpenFile(filename string, append bool) (*os.File, error) {
if append {
func createOrOpenFile(filename string, appendData bool) (*os.File, error) {
if appendData {
return os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600)
}
return os.Create(filename)

@ -22,6 +22,18 @@ import (
"testing"
)
func TestTemplateCmdWithToml(t *testing.T) {
tests := []cmdTestCase{
{
name: "check toToml function rendering",
cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/issue-totoml"),
golden: "output/issue-totoml.txt",
},
}
runTestCmd(t, tests)
}
var chartPath = "testdata/testcharts/subchart"
func TestTemplateCmd(t *testing.T) {
@ -142,7 +154,7 @@ func TestTemplateCmd(t *testing.T) {
golden: "output/issue-9027.txt",
},
{
// Ensure that imported values take precedence over parent chart values
// Ensure that parent chart values take precedence over imported values
name: "template with imported subchart values ensuring import",
cmd: fmt.Sprintf("template '%s' --set configmap.enabled=true --set subchartb.enabled=true", chartPath),
golden: "output/template-subchart-cm.txt",

@ -3,6 +3,7 @@ LAST DEPLOYED: Fri Sep 2 22:04:05 1977
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None
NOTES:
PARENT NOTES

@ -3,4 +3,5 @@ LAST DEPLOYED: Fri Sep 2 22:04:05 1977
NAMESPACE: default
STATUS: deployed
REVISION: 1
DESCRIPTION: Install complete
TEST SUITE: None

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save