Back to Blog
Lesson 6 of the CI/CD: Continuous Integration from Scratch course
DevOpsJuly 6, 20264 min read

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.

GitHub ActionsCI/CDYAMLAutomationDevOps

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.

YAML
jobs:
  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:

  1. | (the pipe character) tells YAML that the following lines are a block scalar, perfect for multi-line scripts.
  2. The runner executes each line sequentially.
  3. 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 FeaturePurpose
nameProvides a readable label in the GitHub UI logs.
runExecutes shell commands on the runner.
shellOptionally specifies which shell to use (e.g., bash, pwsh).
working-directorySets 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:

  1. Add a name to your step so it appears clearly in the logs.
  2. Use the run keyword with the | syntax to:
    • Print the current directory (pwd).
    • List the files in the current workspace (ls -F).
    • Create a dummy "artifact" file.
  3. Commit and push your changes to trigger the workflow.

Common Pitfalls

  • Non-Zero Exit Codes: If your script contains grep "not found" file.txt and the string isn't found, grep returns 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 || true to 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 run keyword is aligned exactly two spaces under the steps list 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.

Similar Posts