@@ -15,6 +15,7 @@ import (
1515 "helm.sh/helm/v3/pkg/cli"
1616 "helm.sh/helm/v3/pkg/downloader"
1717 "helm.sh/helm/v3/pkg/getter"
18+ "helm.sh/helm/v3/pkg/registry"
1819 "helm.sh/helm/v3/pkg/repo"
1920)
2021
@@ -105,7 +106,14 @@ func HelmInstall(
105106}
106107
107108// loadChart determines the chart source and loads it appropriately
109+ // LoadChart determines the chart source and loads it appropriately
108110func LoadChart (chartRef , repoURL , version string , settings * cli.EnvSettings ) (* chart.Chart , error ) {
111+ // Check if it's an OCI registry reference
112+ if strings .HasPrefix (chartRef , "oci://" ) {
113+ fmt .Printf ("🐳 Loading OCI chart from registry...\n " )
114+ return LoadOCIChart (chartRef , version , settings , false )
115+ }
116+
109117 if repoURL != "" {
110118 fmt .Printf ("🌐 Loading remote chart from repository...\n " )
111119 return LoadRemoteChart (chartRef , repoURL , version , settings )
@@ -119,6 +127,116 @@ func LoadChart(chartRef, repoURL, version string, settings *cli.EnvSettings) (*c
119127 return loader .Load (chartRef )
120128}
121129
130+ // LoadOCIChart loads a chart from an OCI registry
131+ func LoadOCIChart (chartRef , version string , settings * cli.EnvSettings , debug bool ) (* chart.Chart , error ) {
132+ if debug {
133+ pterm .Printf ("Loading OCI chart: %s (version: %s)\n " , chartRef , version )
134+ }
135+
136+ // Create registry client
137+ registryClient , err := newRegistryClient (debug )
138+ if err != nil {
139+ return nil , fmt .Errorf ("failed to create registry client: %w" , err )
140+ }
141+
142+ // Create a temporary action configuration with registry client
143+ actionConfig := new (action.Configuration )
144+ actionConfig .RegistryClient = registryClient
145+
146+ // Initialize with default settings
147+ logFn := func (format string , v ... interface {}) {
148+ if debug {
149+ message := fmt .Sprintf (format , v ... )
150+ pterm .Printfln ("OCI-REGISTRY: %s" , strings .TrimSpace (message ))
151+ }
152+ }
153+
154+ if err := actionConfig .Init (settings .RESTClientGetter (), settings .Namespace (), os .Getenv ("HELM_DRIVER" ), logFn ); err != nil {
155+ return nil , fmt .Errorf ("failed to initialize action config for OCI: %w" , err )
156+ }
157+
158+ // Create pull action WITH registry client
159+ pull := action .NewPullWithOpts (action .WithConfig (& action.Configuration {
160+ RegistryClient : registryClient ,
161+ }))
162+ pull .Settings = settings
163+ pull .Version = version
164+ pull .Untar = true
165+ pull .UntarDir = settings .RepositoryCache
166+
167+ // Run the pull command
168+ fmt .Printf ("⬇️ Pulling OCI chart: %s...\n " , chartRef )
169+ output , err := pull .Run (chartRef )
170+ if err != nil {
171+ return nil , fmt .Errorf ("failed to pull OCI chart: %w" , err )
172+ }
173+
174+ // Find the pulled chart - OCI pull returns the path
175+ chartPath := strings .TrimSpace (output )
176+
177+ // For OCI charts, handle the output format
178+ if strings .Contains (chartPath , "Pulled: " ) {
179+ parts := strings .Split (chartPath , "Pulled: " )
180+ if len (parts ) > 1 {
181+ chartPath = strings .TrimSpace (parts [1 ])
182+ }
183+ }
184+
185+ // Sometimes OCI charts are in subdirectories
186+ files , err := os .ReadDir (chartPath )
187+ if err == nil && len (files ) == 1 && files [0 ].IsDir () {
188+ chartPath = filepath .Join (chartPath , files [0 ].Name ())
189+ }
190+
191+ if debug {
192+ pterm .Printf ("OCI chart downloaded to: %s\n " , chartPath )
193+ }
194+
195+ fmt .Printf ("📦 Loading OCI chart into memory...\n " )
196+ return loader .Load (chartPath )
197+ }
198+
199+ // newRegistryClient creates a registry client for OCI operations
200+ func newRegistryClient (debug bool ) (* registry.Client , error ) {
201+ // Create registry client options
202+ opts := []registry.ClientOption {
203+ registry .ClientOptWriter (os .Stdout ),
204+ }
205+
206+ // Try to load credentials from various locations
207+ helmConfig := helmHome ()
208+ credentialFiles := []string {
209+ filepath .Join (helmConfig , "config.json" ),
210+ filepath .Join (os .Getenv ("HOME" ), ".docker/config.json" ),
211+ "/etc/docker/config.json" ,
212+ }
213+
214+ for _ , credFile := range credentialFiles {
215+ if _ , err := os .Stat (credFile ); err == nil {
216+ opts = append (opts , registry .ClientOptCredentialsFile (credFile ))
217+ if debug {
218+ pterm .Printf ("Using credentials file: %s\n " , credFile )
219+ }
220+ break
221+ }
222+ }
223+
224+ // Create and return the registry client
225+ return registry .NewClient (opts ... )
226+ }
227+
228+ // helmHome gets the Helm home directory
229+ func helmHome () string {
230+ if home := os .Getenv ("HELM_HOME" ); home != "" {
231+ return home
232+ }
233+ if home := os .Getenv ("HELM_CONFIG_HOME" ); home != "" {
234+ return home
235+ }
236+ userHome , _ := os .UserHomeDir ()
237+ return filepath .Join (userHome , ".helm" )
238+ }
239+
122240// loadFromLocalRepo loads a chart from a local repository
123241func LoadFromLocalRepo (chartRef , version string , settings * cli.EnvSettings ) (* chart.Chart , error ) {
124242 repoName := strings .Split (chartRef , "/" )[0 ]
0 commit comments