Managing Workflow Execution
GitHub Actions workflows are declarative — you describe what should happen and when. So far we have used simple triggers such as push and pull_request. In this lesson we explore how to refine those triggers with activity types, branch/tag/path filters, manual and programmatic dispatch events, cross-workflow chaining, and concurrency control.
Activity Types
Many events in GitHub carry sub-events called activity types. By default a workflow that listens to pull_request fires on the opened, synchronize, and reopened types. You can override those defaults with the types keyword.
The example above restricts the workflow to run only when a pull request is first opened or marked ready for review, and when an issue is opened or a label is added. Without the types filter every default activity type for that event would trigger a run.
Common Events and Their Activity Types
| Event | Default Types | Other Useful Types |
|---|---|---|
pull_request | opened, synchronize, reopened | closed, ready_for_review, labeled, review_requested |
issues | opened, edited, deleted, transferred, pinned, unpinned, closed, reopened, assigned, unassigned, labeled, unlabeled, etc. | milestoned, demilestoned, locked, unlocked |
label | created, edited, deleted | — |
discussion | created, edited, deleted, transferred, etc. | answered, unanswered, labeled, unlabeled |
Tip: Always restrict activity types to only what your workflow actually needs. Running on every default type wastes runner minutes and can produce confusing status checks.
Filtering by Branch, Tag, and Path
For push-based and pull-request-based events you can narrow execution to specific branches, tags, or file paths. GitHub provides two complementary approaches: include filters and ignore filters.
Branch and Tag Filters
The branches keyword accepts a list of branch names or glob patterns. When present, the workflow only runs if the push or PR targets a matching branch.
This workflow triggers on pushes to main, any branch under release/, or any tag matching a semantic-version pattern like v2.1.0.
Ignore Filters
Instead of listing branches to include you can list branches to exclude. You cannot mix branches and branches-ignore on the same event.
Path Filters
Path filters restrict a workflow to run only when files matching a pattern change. This is especially useful in monorepos where a single push may touch many services.
The workflow above ignores changes to the frontend, documentation, or any other folder. A complementary paths-ignore filter is also available:
Glob Patterns
GitHub Actions supports a rich set of glob characters inside filter values:
| Pattern | Description | Example Match |
|---|---|---|
* | Matches any character except / | feature-* matches feature-login |
** | Matches any number of path segments | src/** matches src/a/b/c.ts |
? | Matches exactly one character | release-?.0 matches release-3.0 |
+ | Matches one or more of the preceding character | v1.0.0+ matches v1.0.00 |
[...] | Matches a character range or set | v[0-9] matches v3 |
! | Negates a previously matched pattern | See below |
Negation with !
The ! character must come after a positive match to carve out an exception. Order matters: GitHub processes filter lines top-to-bottom.
This triggers on all release/ branches except those ending in -alpha. If the ! line came first it would have no effect because there would be nothing to negate yet.
Important: You cannot combinebranchesandbranches-ignorefor the same event. Usebrancheswith negation patterns instead. The same rule applies totags/tags-ignoreandpaths/paths-ignore.
Triggering Without a Code Change
Not every workflow should be tied to a push or pull request. GitHub provides several events that let you run workflows manually, from external systems, or as a reaction to another workflow.
workflow_dispatch — Manual Trigger
Adding workflow_dispatch to the on block exposes a Run workflow button in the GitHub Actions UI. You can optionally define typed inputs that the user fills in before clicking the button.
Supported input types include string, boolean, choice, and environment. Manual runs can also be started from the GitHub CLI (gh workflow run) or the REST API.
repository_dispatch — External Trigger
If you need an external system (a Slack bot, a monitoring tool, or another service) to kick off a workflow, repository_dispatch lets you fire a custom event through the GitHub REST API.
To fire the event from curl:
Note: Every workflow in the repository that listens for the sameevent_typewill run. Use thetypesfilter to limit which workflows respond.
workflow_call — Reusable Workflow Trigger
A workflow with workflow_call acts as a reusable building block. Other workflows invoke it with the uses keyword at the job level. The called workflow can accept inputs, receive secrets, and return outputs.
A caller workflow invokes it like this:
workflow_run — Chaining Workflows
Use workflow_run to start a workflow after another workflow finishes. This is useful for separating concerns: a CI workflow runs tests, then a separate deployment workflow executes only if CI succeeded.
workflows
A list of workflow names (the name: field, not the file name) to watch.
types
Either completed (finished, regardless of result) or requested (queued to run).
branches
Optionally restrict to runs triggered on specific branches.
Warning:workflow_runalways fires on the default branch of the repository. Your workflow file must exist on the default branch for it to work. Theifcondition checkingconclusion == 'success'is essential — otherwise the downstream workflow runs even when the upstream failed.
Concurrency
When you push several commits in quick succession, multiple runs of the same workflow can queue up and waste runner minutes. The concurrency key lets you group runs and optionally cancel redundant ones.
Every run that shares the same group value is treated as part of a single concurrency lane. If a new run enters the group while an older run is still in progress, cancel-in-progress: true cancels the older run automatically.
Choosing a Group Name
The group string is an expression, so you can build dynamic names:
| Pattern | Effect |
|---|---|
ci-${{ github.ref }} | One lane per branch — pushes to the same branch cancel each other |
deploy-production | A single global lane — only one production deploy at a time |
pr-${{ github.event.pull_request.number }} | One lane per pull request |
Workflow-Level vs. Job-Level Concurrency
You can place the concurrency block at the top level (applies to the entire workflow) or inside an individual job. When placed on a job, other jobs in the same workflow are not affected.
Tip: Setcancel-in-progress: falsefor deployments. Canceling a half-finished deploy can leave your environment in a broken state. Usecancel-in-progress: truefor CI checks where only the latest result matters.
Key Takeaways
- Use
typesto restrict events to the exact activity types your workflow needs. - Branch, tag, and path filters let you run workflows only when relevant code changes.
- Glob patterns (
*,**,?,!) give fine-grained control inside filters. workflow_dispatchadds a manual trigger with typed inputs.repository_dispatchlets external systems fire custom events via the REST API.workflow_callturns a workflow into a reusable building block.workflow_runchains workflows so one starts after another completes.concurrencygroups prevent redundant runs and wasted runner minutes.
Complete Working Example
The workflow below demonstrates activity types, branch and path filters, manual dispatch with inputs, and concurrency control in a single file.