Use when creating or modifying GitHub Actions workflow files. Provides guidance on workflow syntax, triggers, jobs, steps, and expressions for creating valid GitHub Actions workflows that can be tested locally with act.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
name: act-workflow-syntax description: Use when creating or modifying GitHub Actions workflow files. Provides guidance on workflow syntax, triggers, jobs, steps, and expressions for creating valid GitHub Actions workflows that can be tested locally with act. allowed-tools:
Use this skill when creating or modifying GitHub Actions workflow files (.github/workflows/*.yml). This covers workflow structure, triggers, jobs, steps, and best practices for workflows that work both on GitHub and locally with act.
Every GitHub Actions workflow follows this basic structure:
name: Workflow Name
on: [push, pull_request] # Triggers
jobs:
job-name:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Step name
run: echo "Commands here"
name: Human-readable workflow name (optional but recommended)on: Trigger events (push, pull_request, workflow_dispatch, etc.)env: Environment variables available to all jobsjobs: Map of job definitionspermissions: Token permissions for the workflow# Single event
on: push
# Multiple events
on: [push, pull_request]
# Event with filters
on:
push:
branches:
- main
- 'releases/**'
paths:
- '**.js'
- '!docs/**'
pull_request:
types: [opened, synchronize, reopened]
on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
on:
schedule:
- cron: '0 9 * * 1' # Every Monday at 9am UTC
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
jobs:
deploy:
runs-on: ubuntu-latest
env:
NODE_ENV: production
API_URL: ${{ secrets.API_URL }}
steps:
- run: echo "Deploying to $NODE_ENV"
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: npm test
deploy:
needs: [build, test]
runs-on: ubuntu-latest
steps:
- run: npm run deploy
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [18, 20, 22]
fail-fast: false
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
steps:
# Checkout code
- uses: actions/checkout@v4
# Setup Node.js
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# Upload artifacts
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
steps:
# Single line
- run: npm install
# Multi-line
- run: |
npm ci
npm run build
npm test
# With name
- name: Install dependencies
run: npm ci
# With working directory
- run: npm test
working-directory: ./packages/core
# With shell
- run: echo "Hello"
shell: bash
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main'
run: npm run deploy
- name: Run on success
if: success()
run: echo "Previous steps succeeded"
- name: Run on failure
if: failure()
run: echo "A step failed"
- name: Always run
if: always()
run: echo "Runs regardless of status"
steps:
- run: echo "Event: ${{ github.event_name }}"
- run: echo "Branch: ${{ github.ref_name }}"
- run: echo "SHA: ${{ github.sha }}"
- run: echo "Actor: ${{ github.actor }}"
- run: echo "Job status: ${{ job.status }}"
- run: echo "Runner OS: ${{ runner.os }}"
steps:
- run: echo "Token is set"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
API_KEY: ${{ secrets.API_KEY }}
steps:
- if: contains(github.event.head_commit.message, '[skip ci]')
run: echo "Skipping CI"
- if: startsWith(github.ref, 'refs/tags/')
run: echo "This is a tag"
- if: endsWith(github.ref, '/main')
run: echo "This is main branch"
- run: echo "${{ format('Hello {0}', github.actor) }}"
# Run all workflows
act
# Run specific event
act push
# Run specific job
act -j build
# Dry run (validate without executing)
act --dryrun
# List workflows
act -l
# Use specific platform
act -P ubuntu-latest=catthehacker/ubuntu:act-latest
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Check environment
run: |
if [ "$ACT" = "true" ]; then
echo "Running in act"
else
echo "Running on GitHub"
fi
Create .secrets file for local testing:
GITHUB_TOKEN=ghp_your_token_here
API_KEY=your_api_key_here
Then run:
act --secret-file .secrets
Or pass secrets individually:
act -s GITHUB_TOKEN=ghp_token -s API_KEY=key
✅ Use semantic job and step names
✅ Pin action versions (actions/checkout@v4)
✅ Use fail-fast: false for matrix builds to see all results
✅ Set appropriate timeout-minutes for jobs
✅ Use working-directory instead of cd commands
✅ Test workflows locally with act --dryrun before pushing
✅ Use caching for dependencies
✅ Use environments for deployment jobs
❌ Hardcode secrets in workflow files
❌ Use latest tags for actions
❌ Run workflows on every file change (use path filters)
❌ Create overly complex workflows (split into multiple files)
❌ Ignore act compatibility when using GitHub-specific features
❌ Forget to validate YAML syntax
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run build
name: Deploy
on:
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- run: echo "Deploying ${{ steps.version.outputs.VERSION }}"
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
api: ${{ steps.changes.outputs.api }}
web: ${{ steps.changes.outputs.web }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
api:
- 'packages/api/**'
web:
- 'packages/web/**'
build-api:
needs: detect-changes
if: needs.detect-changes.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- run: echo "Building API"