A GitHub Action that pushes Helm charts to any OCI registry (GHCR, ECR, GAR, Harbor, Quay, Artifactory). Supports pre-packaged tarball globs, comma-separated chart paths, and directory scans — all from a single step.
- Push to any OCI registry (GHCR by default, plus ECR, GAR, Harbor, Quay, Artifactory, ...)
- Three input modes:
tarballsglob,chartscomma-separated paths,charts_dirauto-scan - Dry-run mode for PR validation without publishing
- Skip existing chart versions to make releases idempotent
continue_on_errorto keep going when one chart fails, with the failures reported infailed_charts- Retries a failed push with backoff, which rides out GHCR's transient
failed to perform "Tag"error on a new package - Built-in
helm registry login(or skip and let your workflow handle it) - Self-contained Docker action —
helmis bundled, noazure/setup-helmrequired
- name: Push chart to GHCR
uses: somaz94/helm-oci-push-action@v1
with:
charts: ./helm/my-app
password: ${{ secrets.GITHUB_TOKEN }}Use this when an earlier step already produced .tgz files (e.g., helm package to a staging directory, or chart-releaser-action).
- name: Package
run: helm package ./helm/my-app -d /tmp/staging
- name: Push to GHCR
uses: somaz94/helm-oci-push-action@v1
with:
tarballs: /tmp/staging/*.tgz
password: ${{ secrets.GITHUB_TOKEN }}- uses: somaz94/helm-oci-push-action@v1
with:
charts: ./helm/my-app
password: ${{ secrets.GITHUB_TOKEN }}- uses: somaz94/helm-oci-push-action@v1
with:
charts: charts/foo,charts/bar,charts/baz
password: ${{ secrets.GITHUB_TOKEN }}Every immediate subdirectory containing a Chart.yaml is packaged and pushed.
- uses: somaz94/helm-oci-push-action@v1
with:
charts_dir: charts
password: ${{ secrets.GITHUB_TOKEN }}chart-releaser-action emits changed_charts as a comma-separated list — feed it directly to the charts input.
- name: Run chart-releaser
id: cr
uses: helm/[email protected]
with:
charts_dir: charts
skip_existing: true
env:
CR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Push released charts to GHCR
if: steps.cr.outputs.changed_charts != ''
uses: somaz94/helm-oci-push-action@v1
with:
charts: ${{ steps.cr.outputs.changed_charts }}
password: ${{ secrets.GITHUB_TOKEN }}When you've already authenticated via a provider-specific action (e.g., aws-actions/amazon-ecr-login), set registry_login: false.
- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
- uses: somaz94/helm-oci-push-action@v1
with:
tarballs: dist/*.tgz
registry: oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/charts
registry_login: falsePackage and validate without publishing — perfect for PR checks.
- uses: somaz94/helm-oci-push-action@v1
with:
charts_dir: charts
dry_run: true
password: ${{ secrets.GITHUB_TOKEN }}Make releases idempotent — chart versions already present in the registry are skipped.
- uses: somaz94/helm-oci-push-action@v1
with:
charts_dir: charts
skip_existing: true
password: ${{ secrets.GITHUB_TOKEN }}| Input | Description | Required | Default |
|---|---|---|---|
tarballs |
Glob pattern for pre-packaged chart tarballs (e.g., dist/*.tgz) |
No | '' |
charts |
Comma-separated chart directory paths to package and push | No | '' |
charts_dir |
Directory whose immediate subdirectories are packaged and pushed | No | '' |
registry |
Target OCI registry URL | No | oci://ghcr.io/${{ github.repository_owner }}/charts |
registry_login |
Run helm registry login inside the action |
No | true |
username |
Registry username | No | ${{ github.actor }} |
password |
Registry password / token (typically secrets.GITHUB_TOKEN for GHCR) |
No | '' |
dry_run |
Package and validate but do not push | No | false |
skip_existing |
Skip chart@version pairs already in the registry (best-effort) | No | false |
continue_on_error |
Continue with the next chart when a push fails; the step still succeeds, so check failed_charts |
No | false |
push_retries |
Retries of a failed helm push before giving up (0 disables) |
No | 3 |
push_retry_delay |
Seconds before the first retry, doubled on each further retry up to 300 | No | 5 |
At least one of tarballs, charts, or charts_dir must be provided.
| Output | Description |
|---|---|
pushed_charts |
Comma-separated name:version pairs actually pushed |
skipped_charts |
Comma-separated name:version pairs skipped (existing or dry-run) |
failed_charts |
Comma-separated name:version pairs that failed to push, or the tarball file name when its Chart.yaml could not be read (non-empty only with continue_on_error) |
Example:
- id: push
uses: somaz94/helm-oci-push-action@v1
with:
charts_dir: charts
skip_existing: true
password: ${{ secrets.GITHUB_TOKEN }}
- run: |
echo "Pushed: ${{ steps.push.outputs.pushed_charts }}"
echo "Skipped: ${{ steps.push.outputs.skipped_charts }}"
echo "Failed: ${{ steps.push.outputs.failed_charts }}"By default the action runs helm registry login against the host portion of registry. If password is empty, the step fails with a clear error.
Set registry_login: false when:
- A previous step already authenticated (e.g.,
docker/login-action,aws-actions/amazon-ecr-login,google-github-actions/auth). - You want to use a non-username/password auth flow your provider supports.
For pushing to GHCR with the default ${{ secrets.GITHUB_TOKEN }}, the calling workflow needs:
permissions:
contents: read
packages: writeThe action installs the latest stable Helm at image build time (resolved via the GitHub releases API). To pin a specific version, fork the repo and override the HELM_VERSION build arg in the Dockerfile (e.g., ARG HELM_VERSION=v3.16.4).
This project is licensed under the MIT License — see the LICENSE file for details.