Skip to content

Commit 1dd72be

Browse files
Feature/added native helm commands support (#3235)
* Added native helm commands support
1 parent 8cfcf22 commit 1dd72be

File tree

11 files changed

+1141
-38
lines changed

11 files changed

+1141
-38
lines changed

.github/workflows/helmTests.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Helm Tests
2+
on:
3+
workflow_dispatch:
4+
push:
5+
branches:
6+
- "master"
7+
# Triggers the workflow on PRs to master branch only.
8+
pull_request_target:
9+
types: [labeled]
10+
branches:
11+
- "master"
12+
13+
# Ensures that only the latest commit is running for each PR at a time.
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
permissions:
18+
id-token: write
19+
contents: read
20+
jobs:
21+
Helm-Tests:
22+
name: Helm tests (${{ matrix.os.name }})
23+
if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'safe to test')
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
os:
28+
- name: ubuntu
29+
version: 24.04
30+
- name: windows
31+
version: 2022
32+
- name: macos
33+
version: 14
34+
runs-on: ${{ matrix.os.name }}-${{ matrix.os.version }}
35+
steps:
36+
- name: Skip macOS - JGC-413
37+
if: matrix.os.name == 'macos'
38+
run: |
39+
echo "::warning::JGC-413 - Skip until artifactory bootstrap in osx is fixed"
40+
exit 0
41+
42+
- name: Checkout code
43+
if: matrix.os.name != 'macos'
44+
uses: actions/checkout@v5
45+
with:
46+
ref: ${{ github.event.pull_request.head.sha || github.ref }}
47+
48+
- name: Setup Go with cache
49+
if: matrix.os.name != 'macos'
50+
uses: jfrog/.github/actions/install-go-with-cache@main
51+
52+
- name: Install Helm
53+
if: matrix.os.name != 'macos'
54+
uses: azure/setup-helm@v4
55+
with:
56+
version: 'latest'
57+
58+
- name: Debug macOS Environment and Set Timeout
59+
if: runner.os == 'macOS'
60+
run: |
61+
echo "=== macOS Debug Information ==="
62+
echo "Architecture: $(uname -m)"
63+
echo "macOS Version: $(sw_vers -productVersion)"
64+
echo "macOS Build: $(sw_vers -buildVersion)"
65+
echo "Available memory: $(system_profiler SPHardwareDataType | grep Memory || echo 'Memory info not available')"
66+
echo "Available disk space: $(df -h)"
67+
echo "Java version: $(java -version 2>&1 || echo 'Java not found')"
68+
echo "Go version: $(go version)"
69+
echo "Setting RT_CONNECTION_TIMEOUT_SECONDS to 2400 for macOS"
70+
echo "RT_CONNECTION_TIMEOUT_SECONDS=2400" >> $GITHUB_ENV
71+
72+
- name: Install local Artifactory
73+
if: matrix.os.name != 'macos'
74+
uses: jfrog/.github/actions/install-local-artifactory@main
75+
with:
76+
RTLIC: ${{ secrets.RTLIC }}
77+
RT_CONNECTION_TIMEOUT_SECONDS: ${{ env.RT_CONNECTION_TIMEOUT_SECONDS || '1200' }}
78+
79+
- name: Get ID Token and Exchange Token
80+
if: matrix.os.name != 'macos'
81+
shell: bash
82+
run: |
83+
ID_TOKEN=$(curl -sLS -H "User-Agent: actions/oidc-client" -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
84+
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=jfrog-github" | jq .value | tr -d '"')
85+
echo "JFROG_CLI_OIDC_EXCHANGE_TOKEN_ID=${ID_TOKEN}" >> $GITHUB_ENV
86+
87+
- name: Run Helm tests
88+
if: matrix.os.name != 'macos'
89+
run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.helm --jfrog.url=http://127.0.0.1:8082 --jfrog.adminToken=${{ env.JFROG_TESTS_LOCAL_ACCESS_TOKEN }}
90+

buildtools/cli.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ import (
1717
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
1818
"github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
1919
"github.com/jfrog/jfrog-cli-security/utils/techutils"
20+
"github.com/jfrog/jfrog-cli/docs/buildtools/helmcommand"
2021
"github.com/jfrog/jfrog-cli/docs/buildtools/rubyconfig"
2122
setupdocs "github.com/jfrog/jfrog-cli/docs/buildtools/setup"
2223

2324
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/container"
2425
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/dotnet"
2526
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/golang"
2627
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/gradle"
28+
helmcmd "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/helm"
2729
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/mvn"
2830
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/npm"
2931
containerutils "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/ocicontainer"
@@ -340,6 +342,19 @@ func GetCommands() []cli.Command {
340342
Category: buildToolsCategory,
341343
Action: PoetryCmd,
342344
},
345+
{
346+
Name: "helm",
347+
Flags: cliutils.GetCommandFlags(cliutils.Helm),
348+
Usage: helmcommand.GetDescription(),
349+
HelpName: corecommon.CreateUsage("helm", helmcommand.GetDescription(), helmcommand.Usage),
350+
UsageText: helmcommand.GetArguments(),
351+
ArgsUsage: common.CreateEnvVars(),
352+
SkipFlagParsing: true,
353+
HideHelp: true,
354+
BashComplete: corecommon.CreateBashCompletionFunc(),
355+
Category: buildToolsCategory,
356+
Action: HelmCmd,
357+
},
343358
{
344359
Name: "conan",
345360
Flags: cliutils.GetCommandFlags(cliutils.Conan),
@@ -1356,6 +1371,108 @@ func PoetryCmd(c *cli.Context) error {
13561371
return pythonCmd(c, project.Poetry)
13571372
}
13581373

1374+
// HelmCmd executes Helm commands with build info collection support
1375+
func HelmCmd(c *cli.Context) error {
1376+
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
1377+
return err
1378+
}
1379+
if c.NArg() < 1 {
1380+
return cliutils.WrongNumberOfArgumentsHandler(c)
1381+
}
1382+
1383+
args := cliutils.ExtractCommand(c)
1384+
cmdName, helmArgs := getCommandName(args)
1385+
1386+
helmArgs, buildConfiguration, err := build.ExtractBuildDetailsFromArgs(helmArgs)
1387+
if err != nil {
1388+
return err
1389+
}
1390+
1391+
helmArgs, serverDetails, err := extractHelmServerDetails(helmArgs)
1392+
if err != nil {
1393+
return err
1394+
}
1395+
1396+
helmArgs, repositoryCachePath := extractRepositoryCacheFromArgs(helmArgs)
1397+
1398+
restoreEnv, err := setHelmRepositoryCache(repositoryCachePath)
1399+
if err != nil {
1400+
return err
1401+
}
1402+
defer restoreEnv()
1403+
1404+
workingDir, err := os.Getwd()
1405+
if err != nil {
1406+
return fmt.Errorf("failed to get working directory: %w", err)
1407+
}
1408+
1409+
helmCmd := helmcmd.NewHelmCommand().
1410+
SetHelmArgs(helmArgs).
1411+
SetBuildConfiguration(buildConfiguration).
1412+
SetServerDetails(serverDetails).
1413+
SetWorkingDirectory(workingDir).
1414+
SetHelmCmdName(cmdName)
1415+
1416+
return commands.Exec(helmCmd)
1417+
}
1418+
1419+
// extractRepositoryCacheFromArgs extracts the --repository-cache flag value from Helm command arguments
1420+
func extractRepositoryCacheFromArgs(args []string) ([]string, string) {
1421+
cleanedArgs, repositoryCachePath, err := coreutils.ExtractStringOptionFromArgs(args, "repository-cache")
1422+
if err != nil {
1423+
return args, ""
1424+
}
1425+
return cleanedArgs, repositoryCachePath
1426+
}
1427+
1428+
// extractHelmServerDetails extracts server ID from arguments and retrieves server details.
1429+
func extractHelmServerDetails(args []string) ([]string, *coreConfig.ServerDetails, error) {
1430+
cleanedArgs, serverID, err := coreutils.ExtractServerIdFromCommand(args)
1431+
if err != nil {
1432+
return nil, nil, fmt.Errorf("failed to extract server ID: %w", err)
1433+
}
1434+
1435+
if serverID == "" {
1436+
serverDetails, err := coreConfig.GetDefaultServerConf()
1437+
if err != nil {
1438+
return cleanedArgs, nil, err
1439+
}
1440+
return cleanedArgs, serverDetails, nil
1441+
}
1442+
1443+
serverDetails, err := coreConfig.GetSpecificConfig(serverID, true, true)
1444+
if err != nil {
1445+
return nil, nil, fmt.Errorf("failed to get server configuration for ID '%s': %w", serverID, err)
1446+
}
1447+
1448+
return cleanedArgs, serverDetails, nil
1449+
}
1450+
1451+
// setHelmRepositoryCache sets or unsets HELM_REPOSITORY_CACHE environment variable.
1452+
func setHelmRepositoryCache(cachePath string) (func(), error) {
1453+
const envVarName = "HELM_REPOSITORY_CACHE"
1454+
originalValue := os.Getenv(envVarName)
1455+
1456+
if cachePath != "" {
1457+
if err := os.Setenv(envVarName, cachePath); err != nil {
1458+
return nil, fmt.Errorf("failed to set %s environment variable: %w", envVarName, err)
1459+
}
1460+
} else {
1461+
if err := os.Unsetenv(envVarName); err != nil {
1462+
return nil, fmt.Errorf("failed to unset %s environment variable: %w", envVarName, err)
1463+
}
1464+
}
1465+
restoreFunc := func() {
1466+
if originalValue != "" {
1467+
_ = os.Setenv(envVarName, originalValue)
1468+
} else {
1469+
_ = os.Unsetenv(envVarName)
1470+
}
1471+
}
1472+
1473+
return restoreFunc, nil
1474+
}
1475+
13591476
func ConanCmd(c *cli.Context) error {
13601477
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
13611478
return err
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package helmcommand
2+
3+
var Usage = []string{"helm <helm arguments> [command options]"}
4+
5+
func GetDescription() string {
6+
return "Run native Helm command"
7+
}
8+
9+
func GetArguments() string {
10+
return ` Examples:
11+
12+
$ jf helm push mychart-0.1.0.tgz oci://myrepo.jfrog.io/helm-local --build-name=my-build --build-number=1
13+
14+
$ jf helm package ./mychart --build-name=my-build --build-number=1
15+
16+
$ jf helm dependency update ./mychart --build-name=my-build --build-number=1
17+
18+
Commands:
19+
20+
install Install a chart.
21+
22+
upgrade Upgrade a release.
23+
24+
package Package a chart directory into a chart archive.
25+
26+
push Push a chart to remote.
27+
28+
pull Download a chart from remote.
29+
30+
repo Add, list, remove, update, and index chart repositories.
31+
32+
dependency Manage a chart's dependencies.
33+
34+
help, h Show help for any command.`
35+
}

go.mod

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ require (
1616
github.com/docker/docker v28.5.2+incompatible
1717
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
1818
github.com/jfrog/archiver/v3 v3.6.1
19-
github.com/jfrog/build-info-go v1.12.5-0.20251209031413-f5f0e93dc8db
19+
github.com/jfrog/build-info-go v1.12.5-0.20251209171349-eb030db986f9
2020
github.com/jfrog/gofrog v1.7.6
2121
github.com/jfrog/jfrog-cli-application v1.0.2-0.20251208114900-b3cc968c8e3d
22-
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20251210074251-c15fabe27f7f
22+
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20251210120128-176c677fed4c
2323
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251210085744-f8481d179ac5
2424
github.com/jfrog/jfrog-cli-evidence v0.8.3-0.20251204144808-73fa744851c0
2525
github.com/jfrog/jfrog-cli-platform-services v1.10.1-0.20251205121610-171eb9b0000e
@@ -34,6 +34,7 @@ require (
3434
github.com/xeipuuv/gojsonschema v1.2.0
3535
golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39
3636
gopkg.in/yaml.v2 v2.4.0
37+
gopkg.in/yaml.v3 v3.0.1
3738
)
3839

3940
require (
@@ -49,12 +50,13 @@ require (
4950
cloud.google.com/go/storage v1.57.2 // indirect
5051
dario.cat/mergo v1.0.2 // indirect
5152
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
52-
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
53+
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
5354
github.com/CycloneDX/cyclonedx-go v0.9.3 // indirect
5455
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 // indirect
5556
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
5657
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 // indirect
5758
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect
59+
github.com/Masterminds/semver/v3 v3.4.0 // indirect
5860
github.com/Microsoft/go-winio v0.6.2 // indirect
5961
github.com/ProtonMail/go-crypto v1.3.0 // indirect
6062
github.com/VividCortex/ewma v1.2.0 // indirect
@@ -72,6 +74,8 @@ require (
7274
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
7375
github.com/cloudflare/circl v1.6.1 // indirect
7476
github.com/cncf/xds/go v0.0.0-20251110193048-8bfbf64dc13e // indirect
77+
github.com/containerd/containerd v1.7.29 // indirect
78+
github.com/containerd/errdefs v1.0.0 // indirect
7579
github.com/containerd/log v0.1.0 // indirect
7680
github.com/containerd/platforms v0.2.1 // indirect
7781
github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect
@@ -177,7 +181,7 @@ require (
177181
github.com/moby/sys/sequential v0.6.0 // indirect
178182
github.com/moby/sys/user v0.4.0 // indirect
179183
github.com/moby/sys/userns v0.1.0 // indirect
180-
github.com/moby/term v0.5.0 // indirect
184+
github.com/moby/term v0.5.2 // indirect
181185
github.com/morikuni/aec v1.0.0 // indirect
182186
github.com/nwaples/rardecode v1.1.3 // indirect
183187
github.com/oklog/run v1.0.0 // indirect
@@ -247,6 +251,7 @@ require (
247251
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
248252
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
249253
go.opentelemetry.io/otel/trace v1.38.0 // indirect
254+
go.yaml.in/yaml/v2 v2.4.2 // indirect
250255
go.yaml.in/yaml/v3 v3.0.4 // indirect
251256
golang.org/x/crypto v0.45.0 // indirect
252257
golang.org/x/mod v0.30.0 // indirect
@@ -265,9 +270,12 @@ require (
265270
google.golang.org/protobuf v1.36.10 // indirect
266271
gopkg.in/ini.v1 v1.67.0 // indirect
267272
gopkg.in/warnings.v0 v0.1.2 // indirect
268-
gopkg.in/yaml.v3 v3.0.1 // indirect
269273
gotest.tools/v3 v3.5.2 // indirect
274+
helm.sh/helm/v3 v3.19.2 // indirect
275+
k8s.io/client-go v0.34.0 // indirect
270276
k8s.io/klog/v2 v2.130.1 // indirect
277+
oras.land/oras-go/v2 v2.6.0 // indirect
278+
sigs.k8s.io/yaml v1.6.0 // indirect
271279
)
272280

273281
replace github.com/docker/docker => github.com/docker/docker v27.5.1+incompatible

0 commit comments

Comments
 (0)