Skip to content

feat(883): helm transfer#1832

Merged
matthiasbruns merged 25 commits into
open-component-model:mainfrom
matthiasbruns:feat/883_helm_transfer
Mar 8, 2026
Merged

feat(883): helm transfer#1832
matthiasbruns merged 25 commits into
open-component-model:mainfrom
matthiasbruns:feat/883_helm_transfer

Conversation

@matthiasbruns

@matthiasbruns matthiasbruns commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it

This pull-request adds the "download classic helm chart" to the transformer api.

This PR adds a bigger refactoring to the existing inputand oci-image-creation` - the concepts stayed the same, just technical changes.

`
introduce helm access type from ocmv1
It also adds the missing Helm access type to the new ocm.

type Helm struct {
	// +ocm:jsonschema-gen:enum=Helm/v1,helm/v1
	// +ocm:jsonschema-gen:enum:deprecated=Helm,helm
	Type runtime.Type `json:"type"`

	// HelmRepository is the URL of the helm repository to load the chart from.
	HelmRepository string `json:"helmRepository"`

	// HelmChart if the name of the helm chart and its version separated by a colon.
	HelmChart string `json:"helmChart"`

	// Version can either be specified as part of the chart name or separately.
	Version string `json:"version,omitempty"`
}

It is important to note what I dropped deprecated fields from the spec. CA and Keyring related fields should be loaded from the credentials as already done in the helm input.

reuse blob download from helm input
Additionally, this PR refactors the download helm blob from input to generalize the logic and make it reusable for helm access as well.

Reuse chart to OCI from help input
I also generalized the chart->OCI logic from input and made it reusable. It is now used in both input and the convert transformer.

Refactor CopyChartToOCILayout
I had to refactor CopyChartToOCILayout to be able to get the ociImageSpecV1.Descriptor. The problem was that the whole impl was async. Channels lead to blocking goroutines since io.Pipe was not done writing at the point where I needed the descriptor. I had to create

type Result struct {
	*direct.Blob
	desc chan descriptorOrError
}

type descriptorOrError struct {
	Descriptor ociImageSpecV1.Descriptor
	Err        error
}

to delay the access to said data.

GetHelmChart transformer implementation

The get transformer does the following:

  • validate input spec against new v1alpha1.GetHelmChart
  • generate output paths for the spec output
  • get credentials by requesting ResourceConsumerIdentityProvider.GetResourceCredentialConsumerIdentity which is implemented in the HelmAccess
  • delegate download to generalized helm download from the input package (now downloads)
    • in theory, we could use this transformer also for oci helm download, since the downloader supports the feature
    • but we will use the native GetOCIArtifact from transformers (probably)
  • resulting blobs (chart and optionally a prov) will be copied into *v1alpha1.File
  • the output contains
    • the original Resource
    • the chart file pointer with v1alpha1.File
    • (optionally) the prov file pointer with *v1alpha1.File

ConvertHelmChartToOCI transformer implementation

The convertion transformer does the following:

  • expect chart, prov and resources as input
  • reuse the oci creation logic from input
  • calculate the correct ImageReference for the OCIImage access
  • push the artifact as localblob as an output back the the graph

testing

  • The pr spawns a repotest.NewTempServer() mock http server pointing to the shared helm testdata/mychart-0.1.0.tgz dir
  • In the test we create Resources with several variations of the Helm access
  • Each test verifies that the files are correctly downloaded and equal the original file
  • provenance file download added
    • Open Question: should we also verify during transform? Currently, we only download the .prov file
  • helm to oci tests and validated the created oci image and the prov files

Which issue(s) this PR fixes

Contributes: open-component-model/ocm-project#883

Testing

How to test the changes

See #1846 for testing

make sure helm input did not break

#!/bin/zsh

alias OCM='go run ../../main.go'

# Test helm input with local path
CHART_PATH="../../../bindings/go/helm/testdata/mychart"

# stat the chart path to ensure it exists
if [ ! -d "$CHART_PATH" ]; then
  echo "Error: Chart path $CHART_PATH does not exist"
  exit 1
fi

cat <<EOF > constructor-input-local.yaml
components:
- name: ocm.software/helm-input-local
  version: 0.1.0
  provider:
    name: ocm.software
  resources:
  - name: mychart
    version: 0.1.0
    type: helmChart
    input:
      type: helm/v1
      path: $CHART_PATH
EOF

# create dir relative to current directory to avoid issues with absolute paths in the constructor
INPUT_CTF_LOCAL="input-ctf-local-$(date +%s)"
mkdir $INPUT_CTF_LOCAL
echo "Using temporary directory for helm input (local): $INPUT_CTF_LOCAL"

OCM add cv --repository ctf::$INPUT_CTF_LOCAL --constructor constructor-input-local.yaml

echo "--- Local helm input test passed ---"

# Test helm input with remote repository (uses same podinfo chart)
cat <<EOF > constructor-input-remote.yaml
components:
- name: ocm.software/helm-input-remote
  version: 6.9.1
  provider:
    name: ocm.software
  resources:
  - name: podinfo
    version: 6.9.1
    type: helmChart
    input:
      type: helm/v1
      helmRepository: https://stefanprodan.github.io/podinfo/podinfo-6.9.1.tgz
EOF

INPUT_CTF_REMOTE="input-ctf-remote-$(date +%s)"
mkdir $INPUT_CTF_REMOTE
echo "Using temporary directory for helm input (remote): $INPUT_CTF_REMOTE"

OCM add cv --repository ctf::$INPUT_CTF_REMOTE --constructor constructor-input-remote.yaml

echo "--- Remote helm input test passed ---"
Verification
  • I have tested the changes locally by running ocm

Summary by CodeRabbit

  • New Features

    • Added Helm access spec, credential-aware identity resolution, and options for TLS/credentials.
    • Remote Helm chart retrieval (HTTP/OCI) with provenance support and configurable buffering/output path handling.
    • Conversion of Helm charts into OCI image layouts and transformers to fetch charts and produce OCI artifacts.
  • Tests

    • Extensive unit and integration tests covering access parsing, identity/error cases, downloads, provenance, OCI conversion, and transformers.

@github-actions github-actions Bot added kind/feature new feature, enhancement, improvement, extension size/l Large labels Feb 24, 2026

@fabianburth fabianburth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already asked in the war room today about the helm repository - back then, I said let's not implement it, if we don't need it.
I think, I just did not really understand it yet. I thought this was just an implementation detail.
Now, after looking over this again, I think - to be conceptually correct - we would need a ResourceRepository implementation for helm. To be more precise a ReadOnlyResourceRepository. In the plugin contracts, we do have this distinction already (see here). In the library interfaces and the plugin manager, we do not have such a fine granular differentiation (see here).
I think, we will likely go with a workaround now. If we implement the helm as a resource repository, we just return an error if someone wants to use the upload for now.

But I think, you should still bring a larger conceptual discussion to the team to decide how we want to deal with this in the future.

@matthiasbruns

Copy link
Copy Markdown
Contributor Author

You already asked in the war room today about the helm repository - back then, I said let's not implement it, if we don't need it. I think, I just did not really understand it yet. I thought this was just an implementation detail. Now, after looking over this again, I think - to be conceptually correct - we would need a ResourceRepository implementation for helm. To be more precise a ReadOnlyResourceRepository. In the plugin contracts, we do have this distinction already (see here). In the library interfaces and the plugin manager, we do not have such a fine granular differentiation (see here). I think, we will likely go with a workaround now. If we implement the helm as a resource repository, we just return an error if someone wants to use the upload for now.

But I think, you should still bring a larger conceptual discussion to the team to decide how we want to deal with this in the future.

Sure, I am still discovering - I also think it might make sense to introduce the repository (either in this PR or in a patch pr afterwards) and will discuss this in the next WR (tomorrow).

@matthiasbruns

Copy link
Copy Markdown
Contributor Author

repotest.NewTempServer(t, repotest.WithChartSourceGlob("../testdata/provenance/mychart-*")) is not working with otel 1.40

go mod tidy
go: finding module for package go.opentelemetry.io/otel/sdk/internal/internaltest
go: ocm.software/open-component-model/bindings/go/helm/download imports
        helm.sh/helm/v4/pkg/registry tested by
        helm.sh/helm/v4/pkg/registry.test imports
        github.com/distribution/distribution/v3/registry imports
        github.com/distribution/distribution/v3/tracing imports
        go.opentelemetry.io/contrib/exporters/autoexport imports
        go.opentelemetry.io/otel/sdk/log tested by
        go.opentelemetry.io/otel/sdk/log.test imports
        go.opentelemetry.io/otel/sdk/internal/internaltest: module go.opentelemetry.io/otel/sdk@latest found (v1.40.0), but does not contain package go.opentelemetry.io/otel/sdk/internal/internaltest

@matthiasbruns

Copy link
Copy Markdown
Contributor Author

@fabianburth I prepared a ticket for the resource repository
open-component-model/ocm-project#911

I quickly talked to jakob about this and came to the conclusion that we should tackle this as a follow up in favor of unblocking the helm transfer.
We can talk about this in the refinement today :)

@matthiasbruns matthiasbruns force-pushed the feat/883_helm_transfer branch 2 times, most recently from 0cdd839 to 5b889c9 Compare February 26, 2026 07:34
@matthiasbruns

