Skip to content

feat(go-licenses): add composite action with check + report modes#100

Merged
sydorovdmytro merged 1 commit intomainfrom
feat/go-licenses
Apr 15, 2026
Merged

feat(go-licenses): add composite action with check + report modes#100
sydorovdmytro merged 1 commit intomainfrom
feat/go-licenses

Conversation

@sydorovdmytro
Copy link
Copy Markdown
Collaborator

@sydorovdmytro sydorovdmytro commented Apr 15, 2026

Why

Reusable workflows can't resolve their own ref at call time — both github.workflow_ref and github.workflow_sha leak the caller's context under workflow_call, so any sparse-checkout of loft-sh/github-actions for scripts fails on PR-triggered callers with:

fatal: couldn't find remote ref refs/pull/N/merge

This was just observed in the sibling govulncheck work (loft-enterprise#6676) and fixed by converting to a composite action in #109. github.job_workflow_sha is documented but only exposed as an OIDC claim (actions/runner#2417).

Ship go-licenses as a composite action from the start to avoid the same bug class — the pattern already used by ci-test-notify, release-notification, linear-release-sync, semver-validation, and govulncheck.

What

One composite action at .github/actions/go-licenses/ with a mode: check|report input, rather than two separate actions. Both modes share ~90% of the setup (setup-go, install go-licenses, private-repo git auth); only the trailing steps differ (go-licenses check vs go-licenses report + peter-evans/create-pull-request). The existing run.sh with check/report subcommands ports unchanged.

Caller contract

Each caller provides their own actions/checkout, runs-on, timeout-minutes, and fork guard at the job level.

Check mode:

jobs:
  check:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'loft-sh'
    permissions:
      contents: read
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          persist-credentials: false
      - uses: loft-sh/github-actions/.github/actions/go-licenses@go-licenses/v1
        with:
          mode: check
          gh-access-token: ${{ secrets.GH_ACCESS_TOKEN }}

Report mode:

jobs:
  report:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'loft-sh'
    permissions:
      contents: write
      pull-requests: write
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          persist-credentials: false
      - uses: loft-sh/github-actions/.github/actions/go-licenses@go-licenses/v1
        with:
          mode: report
          template-path: .github/licenses.tmpl
          output-path: docs/pages/licenses/vcluster.mdx
          pr-branch: licenses/vcluster
          pr-title: "Update vcluster licenses"
          gh-access-token: ${{ secrets.GH_ACCESS_TOKEN }}

Files

  • .github/actions/go-licenses/action.yml — composite action
  • .github/actions/go-licenses/run.sh — unchanged, moved from .github/scripts/
  • .github/actions/go-licenses/test/run.bats — unchanged, 17 tests
  • .github/actions/go-licenses/README.md — inputs + usage
  • .github/workflows/test-go-licenses.yaml — path filter retargeted
  • Makefiletest-go-licenses now points at ACTIONS_DIR

Test plan

  • make test-go-licenses — 17/17 bats tests pass
  • make lint — actionlint + zizmor clean
  • shellcheck .github/actions/go-licenses/run.sh — clean
  • Script executable (mode 100755)
  • CI green on this PR
  • After merge: push go-licenses/v1 tag; migrate callers (vcluster, vcluster-pro, loft-enterprise)

Refs DEVOPS-772

Reusable workflows can't resolve their own ref from the `github`
context: both `github.workflow_ref` and `github.workflow_sha` leak the
caller under `workflow_call`, so the sparse-checkout of
`loft-sh/github-actions` for the scripts fails on any PR-triggered
call (observed in loft-enterprise#6676 for the sibling govulncheck
workflow). `github.job_workflow_sha` is documented but only exposed
as an OIDC claim (actions/runner#2417).

Ship this as a composite action from the start — the same pattern
already used by ci-test-notify, release-notification,
linear-release-sync, semver-validation, and (post-#109) govulncheck:
composite actions get `github.action_path` and the repo is
auto-checked-out at the correct ref, so no ref resolution needed.

One action with a `mode: check|report` input instead of two: both
flows share the same setup (setup-go, install go-licenses, private-
repo git auth) — only the trailing steps differ (`go-licenses check`
vs `go-licenses report` + peter-evans/create-pull-request). The same
`run.sh` with check/report subcommands ports unchanged, keeping all
17 bats tests valid.

Caller contract (each caller provides their own checkout + fork
guard + timeout-minutes at job level):

  jobs:
    check:
      runs-on: ubuntu-latest
      if: github.repository_owner == 'loft-sh'
      permissions:
        contents: read
      timeout-minutes: 15
      steps:
        - uses: actions/checkout@...
          with: {persist-credentials: false}
        - uses: loft-sh/github-actions/.github/actions/go-licenses@go-licenses/v1
          with:
            mode: check   # or: report
            ...

- `.github/actions/go-licenses/action.yml` — composite action
- `.github/actions/go-licenses/run.sh` — moved from `.github/scripts/`
- `.github/actions/go-licenses/test/run.bats` — moved, 17/17 pass
- `.github/actions/go-licenses/README.md` — usage + inputs
- `.github/workflows/go-licenses-check.yaml` — deleted
- `.github/workflows/go-licenses-report.yaml` — deleted
- `test-go-licenses.yaml` + `Makefile` — retargeted at the new path

Refs DEVOPS-772
@sydorovdmytro sydorovdmytro changed the title feat(go-licenses): reusable workflows for license check and report feat(go-licenses): add composite action with check + report modes Apr 15, 2026
@sydorovdmytro sydorovdmytro merged commit 02513fb into main Apr 15, 2026
4 checks passed
@sydorovdmytro sydorovdmytro deleted the feat/go-licenses branch April 15, 2026 13:54
sydorovdmytro added a commit that referenced this pull request Apr 15, 2026
…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 added a commit that referenced this pull request Apr 15, 2026
…action (#115)

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
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