Recommended resources
The freely available resources this lesson will direct you to.
- GitHub Actions Triggers & Runners Explained | Events, Contexts & Hosted Runnerswww.youtube.com
- GitHub Actions Workflow Logic Explained | Filters, Contexts, Variables & Expressionswww.youtube.com
- Triggering a workflow - GitHub Docsdocs.github.com
- Choosing the runner for a job - GitHub Docsdocs.github.com
Hello! Last lesson established the execution structure: a workflow contains jobs, each job has its own runner, and steps within a job run in order. We also noted that on: push could start a workflow, but left the details open. Today we’ll answer two practical questions for your Azure deployment workshop: Which changes cause a run? And where does each job execute, with which credentials available?
Allow about 35–40 minutes for this lesson. The same ideas can become a roughly two-minute explanation in your eventual 20-minute workshop.
A trigger decides whether a workflow run starts
A workflow file can sit in .github/workflows without doing anything. An event named under on gives GitHub a reason to start it. Common choices for a web application are a push, a pull_request, and workflow_dispatch (an on-demand run). If you configure more than one event, each is an independent way to start a run; two qualifying events can produce two runs.
First, read GitHub’s examples of single and multiple events, then its explanation of filters. Pay particular attention to the distinction between starting a workflow and later deciding which jobs in that workflow should run.
Triggering a workflow - GitHub Docs
GitHub’s “Triggering a workflow” documentation establishes the rules we will use to predict runs, rather than relying on what a workflow name suggests it does.
In “Using events to trigger workflows,” read “Using a single event” and “Using multiple events,” especially multiple events. Then, in “Using event activity types,” read the label example from activity types. In “Using filters,” begin with branch filtering. Continue through “Using filters to target specific branches for pull request events” and its “Example: Including branches,” then read the opening explanation in “Using filters to target specific branches or tags for push events.” Look for which branch each event’s filter examines. You may skim the more elaborate pattern-exclusion examples.
The basic decision is: Does this event match one of the configured triggers and that event’s filters? A job’s runs-on value or a secret’s existence cannot make a non-matching push start a workflow.
Branch filters answer different questions for pushes and pull requests
Watch this short explanation of why teams narrow broad triggers. It connects branch and path filters to the cost and noise of unnecessary CI runs.
GitHub Actions Workflow Logic Explained | Filters, Contexts, Variables & Expressions
In “GitHub Actions Workflow Logic Explained | Filters, Contexts, Variables & Expressions,” Cloud With VarJosh illustrates how filters refine an event rather than replace it.
Watch filter logic. Focus on why a configured push needs both its branch and path conditions satisfied when both filters are present, and on the distinction between including and ignoring branches.
For the workshop, two branch-filter meanings matter most:
- Under
push,branches: [main]means the branch receiving the push must bemain. - Under
pull_request,branches: [main]means the PR’s target (base) branch must bemain. A PR can originate fromfeature/home-pageand still qualify because it proposes merging intomain.
If an event has both a branch filter and a path filter, both must match. Different events, however, are alternative triggers: a matching PR need not also satisfy the push configuration. Branch filters under push also do not automatically govern a separately configured manual trigger.
This distinction prevents a common workshop misstatement: “The workflow only runs on main” is ambiguous. Does that mean pushes to main, PRs targeting main, or jobs that are allowed to deploy from main? Those are different rules.
A runner decides where a qualifying job executes
Once a run exists and a job is eligible, runs-on selects the kind of runner that executes that job. The runner is the machine performing commands such as checkout or build; it is not the Azure App Service that will eventually host the website.
Choosing the runner for a job - GitHub Docs
GitHub’s “Choosing the runner for a job” documentation shows how a job requests an execution machine and what changes when that machine is self-hosted.
Read “Overview,” including runner choices. In “Choosing GitHub-hosted runners,” read the opening paragraph and “Example: Specifying an operating system”; focus on fresh instances. Finally, in “Choosing self-hosted runners,” read the opening paragraph and “Example: Using labels for runner selection.” Follow label matching rather than the later runner-group details.
For example, runs-on: ubuntu-latest requests a GitHub-hosted Ubuntu runner for that job. runs-on: windows-latest requests Windows instead. A self-hosted runner is infrastructure you operate; a label such as [self-hosted, linux] restricts the job to an eligible runner with the requested labels. If none is available, the job can remain queued rather than magically switch to an Ubuntu machine hosted by GitHub.
Recall the job boundary from last lesson: GitHub-hosted jobs receive separate fresh runner instances. A second job does not inherit files or a checkout from the first. Runner selection also does not determine the operating system of the eventual Azure App Service. A Linux runner can build and send an appropriate package to an Azure target; the Azure runtime and deployment configuration are separate decisions we will address later.
Secrets provide values to work that is allowed to run
A deployment needs a credential, but putting it directly in workflow YAML would expose it in repository history. GitHub Actions secrets let you store sensitive values outside the file and reference them through the secrets context, for example secrets.AZURE_CLIENT_ID. Secrets can be configured at repository, organization, or environment scope; availability depends on that scope and the run’s context.
Here is the crucial scheduling distinction:
An encrypted secret is not a trigger, a branch filter, or a runner. It supplies a value to an eligible job or step that references it. A missing or unavailable secret does not start a workflow or automatically skip the whole workflow; a step needing the value may instead fail.
GitHub masks known secret values in logs, but masking is not permission to print credentials. A transformed value or one sent to another service may not be protected as you expect. Keep credentials out of run commands that display them, screenshots, and demo transcripts. Also, repository secrets generally are not supplied to workflows triggered by pull requests from forks (apart from GitHub’s limited GITHUB_TOKEN). That is an important boundary when a repository accepts contributions from others: PR validation should not require Azure deployment credentials.
Later lessons will configure an Azure identity and use it for a real sign-in. For now, we only need to predict whether a credential would be available, not create one. Environment-scoped secrets and approval rules can impose an additional gate; we will examine that governance separately.
Trace one small workflow end to end
This is an execution-prediction example, not a deployment workflow. It deliberately avoids building an app or signing in to Azure.
name: Web app routing example
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Identify this run
run: |
echo "Event: $GITHUB_EVENT_NAME"
echo "Ref: $GITHUB_REF"
release_gate:
needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Check that a credential is configured
env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
run: test -n "$AZURE_CLIENT_ID"
Read the file in two passes. First, on decides whether there is a run at all. Then the jobs decide what work occurs within that run. check runs on an Ubuntu runner for any qualifying event. release_gate waits for a successful check, but its if condition limits it to a push to main. workflow_dispatch is an alternative way to start the workflow; it does not bypass that job condition.
The last step checks only whether the secret resolves to a non-empty value. It does not prove the credential is valid or deploy anything. Notice that the value is passed through env, not echoed. The check job has no reason to receive an Azure credential.
| Occurrence | Workflow run? | check |
release_gate |
|---|---|---|---|
Push to feature/home-page |
No: push branch filter fails | — | — |
Push to main |
Yes | Ubuntu runner | Runs on its own Ubuntu runner after check; fails if the secret is unavailable |
PR from feature/home-page into main |
Yes | Ubuntu runner | Skipped: the event is a PR, not a push |
PR from feature/home-page into develop |
No: PR target filter fails | — | — |
| Manual run | Yes, when the workflow is available for manual dispatch | Ubuntu runner | Skipped: the event is workflow_dispatch |
A workflow using workflow_dispatch must be present on the repository’s default branch to receive manual-dispatch events. The person starting a manual run can select a ref; that choice does not turn the event into a push. The table also illustrates why a main-branch trigger is not, by itself, a complete deployment safety rule: if you add another trigger, inspect the deployment job’s own conditions as well.
You can represent the decisions on an ASCII workshop slide without implying that every run deploys:
GitHub repository
Workflow file
Trigger rules: push to main | PR into main | manual request
Job: check
Runner: GitHub-hosted Ubuntu
Credential: not needed
Job: release_gate
Eligibility: successful check AND push to main
Runner: a separate GitHub-hosted Ubuntu instance
Credential: AZURE_CLIENT_ID, if available
Workshop-ready speaking notes — about 2 minutes
Talking point: “The trigger determines whether GitHub starts a run. A branch filter narrows that event. A job’s runs-on determines where its commands execute, while a secret provides a value only to work that references it.”
Speaker notes: Point to the PR example: its source branch can be a feature branch because the PR filter tests the target branch. Point to the manual example: a manual run does not satisfy a push-only deployment condition. Finally, distinguish the Ubuntu runner from the Azure website that will receive the finished application.
Demo instruction for later: In the repository’s Actions tab, open a run and show its event and branch, then select a job to show its runner and steps. Do not open or display a credential value.
Key takeaway: Event and filters select runs; job conditions select work within runs; runners execute that work; secrets are controlled inputs, not scheduling rules.
Conclusion
You can now predict GitHub Actions execution in layers. Check the configured event first, then its branch filters—remembering that pushes examine the receiving branch while PR filters examine the target branch. For a qualifying run, inspect each job’s dependencies and conditions, use runs-on to determine its execution machine, and check whether any referenced secret is available in that run’s context. None of these layers should be mistaken for the Azure App Service itself.
Next, we’ll give that Azure destination a clear identity by explaining App Service plans, web apps, runtimes, configuration, and deployment endpoints before drawing the full deployment architecture.