Documentation/Buki/Act/ skills /act-workflow-syntax

📖 act-workflow-syntax

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.



Overview

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