A callable workflow is invoked as a job rather than a step, which constrains what can be passed into it and what can come back out.
# .github/workflows/php-ci.yml, in a shared repo
on:
workflow_call:
inputs:
php-version: { type: string, default: '8.0' }
secrets:
COMPOSER_AUTH: { required: false }
# and the caller
jobs:
ci:
uses: org/workflows/.github/workflows/php-ci.yml@v3
with:
php-version: '8.0'
secrets: inherit
Inputs are typed and limited to strings, numbers and booleans, so anything structured has to be JSON in a string and parsed on the other side. The called workflow runs on its own runners and cannot see the caller’s checkout, which is the constraint people hit first — it must check out the repository itself. secrets: inherit is convenient and passes everything, which is the wrong default for a workflow in another organisation.