Chapter 01Lesson 02~120 minutes

GitHub Actions Foundations, Automation Model, and CI/CD Concepts: Guided Hands-On Workflow

This lesson turns the mental model into evidence. You will create one deliberately small workflow in a disposable repository, trigger it once by push and once manually, then compare the two runs using their event, ref/SHA, run ID, attempt, job, runner, and logs. The workflow performs no deployment, uses no secret, invokes no external action, and requests no repository token permissions.

Hands-onPushworkflow_dispatchEvidenceDisposable repo

Learning objectives

  • Create a minimal workflow that supports both push and workflow_dispatch without introducing third-party dependencies.
  • Predict and verify event/ref/SHA, run ID/attempt, job/step state, runner identity, and local workspace behavior.
  • Use selected event-payload fields safely instead of dumping complete contexts into logs.
  • Compare a repository push run with a manual run and explain which state is equal, different, or event-dependent.
  • Clean up only the disposable resources created by the lab and preserve a small evidence packet before deletion.

1. Lab scope and safety boundary

Use a throwaway repository such as gha-ch01-lab. A public repository is the simplest no-cost path for standard GitHub-hosted runners; a private repository also works if your account has available Actions minutes. Do not use an employer/customer repository, a protected production branch, a repository with real deployment secrets, or a self-hosted runner.

Item Required lab value
Repository Disposable personal training repository only.
Default branch main.
Workflow path .github/workflows/ch01-foundations.yml.
Runner ubuntu-24.04.
Repository token permissions permissions: {}.
Secrets None.
External side effects None; no packages, releases, deployments, cloud, registry, or issue/PR mutation.
Evidence Run IDs, attempts, event/ref/SHA, runner metadata, job/step conclusions, selected event fields.

2. Preflight: inspect before changing anything

  1. Confirm the repository is disposable and owned/controlled by you.
  2. Confirm main is the default branch.
  3. Open the repository's Actions tab and note whether it currently has any runs.
  4. Check Settings → Actions → General only if you need to understand why Actions is disabled. Do not broaden organization/repository policy just to make the lab work; use another disposable repository if policy is intentionally restrictive.
  5. Record the current commit SHA on main. This is your “before” revision.

If you use Git locally, a read-only identity check is:

git branch --show-current
git rev-parse HEAD
git status --short

3. Create the smallest useful workflow

Create .github/workflows/ch01-foundations.yml with the following content. The workflow intentionally does not use actions/checkout; this makes runner state and event identity easier to see.

name: Chapter 01 - Foundations Evidence

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions: {}

jobs:
  observe:
    name: Observe event and runner state
    runs-on: ubuntu-24.04
    steps:
      - name: Record immutable run identity
        shell: bash
        run: |
          set -euo pipefail
          printf 'event=%s\n' "$GITHUB_EVENT_NAME"
          printf 'ref=%s\n' "$GITHUB_REF"
          printf 'sha=%s\n' "$GITHUB_SHA"
          printf 'run_id=%s\n' "$GITHUB_RUN_ID"
          printf 'run_attempt=%s\n' "$GITHUB_RUN_ATTEMPT"
          printf 'workflow=%s\n' "$GITHUB_WORKFLOW"

      - name: Inspect selected event fields safely
        shell: bash
        run: |
          set -euo pipefail
          python3 - <<'PY'
          import json, os
          with open(os.environ['GITHUB_EVENT_PATH'], encoding='utf-8') as fh:
              event = json.load(fh)
          keep = {
              'event_name': os.environ['GITHUB_EVENT_NAME'],
              'ref': event.get('ref'),
              'before': event.get('before'),
              'after': event.get('after'),
              'repository': event.get('repository', {}).get('full_name'),
          }
          print(json.dumps(keep, indent=2, sort_keys=True))
          PY

      - name: Prove same-job filesystem continuity
        shell: bash
        run: |
          set -euo pipefail
          printf '%s\n' "$GITHUB_RUN_ID:$GITHUB_RUN_ATTEMPT:$GITHUB_SHA" > evidence.txt
          test -s evidence.txt
          cat evidence.txt

      - name: Write a safe job summary
        shell: bash
        run: |
          {
            echo '## Chapter 01 evidence'
            echo
            echo "- Event: \`$GITHUB_EVENT_NAME\`"
            echo "- Ref: \`$GITHUB_REF\`"
            echo "- SHA: \`$GITHUB_SHA\`"
            echo "- Run: \`$GITHUB_RUN_ID\`, attempt \`$GITHUB_RUN_ATTEMPT\`"
            echo "- Runner OS: \`$RUNNER_OS\` / \`$RUNNER_ARCH\`"
          } >> "$GITHUB_STEP_SUMMARY"
Why no checkout? The workflow is examining GitHub's execution model, not building repository source yet. Omitting checkout makes it obvious that event metadata exists independently from a checked-out workspace. Chapter 02 can introduce workflow structure in more depth; later CI chapters can add source checkout and build tools.

4. Predict the push run before committing

Write these predictions in a local note before pushing:

  1. The commit that adds the workflow to main should match the push branch filter and create a run.
  2. The run's event should be push.
  3. The first attempt should be 1.
  4. The job should use a new ubuntu-24.04 hosted runner.
  5. The first two steps should print selected metadata only.
  6. The third step should create and read evidence.txt because steps in the same job share the job filesystem.
  7. The workflow should need no repository write permission and no secret.

