Switch Language
Toggle Theme

GitHub Actions Basics: YAML Workflow Structure and Trigger Configuration

Honestly, the first time I saw a YAML file in .github/workflows, I was confused. A bunch of indentation, colons, and keywords I couldn’t figure out—on, jobs, runs-on. What is this? Can I eat it?

Later, as projects piled up, manually running tests and deploying after each code push got tedious. That’s when I realized: GitHub Actions can save you a lot of trouble.

This article covers the basics of GitHub Actions—how to write YAML workflows and configure triggers. No fancy advanced features, just the practical stuff you’ll actually use. Getting started is what matters.


What GitHub Actions Can Do for You

Simple answer: automation.

Before, pushing code meant manually doing a bunch of things: run tests, check code formatting, build the project, deploy to server. Now you write these steps in a YAML file, and GitHub executes them automatically.

For example: every time you push code to main, GitHub Actions automatically runs your tests. Tests must pass before a PR can merge. This way, code quality is guaranteed, and you don’t have to worry.

Another benefit: it’s free. Public repositories are completely free. Private repos get 2000 free minutes per month (more than enough for personal projects).


What a YAML Workflow Looks Like

Let’s start with the simplest example:

name: Test Workflow

on: push

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Print greeting
        run: echo "Hello, GitHub Actions!"

Line by line breakdown:

name: The workflow’s name. Choose whatever you want, helps identify it on the GitHub Actions page.

on: Trigger condition. Writing push here means the workflow triggers every time you push code.

jobs: Task block. A workflow can contain multiple jobs that run in parallel or sequentially.

build: This job’s ID. Name it however you like, but make it meaningful (like build, test, deploy).

runs-on: Execution environment. ubuntu-latest means running on the latest Ubuntu server. You can also choose windows-latest, macos-latest.

steps: Step list. A job consists of multiple steps executed in order.

run: The command to execute. Here it just prints a message.

This is the basic skeleton. Complex workflows just pile on more jobs, more steps, more logic on top of this. Don’t panic though—master the basics first, the complex stuff will come naturally.


Triggers: When Does Your Workflow Run

The trigger is the “switch” for your entire workflow. Configured correctly, it runs at the right time.

There are four common triggers: push, pull_request, schedule, workflow_dispatch. Let’s go through each.

1. push: Trigger on Code Push

The most commonly used trigger.

on: push

This way, every code push to any branch triggers it.

But usually we only want triggers on specific branches:

on:
  push:
    branches:
      - main
      - dev

This configuration means: only trigger when pushing to main or dev branches. Other branches? Ignored.

You can be even more specific—only monitor changes to certain paths:

on:
  push:
    branches:
      - main
    paths:
      - 'src/**'
      - 'package.json'

Only triggers when files in src/ directory or package.json change. This way, changing docs or config files won’t waste a CI run.

2. pull_request: Trigger on PR Operations

PRs (Pull Requests) are the core of team collaboration.

on: pull_request

This triggers on all PR operations: opening a PR, updating a PR, reopening a PR.

But you can also limit to specific branches:

on:
  pull_request:
    branches:
      - main

Only triggers when the PR’s target branch is main.

You can also specify PR operation types:

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened
  • opened: When a PR is just opened
  • synchronize: When a PR gets new commits (like when you push new changes)
  • reopened: When a closed PR is reopened

This configuration is common—run tests when a PR opens to ensure changes are solid.

3. schedule: Scheduled Triggering

Like a cron job, runs the workflow on schedule.

on:
  schedule:
    - cron: '0 0 * * *'

This is a cron expression, format: minute hour day month weekday.

The line above means: run once daily at UTC 0:00 (8:00 AM Beijing time).

Common use cases: daily automated tests, periodic cache cleanup, scheduled report generation.

Honestly, cron expressions are easy to mess up. There are online tools to help verify, like crontab.guru.

4. workflow_dispatch: Manual Triggering

Sometimes we need manual triggers—like one-click deploy, or manually running a specific task.

on: workflow_dispatch

After configuration, a “Run workflow” button appears on the GitHub Actions page. Click it to trigger manually.

You can also configure input parameters:

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

When manually triggering, you can choose which environment to deploy to. Deploy to test or production with one click. Convenient, right?

