Skip to content

refactor(publish-helm-chart): convert reusable workflow to composite action#115

Merged
sydorovdmytro merged 1 commit intomainfrom
devops-772/publish-helm-chart-composite-action
Apr 15, 2026
Merged

refactor(publish-helm-chart): convert reusable workflow to composite action#115
sydorovdmytro merged 1 commit intomainfrom
devops-772/publish-helm-chart-composite-action

Conversation

@sydorovdmytro
Copy link
Copy Markdown
Collaborator

@sydorovdmytro sydorovdmytro commented Apr 15, 2026

Why

The reusable workflow couldn't resolve its own ref at call time — github.workflow_ref and github.workflow_sha both leak the caller's context under workflow_call. The sparse-checkout of loft-sh/github-actions for the scripts used the parsed caller ref:

  • Push-to-main callers: the caller's ref is refs/heads/main, which happens to exist in this repo too, so checkout "succeeds" — but scripts come from loft-sh/github-actions@main, not from the pinned @publish-helm-chart/v1 tag. Tag pinning was a lie; callers silently drifted whenever main diverged from the tag.
  • Release-event callers (pending loft-enterprise release-chart migration): the caller's ref is refs/tags/v4.6.0, which doesn't exist in loft-sh/github-actions. First release-triggered call would fail with fatal: couldn't find remote ref.

Same bug class as #109 (govulncheck) and #100 (go-licenses); same fix: convert to a composite action so github.action_path is correct automatically.

What

  • .github/scripts/publish-helm-chart/.github/actions/publish-helm-chart/ (code + 24 bats tests unchanged)
  • .github/actions/publish-helm-chart/action.yml — new composite action
  • .github/workflows/publish-helm-chart.yamldeleted
  • test-publish-helm-chart.yaml, Makefile, README.md — path updates

Caller contract (breaking, v1 tag force-updated in place)

Before (reusable workflow) After (composite action)
uses: at job level uses: at step level
runs-on, timeout-minutes, permissions baked in caller sets on job
ref input (caller git ref for checkout) caller owns actions/checkout, passes ref there
secrets: chart-museum-user, chart-museum-password regular inputs (caller interpolates)
No checkout from caller caller does actions/checkout first

Tag strategy

Tag stays at publish-helm-chart/v1 and will be force-updated to this merge commit. Rationale: the only existing caller (loft-enterprise push-head-images.yaml, merged yesterday via #6673) was silently drifting anyway — v2 would be churn. Loft-enterprise migration PR follows concurrently.

New caller shape

jobs:
  publish:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - uses: loft-sh/github-actions/.github/actions/publish-helm-chart@publish-helm-chart/v1
        with:
          chart-name: vcluster-head
          app-version: head-${{ github.sha }}
          chart-versions: '["0.0.0-latest","0.0.0-${{ github.sha }}"]'
          chart-museum-user: ${{ secrets.CHART_MUSEUM_USER }}
          chart-museum-password: ${{ secrets.CHART_MUSEUM_PASSWORD }}

Test plan

  • make test-publish-helm-chart — 24 tests (skip locally due to mikefarah/yq absence; CI runs full suite)
  • make lint — actionlint + zizmor clean
  • shellcheck .github/actions/publish-helm-chart/run.sh — clean
  • Script executable (mode 100755)
  • CI green on this PR
  • Coordinated merge: this PR + tag force-update + loft-enterprise migration PR

Refs DEVOPS-772

…action

The reusable workflow couldn't resolve its own ref at call time.
`github.workflow_ref` and `github.workflow_sha` both leak the caller's
context under `workflow_call` — the sparse-checkout of
`loft-sh/github-actions` for the scripts used the parsed workflow_ref
(`refs/heads/main` for push events, `refs/tags/vX` for releases),
which:

- On push-to-main: happened to resolve because main/main lined up,
  but silently pulled scripts from `main` rather than the pinned
  `@publish-helm-chart/v1` — tag pinning was a lie.
- On release events: the caller's `refs/tags/v4.6.0` doesn't exist in
  `loft-sh/github-actions`, so the first release-triggered caller
  (pending loft-enterprise release-chart migration) would fail with
  `fatal: couldn't find remote ref`.

Converting to a composite action sidesteps both — `github.action_path`
is correct automatically, tag pinning is real, no release-event
breakage. Same pattern already used by ci-test-notify,
release-notification, linear-release-sync, semver-validation,
govulncheck (#109), and go-licenses (#100).

Caller contract change (v2, breaking):
- `ref` input removed — caller checks out the desired ref themselves
- `runs-on` / `timeout-minutes` / `permissions` set by caller
- `secrets: chart-museum-*` → regular inputs (actions can't declare
  secrets; caller interpolates `${{ secrets.* }}`)
- Caller supplies `actions/checkout` before the action

Only caller today is loft-enterprise push-head-images.yaml (merged via
#6673 yesterday) — one migration PR follows.

- `.github/scripts/publish-helm-chart/` → `.github/actions/publish-helm-chart/`
- Delete `.github/workflows/publish-helm-chart.yaml`
- Retarget `test-publish-helm-chart.yaml` path filter + test target
- Rewrite README section for the composite-action contract

Refs DEVOPS-772
@sydorovdmytro sydorovdmytro merged commit 43fbd58 into main Apr 15, 2026
4 checks passed
@sydorovdmytro sydorovdmytro deleted the devops-772/publish-helm-chart-composite-action branch April 15, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant