GitHub Actions study materials
GitHub Actions study resources

GitHub Actions: Automation: scheduled, dispatched, and cross-workflow jobs

GitHub Actions · Work through your exam, one topic at a time.

All guide sections

Automation: scheduled, dispatched, and cross-workflow jobs

What this domain tests

Automation goes beyond test-on-push. Expect questions on non-code triggers (schedule, workflow_dispatch, repository_dispatch, workflow_run), passing inputs safely, moving data between jobs with artifacts and cache, and the token rules that decide whether one workflow can start another.

Inputs and dispatch

  • workflow_dispatch supports at most 10 inputs typed as string, choice, boolean, or environment, each with required and default.
  • Read them through the inputs context to preserve types: inputs.dry_run is a real boolean, while github.event.inputs.dry_run is always a string.
  • repository_dispatch is fired through the REST API with a type and arbitrary client_payload; the calling token needs write access to the repository.
  • workflow_run triggers after another workflow completes and runs in the base repository context, with secrets - treat it as privileged.

Data between jobs and runs

  • Artifacts (actions/upload-artifact@v4 / download-artifact@v4) are immutable and require unique names per job; v3 is retired.
  • Cache matching tries the exact key first, then prefix restore-keys. Caches are branch-scoped: default-branch caches are visible elsewhere, but a feature branch's cache is not.
  • Caches are evicted after roughly seven days unused; artifacts follow the retention-days setting instead.

Token behaviour rule

Events created with the automatic GITHUB_TOKEN do not start new workflow runs, except for workflow_dispatch and repository_dispatch. If a job must chain into another workflow, use a GitHub App token or a PAT.

Worked example

A nightly housekeeping job with a manual dry-run option:

on:
  schedule: [{cron: '0 3 * * *'}]
  workflow_dispatch:
    inputs:
      dry_run: {type: boolean, default: true}
permissions:
  issues: write
  contents: read
jobs:
  stale:
    runs-on: ubuntu-latest
    if: github.repository == 'acme/app'
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            if (inputs.dry_run) { core.info('dry run'); return }
            await github.rest.issues.listForRepo({owner: context.repo.owner, repo: context.repo.repo, state: 'open'})

Reasoning: inputs.dry_run gives a boolean instead of the truthy string 'false'; the if guard stops forks of the repo from running the schedule; issues: write is the only elevated scope granted.

Traps that cost marks

  • Scheduled workflows are disabled after about 60 days of repository inactivity.
  • workflow_run combined with checking out untrusted PR code hands an attacker secrets and a write token.
  • Array and object client_payload values arrive as JSON strings and need fromJSON.
  • Downloading artifacts requires the same run (or explicit run-id with a token); jobs on different runners share nothing else.