Combining Triggers

Real projects often need multiple triggers:

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
  schedule:
    - cron: '0 6 * * 1'  # Every Monday 6:00 UTC
  workflow_dispatch:

This configuration means:

  • Trigger when pushing to main branch
  • Trigger when PR targets main branch
  • Run once every Monday morning
  • Support manual triggering

One workflow, multiple trigger methods. A common configuration pattern.


Practical Example: Automated Testing for Node.js

Let’s write a practical example—automated testing for a Node.js project.

Assume your project structure looks like this:

my-project/
├── src/
├── tests/
├── package.json
└── .github/
    └── workflows/
        └── test.yml

Create .github/workflows/test.yml file:

name: Automated Testing

on:
  push:
    branches:
      - main
      - dev
  pull_request:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      # 1. Checkout code
      - name: Checkout code
        uses: actions/checkout@v4

      # 2. Setup Node.js
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      # 3. Install dependencies
      - name: Install dependencies
        run: npm ci

      # 4. Run tests
      - name: Run tests
        run: npm test

      # 5. Code format check (optional)
      - name: Check code format
        run: npm run lint

Step by step explanation:

Checkout code: actions/checkout@v4 is an official Action that downloads your repository to the runtime environment.

Setup Node.js: actions/setup-node@v4 is also an official Action, installs the specified Node.js version. node-version: '20' means using Node.js 20.

Install dependencies: npm ci is better suited for CI environments than npm install—it strictly follows package-lock.json, faster and more reliable.

Run tests: Directly runs your test command. Requires test script configured in package.json.

Code format check: If you configured ESLint or Prettier, this step automatically checks code format.

This workflow triggers when pushing to main or dev branches, and when PRs target main. Every change gets tested.


Common Pitfalls

Honestly, I hit quite a few pitfalls when I started writing YAML. Here are some common ones:

1. Indentation Errors

YAML is extremely sensitive to indentation. Must use spaces, not tabs. Indentation levels must be consistent.

# ❌ Wrong: inconsistent indentation
jobs:
build:
  runs-on: ubuntu-latest

# ✅ Correct: consistent indentation
jobs:
  build:
    runs-on: ubuntu-latest

Recommend using a YAML plugin in your editor—it automatically highlights indentation errors.

2. Triggers Not Working

Sometimes configured triggers don’t fire the workflow. Common reasons:

  • Wrong branch configuration: Like pushing to develop branch, but configuration only monitors main
  • Forked repository: Pushing from a fork repository won’t trigger push event workflows
  • PR from fork: PRs from forks won’t trigger certain triggers by default

3. Forgot to Checkout Code

The most common mistake beginners make: forgot to use actions/checkout@v4.

Without this step, the workflow environment is empty—your code isn’t even there.

steps:
  # ❌ Wrong: running commands before checking out code
  - run: npm test

  # ✅ Correct: checkout code first
  - uses: actions/checkout@v4
  - run: npm test

4. Outdated Action Versions

Some tutorials use actions/checkout@v2 or v3. But I recommend using the latest version v4—more complete features, better performance.

Regularly check if the Actions you use have new versions. GitHub will remind you about outdated versions.


Some Practical Tips

Trigger Selection Principles

Ask yourself a few questions:

  • Want automatic or manual runs? → Automatic: use push/pull_request, Manual: use workflow_dispatch
  • High frequency? → High frequency means limit branches or paths, avoid wasting resources
  • Team collaboration? → PR scenarios must configure pull_request

Performance Optimization Tips

  • Limit trigger scope: Only monitor key branches and paths
  • Parallel execution: Multiple independent jobs can run in parallel
  • Use caching: actions/setup-node automatically caches Node.js and dependencies, no reinstall needed every time

Clear Naming

# ❌ Vague naming
jobs:
  job1:
    steps:
      - name: step1

# ✅ Clear naming
jobs:
  test:
    steps:
      - name: Install dependencies

Clear names make troubleshooting easier—GitHub Actions page displays each step’s name.