Copy link
Copy Markdown
Contributor Author

helm-to-oci transformation will be added today

@matthiasbruns matthiasbruns mentioned this pull request Feb 26, 2026
1 task
Comment thread bindings/go/helm/transformation/convert_helm_chart_to_oci.go Outdated
Comment thread bindings/go/helm/transformation/convert_helm_chart_to_oci_test.go
@github-actions github-actions Bot added the component/github-actions Changes on GitHub Actions or within `.github/` directory label Feb 27, 2026
@matthiasbruns matthiasbruns force-pushed the feat/883_helm_transfer branch 5 times, most recently from d4d0119 to 4cded99 Compare February 27, 2026 16:30
Comment thread bindings/go/helm/types.go Outdated
Comment thread bindings/go/helm/input/convert.go Outdated
@matthiasbruns

Copy link
Copy Markdown
Contributor Author

waiting for #1862 to be merged to support nullable fields

@matthiasbruns matthiasbruns force-pushed the feat/883_helm_transfer branch from 439c8ef to 1d47a2d Compare March 3, 2026 13:23
@matthiasbruns matthiasbruns marked this pull request as ready for review March 3, 2026 13:26
@matthiasbruns matthiasbruns requested a review from a team as a code owner March 3, 2026 13:26

@Skarlso Skarlso left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments before testing commences. :)

Comment thread bindings/go/helm/oci/oci.go Outdated
Comment thread bindings/go/helm/download/download.go Outdated
Comment thread bindings/go/helm/transformation/convert_helm_chart_to_oci.go Outdated
Comment thread bindings/go/helm/transformation/convert_helm_chart_to_oci.go Outdated
Comment thread bindings/go/helm/oci/oci.go Outdated
Comment thread bindings/go/helm/download/download.go Outdated
Comment thread bindings/go/helm/types.go Outdated
Comment thread bindings/go/helm/transformation/output.go
Comment thread bindings/go/helm/types.go Outdated
piotrjanik
piotrjanik previously approved these changes Mar 4, 2026

@piotrjanik piotrjanik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested implementation locally. LGTM

Comment thread bindings/go/helm/interface.go
jakobmoellerdev
jakobmoellerdev previously approved these changes Mar 6, 2026
Comment thread bindings/go/helm/transformation/output.go

@Skarlso Skarlso left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few couple of observations here and there.

My main concern is the duplication which is weird. But otherwise, looks okay.

Comment thread bindings/go/helm/internal/download/download.go Outdated
Comment thread bindings/go/helm/input/blob.go
Comment thread bindings/go/helm/internal/download/doc.go Outdated
Comment thread bindings/go/helm/transformation/output.go
Comment thread bindings/go/helm/transformation/convert_helm_chart_to_oci.go Outdated
Comment thread bindings/go/helm/transformation/convert_helm_chart_to_oci.go Outdated
@matthiasbruns matthiasbruns force-pushed the feat/883_helm_transfer branch 2 times, most recently from dd83118 to ed47ccd Compare March 6, 2026 18:04
…le types

On-behalf-of: SAP <matthias.bruns@sap.com>
Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
@matthiasbruns matthiasbruns force-pushed the feat/883_helm_transfer branch from ed47ccd to 1b80f81 Compare March 6, 2026 18:07
@matthiasbruns matthiasbruns dismissed fabianburth’s stale review March 8, 2026 11:40

moved everything that shouldnt be public into internal

@matthiasbruns matthiasbruns merged commit a3c1deb into open-component-model:main Mar 8, 2026
21 checks passed
@matthiasbruns matthiasbruns linked an issue Mar 10, 2026 that may be closed by this pull request
8 tasks
matthiasbruns added a commit that referenced this pull request Mar 10, 2026
<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

This PR implements the transformers from
#1832
It creates a 3-step-transformation for the helm chart
transformation-chain:

```mermaid
flowchart TD
    A[Transfer Helm Chart] -->B(Get Helm Chart)
    B --> C{Download from Repo}
    C -->|Has Prov| D[Download from Repo]
    C --> E{Pack Helm LocalBlob}
    D --> E
    E -->|GetHelmOutput| F{Convert to OCI}
    F -->|ConverToHelm| G{Pack OCI from Helm}
    F -->|ConverToHelm with prov| G{Pack OCI from Helm}
    G -->|upload-as localBlob/default | I{Upload as localBlob}
    G -->|upload-as ociArtifact | H{Upload as OCI Image}
```

1. get helm chart with `GetHelmChartV1alpha1`
2. convert helm to OCI with `ConvertHelmToOCIV1alpha1`
3. upload
  a.  localBlobl: the oci artifact with `OCIAddLocalResourceV1alpha1`
  b.  ociArtifact: the oci artifact with `AddOCIArtifactType`

