Mastering GitHub Actions: Using Contexts and Expressions

In our previous lesson on GitHub Actions Environment Variables, we learned how to store and retrieve data. However, to build truly dynamic and intelligent CI/CD pipelines, you need to go beyond static variables. This is where Contexts and Expressions come into play. They allow your workflows to make decisions, access metadata about the repository, and handle sensitive information securely.

What are Expressions in GitHub Actions?

Expressions are used to programmatically set variables in workflow files and access contexts. An expression can be any combination of literal values, context references, or functions. They are evaluated before the workflow step is executed.

The syntax for an expression is: ${{ <expression> }}.

Common Operators in Expressions

  • Logical: && (And), || (Or), ! (Not)
  • Comparison: ==, !=, <, >, <=, >=
  • Functions: contains(), startsWith(), endsWith(), format(), and join().

Understanding Contexts

Contexts are a way to access information about workflow runs, runner environments, jobs, and steps. Each context is an object that contains various properties.

Essential GitHub Contexts

  • github: Contains information about the workflow run, such as the repository name, the actor who triggered the event, and the event type.
  • env: Contains variables set in a workflow, job, or step.
  • vars: Contains custom configuration variables defined at the repository, environment, or organization level.
  • secrets: Contains sensitive information like API tokens or passwords.
  • steps: Contains information about the steps that have already run in the current job.
  • runner: Contains information about the machine executing the current job.

Flow Chart: How Expressions are Evaluated

[Workflow Triggered]
      |
      v
[Gather Context Data] (GitHub, Secrets, Env, Runner)
      |
      v
[Evaluate Expressions] (Check 'if' conditions, resolve ${{ }})
      |
      v
[Execute Job/Step]
      |
      v
[Update 'steps' Context] (Status, Outputs)
    

Practical Examples

1. Conditional Execution Based on Branch

You might want to run a deployment step only when code is pushed to the main branch. We use the github context and an if expression for this.

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy to Production
        run: echo "Deploying to production server..."
    

2. Using Functions to Check for Strings

The contains() function is extremely useful for checking if a commit message or a branch name contains a specific keyword.

jobs:
  notify:
    runs-on: ubuntu-latest
    if: contains(github.event.head_commit.message, 'FIX')
    steps:
      - name: Alert Team
        run: echo "A critical fix was pushed!"
    

Common Mistakes to Avoid

  • Missing Curly Braces: Forgetting the ${{ }} wrapper is the most common error. Without them, GitHub treats the expression as a plain string.
  • Case Sensitivity: Context keys and function names are generally case-sensitive. Always use github.ref, not GitHub.Ref.
  • Printing Secrets: Never use expressions to echo secrets in your logs. GitHub attempts to mask them, but it is not a foolproof security measure.
  • Using Contexts in the Wrong Place: Some contexts, like steps, are only available after a step has actually run.

Real-World Use Cases

  • Dynamic Environment Selection: Use ${{ github.event_name == 'push' ? 'production' : 'staging' }} to decide where to deploy.
  • Matrix Filtering: Use expressions to include or exclude specific operating systems or versions in a build matrix.
  • Step Dependencies: Use ${{ success() }} or ${{ failure() }} in an if condition to trigger cleanup tasks or notifications based on previous step results.

Interview Notes: Contexts and Expressions

  • Question: What is the difference between env and vars contexts?
  • Answer: The env context is used for variables defined within the YAML file itself (workflow, job, or step level). The vars context is used for non-sensitive configuration variables defined in the GitHub Repository Settings.
  • Question: How do you access the output of a previous step?
  • Answer: You must first give the step an id, then access it using ${{ steps.<step_id>.outputs.<output_name> }}.
  • Question: Can you use expressions inside a run script?
  • Answer: Yes, but be careful with shell escaping. It is often safer to map the expression to an environment variable first.

Summary

Contexts and Expressions are the "brain" of your GitHub Actions. They allow your workflows to be dynamic, secure, and context-aware. By mastering the github, secrets, and steps contexts, along with logical functions, you can automate complex CI/CD patterns with ease. In the next topic, we will dive deeper into GitHub Actions Secrets Management to ensure your data remains protected.

Continue your journey by exploring the next lesson: Managing Secrets and Security.