GitHub Actions Tutorial — Inputs, Outputs, Secrets
Inputs, Outputs, Secrets
Reusable workflows accept inputs (parameters), return outputs (results), and can receive secrets — just like function arguments and return values.
Inputs
Define inputs in the reusable workflow under workflow_call:
# .github/workflows/deploy.yml (reusable)
on:
workflow_call:
inputs:
environment:
description: 'Target environment'
required: true
type: string
version:
description: 'Version to deploy'
required: true
type: string
dry-run:
description: 'Skip actual deployment'
required: false
type: boolean
default: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: |
echo "Deploying v${{ inputs.version }} to ${{ inputs.environment }}"
echo "Dry run: ${{ inputs.dry-run }}"
Input Types
| Type | Example | Notes |
|---|---|---|
string |
'production' |
Default type |
boolean |
true / false |
Must be true or false |
number |
42 |
Passed as a string, parsed as number |
Caller Passing Inputs
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: production
version: '2.1.0'
dry-run: false
Secrets
Reusable workflows don’t automatically inherit the caller’s secrets. You must declare them:
# .github/workflows/deploy.yml (reusable)
on:
workflow_call:
inputs:
environment:
type: string
required: true
secrets:
AWS_ACCESS_KEY_ID:
required: true
AWS_SECRET_ACCESS_KEY:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- run: echo "Deploying with AWS credentials..."
Caller Passing Secrets
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: production
secrets:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
inherit — Pass All Secrets
Instead of listing each secret, use secrets: inherit:
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: production
secrets: inherit # pass ALL caller secrets
secrets: inherit forwards every secret the caller has access to. Simple but less explicit.
Outputs
Reusable workflows can return values via outputs:
# .github/workflows/build.yml (reusable)
on:
workflow_call:
outputs:
version:
description: 'Built version'
value: ${{ jobs.build.outputs.version }}
artifact-name:
description: 'Artifact name'
value: ${{ jobs.build.outputs.artifact-name }}
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.ver.outputs.version }}
artifact-name: ${{ steps.ver.outputs.artifact-name }}
steps:
- id: ver
run: |
VERSION="2.1.0-$(date +%s)"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "artifact-name=build-$VERSION" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: build-${{ steps.ver.outputs.version }}
path: ./dist
Caller Reading Outputs
jobs:
build:
uses: ./.github/workflows/build.yml
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: |
echo "Version: ${{ needs.build.outputs.version }}"
echo "Artifact: ${{ needs.build.outputs.artifact-name }}"
Complete Example
# Reusable: .github/workflows/test-and-build.yml
on:
workflow_call:
inputs:
node-version:
type: string
default: '20'
secrets:
NPM_TOKEN:
required: false
outputs:
passed:
description: 'Whether tests passed'
value: ${{ jobs.test.outputs.passed }}
jobs:
test:
runs-on: ubuntu-latest
outputs:
passed: ${{ steps.result.outputs.passed }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: npm test
- id: result
if: success()
run: echo "passed=true" >> "$GITHUB_OUTPUT"
# Caller: .github/workflows/ci.yml
on:
push:
branches: [main]
jobs:
test:
uses: ./.github/workflows/test-and-build.yml
with:
node-version: '20'
secrets: inherit
deploy:
needs: test
if: needs.test.outputs.passed == 'true'
runs-on: ubuntu-latest
steps:
- run: echo "Deploying — tests passed!"
Common Mistakes
| Mistake | Fix |
|---|---|
| Secret not available in reusable workflow | Declare it under secrets: or use secrets: inherit |
| Output is empty | Chain: step output → job output → workflow output |
| Input type mismatch | Boolean inputs must be true/false, not strings |
Using env context for inputs |
Use inputs.name, not env.name |
Quick Reference
| Feature | Reusable Workflow | Caller |
|---|---|---|
| Inputs | workflow_call.inputs.<name> |
with: <name>: value |
| Secrets | workflow_call.secrets.<name> |
secrets: <name>: value |
| All secrets | — | secrets: inherit |
| Outputs | workflow_call.outputs.<name>.value |
needs.<job>.outputs.<name> |
← Previous: workflow_call
Next: Calling Workflows →