Skip to content

Package charts and manage dependencies

HelmSharp supports the traditional HTTP chart-repository workflow in managed code: package a chart, produce index.yaml, manage isolated repository state, pull an archive, and resolve dependencies. It does not require the Helm CLI at runtime.

Scope of this guide

OCI authentication and push/pull parity, provenance files, signing, and signature verification are not part of this workflow. See Compatibility before building a production repository service around those capabilities.

Package a chart

Use the request overload when the build needs metadata overrides or a dependency refresh:

csharp
public static Task<CommandResult> PackageChartAsync(
    IHelmClient client,
    string chartPath,
    string outputDirectory,
    CancellationToken cancellationToken)
    => client.PackageAsync(new HelmPackageRequest
    {
        ChartPath = chartPath,
        Destination = outputDirectory,
        Version = "1.3.2",
        AppVersion = "2026.07",
        DependencyUpdate = true
    }, cancellationToken);

Version and AppVersion change the packaged Chart.yaml, not the source file. The archive is named <chart-name>-<version>.tgz, has one chart root, includes nested charts and CRDs, and skips symbolic links. .helmignore supports file, directory, *, ?, character-class, rooted, and ! negation patterns; ** is rejected explicitly.

Produce repository metadata

Place the chart archives in a directory and create its index:

csharp
public static Task<CommandResult> GenerateRepositoryIndexAsync(
    IHelmClient client,
    string packageDirectory,
    CancellationToken cancellationToken)
    => client.RepoIndexAsync(new HelmRepoIndexRequest
    {
        DirectoryPath = packageDirectory,
        Url = "https://charts.example.com/stable",
        MergeIndexPath = Path.Combine(packageDirectory, "previous-index.yaml"),
        OutputPath = Path.Combine(packageDirectory, "index.yaml"),
        FailOnInvalidPackage = true
    }, cancellationToken);

Url is the package base URL. MergeIndexPath retains historical entries that are no longer in the directory. Set FailOnInvalidPackage when an invalid archive must stop a publish; otherwise inspect diagnostics for skipped packages. OutputPath defaults to index.yaml under DirectoryPath.

Isolate repository state in a service

Do not let tenants, tests, or concurrent jobs share repository config and cache directories.

csharp
using var repository = new HelmChartRepository(new HelmRepositoryOptions
{
    RepositoryConfigPath = Path.Combine(tenantRoot, "repositories.yaml"),
    CacheDirectory = Path.Combine(tenantRoot, "cache")
});

The repository config stores definitions; cached indexes use <repository-name>-index.yaml. If paths are omitted, Helm-compatible environment variables and platform defaults are used. The keyword-only SearchRepoAsync overload searches those configured caches without a network request. The overload that accepts a repository URL fetches and caches that repository index before searching.

Pull a chart safely

csharp
public static async Task<string> PullTraditionalChartAsync(
    string repositoryConfigPath,
    string repositoryCacheDirectory,
    string outputDirectory,
    CancellationToken cancellationToken)
{
    using var repository = new HelmChartRepository(new HelmRepositoryOptions
    {
        RepositoryConfigPath = repositoryConfigPath,
        CacheDirectory = repositoryCacheDirectory
    });

    await repository.AddRepositoryAsync(
        "stable",
        "https://charts.example.com/stable",
        cancellationToken: cancellationToken);
    var configured = (await repository.ListRepositoriesAsync(cancellationToken))
        .Single(item => item.Name == "stable");
    await repository.FetchRepoIndexAsync(configured, cancellationToken);

    return await repository.PullChartAsync(new HelmPullRequest
    {
        ChartReference = "stable/app",
        Version = "~1.3.2",
        Destination = outputDirectory,
        Untar = true,
        UntarDirectory = Path.Combine(outputDirectory, "expanded"),
        VerifyDigest = true
    }, cancellationToken);
}

The pull request accepts repo/chart, a chart name plus RepositoryUrl, or a direct https://…tgz URL. The downloaded archive is stored under Destination. When Untar is enabled, UntarDirectory selects the extraction root; otherwise Destination is the extraction root. Extraction rejects entries that escape the selected root. Credentials stay on the repository origin by default. Enable PassCredentialsAll only when a trusted repository intentionally redirects archives to another authenticated origin.

Make dependency builds reproducible

Declare aliases and local references in Chart.yaml as usual:

yaml
dependencies:
  - name: redis
    alias: cache
    version: ~18.0.0
    repository: "@stable"
  - name: shared-templates
    version: 1.2.3
    repository: file://../shared-templates

An alias changes both the subchart identity and its values key, so the first dependency receives values under cache:, not redis:. DependencyUpdateAsync resolves constraints, refreshes dependencies, and writes Chart.lock. DependencyBuildAsync is the CI path: it verifies the lock against Chart.yaml, restores the exact locked versions, and does not rewrite the lock.

csharp
public static Task<CommandResult> UpdateDependenciesAsync(
    IHelmClient client,
    string chartPath,
    string repositoryConfigPath,
    string repositoryCacheDirectory,
    CancellationToken cancellationToken)
    => client.DependencyUpdateAsync(new HelmDependencyUpdateRequest
    {
        ChartPath = chartPath,
        RepositoryConfigPath = repositoryConfigPath,
        RepositoryCachePath = repositoryCacheDirectory,
        SkipRepositoryRefresh = false
    }, cancellationToken);
csharp
public static Task<CommandResult> BuildDependenciesAsync(
    IHelmClient client,
    string chartPath,
    string repositoryConfigPath,
    string repositoryCacheDirectory,
    CancellationToken cancellationToken)
    => client.DependencyBuildAsync(new HelmDependencyBuildRequest
    {
        ChartPath = chartPath,
        RepositoryConfigPath = repositoryConfigPath,
        RepositoryCachePath = repositoryCacheDirectory,
        VerifyDigests = true
    }, cancellationToken);

Run DependencyListAsync before packaging when you need to surface missing, wrong-version, unpacked, or inconsistent dependencies to a user. High-level client methods return CommandResult; lower-level repository methods throw exceptions. Troubleshoot failures explains how to preserve both kinds of diagnostics.

Released under the MIT License.