GitHub Actions Workflow Syntax

VerifiedCaution

Guidance on creating and modifying GitHub Actions workflow files, compatible with local testing using act.

Sby Skills Guide Bot
DevOpsIntermediate
7/26/2026
Claude CodeCursor
#github-actions#ci-cd#workflow-syntax#yaml#act

Recommended for

Automated summary

This skill guides the creation and modification of GitHub Actions workflow files (.github/workflows/*.yml), with examples of triggers, jobs, steps, and matrices that also run locally with act.

Strengths

  • Provides concrete, copy-pasteable YAML for triggers (push, pull_request, workflow_dispatch, schedule), jobs, needs-based dependencies, and build matrices
  • Covers conditional steps (if, success(), failure(), always()) plus with, working-directory, and shell usage
  • Steers toward workflows that work both on GitHub and locally through act

Limitations

  • The expressions and contexts section is only started and cut off, so the ${{ }} reference stays partial
  • Does not explain how to install act or use its CLI flags for local runs
  • Limited depth on security hardening (least-privilege permissions, SHA pinning, OIDC)
When to use it

Use it when you need to write or fix a valid GitHub Actions workflow that can also be tested locally.

When not to use it

Avoid it if you need a complete GitHub Actions expressions reference or a full guide to installing and configuring act.

Security analysis

Caution
Quality score76/100

This is reference documentation for GitHub Actions YAML syntax with no obfuscated payloads, no curl|sh installs, no destructive commands, and no instructions to send secrets to external endpoints. Its Bash usage is limited to invoking `act` locally, and the only secret-handling example uses placeholder tokens written to a local file. Because it authorizes Bash/Write/Edit and touches secret handling, it warrants mild caution, but nothing indicates malicious intent.

Findings
  • •Declares Bash/Write/Edit tools and shows running `act` commands that execute workflow jobs locally, which is powerful but legitimate
  • •Example writes tokens to a local `.secrets` file and passes them via `act --secret-file`; should remind users to gitignore that file so secrets are not committed
How this analysis is made

Examples

Matrix test workflow
Create a GitHub Actions workflow that runs npm test on Node 18, 20 and 22 across ubuntu-latest and macos-latest with fail-fast disabled.
Manual deploy trigger
Add a workflow_dispatch trigger to my deploy workflow with a choice input for staging or production, and gate the deploy step on that input.
Conditional failure step
Edit .github/workflows/ci.yml so a step runs only when a previous step fails and another always runs, then verify it locally with act.

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:

  • Read
  • Write
  • Edit
  • Bash
  • Grep
  • Glob

Act - GitHub Actions Workflow Syntax

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.

Workflow File Structure

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"

Top-Level Fields

  • name: Human-readable workflow name (optional but recommended)
  • on: Trigger events (push, pull_request, workflow_dispatch, etc.)
  • env: Environment variables available to all jobs
  • jobs: Map of job definitions
  • permissions: Token permissions for the workflow

Workflow Triggers

Event Triggers

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

Manual Triggers

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Target environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

Schedule Triggers

on:
  schedule:
    - cron: '0 9 * * 1'  # Every Monday at 9am UTC

Job Configuration

Basic Job

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

Job with Environment Variables

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: production
      API_URL: ${{ secrets.API_URL }}
    steps:
      - run: echo "Deploying to $NODE_ENV"

Job Dependencies

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

Matrix Builds

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

Using Actions

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/

Running Commands

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

Conditional Steps

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"

Expressions and Contexts

Common Contexts

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

Using Secrets

steps:
  - run: echo "Token is set"
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      API_KEY: ${{ secrets.API_KEY }}

Functions

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) }}"

Act-Specific Considerations

Testing Locally with Act

# 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

Environment Variables for Act

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

Secrets with Act

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

Best Practices

DO

✅ 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

DON'T

❌ 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

Common Patterns

Build and Test

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

Deploy on Tag

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

Monorepo with Changed Files

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"

Related Skills

  • act-local-testing: Testing workflows locally before pushing
  • act-docker-setup: Configuring Docker environments for act
  • act-secrets-management: Managing secrets for local testing

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/github-actions-workflow-syntax-ms1aw4kg/download' --output '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md.part' && mv '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md.part' '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md' && test -s '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/act-workflow-syntax' && cp -R '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg' '.claude/skills/act-workflow-syntax'

Cursor
test -f '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md' && test -s '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/act-workflow-syntax' && cp -R '.skill-downloads/github-actions-workflow-syntax-ms1aw4kg' '.cursor/skills/act-workflow-syntax'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/github-actions-workflow-syntax-ms1aw4kg'

openskills read 'github-actions-workflow-syntax-ms1aw4kg'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/github-actions-workflow-syntax-ms1aw4kg/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills