Workflow Syntax Deep Dive: Mastering GitHub Actions Steps
Master the essential GitHub Actions syntax to execute shell commands and chain multi-step workflows. Learn the 'run' keyword to automate your CI pipeline.
Previously in this course, we explored Understanding GitHub Runners and Jobs in CI/CD Pipelines to define where our code executes. Now that you can spin up a runner, it’s time to make it do useful work. In this lesson, we will dive into the specific syntax required to execute commands, manage step sequences, and control the environment using the CLI within your YAML workflows.
The Power of the 'run' Keyword
In Anatomy of a Workflow: Mastering GitHub Actions YAML Structure, we introduced the basic YAML hierarchy. The heart of any job is its steps list. To run a command on the runner's operating system, we use the run keyword.
When you use run, GitHub Actions automatically invokes a shell (typically bash on Ubuntu runners) to execute your command. Think of this as opening a terminal session directly on the remote server where your code is checked out.
Worked Example: Chaining Commands
You aren't limited to a single command per step. You can execute multiple commands by providing a multi-line string. Each line is treated as a separate command in the shell script.
YAMLjobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run multi-line shell script run: | echo "Starting the build process..." mkdir -p build date > build/timestamp.txt ls -la build/
In this example:
|(the pipe character) tells YAML that the following lines are a block scalar, perfect for multi-line scripts.- The runner executes each line sequentially.
- If any command in this block exits with a non-zero status (an error), the entire step fails, and the workflow stops.
Managing Step Sequences
A job is a sequence of steps that run in order. While run executes shell commands, steps can also use uses to call pre-built actions. Understanding how these interact is key to building a robust pipeline for our ongoing project.
| Syntax Feature | Purpose |
|---|---|
name | Provides a readable label in the GitHub UI logs. |
run | Executes shell commands on the runner. |
shell | Optionally specifies which shell to use (e.g., bash, pwsh). |
working-directory | Sets the folder where the command executes. |
Hands-on Exercise: Building Your Pipeline Step
We are going to advance our running project. Open your existing workflow file (from our Hello World Pipeline: Your First GitHub Actions Automation lesson) and replace the simple echo step with a sequence that prepares a build environment:
- Add a
nameto your step so it appears clearly in the logs. - Use the
runkeyword with the|syntax to:- Print the current directory (
pwd). - List the files in the current workspace (
ls -F). - Create a dummy "artifact" file.
- Print the current directory (
- Commit and push your changes to trigger the workflow.
Common Pitfalls
- Non-Zero Exit Codes: If your script contains
grep "not found" file.txtand the string isn't found,grepreturns an exit code of 1. GitHub Actions interprets this as a failure and will stop the job. If you want to allow a command to fail without stopping the pipeline, append|| trueto your command. - Shell Context: Don't assume your environment variables or aliases from your local machine exist on the runner. The runner starts with a clean slate.
- Indentation Errors: YAML is strictly indentation-sensitive. Ensure the
runkeyword is aligned exactly two spaces under thestepslist items.
FAQ
Can I run Python or Node.js scripts directly in run?
Yes. As long as the runner has the interpreter installed (which it does for standard Ubuntu images), you can use run: python my_script.py.
How do I change the shell?
You can specify shell: bash, shell: python, or even shell: pwsh if you need to run PowerShell on Windows runners.
Does run create a new shell for every line?
When using the | syntax, all lines are executed within the same shell session. This means variables defined in one line are available in the next.
Recap
We've moved beyond simple "Hello World" prints. By mastering the run keyword and the | syntax, you can now orchestrate complex shell operations. You’ve learned that steps are sequential and that every command’s exit code determines the fate of your CI pipeline.
Up next: We will introduce automated testing and learn how to run actual verification scripts within these steps.
Work with me

CI/CD Pipeline & Docker Containerization
Ship with confidence: automated CI/CD pipelines and Docker setups so every push is tested and deployed — no more manual, error-prone releases.

AI Automation & Agentic Workflow Development
Automate the repetitive work eating your time — content pipelines, data workflows, and agentic AI tasks that run themselves.