Then commit and push. A simple local sequence is:

git add .github/workflows/ch01-foundations.yml
git commit -m "add Chapter 01 Actions evidence workflow"
git push origin main

If your repository uses a pull-request-only policy, do not bypass it. Use the repository's normal allowed path or a separate disposable repository where you can safely push to main.

5. Inspect the push run as an evidence chain

Open Actions → Chapter 01 - Foundations Evidence → latest run. Record, do not merely glance at:

Event

Expected: push.

Head SHA

Expected: the exact commit that added/changed the workflow.

Run / attempt

A new run ID with attempt 1.

Job

Observe event and runner state.

Runner

Ubuntu 24.04 hosted execution plus current image/tool metadata in setup logs.

Conclusion

Expected success if every shell command exits 0.

Compare the workflow's printed SHA with the commit shown by the run UI. If they differ, stop and explain the event semantics before continuing. Do not “fix” a mismatch by changing printed evidence.

6. Trigger a second run manually

The workflow_dispatch trigger allows a manual run. The workflow file must exist on the default branch for this trigger to be available. Use the Actions UI and select Run workflow. Keep the selected ref explicit; for this first lab, choose main.

Before clicking, predict:

  • The event name changes from push to workflow_dispatch.
  • A new run ID is created; this is not attempt 2 of the push run.
  • GITHUB_RUN_ATTEMPT should again start at 1 for the new run.
  • The selected ref/SHA should resolve to the chosen main revision at dispatch time.
  • The same workflow/job/step definitions execute, but the event payload shape differs.

Run it and verify each prediction independently.

7. Compare the two runs instead of calling both “green”

Evidence Push run Manual run Interpretation
Event push workflow_dispatch Different trigger intent.
Run ID Unique ID A Unique ID B Two separate workflow runs.
Attempt 1 1 Neither is a rerun.
Workflow name Same Same Same automation definition surface.
SHA Push event's commit Chosen ref's resolved commit Always record; do not assume.
Job/steps Same definitions Same definitions Execution context differs even when YAML is equal.

8. Optional read-only CLI inspection

If GitHub CLI is already installed and authenticated on your workstation, you can correlate UI evidence without exposing tokens inside the workflow:

gh run list --workflow ch01-foundations.yml --limit 10
gh run view RUN_ID
gh run view RUN_ID --log

For a compact API evidence file, after substituting your own disposable repository and run ID:

gh api repos/OWNER/REPO/actions/runs/RUN_ID \
  --jq '{id,run_attempt,event,head_branch,head_sha,status,conclusion,html_url}' \
  > run-evidence.json

Local gh authentication is outside the workflow and should follow your normal credential storage. Never paste a PAT into the workflow YAML or commit it to the repository.

9. Map the experiment to automation, CI, and CD

This workflow is automation because it executes repeatable work on events. It is only a tiny CI skeleton: it records revision identity and proves the runner can execute steps, but it does not yet check out source, compile, test, lint, or publish a result artifact. It is not continuous delivery or deployment because there is no releasable artifact, deployment authorization, or external target.

Do not overclaim the result. A successful Chapter 01 run proves that GitHub selected the workflow, created the run, assigned the job to a hosted runner, and the listed steps exited successfully under the observed context. It does not prove the repository's application code is correct or deployable.

10. Challenge: find the owning layer

Without changing permissions or adding actions, change the workflow's push filter temporarily to a branch name that does not match your next push. Push a harmless documentation commit to main. If no run is created, identify the cause correctly: the failure is at event/workflow selection, not runner capacity or shell execution. Restore the filter to main afterward and verify the next harmless push creates a run again.

This challenge is optional if repository policy makes extra pushes undesirable. The conceptual requirement is to distinguish “no run selected” from “run selected but job failed.”

11. Verification and cleanup

  • Two controlled runs exist: one push and one manual dispatch.
  • Each has a unique run ID and attempt 1.
  • The observed SHA/ref/event fields are recorded.
  • Runner and step evidence is visible.
  • No secret or full context dump appears in logs.
  • No external action, package, release, deployment, cloud, registry, or self-hosted runner was used.
  • No repository token permission was required beyond permissions: {}.

Before deleting anything, save the run URLs/IDs and a short comparison note. Cleanup can mean keeping the disposable repository for the rest of the course, disabling/removing this single workflow, or deleting the throwaway repository if you are finished. Delete only the exact resource you created; never use ambiguous “latest repository/run” cleanup automation.

Next lesson

Configuration, Design Patterns, and Trade-Offs

Move from “it runs” to “it is the right automation boundary”: compare CI/CD scope, repository-native versus external orchestration, required checks, scripts, reusable actions, cost, and governance.

Knowledge check

Why are the push run and manual run not attempts 1 and 2 of the same run?

The workflow is green. Has it proved the repository source builds?

Why print selected event fields instead of the entire github context?

A push to feature/demo creates no run because the workflow filters only main. Which layer owns the behavior?

What should you preserve before deleting the disposable repository?

Official references and version notes

Version and compatibility note

Rechecked on 2026-09-09. The mandatory workflow uses only GitHub workflow syntax and shell/Python already available on the pinned ubuntu-24.04 hosted image; it invokes no external action and uses no secret. workflow_dispatch availability and event-specific SHA/ref semantics should always be verified against current GitHub documentation when this lesson is regenerated.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.