The Kingdom's Actions
GitHub Actions, YAML workflows and automation
The Master Archivist guides you into the depths of the Great Forge. Around you, immense machines turn without rest, powered by streams of blue magic. Scrolls descend on conveyor belts, pass under automatic stamps, then are sorted and stored in luminous alcoves. Not a single blacksmith in sight - everything runs on its own.
"In your last quest, you discovered the Eternal Forges and understood the principles of continuous integration and deployment. Today, you will learn to program the Forges yourself. Every Forge obeys Actions - enchanted instruction scrolls that tell it exactly what to do when new chronicles arrive. You are going to write your first Action."
What is a GitHub Action?
A GitHub Action is an automated workflow that runs directly on GitHub's servers. You don't need to install anything, configure anything on your machine - GitHub takes care of everything.
The principle is simple: you describe in a file what you want to happen and when you want it to happen. GitHub executes these instructions automatically.
For example:
- On every
push, run the tests - On every
pull request, check the code style - Every night at midnight, generate a report
- When a version is tagged, deploy the application
"Imagine a tireless scribe waiting in the Forge, day and night. As soon as a new scroll arrives, he reads the instructions you wrote and executes them to the letter. That is exactly what Actions do."
Key concepts
Before writing your first Action, you need to understand the vocabulary. GitHub Actions uses five fundamental concepts:
Workflow - the instruction scroll
A workflow is a YAML file describing a complete automated process. It is the scroll you write for the Forge. A repository can contain multiple workflows.
Job - the task
A job is a set of steps that run on the same machine. A workflow can contain multiple jobs. By default, jobs run in parallel (at the same time).
Step - the individual action
A step is an individual action inside a job. Each step is either a shell command (run:) or a reusable action (uses:).
Action - the reusable component
An action (with a lowercase "a") is a prefabricated component you can use in your steps. Thousands exist on the GitHub marketplace. The most common is actions/checkout@v4 which retrieves your code.
Runner - the execution machine
A runner is the virtual machine on which your job runs. GitHub offers free runners on Ubuntu, Windows and macOS.
| Concept | Forge analogy | Description |
|---|---|---|
| Workflow | The instruction scroll | YAML file describing the complete process |
| Job | A task in the Forge | Set of steps on the same machine |
| Step | A blacksmith's gesture | An individual action (command or action) |
| Action | A prefabricated tool | Reusable component from the marketplace |
| Runner | The Forge's anvil | Virtual machine that runs the job |
"An instruction scroll (workflow) contains one or more tasks (jobs). Each task is a series of gestures (steps). Some gestures use prefabricated tools (actions) forged by other Masters. And the whole thing runs on a magical anvil (runner) that GitHub lends you for free."
Where to place workflows?
GitHub looks for workflows in a very specific folder:
your-repo/
.github/
workflows/
my-workflow.yml
another-workflow.yml The path is always .github/workflows/. If you place your file elsewhere, GitHub will ignore it.
Important: The .github folder starts with a dot - it's a hidden folder. On Linux/macOS, use ls -a to see it. On Windows, enable hidden file display.
Files must have the .yml or .yaml extension.
YAML basics
GitHub Actions workflows are written in YAML (YAML Ain't Markup Language). It's a data format readable by humans. Here are the essential rules:
Indentation is crucial
In YAML, indentation defines the structure. We use spaces (never tabs!). Each nesting level adds 2 spaces.
# Correct
parent:
child:
grandchild: value
# INCORRECT - mixed indentation
parent:
child:
grandchild: value # 6 spaces instead of 4! Warning: Never use tabs in YAML! Only spaces. This is the most common mistake for beginners.
Key-value pairs
name: Tests
version: 1.0
active: true Lists
List items start with a dash followed by a space:
fruits:
- apple
- banana
- cherry Multi-line text
The | character allows writing text on multiple lines:
script: |
echo "First line"
echo "Second line"
echo "Third line" "YAML is like the calligraphy of enchanted scrolls. Every space counts, every indentation has meaning. One space too many or too few, and the enchantment fails. Rigor is the key."
Your first workflow
Here is the simplest possible workflow. It runs on every push and displays a message:
name: Tests
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Tests pass!" Let's break down each line:
| Line | Meaning |
|---|---|
name: Tests | The workflow name (displayed in GitHub) |
on: push | Trigger: runs on every push |
jobs: | Start of the task list |
test: | Job name (you choose the name) |
runs-on: ubuntu-latest | Ubuntu virtual machine |
steps: | Start of the step list |
- uses: actions/checkout@v4 | Step 1: retrieve the repository code |
- run: echo "..." | Step 2: execute a shell command |
The actions/checkout@v4 action is almost always the first step. Without it, your code is not available on the runner - the job runs on a blank machine!
Triggers
The on: keyword defines when the workflow starts. Here are the most common triggers:
On every push
on: push On every pull request
on: pull_request On multiple events
on: [push, pull_request] On specific branches
on:
push:
branches: [main, develop]
pull_request:
branches: [main] This workflow only triggers for pushes to main or develop, and for pull requests targeting main.
On a schedule (cron)
on:
schedule:
- cron: '0 0 * * *' # Every day at midnight UTC Manually
on: workflow_dispatch This trigger adds a "Run workflow" button in the GitHub interface. Very handy for manual deployments.
"Triggers are the sentinels of the Forge. They keep watch at all times and wake the machines at the right moment. A push? Tests launch. A pull request? The automatic review starts. Midnight? The report is generated. Everything is orchestrated."
A more complete workflow
Here is a realistic workflow for a project with tests:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Check style
run: npm run lint Each step can have a name that appears in the logs. It's optional but highly recommended for readability.
Multiple jobs and dependencies
By default, jobs run in parallel. To create dependencies between jobs, use the needs: keyword.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Deploying..." In this example:
testruns firstbuildwaits fortestto finish successfullydeploywaits forbuildto finish successfully
If test fails, neither build nor deploy will run. That's exactly the desired behavior - no deployment if tests fail!
A job can also depend on multiple jobs:
deploy:
needs: [test, build, lint] "In the Forge, certain operations must be done in order. You don't quench the sword before forging it, and you don't polish it before quenching. The needs keyword enforces this sacred order."
The marketplace - Tools from other Masters
GitHub has a marketplace with thousands of prefabricated actions. Instead of writing everything yourself, you can reuse the work of others.
Some popular actions:
| Action | Usage |
|---|---|
actions/checkout@v4 | Retrieve the repository code |
actions/setup-node@v4 | Install Node.js |
actions/setup-python@v5 | Install Python |
actions/upload-artifact@v4 | Save produced files |
actions/cache@v4 | Cache dependencies |
Example with Node.js:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
- run: npm test The with: keyword passes parameters to an action. Here, we request Node.js version 20.
"The marketplace is the great library of the Forges. Thousands of Master Blacksmiths have deposited their tools there for others to use. Why reinvent the anvil when someone else has already forged it perfectly?"
Secrets and environment variables
Some information must never appear in your code: passwords, API keys, access tokens. GitHub lets you store them as secrets.
Using a secret in a workflow
steps:
- name: Deploy
run: ./deploy.sh
env:
API_KEY: ${{ secrets.API_KEY }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }} Secrets are configured in the repository's Settings on GitHub, under "Secrets and variables" > "Actions".
Simple environment variables
For non-sensitive values, you can define environment variables directly:
env:
NODE_ENV: production
APP_NAME: my-application
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Application: $APP_NAME" Variables defined at root level are available in all jobs. You can also define them at the job or step level.
"Secrets are like the protected formulas of the Master Blacksmiths. You never write them on an ordinary scroll - you lock them in a magic chest and reference them by name. Even if someone steals the scroll, they will only see the name, never the formula."
Checking results
When a workflow runs, you can follow its progress in the Actions tab of your GitHub repository.
Logs
Each step produces logs you can view by clicking on it. This is where you'll see error messages if something fails.
Statuses
Each run has a status:
- Green (success): all jobs passed
- Red (failure): at least one job failed
- Yellow (in progress): the workflow is currently running
- Grey (cancelled): the workflow was cancelled
Status badges
You can add a badge to your README that displays the status of the latest workflow:
 This badge updates automatically. Green if tests pass, red otherwise. It's a trust signal for everyone who visits your repository.