This article only covered basics. If you want to dive deeper, continue learning:

  1. Matrix builds: Run tests in multiple environments (Node.js 16, 18, 20) simultaneously
  2. Cache optimization: Manually configure caching to speed up builds
  3. Secret management: Securely store API keys, passwords, and sensitive information
  4. Custom Actions: Encapsulate your own Actions for reusable logic
  5. Deployment automation: Automatically deploy to servers or cloud platforms

The official documentation is the best resource: GitHub Actions Docs. Comprehensive content, just a bit lengthy.

Another suggestion: look at others’ configurations. Many open source projects on GitHub have complete configurations in .github/workflows folders—copying homework is fine too.


Final Thoughts

After all this talk, the core is really just three things:

  1. YAML structure: name, on, jobs, steps—four keywords build the skeleton
  2. Trigger configuration: push, pull_request, schedule, workflow_dispatch—four most commonly used
  3. Practical example: Node.js automated testing is the classic beginner scenario

I still remember the feeling after getting my first workflow working—pushing code, GitHub automatically running tests, then sending me an email saying “tests passed.” That sense of “everything under control” was really satisfying.

Later if you want to learn advanced features (matrix builds, cache optimization, deployment automation), with the foundation laid, it won’t be hard. GitHub Actions has a low entry barrier, but mastering it can really save a lot of time. At least you won’t have to manually run tests every time you push code, right?

Configure GitHub Actions Automated Testing Workflow

Create an automated testing workflow for Node.js projects that runs tests automatically when code is pushed

⏱️ Estimated time: 15 min

  1. 1

    Step1: Create Workflow File

    Create .github/workflows/test.yml in project root:

    • Path must be exact: .github/workflows/
    • Filename should be meaningful: test.yml, ci.yml
    • File extension must be .yml or .yaml
  2. 2

    Step2: Configure Triggers

    Set workflow trigger conditions:

    • push trigger: monitor main, dev branches
    • pull_request trigger: monitor PRs targeting main
    • Can limit paths: paths: ['src/**']
  3. 3

    Step3: Configure Runtime Environment

    Specify runtime environment and Node.js version:

    • runs-on: ubuntu-latest (latest Ubuntu)
    • uses: actions/setup-node@v4
    • node-version: '20' (Node.js 20)
  4. 4

    Step4: Write Test Steps

    Execute testing process in order:

    • Checkout code: uses: actions/checkout@v4
    • Install dependencies: run: npm ci (prefer ci over install)
    • Run tests: run: npm test
    • Format check: run: npm run lint (optional)
  5. 5

    Step5: Verify Workflow

    Check run results after pushing code:

    • View run status on GitHub Actions page
    • Check log output for each step
    • Confirm tests pass before merging PR

FAQ

What's the difference between GitHub Actions and other CI/CD tools like Jenkins?
GitHub Actions is built into GitHub, making configuration simpler and suitable for GitHub projects. Jenkins is more powerful but requires separate deployment and maintenance. For personal projects and small teams, GitHub Actions is easier to get started with.
Must YAML files be placed in .github/workflows directory?
Yes, the path must be exact. GitHub only recognizes YAML files in the .github/workflows/ directory as workflow configurations. Files elsewhere won't be executed.
Why didn't my workflow trigger after pushing to a branch?
Common reasons: 1) Wrong branch configuration (pushed branch not in monitor list); 2) Repository is a fork (push events from forks don't trigger by default); 3) Path limitation (changed files not in monitored paths). Checking these usually finds the issue.
What's the difference between npm ci and npm install?
npm ci (Clean Install) strictly follows package-lock.json, faster and more reliable, suitable for CI environments. npm install tries to resolve version conflicts and may produce inconsistent results. Automated testing should use npm ci.
Are GitHub Actions for private repositories free?
Private repositories get 2000 free minutes per month (MacOS environment counts as 10 minutes). Beyond that, billing by usage: Linux $0.008/minute, Windows $0.016/minute, MacOS $0.08/minute. Usually sufficient for personal projects.
How to avoid triggering workflow on every push?
Limit trigger scope: 1) Only monitor key branches (branches: [main]); 2) Only monitor key paths (paths: ['src/**']); 3) Combine them, like triggering only when src/ changes on main branch.

References

9 min read · Published on: Apr 5, 2026 · Modified on: Apr 5, 2026

Comments

Sign in with GitHub to leave a comment

Related Posts