| marp | true |
|---|---|
| theme | custom-github |
| footer | @Chris_L_Ayers - https://chris-ayers.com |
BlueSky: @chris-ayers.com
LinkedIn: - chris-l-ayers
Blog: https://chris-ayers.com/
GitHub: Codebytes
Mastodon: @Chrisayers@hachyderm.io
Twitter: @Chris_L_Ayers
| Feature | Description |
|---|---|
| Lists | Start with a – |
| Key-Value | Key: value |
| Objects | Objects: Properties of objects |
- Live in the
.github/workflowsfolder - Workflows are defined in YAML
- Workflows are Event Driven
<style scoped> pre { font-size: 72%; } </style>
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
- name: Build
run: npm run build<style scoped> li { font-size: 84%; margin: 0.15em 0; line-height: 1.3; } a { font-size: 90%; } </style>
- branch_protection_rule
- checks
- create / delete
- deployment
- discussion
- fork
- issue_comment
- issues
- label
- page_build
- pull_request
- pull_request_review
- pull_request_review_comment
- push
- release
- schedule / status
- workflow_call / workflow_dispatch
- Events trigger workflows
- Workflows contain jobs
- Jobs contain steps
- Steps are commands or actions
- Workflows can contain multiple jobs
- Jobs run in parallel by default
- Each job runs on a Runner
- Steps and Shell Commands run in sequence
<style scoped> pre { font-size: 66%; line-height: 1.3; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.15em; padding-bottom: 0; } </style>
steps:
- name: Single line
run: echo "Hello"
- name: Multi-line
run: |
echo "Building..."
npm ci
npm run build
- name: Use a different shell
run: Get-Process
shell: pwsh<style scoped> pre { font-size: 66%; line-height: 1.3; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.2em; padding-bottom: 0; } </style>
jobs:
build:
runs-on: ubuntu-latest
steps: [...]
test:
needs: build # waits for build
runs-on: ubuntu-latest
steps: [...]
deploy:
needs: [build, test]
runs-on: ubuntu-latest- Specify the type of runner with
runs-on(e.g.,ubuntu-latest). - GitHub provisions a new VM for each job.
- Steps in a job share information using the runner's filesystem.
- VM is decommissioned after job completion.
- GitHub-hosted runner application is open source.
- 🪟 Windows · 🐧 Linux · 🍎 macOS
- Runners include preinstalled software, updated weekly.
- There are also Large Hosted Runners
- 🏠 Self-Hosted Runners
- You can install additional software on runners.
<style scoped> th, td { padding: 6px 12px; } </style>
Scoping: workflow → job → step
env:
APP_ENV: production
jobs:
build:
env:
NODE_ENV: test
steps:
- run: echo $APP_ENV
env:
LOG_LEVEL: debugContexts provide runtime info
| Context | Example |
|---|---|
github.* |
github.sha, github.ref |
env.* |
env.APP_ENV |
secrets.* |
secrets.API_KEY |
runner.* |
runner.os |
matrix.* |
matrix.node-version |
<style scoped> h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.3em; padding-bottom: 0; } </style>
on:
push:
paths:
- 'src/**'
- 'package.json'
paths-ignore:
- 'docs/**'
- '*.md'Only triggers when relevant files change
<style scoped> pre { font-size: 68%; line-height: 1.35; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.2em; padding-bottom: 0; } table { font-size: 78%; } th, td { padding: 6px 12px; } pre code span.hljs-template-variable { color: #ffa657; } </style>
steps:
- name: Greet
run: echo "SHA is ${{ github.sha }}"
- name: Only on main
if: github.ref == 'refs/heads/main'
run: echo "Deploying..."
- name: Skip on fork
if: github.event.pull_request.head
.repo.fork == false
run: npm run deploy<style scoped> pre { font-size: 68%; line-height: 1.3; } p { font-size: 88%; margin: 0.2em 0; } h1 { margin-bottom: 0.2em; } </style>
Test across multiple configurations simultaneously
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test🎯 Creates 9 parallel jobs (3 OS × 3 versions)
<style scoped> h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.3em; padding-bottom: 0; } </style>
steps:
- name: Deploy
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: ./deploy.shecho secrets in logs
<style scoped> pre { font-size: 72%; line-height: 1.1; margin: 0; } h1 { font-size: 1.6em; margin: 0 0 0.2em; padding-bottom: 0.15em; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin: 0 0 0.2em; padding-bottom: 0; } p { font-size: 85%; margin: 0.2em 0; } </style>
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- run: dotnet restore
- run: dotnet build -c Release
- run: dotnet test -c Release
- run: dotnet publish -c Release
-o ./webapp
- uses: actions/upload-artifact@v4
with:
name: webapp
path: ./webapp<style scoped> pre { font-size: 72%; line-height: 1.1; margin: 0; } h1 { font-size: 1.6em; margin: 0 0 0.2em; padding-bottom: 0.15em; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin: 0 0 0.2em; padding-bottom: 0; } p { font-size: 85%; margin: 0.2em 0; } </style>
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.azurewebsites.net
steps:
- uses: azure/login@v2
with:
client-id: ${{ secrets.
AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.
AZURE_TENANT_ID }}
- uses: actions/download-artifact@v4
with:
name: webapp
- uses: azure/webapps-deploy@v3
with:
app-name: myapp📂 Demo: 10-dotnet.yml
<style scoped> pre { font-size: 68%; line-height: 1.3; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.2em; padding-bottom: 0; } p { font-size: 88%; margin: 0.2em 0; } </style>
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
# In another job:
- uses: actions/download-artifact@v4
with:
name: build-outputSanjulaGanepola/github-local-actions
<style scoped> pre { font-size: 54%; line-height: 1.15; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.1em; padding-bottom: 0; } p { font-size: 78%; margin: 0.1em 0; } h1 { font-size: 1.6em; margin-bottom: 0.1em; } </style>
# .github/workflows/ci.yml
on:
workflow_call:
inputs:
environment:
type: string
required: trueCalled from another workflow:
jobs:
deploy:
uses: ./.github/workflows/ci.yml
with:
environment: staging<style scoped> li { font-size: 80%; margin: 0.15em 0; line-height: 1.3; } th, td { font-size: 75%; padding: 6px 10px; } h2 { font-size: 1.5em; margin-bottom: 0.3em; } </style>
| 🧩 Composite Action | 🔄 Reusable Workflow | |
|---|---|---|
| Scope | Runs as a step inside a job | Runs as an entire job (or jobs) |
| Location | action.yml in any directory |
Must be in .github/workflows/ |
| Sharing | Can publish to Marketplace | Shared via workflow_call trigger |
| Secrets | Inherits caller's context | Must be passed explicitly (or inherit) |
| Runners | Uses the caller's runner | Can specify its own runner |
| Outputs | Step-level outputs | Job-level outputs |
✅ Use Composite Actions for:
- Bundling related steps (setup, lint)
- Reusable across many workflows
- Publishing to the Marketplace
✅ Use Reusable Workflows for:
- Complete CI/CD pipelines
- Multi-job orchestration
- Enforcing org-wide standards
<style scoped> pre { font-size: 64%; line-height: 1.25; } h2 { color: var(--gh-fg-muted); font-size: 1.25em; margin-bottom: 0.15em; padding-bottom: 0; } li { font-size: 88%; margin: 0.1em 0; } p { font-size: 85%; margin: 0.15em 0; } </style>
- Protection rules — require approvals
- Wait timers — delay before deploy
- Branch restrictions — only
main→ prod - Environment secrets — scoped per env
jobs:
deploy-prod:
environment:
name: production
url: https://myapp.com
runs-on: ubuntu-latest- Never use structured data as a secret
- Register all secrets used within workflows
- Audit how secrets are handled
- Use credentials that are minimally scoped
- Audit and rotate registered secrets
- Consider requiring review for access to secrets
- Use an action instead of an inline script (recommended)
- Use an intermediate environment variable
- Use OpenID Connect to access cloud resources
- Pin third-party actions to a full length commit SHA
- Actions are regularly updated for enhanced automation.
- Dependabot keeps GitHub Actions references in workflow.yml up-to-date.
- If newer action versions exist, Dependabot sends an update pull request.
- Dependabot also updates git references for reusable workflows.
.github/dependabot.yml
version: 2
updates:
# See documentation for possible values
- package-ecosystem: "github-actions"
# Location of package manifests
directory: "/"
schedule:
interval: "weekly"- GitHub Actions triggers, creating a runner.
- Runner service deploys the runner's NIC into your Azure VNET.
- The runner agent picks up the workflow job.
- Runner sends logs back; NIC accesses private resources.
- Community-driven guide for deploying GitHub effectively.
- Design principles
- Framework pillars
- Actionable, prescriptive advice
- 🔐 Security
- 📈 Scalability
- ⚙️ Automation
- 🤝 Collaboration
- 👁️ Observability
- 🚀 Performance
- 🏛️ Governance
- 💡 Innovation
- AI-powered GitHub Actions
- Write automation in markdown
- Agents understand context, make decisions, and act
- Compiled to hardened
.lock.ymlfiles
<style scoped> pre { font-size: 60%; line-height: 1.25; } pre code span.hljs-section { color: #79c0ff; } h3 { margin-bottom: 0.1em; } </style>
---
on:
issues:
types: [opened]
permissions: read-all
tools:
github:
toolsets: [issues, labels]
safe-outputs:
add-comment:
add-labels:
allowed: [bug, feature, question]
---# Issue Triage Agent
Analyze new issues and categorize
them with the appropriate label.
Skip issues that:
- Already have labels
- Have been assigned to a user
After adding a label, comment
mentioning the author with your
reasoning.📝 The AI agent reads and executes these instructions at runtime
<style scoped> li { font-size: 80%; margin: 0.1em 0; line-height: 1.3; } h3 { margin-bottom: 0.1em; font-size: 1.1em; } h2 { margin-bottom: 0.2em; } </style>
- Read-only by default
- Role-based access (
roles:) strict: trueenforced
- Validated write operations
- Scoped to specific actions
What it does:
- Lists unlabeled issues
- Analyzes title & body with AI
- Adds appropriate labels
- Comments with reasoning
Triggers:
- Schedule (weekday afternoons)
- Manual dispatch
Safe outputs:
add-labels(scoped allowlist)add-comment
Security:
strict: truelockdown: true- Role-based access
<style scoped> footer { color: var(--gh-fg-default); background: var(--gh-bg-default); padding: 0.2em 0.5em; border-radius: 4px; } </style>
BlueSky: @chris-ayers.com
LinkedIn: - chris-l-ayers
Blog: https://chris-ayers.com/
GitHub: Codebytes
Mastodon: @Chrisayers@hachyderm.io
Twitter: @Chris_L_Ayers