"Badges are the emblems of the Forge. They show everyone that your code is tested, verified, approved by the machines. A repository without a badge is a castle without a flag - you don't know if it's inhabited or abandoned."
Hands-on exercise - Write your first Action
Write your first instruction scroll for the Forge:
- Create a
forge-actionsrepository - Create the
.github/workflows/folder - Create a test script
test.sh - Create a
README.md - Write a
verification.ymlworkflow that triggers on push, retrieves the code and runs the tests - Commit everything
- Run the verification script
Step 1 - Create the repository
mkdir forge-actions
cd forge-actions
git init -b main Step 2 - Create the workflow structure
mkdir -p .github/workflows This is the magic folder that GitHub watches. Any .yml file placed here will be treated as a workflow.
Step 3 - Create a test script
Before writing the workflow, create a small script the workflow can execute:
cat > test.sh << 'EOF'
#!/bin/bash
echo "=== Chronicle verification ==="
echo "Test 1: The README file exists..."
if [ -f "README.md" ]; then
echo " SUCCESS"
else
echo " FAILURE"
exit 1
fi
echo "Test 2: The README is not empty..."
if [ -s "README.md" ]; then
echo " SUCCESS"
else
echo " FAILURE"
exit 1
fi
echo "=== All tests pass ==="
EOF chmod +x test.sh Step 4 - Create the README
cat > README.md << 'EOF'
# Forge Actions
My first repository with GitHub Actions.

EOF Step 5 - Write the workflow
This is the crucial moment. Create the file .github/workflows/verification.yml:
name: Chronicle verification
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Retrieve the code
uses: actions/checkout@v4
- name: Make the script executable
run: chmod +x test.sh
- name: Run the tests
run: ./test.sh Verify the file is correct:
cat .github/workflows/verification.yml Step 6 - Commit everything
git add .
git commit -m "Add the chronicle verification workflow" Step 7 - Run the verification
bash verifier.sh .\verifier.ps1 Concept summary
| Concept | Description |
|---|---|
| Workflow | YAML file describing an automated process |
| Job | Set of steps executed on the same machine |
| Step | Individual action within a job |
| Action | Reusable marketplace component |
| Runner | Virtual machine that runs the jobs |
name: | Workflow name |
on: | Workflow trigger(s) |
jobs: | List of tasks to execute |
runs-on: | Virtual machine type |
steps: | List of steps in a job |
uses: | Use a prefabricated action |
run: | Execute a shell command |
needs: | Dependency between jobs |
with: | Parameters passed to an action |
env: | Environment variables |
secrets.* | Access to repository secrets |
The Master Archivist observes the instruction scroll you just wrote. He holds it up to the light, scans every line with his eyes, then slowly nods his head.
"Your first scroll for the Forge is impeccable. The triggers are well defined, the steps are clear, and the structure follows the rules of YAML. When you push these instructions to a remote Forge, they will execute automatically, without you needing to lift a finger."
He carefully stores the scroll in a leather case marked with the seal of the Forges.
"You now know how to program the Forges. But a single Action is not enough for a true Master. In your next quest, you will learn to create complex pipelines - chains of Actions that test, build and deploy your code in a single fluid motion. The Forges will soon hold no more secrets for you."