← Back to all posts

Your First GitHub Actions Workflow Should Fail Once

Build a small acceptance check, deliberately break it, and use the failing commit and step to verify that your first GitHub Actions workflow tests the intended files.

Blue and violet workflow arrows joined by three rounded nodes

A green workflow is useful only if it would turn red when the thing you care about breaks. For a first GitHub Actions project, make that property visible: check one file, deliberately put the wrong value in it, then repair it.

This example separates the check from the service that runs it. The shell check was tested locally with both values. The GitHub configuration follows current official documentation, checked October 13, 2026; the hosted runs described below are steps for you to perform, not a claimed account test.

Make a check with a visible failure condition

Use a disposable learning repository. You need Bash and Git locally, then a GitHub repository where you can commit and run Actions. Repository or organization policy can disable Actions; a file on disk alone cannot create a hosted run.

Create message.txt containing:

ready

Create check.sh:

#!/usr/bin/env bash
set -eu
actual=$(cat message.txt)
if [ "$actual" != 'ready' ]; then
  printf 'Expected ready, got <%s>\n' "$actual" >&2
  exit 1
fi
printf 'message check passed\n'

Run it from the repository root:

bash check.sh

It prints message check passed and exits with status 0. Change the file to broken, run the check again, and inspect the status immediately:

printf 'broken\n' > message.txt
bash check.sh
printf 'status=%s\n' "$?"

Run these commands in an interactive shell without set -e. The check prints Expected ready, got <broken> to stderr and returns status 1. Restore ready before your first hosted run.

The contract is deliberately small: command substitution removes trailing newlines, so this checks the resulting text value, not byte-for-byte formatting. A missing file causes cat to fail and the script to stop. If your real task concerns exact formatting, replace this contract with an appropriate byte or parser check.

Give Actions the same command

Save this as .github/workflows/message-check.yml:

name: Message check
on:
  push:
  pull_request:
  workflow_dispatch:
permissions:
  contents: read
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Check out this revision
        uses: actions/checkout@v6
      - name: Check message
        run: bash check.sh

The Actions quickstart establishes the workflow directory and basic job structure. Checkout supplies repository files before the shell command runs. This example uses the current major shown by the checkout project, v6; a major tag can move, while a reviewed full commit SHA fixes an exact action revision.

There is no deployment, upload, secret, or write permission in this workflow. GitHub provides the selected hosted runner; ubuntu-latest is a moving runner label, not a promise of an eternally fixed OS image. If you later add runtime-sensitive tests, record or install the versions those tests need.

Commit the script, message, and workflow to your learning repository and push through your normal GitHub workflow. Open Actions and locate the run for that commit. Expand Check message and look for the success line. The local script does not need an executable bit because the workflow invokes it through Bash.

Prove that the check detects a broken revision

Change only message.txt to broken, commit that change, and push it. In the new run, verify the commit identity before inspecting the failing step. You should find the deliberate error and a failed job.

Do not add continue-on-error or append || true to obtain a green display. Those changes alter how failure propagates rather than correcting the value. Repair message.txt, commit the repair, and confirm that the new run passes.

The three runs should answer three different questions:

Run Evidence to inspect What it establishes
Baseline Checkout and success from check.sh The intended command can pass
Broken revision Matching commit, error line, failed step The check rejects the wrong value
Repaired revision New commit and restored success The repair satisfies the same contract

A rerun of the old failed revision is not a test of your unpushed repair. Follow the commit identifier, not just the latest timestamp.

Distinguish “no run” from “failed run”

If there is no run, first inspect whether the workflow is committed in the expected directory and whether Actions is enabled. Then compare the event with the configured triggers. Syntax problems, policy restrictions, and event rules are different from a script that started and failed.

workflow_dispatch provides manual execution, but the workflow must exist on the default branch for the manual-run feature. GitHub’s manual execution instructions explain the UI and branch selection.

A queued job has not yet tested the file. A skipped job is also different from a passed assertion. Runner availability, account limits, and permissions matter; consult the current hosted runner reference for your repository’s conditions.

Before using the result as a merge requirement, make the check fail for a real error in your project and explain its log without guessing. That evidence makes a green check meaningful; a workflow that only prints a greeting cannot establish that your application works.

References