name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test | Basic workflow structure |
on: push | Trigger on any push |
on: [push, pull_request] | Multiple events |
on:
push:
branches: [main, develop]
paths:
- "src/**"
- "!**.md" | Branch and path filters |
on:
schedule:
- cron: "0 0 * * *" | Scheduled (daily at midnight) |
on:
workflow_dispatch:
inputs:
environment:
type: choice
options: [dev, prod] | Manual trigger with inputs |
on:
release:
types: [published] | On release published |
on:
workflow_call:
inputs:
config:
type: string
required: true | Reusable workflow |
jobs:
build:
runs-on: ubuntu-latest | Basic job on Ubuntu |
jobs:
build:
runs-on: windows-latest | Run on Windows |
jobs:
build:
runs-on: macos-latest | Run on macOS |
jobs:
build:
runs-on: [self-hosted, linux] | Self-hosted runner |
jobs:
test:
needs: build
runs-on: ubuntu-latest | Job dependency |
jobs:
deploy:
needs: [build, test]
if: github.ref == 'refs/heads/main' | Conditional job |
jobs:
build:
timeout-minutes: 30
continue-on-error: true | Timeout and error handling |
jobs:
test:
strategy:
matrix:
node: [16, 18, 20]
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }} | Multi-version matrix |
strategy:
matrix:
include:
- node: 18
experimental: true
exclude:
- os: windows-latest
node: 16 | Include/exclude combinations |
strategy:
fail-fast: false
max-parallel: 2 | Matrix options |
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true | Cancel duplicate workflows |
steps:
- name: Checkout
uses: actions/checkout@v4 | Use an action |
steps:
- name: Run script
run: echo "Hello" | Run shell command |
steps:
- name: Multi-line script
run: |
echo "Line 1"
echo "Line 2" | Multi-line script |
steps:
- run: dir
shell: pwsh | Specify shell (pwsh, bash, python) |
steps:
- name: Conditional step
if: github.event_name == 'push'
run: echo "Pushed" | Conditional step |
steps:
- name: Always run
if: always()
run: echo "Cleanup" | Always run (even on failure) |
steps:
- run: npm test
working-directory: ./frontend | Set working directory |
steps:
- run: long-task
timeout-minutes: 10 | Step timeout |
steps:
- run: risky-command
continue-on-error: true | Continue on error |
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history | Checkout repository |
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm | Setup Node.js with cache |
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip | Setup Python |
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin | Setup Java |
- uses: actions/setup-go@v5
with:
go-version: "1.21" | Setup Go |
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm- | Cache dependencies |
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 5 | Upload artifact |
- uses: actions/download-artifact@v4
with:
name: build-output
path: ./dist | Download artifact |
- uses: docker/setup-buildx-action@v3 | Setup Docker Buildx |
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} | Login to GHCR |
- uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max | Build and push Docker image |
env:
NODE_ENV: production
API_URL: https://api.example.com | Workflow-level env |
jobs:
build:
env:
CI: true | Job-level env |
steps:
- run: echo $MY_VAR
env:
MY_VAR: value | Step-level env |
steps:
- run: echo "value=${{ env.MY_VAR }}" >> $GITHUB_OUTPUT
id: step1
- run: echo ${{ steps.step1.outputs.value }} | Pass output between steps |
steps:
- run: echo "${{ secrets.API_KEY }}" | Use repository secret |
steps:
- run: ./deploy.sh
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET }} | Secrets as env vars |
jobs:
deploy:
environment: production
steps:
- run: echo ${{ secrets.PROD_KEY }} | Environment-specific secrets |
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }} | Auto-generated token |
permissions:
contents: write
pull-requests: write
issues: read | Token permissions |
${{ github.repository }} | owner/repo |
${{ github.ref }} | refs/heads/main or refs/tags/v1 |
${{ github.sha }} | Commit SHA |
${{ github.actor }} | User who triggered |
${{ github.event_name }} | push, pull_request, etc. |
${{ github.run_number }} | Workflow run number |
${{ github.workspace }} | Workspace path |
if: ${{ github.ref == 'refs/heads/main' }} | String comparison |
if: contains(github.event.head_commit.message, '[skip ci]') | Contains check |
if: startsWith(github.ref, 'refs/tags/') | Starts with check |
if: success() && github.ref == 'refs/heads/main' | Combined conditions |
if: failure() | Run on failure |
if: cancelled() | Run on cancel |
${{ toJson(github.event) }} | Convert to JSON |
${{ fromJson(needs.job1.outputs.matrix) }} | Parse JSON |
jobs:
deploy:
environment:
name: production
url: https://example.com
steps:
- run: ./deploy.sh | Deploy to environment |
jobs:
deploy:
environment:
name: staging
concurrency:
group: staging
cancel-in-progress: true | Environment with concurrency |
permissions:
pages: write
id-token: write
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/configure-pages@v4
- uses: actions/upload-pages-artifact@v3
with:
path: ./dist
- uses: actions/deploy-pages@v4
id: deployment | Deploy to GitHub Pages |
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET }}
aws-region: us-east-1
- run: aws s3 sync ./dist s3://bucket | Deploy to AWS S3 |
- uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- uses: azure/webapps-deploy@v2
with:
app-name: my-app
package: ./dist | Deploy to Azure |