The [spec
PR](#1832)
will be kept in sync with changed from here in the `bindings/go/helm`
package.

#### Which issue(s) this PR fixes
Contributes:
open-component-model/ocm-project#883

#### Testing

##### How to test the changes

```bash
#!/bin/zsh

alias OCM='go run ../../main.go'
REGISTRY="ghcr.io/matthiasbruns/ocm-tutorials"
REGISTRY2="ghcr.io/matthiasbruns/ocm-tutorials-2"

pause() {
  echo "\n>>> Next: $1"
  echo "--- Press Enter to continue ---"
  read
}

# OCM --help

# create constructor.yaml
# https://stefanprodan.github.io/podinfo/podinfo-6.9.1.tgz
cat <<EOF > constructor.yaml
components:
- name: ocm.software/podinfo
  version: 6.9.1
  provider:
    name: ocm.software
  resources:
  - name: podinfo
    version: 6.9.1
    type: helmChart
    access:
      type: helm/v1
      helmRepository: https://stefanprodan.github.io/podinfo
      helmChart: podinfo-6.9.1.tgz
EOF

CTF_DIR=$(mktemp -d)
echo "Using temporary directory: $CTF_DIR"

pause "Add component version to CTF from constructor.yaml"

# add cv command
OCM add cv --repository ctf::$CTF_DIR --constructor constructor.yaml --skip-reference-digest-processing

HELM_REF="ctf::$CTF_DIR//ocm.software/podinfo:6.9.1"

pause "Create component version ($REGISTRY)"

# transfer
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources

pause "Transfer with --upload-as localBlob (OCI registry)"

# transfer  --upload-as localBlob
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources --upload-as localBlob

pause "Transfer with --upload-as ociArtifact (OCI registry)"

# transfer  --upload-as ociArtifact
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources --upload-as ociArtifact

pause "Download component descriptor with oras"

# download with oras
oras pull $REGISTRY/component-descriptors/ocm.software/podinfo:6.9.1 --output .

pause "Download resource using OCM CLI"

# rm downloaded if exists
rm -rf downloaded

# downloadCMD resource
OCM download resource https://$REGISTRY//ocm.software/podinfo:6.9.1 --identity name=podinfo,version=6.9.1 --output ./downloaded
```

You can also unpack the blob and it should contain the `podinfo` chart
contents

# transfer oci helm to another oci
`ocm transfer component-version
http://$REGISTRY//ocm.software/podinfo:6.9.1 $REGISTRY2 --copy-resources
--upload-as ociArtifact`

##### Verification

- [x] I have tested the changes locally by running `ocm`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for transferring Helm charts as part of component
version transfers
  * Helm charts can now be converted to OCI artifacts during transfer
* Support for both local Helm chart paths and remote Helm repositories
as transfer sources

* **Documentation**
* Updated transfer command documentation with Helm chart transfer
capabilities and examples
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
frewilhelm pushed a commit to frewilhelm/open-component-model that referenced this pull request Mar 12, 2026
<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it
This pull-request adds the "download classic helm chart" to the
transformer api.

`This PR adds a bigger refactoring to the existing `input`and
`oci-image-creation` - the concepts stayed the same, just technical
changes.

`
**introduce helm access type from ocmv1**
It also adds the missing Helm access type to the new ocm.
```go
type Helm struct {
	// +ocm:jsonschema-gen:enum=Helm/v1,helm/v1
	// +ocm:jsonschema-gen:enum:deprecated=Helm,helm
	Type runtime.Type `json:"type"`

	// HelmRepository is the URL of the helm repository to load the chart from.
	HelmRepository string `json:"helmRepository"`

	// HelmChart if the name of the helm chart and its version separated by a colon.
	HelmChart string `json:"helmChart"`

	// Version can either be specified as part of the chart name or separately.
	Version string `json:"version,omitempty"`
}

```
It is important to note what I dropped deprecated fields from the spec.
CA and Keyring related fields should be loaded from the credentials as
already done in the helm input.


**reuse blob download from helm input**
Additionally, this PR refactors the `download helm blob `from input to
generalize the logic and make it reusable for helm access as well.

**Reuse chart to OCI from help input**
I also generalized the chart->OCI logic from `input` and made it
reusable. It is now used in both `input` and the `convert transformer.`

**Refactor `CopyChartToOCILayout`**
I had to refactor `CopyChartToOCILayout` to be able to get the
`ociImageSpecV1.Descriptor`. The problem was that the whole impl was
async. Channels lead to blocking goroutines since `io.Pipe` was not done
writing at the point where I needed the descriptor. I had to create

```go
type Result struct {
	*direct.Blob
	desc chan descriptorOrError
}

type descriptorOrError struct {
	Descriptor ociImageSpecV1.Descriptor
	Err        error
}
```
to delay the access to said data.

**GetHelmChart transformer implementation**

The get transformer does the following:
- validate input spec against new `v1alpha1.GetHelmChart`
- generate output paths for the spec output
- get credentials by requesting
`ResourceConsumerIdentityProvider.GetResourceCredentialConsumerIdentity`
which is implemented in the `HelmAccess`
- delegate download to generalized helm download from the input package
(now downloads)
- in theory, we could use this transformer also for oci helm download,
since the downloader supports the feature
- but we will use the native GetOCIArtifact from transformers (probably)
- resulting blobs (chart and optionally a prov) will be copied into
`*v1alpha1.File`
- the output contains 
  - the original `Resource`  
  - the chart file pointer with `v1alpha1.File`
  - (optionally) the prov file pointer with `*v1alpha1.File`

**ConvertHelmChartToOCI transformer implementation**

The convertion transformer does the following:
- expect chart, prov and resources as input
- reuse the oci creation logic from input
- calculate the correct `ImageReference` for the `OCIImage` access
- push the artifact as localblob as an output back the the graph

 **testing**
- The pr spawns a ~~repotest.NewTempServer()~~ mock http server pointing
to the shared helm `testdata/mychart-0.1.0.tgz` dir
- In the test we create Resources with several variations of the `Helm`
access
- Each test verifies that the files are correctly downloaded and equal
the original file
- provenance file download added
- **Open Question**: should we also verify during transform? Currently,
we only download the .prov file
- helm to oci tests and validated the created oci image and the prov
files


#### Which issue(s) this PR fixes
Contributes:
open-component-model/ocm-project#883

#### Testing

##### How to test the changes

See
open-component-model#1846
for testing


**make sure helm input did not break**
```bash
#!/bin/zsh

alias OCM='go run ../../main.go'

# Test helm input with local path
CHART_PATH="../../../bindings/go/helm/testdata/mychart"

# stat the chart path to ensure it exists
if [ ! -d "$CHART_PATH" ]; then
  echo "Error: Chart path $CHART_PATH does not exist"
  exit 1
fi

cat <<EOF > constructor-input-local.yaml
components:
- name: ocm.software/helm-input-local
  version: 0.1.0
  provider:
    name: ocm.software
  resources:
  - name: mychart
    version: 0.1.0
    type: helmChart
    input:
      type: helm/v1
      path: $CHART_PATH
EOF

# create dir relative to current directory to avoid issues with absolute paths in the constructor
INPUT_CTF_LOCAL="input-ctf-local-$(date +%s)"
mkdir $INPUT_CTF_LOCAL
echo "Using temporary directory for helm input (local): $INPUT_CTF_LOCAL"

OCM add cv --repository ctf::$INPUT_CTF_LOCAL --constructor constructor-input-local.yaml

echo "--- Local helm input test passed ---"

# Test helm input with remote repository (uses same podinfo chart)
cat <<EOF > constructor-input-remote.yaml
components:
- name: ocm.software/helm-input-remote
  version: 6.9.1
  provider:
    name: ocm.software
  resources:
  - name: podinfo
    version: 6.9.1
    type: helmChart
    input:
      type: helm/v1
      helmRepository: https://stefanprodan.github.io/podinfo/podinfo-6.9.1.tgz
EOF

INPUT_CTF_REMOTE="input-ctf-remote-$(date +%s)"
mkdir $INPUT_CTF_REMOTE
echo "Using temporary directory for helm input (remote): $INPUT_CTF_REMOTE"

OCM add cv --repository ctf::$INPUT_CTF_REMOTE --constructor constructor-input-remote.yaml

echo "--- Remote helm input test passed ---"
```

##### Verification

- [x] I have tested the changes locally by running `ocm`
  

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Helm access spec, credential-aware identity resolution, and
options for TLS/credentials.
* Remote Helm chart retrieval (HTTP/OCI) with provenance support and
configurable buffering/output path handling.
* Conversion of Helm charts into OCI image layouts and transformers to
fetch charts and produce OCI artifacts.

* **Tests**
* Extensive unit and integration tests covering access parsing,
identity/error cases, downloads, provenance, OCI conversion, and
transformers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
frewilhelm pushed a commit to frewilhelm/open-component-model that referenced this pull request Mar 12, 2026
<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

This PR implements the transformers from
open-component-model#1832
It creates a 3-step-transformation for the helm chart
transformation-chain:

```mermaid
flowchart TD
    A[Transfer Helm Chart] -->B(Get Helm Chart)
    B --> C{Download from Repo}
    C -->|Has Prov| D[Download from Repo]
    C --> E{Pack Helm LocalBlob}
    D --> E
    E -->|GetHelmOutput| F{Convert to OCI}
    F -->|ConverToHelm| G{Pack OCI from Helm}
    F -->|ConverToHelm with prov| G{Pack OCI from Helm}
    G -->|upload-as localBlob/default | I{Upload as localBlob}
    G -->|upload-as ociArtifact | H{Upload as OCI Image}
```

1. get helm chart with `GetHelmChartV1alpha1`
2. convert helm to OCI with `ConvertHelmToOCIV1alpha1`
3. upload
  a.  localBlobl: the oci artifact with `OCIAddLocalResourceV1alpha1`
  b.  ociArtifact: the oci artifact with `AddOCIArtifactType`

The [spec
PR](open-component-model#1832)
will be kept in sync with changed from here in the `bindings/go/helm`
package.

#### Which issue(s) this PR fixes
Contributes:
open-component-model/ocm-project#883

#### Testing

##### How to test the changes

```bash
#!/bin/zsh

alias OCM='go run ../../main.go'
REGISTRY="ghcr.io/matthiasbruns/ocm-tutorials"
REGISTRY2="ghcr.io/matthiasbruns/ocm-tutorials-2"

pause() {
  echo "\n>>> Next: $1"
  echo "--- Press Enter to continue ---"
  read
}

# OCM --help

# create constructor.yaml
# https://stefanprodan.github.io/podinfo/podinfo-6.9.1.tgz
cat <<EOF > constructor.yaml
components:
- name: ocm.software/podinfo
  version: 6.9.1
  provider:
    name: ocm.software
  resources:
  - name: podinfo
    version: 6.9.1
    type: helmChart
    access:
      type: helm/v1
      helmRepository: https://stefanprodan.github.io/podinfo
      helmChart: podinfo-6.9.1.tgz
EOF

CTF_DIR=$(mktemp -d)
echo "Using temporary directory: $CTF_DIR"

pause "Add component version to CTF from constructor.yaml"

# add cv command
OCM add cv --repository ctf::$CTF_DIR --constructor constructor.yaml --skip-reference-digest-processing

HELM_REF="ctf::$CTF_DIR//ocm.software/podinfo:6.9.1"

pause "Create component version ($REGISTRY)"

# transfer
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources

pause "Transfer with --upload-as localBlob (OCI registry)"

# transfer  --upload-as localBlob
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources --upload-as localBlob

pause "Transfer with --upload-as ociArtifact (OCI registry)"

# transfer  --upload-as ociArtifact
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources --upload-as ociArtifact

pause "Download component descriptor with oras"

# download with oras
oras pull $REGISTRY/component-descriptors/ocm.software/podinfo:6.9.1 --output .

pause "Download resource using OCM CLI"

# rm downloaded if exists
rm -rf downloaded

# downloadCMD resource
OCM download resource https://$REGISTRY//ocm.software/podinfo:6.9.1 --identity name=podinfo,version=6.9.1 --output ./downloaded
```

You can also unpack the blob and it should contain the `podinfo` chart
contents

# transfer oci helm to another oci
`ocm transfer component-version
http://$REGISTRY//ocm.software/podinfo:6.9.1 $REGISTRY2 --copy-resources
--upload-as ociArtifact`

##### Verification

- [x] I have tested the changes locally by running `ocm`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for transferring Helm charts as part of component
version transfers
  * Helm charts can now be converted to OCI artifacts during transfer
* Support for both local Helm chart paths and remote Helm repositories
as transfer sources

* **Documentation**
* Updated transfer command documentation with Helm chart transfer
capabilities and examples
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Mar 18, 2026
<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it
This pull-request adds the "download classic helm chart" to the
transformer api.

`This PR adds a bigger refactoring to the existing `input`and
`oci-image-creation` - the concepts stayed the same, just technical
changes.

`
**introduce helm access type from ocmv1**
It also adds the missing Helm access type to the new ocm.
```go
type Helm struct {
	// +ocm:jsonschema-gen:enum=Helm/v1,helm/v1
	// +ocm:jsonschema-gen:enum:deprecated=Helm,helm
	Type runtime.Type `json:"type"`

	// HelmRepository is the URL of the helm repository to load the chart from.
	HelmRepository string `json:"helmRepository"`

	// HelmChart if the name of the helm chart and its version separated by a colon.
	HelmChart string `json:"helmChart"`

	// Version can either be specified as part of the chart name or separately.
	Version string `json:"version,omitempty"`
}

```
It is important to note what I dropped deprecated fields from the spec.
CA and Keyring related fields should be loaded from the credentials as
already done in the helm input.

**reuse blob download from helm input**
Additionally, this PR refactors the `download helm blob `from input to
generalize the logic and make it reusable for helm access as well.

**Reuse chart to OCI from help input**
I also generalized the chart->OCI logic from `input` and made it
reusable. It is now used in both `input` and the `convert transformer.`

**Refactor `CopyChartToOCILayout`**
I had to refactor `CopyChartToOCILayout` to be able to get the
`ociImageSpecV1.Descriptor`. The problem was that the whole impl was
async. Channels lead to blocking goroutines since `io.Pipe` was not done
writing at the point where I needed the descriptor. I had to create

```go
type Result struct {
	*direct.Blob
	desc chan descriptorOrError
}

type descriptorOrError struct {
	Descriptor ociImageSpecV1.Descriptor
	Err        error
}
```
to delay the access to said data.

**GetHelmChart transformer implementation**

The get transformer does the following:
- validate input spec against new `v1alpha1.GetHelmChart`
- generate output paths for the spec output
- get credentials by requesting
`ResourceConsumerIdentityProvider.GetResourceCredentialConsumerIdentity`
which is implemented in the `HelmAccess`
- delegate download to generalized helm download from the input package
(now downloads)
- in theory, we could use this transformer also for oci helm download,
since the downloader supports the feature
- but we will use the native GetOCIArtifact from transformers (probably)
- resulting blobs (chart and optionally a prov) will be copied into
`*v1alpha1.File`
- the output contains
  - the original `Resource`
  - the chart file pointer with `v1alpha1.File`
  - (optionally) the prov file pointer with `*v1alpha1.File`

**ConvertHelmChartToOCI transformer implementation**

The convertion transformer does the following:
- expect chart, prov and resources as input
- reuse the oci creation logic from input
- calculate the correct `ImageReference` for the `OCIImage` access
- push the artifact as localblob as an output back the the graph

 **testing**
- The pr spawns a ~~repotest.NewTempServer()~~ mock http server pointing
to the shared helm `testdata/mychart-0.1.0.tgz` dir
- In the test we create Resources with several variations of the `Helm`
access
- Each test verifies that the files are correctly downloaded and equal
the original file
- provenance file download added
- **Open Question**: should we also verify during transform? Currently,
we only download the .prov file
- helm to oci tests and validated the created oci image and the prov
files

#### Which issue(s) this PR fixes
Contributes:
open-component-model/ocm-project#883

#### Testing

##### How to test the changes

See
open-component-model#1846
for testing

**make sure helm input did not break**
```bash
#!/bin/zsh

alias OCM='go run ../../main.go'

# Test helm input with local path
CHART_PATH="../../../bindings/go/helm/testdata/mychart"

# stat the chart path to ensure it exists
if [ ! -d "$CHART_PATH" ]; then
  echo "Error: Chart path $CHART_PATH does not exist"
  exit 1
fi

cat <<EOF > constructor-input-local.yaml
components:
- name: ocm.software/helm-input-local
  version: 0.1.0
  provider:
    name: ocm.software
  resources:
  - name: mychart
    version: 0.1.0
    type: helmChart
    input:
      type: helm/v1
      path: $CHART_PATH
EOF

# create dir relative to current directory to avoid issues with absolute paths in the constructor
INPUT_CTF_LOCAL="input-ctf-local-$(date +%s)"
mkdir $INPUT_CTF_LOCAL
echo "Using temporary directory for helm input (local): $INPUT_CTF_LOCAL"

OCM add cv --repository ctf::$INPUT_CTF_LOCAL --constructor constructor-input-local.yaml

echo "--- Local helm input test passed ---"

# Test helm input with remote repository (uses same podinfo chart)
cat <<EOF > constructor-input-remote.yaml
components:
- name: ocm.software/helm-input-remote
  version: 6.9.1
  provider:
    name: ocm.software
  resources:
  - name: podinfo
    version: 6.9.1
    type: helmChart
    input:
      type: helm/v1
      helmRepository: https://stefanprodan.github.io/podinfo/podinfo-6.9.1.tgz
EOF

INPUT_CTF_REMOTE="input-ctf-remote-$(date +%s)"
mkdir $INPUT_CTF_REMOTE
echo "Using temporary directory for helm input (remote): $INPUT_CTF_REMOTE"

OCM add cv --repository ctf::$INPUT_CTF_REMOTE --constructor constructor-input-remote.yaml

echo "--- Remote helm input test passed ---"
```

##### Verification

- [x] I have tested the changes locally by running `ocm`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Helm access spec, credential-aware identity resolution, and
options for TLS/credentials.
* Remote Helm chart retrieval (HTTP/OCI) with provenance support and
configurable buffering/output path handling.
* Conversion of Helm charts into OCI image layouts and transformers to
fetch charts and produce OCI artifacts.

* **Tests**
* Extensive unit and integration tests covering access parsing,
identity/error cases, downloads, provenance, OCI conversion, and
transformers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
morri-son pushed a commit to morri-son/open-component-model that referenced this pull request Mar 18, 2026
<!-- markdownlint-disable MD041 -->
#### What this PR does / why we need it

This PR implements the transformers from
open-component-model#1832
It creates a 3-step-transformation for the helm chart
transformation-chain:

```mermaid
flowchart TD
    A[Transfer Helm Chart] -->B(Get Helm Chart)
    B --> C{Download from Repo}
    C -->|Has Prov| D[Download from Repo]
    C --> E{Pack Helm LocalBlob}
    D --> E
    E -->|GetHelmOutput| F{Convert to OCI}
    F -->|ConverToHelm| G{Pack OCI from Helm}
    F -->|ConverToHelm with prov| G{Pack OCI from Helm}
    G -->|upload-as localBlob/default | I{Upload as localBlob}
    G -->|upload-as ociArtifact | H{Upload as OCI Image}
```

1. get helm chart with `GetHelmChartV1alpha1`
2. convert helm to OCI with `ConvertHelmToOCIV1alpha1`
3. upload
  a.  localBlobl: the oci artifact with `OCIAddLocalResourceV1alpha1`
  b.  ociArtifact: the oci artifact with `AddOCIArtifactType`

The [spec
PR](open-component-model#1832)
will be kept in sync with changed from here in the `bindings/go/helm`
package.

#### Which issue(s) this PR fixes
Contributes:
open-component-model/ocm-project#883

#### Testing

##### How to test the changes

```bash
#!/bin/zsh

alias OCM='go run ../../main.go'
REGISTRY="ghcr.io/matthiasbruns/ocm-tutorials"
REGISTRY2="ghcr.io/matthiasbruns/ocm-tutorials-2"

pause() {
  echo "\n>>> Next: $1"
  echo "--- Press Enter to continue ---"
  read
}

# OCM --help

# create constructor.yaml
# https://stefanprodan.github.io/podinfo/podinfo-6.9.1.tgz
cat <<EOF > constructor.yaml
components:
- name: ocm.software/podinfo
  version: 6.9.1
  provider:
    name: ocm.software
  resources:
  - name: podinfo
    version: 6.9.1
    type: helmChart
    access:
      type: helm/v1
      helmRepository: https://stefanprodan.github.io/podinfo
      helmChart: podinfo-6.9.1.tgz
EOF

CTF_DIR=$(mktemp -d)
echo "Using temporary directory: $CTF_DIR"

pause "Add component version to CTF from constructor.yaml"

# add cv command
OCM add cv --repository ctf::$CTF_DIR --constructor constructor.yaml --skip-reference-digest-processing

HELM_REF="ctf::$CTF_DIR//ocm.software/podinfo:6.9.1"

pause "Create component version ($REGISTRY)"

# transfer
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources

pause "Transfer with --upload-as localBlob (OCI registry)"

# transfer  --upload-as localBlob
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources --upload-as localBlob

pause "Transfer with --upload-as ociArtifact (OCI registry)"

# transfer  --upload-as ociArtifact
OCM transfer component-version $HELM_REF $REGISTRY --copy-resources --upload-as ociArtifact

pause "Download component descriptor with oras"

# download with oras
oras pull $REGISTRY/component-descriptors/ocm.software/podinfo:6.9.1 --output .

pause "Download resource using OCM CLI"

# rm downloaded if exists
rm -rf downloaded

# downloadCMD resource
OCM download resource https://$REGISTRY//ocm.software/podinfo:6.9.1 --identity name=podinfo,version=6.9.1 --output ./downloaded
```

You can also unpack the blob and it should contain the `podinfo` chart
contents

# transfer oci helm to another oci
`ocm transfer component-version
http://$REGISTRY//ocm.software/podinfo:6.9.1 $REGISTRY2 --copy-resources
--upload-as ociArtifact`

##### Verification

- [x] I have tested the changes locally by running `ocm`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for transferring Helm charts as part of component
version transfers
  * Helm charts can now be converted to OCI artifacts during transfer
* Support for both local Helm chart paths and remote Helm repositories
as transfer sources

* **Documentation**
* Updated transfer command documentation with Helm chart transfer
capabilities and examples
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Matthias Bruns <git@matthiasbruns.com>
Signed-off-by: Gerald Morrison (SAP) <gerald.morrison@sap.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/github-actions Changes on GitHub Actions or within `.github/` directory kind/feature new feature, enhancement, improvement, extension size/l Large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement helm chart download transformer

5 participants