Writing your first YAML workflow with the name, on, jobs, runs-on, and steps structure, then mastering various event triggers like push, workflow_dispatch with an input form, schedule cron, along with event filtering.

In episode 2 you understood the architecture: workflow, event, job, step, action, and runner. Now it's time to write a workflow that actually runs — not just theory. This episode is the most exciting moment in the series: you'll create your first workflow, push it to GitHub, and watch the pipeline come alive in the Actions tab.
Besides writing a workflow, we'll dissect one of the most important yet most confusing parts for beginners: event triggers. What's the difference between push and pull_request, how to make a workflow that can be run manually with an input form, how to schedule a build every night, and how to restrict a workflow to only run on certain branches, paths, or tags.
Every workflow is built from four main parts:
name — the name shown in the Actions tab (optional but recommended).on — the list of events that trigger the workflow.jobs — the collection of jobs to be run.runs-on and steps — inside each job: the runner used and its steps.name: Nama Workflow
on: <event-trigger>
jobs:
<nama-job>:
runs-on: <tipe-runner>
steps:
- name: Nama step
run: perintah shellLet's start with the simplest thing. Create the file .github/workflows/hello.yml in your repository:
name: Hello World
on: push
jobs:
greet:
runs-on: ubuntu-latest
steps:
- name: Ucapkan salam
run: echo "Halo dari GitHub Actions!"After the file is pushed to the main branch, open the Actions tab in your repository — you'll see the Hello World workflow executed and the greet job running on the ubuntu-latest runner. The steps:
push event is detected by GitHub.Ucapkan salam step executes echo and displays the output in the log.Congratulations — you just ran your first CI/CD pipeline!
The on key accepts one or many events. Its simplest form:
name: CI
on: pushThe workflow above runs every time there's a push to any branch. To trigger from multiple events at once, use the list syntax:
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latestThe workflow_dispatch event allows a workflow to be run manually — via a button in the Actions tab, or the gh workflow run command. Even better, you can add an interactive input form that appears when the workflow is run manually:
name: Deploy Manual
on:
workflow_dispatch:
inputs:
environment:
description: Target lingkungan deploy
required: true
default: staging
type: choice
options:
- staging
- production
dry-run:
description: Jalankan tanpa deploy sungguhan
required: false
default: false
type: boolean
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Info deploy
run: echo "Deploy ke ${{ inputs.environment }}"When you press the Run workflow button in the Actions tab, GitHub shows the environment dropdown and the dry-run checkbox as defined above. The selected values can be accessed through the inputs context — notice how the expression ${{ inputs.environment }} is used inside the step. Also try from the terminal: gh workflow run deploy.yml -f environment=production to trigger without the browser.
The schedule event runs a workflow based on a cron schedule. GitHub Actions uses UTC time, and the fastest recommended schedule is once every 5 minutes:
name: Nightly Build
on:
schedule:
- cron: "0 2 * * *"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Job malam hari
run: echo "Build dijalankan pukul 02.00 UTC setiap hari"The cron syntax has five columns: minute, hour, day of month, month, and day of week. "0 2 * * *" means every day at 02:00 UTC. Remember: * means every, so a schedule like "30 1 * * 1-5" means every weekday (Monday-Friday) at 01:30 UTC.
Note
GitHub does not guarantee that cron schedules run to the exact minute; there may be a delay of several minutes when the runner queue is full. Don't build workflows that depend heavily on second or minute precision.
Without filters, a workflow runs on every push — including documentation changes and pushes to experimental branches. Filtering makes workflows more efficient and saves quota. There are three main filters:
name: CI
on:
push:
branches:
- main
- "feature/*"
tags:
- "v*"
paths:
- "src/**"
- "!**.md"Let's dissect each one:
branches — the workflow only runs when pushing to main or a branch matching the feature/* pattern (wildcard).tags — besides branches, the workflow also runs when a tag matching v* is created (e.g., v1.0.0). Useful for release workflows.paths — the workflow only runs when files inside src/** change, and is ignored for **.md changes (the ! prefix marks an exception). Perfect for avoiding rebuilds when you just edit the README.The same filters also apply to the pull_request event — for example, running tests only if the code in the frontend directory changes. There are also the negative variants branches-ignore and paths-ignore, plus the types filter for events with sub-types like pull_request that lets you select the kind of PR activity (opened, synchronize, closed, and others).
Tip
The most common real-world combination: a ci.yml workflow triggered by pull_request and push to main with the path filter paths: ['src/**', '!**.md']. Result: every real code change is tested immediately, while documentation edits don't burn runner minutes.
Invalid workflow file error appears in the Actions tab. Most common causes: tabs vs spaces, or inconsistent indentation..github/workflows/, not .github/workflow or workflows/.push event doesn't cover PRs. A workflow that only uses on: push won't run when a pull request is opened — add pull_request if needed.${{ ... }} usage must be inside a valid workflow YAML context, not inserted carelessly into plain text.In episode 3 you wrote and understood your first workflow:
name, on, jobs, runs-on, and steps.push event that actually runs in the Actions tab.workflow_dispatch with an input form, and schedule with cron syntax.branches, tags, and paths so the workflow only runs when needed.Your workflow can now display messages, accept manual input, run on schedule, and only activate in the right places. In episode 4 we'll level up: dissecting the steps inside a job — how run executes shell commands (single-line, multi-line, working-directory, default shell) and how uses leverages Marketplace actions with proper version selection. See you in episode 4!