{"id":1187,"date":"2026-09-26T04:35:27","date_gmt":"2026-09-26T04:35:27","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=1187"},"modified":"2026-09-26T04:35:31","modified_gmt":"2026-09-26T04:35:31","slug":"github-actions-complete-tutorial-production-handbook","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/github-actions-complete-tutorial-production-handbook\/","title":{"rendered":"GitHub Actions \u2014 Complete Tutorial &#038; Production Handbook"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Audience:<\/strong>&nbsp;Developers, DevOps engineers, platform engineers, SREs, security engineers, technical leads, and architects<br><strong>Level:<\/strong>&nbsp;Beginner \u2192 Intermediate \u2192 Advanced \u2192 Enterprise<br><strong>Last verified:<\/strong>&nbsp;2026-09-26<br><strong>Primary source:<\/strong>&nbsp;GitHub Actions official documentation, plus the supplied 62-section topic map<br><strong>Goal:<\/strong>&nbsp;Teach GitHub Actions as a working CI\/CD and automation platform\u2014not merely as YAML syntax.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Main Curriculum \u2014 Table of Contents<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-right\" data-align=\"right\">#<\/th><th class=\"has-text-align-left\" data-align=\"left\">Chapter<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-right\" data-align=\"right\">1<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#1-basic-github-actions-foundations\">BASIC \u2014 GitHub Actions Foundations<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">2<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#2-fundamentals-workflow-syntax-and-execution\">FUNDAMENTALS \u2014 Workflow Syntax and Execution<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">3<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#3-fundamentals-events-and-workflow-triggers\">FUNDAMENTALS \u2014 Events and Workflow Triggers<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">4<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#4-fundamentals-jobs\">FUNDAMENTALS \u2014 Jobs<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">5<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#5-fundamentals-steps\">FUNDAMENTALS \u2014 Steps<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">6<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#6-essentials-runners\">ESSENTIALS \u2014 Runners<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">7<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#7-essentials-variables-and-environment-variables\">ESSENTIALS \u2014 Variables and Environment Variables<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">8<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#8-essentials-contexts\">ESSENTIALS \u2014 Contexts<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">9<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#9-essentials-expressions\">ESSENTIALS \u2014 Expressions<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">10<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#10-essentials-secrets\">ESSENTIALS \u2014 Secrets<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">11<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#11-essentials-githubtoken\">ESSENTIALS \u2014&nbsp;<code>GITHUB_TOKEN<\/code><\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">12<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#12-essentials-workflow-commands\">ESSENTIALS \u2014 Workflow Commands<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">13<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#13-essentials-matrix-builds\">ESSENTIALS \u2014 Matrix Builds<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">14<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#14-essentials-caching\">ESSENTIALS \u2014 Caching<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">15<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#15-essentials-artifacts\">ESSENTIALS \u2014 Artifacts<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">16<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#16-essentials-concurrency\">ESSENTIALS \u2014 Concurrency<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">17<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#17-essentials-containers\">ESSENTIALS \u2014 Containers<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">18<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#18-essentials-using-actions\">ESSENTIALS \u2014 Using Actions<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">19<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#19-essentials-reusable-automation\">ESSENTIALS \u2014 Reusable Automation<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">20<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#20-essentials-ci-pipelines\">ESSENTIALS \u2014 CI Pipelines<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">21<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#21-essentials-cd-pipelines\">ESSENTIALS \u2014 CD Pipelines<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">22<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#22-essentials-environments-and-deployments\">ESSENTIALS \u2014 Environments and Deployments<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">23<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#23-advanced-custom-actions\">ADVANCED \u2014 Custom Actions<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">24<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#24-advanced-workflow-data-flow\">ADVANCED \u2014 Workflow Data Flow<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">25<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#25-advanced-conditional-workflows\">ADVANCED \u2014 Conditional Workflows<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">26<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#26-advanced-error-handling\">ADVANCED \u2014 Error Handling<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">27<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#27-advanced-troubleshooting-and-debugging\">ADVANCED \u2014 Troubleshooting and Debugging<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">28<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#28-advanced-security-hardening\">ADVANCED \u2014 Security Hardening<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">29<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#29-advanced-openid-connect\">ADVANCED \u2014 OpenID Connect<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">30<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#30-advanced-artifact-attestations\">ADVANCED \u2014 Artifact Attestations<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">31<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#31-advanced-docker-and-container-cicd\">ADVANCED \u2014 Docker and Container CI\/CD<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">32<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#32-advanced-package-publishing\">ADVANCED \u2014 Package Publishing<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">33<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#33-advanced-releases\">ADVANCED \u2014 Releases<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">34<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#34-advanced-cloud-deployments\">ADVANCED \u2014 Cloud Deployments<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">35<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#35-advanced-kubernetes-cicd\">ADVANCED \u2014 Kubernetes CI\/CD<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">36<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#36-advanced-infrastructure-as-code\">ADVANCED \u2014 Infrastructure as Code<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">37<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#37-advanced-self-hosted-runner-architecture\">ADVANCED \u2014 Self-Hosted Runner Architecture<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">38<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#38-advanced-actions-runner-controller-arc\">ADVANCED \u2014 Actions Runner Controller (ARC)<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">39<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#39-advanced-workflow-performance\">ADVANCED \u2014 Workflow Performance<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">40<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#40-advanced-cost-optimization\">ADVANCED \u2014 Cost Optimization<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">41<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#41-advanced-monorepo-workflows\">ADVANCED \u2014 Monorepo Workflows<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">42<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#42-advanced-multi-repository-workflows\">ADVANCED \u2014 Multi-Repository Workflows<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">43<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#43-advanced-workflow-chaining\">ADVANCED \u2014 Workflow Chaining<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">44<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#44-advanced-github-api-integration\">ADVANCED \u2014 GitHub API Integration<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">45<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#45-advanced-repository-governance\">ADVANCED \u2014 Repository Governance<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">46<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#46-advanced-organization-governance\">ADVANCED \u2014 Organization Governance<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">47<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#47-advanced-enterprise-governance\">ADVANCED \u2014 Enterprise Governance<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">48<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#48-advanced-workflow-execution-policies\">ADVANCED \u2014 Workflow Execution Policies<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">49<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#49-advanced-metrics-and-observability\">ADVANCED \u2014 Metrics and Observability<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">50<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#50-advanced-workflow-notifications\">ADVANCED \u2014 Workflow Notifications<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">51<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#51-advanced-workflow-cancellation\">ADVANCED \u2014 Workflow Cancellation<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">52<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#52-advanced-pull-request-ci-patterns\">ADVANCED \u2014 Pull Request CI Patterns<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">53<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#53-advanced-branch-and-release-strategies\">ADVANCED \u2014 Branch and Release Strategies<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">54<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#54-advanced-deployment-strategies\">ADVANCED \u2014 Deployment Strategies<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">55<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#55-advanced-testing-github-actions\">ADVANCED \u2014 Testing GitHub Actions<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">56<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#56-advanced-workflow-maintenance\">ADVANCED \u2014 Workflow Maintenance<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">57<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#57-advanced-github-actions-limits\">ADVANCED \u2014 GitHub Actions Limits<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">58<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#58-advanced-github-actions-importer\">ADVANCED \u2014 GitHub Actions Importer<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">59<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#59-advanced-common-cicd-design-patterns\">ADVANCED \u2014 Common CI\/CD Design Patterns<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">60<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#60-advanced-github-actions-anti-patterns\">ADVANCED \u2014 GitHub Actions Anti-Patterns<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">61<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#61-advanced-production-best-practices\">ADVANCED \u2014 Production Best Practices<\/a><\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">62<\/td><td><a href=\"https:\/\/file+.vscode-resource.vscode-cdn.net\/Users\/rajesh\/Downloads\/GitHub-Actions-Complete-Tutorial-2026.md#62-advanced-reference-and-administration\">ADVANCED \u2014 Reference and Administration<\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Hands-on labs<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Lab A \u2014 Production-quality Node.js CI<\/li>\n\n\n\n<li>Lab B \u2014 Reusable organization CI<\/li>\n\n\n\n<li>Lab C \u2014 Secure AWS OIDC deployment<\/li>\n\n\n\n<li>Lab D \u2014 Container-to-Kubernetes deployment<\/li>\n\n\n\n<li>Lab E \u2014 Terraform plan \/ approval \/ apply<\/li>\n\n\n\n<li>Lab F \u2014 ARC runner fleet for private infrastructure<\/li>\n\n\n\n<li>Lab G \u2014 Systematic workflow debugging<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Suggested learning paths<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Goal<\/th><th class=\"has-text-align-left\" data-align=\"left\">Recommended chapters<\/th><\/tr><\/thead><tbody><tr><td>New to GitHub Actions<\/td><td>1\u201312, then Lab A<\/td><\/tr><tr><td>Application CI\/CD engineer<\/td><td>1\u201322, 25\u201331, 52\u201356<\/td><\/tr><tr><td>DevOps \/ cloud engineer<\/td><td>1\u201322, 28\u201340, Labs C\u2013F<\/td><\/tr><tr><td>Platform engineer<\/td><td>19, 28\u201349, 55\u201362<\/td><\/tr><tr><td>Security engineer<\/td><td>10\u201311, 14, 18, 28\u201330, 45\u201348, 60\u201361<\/td><\/tr><tr><td>Enterprise administrator<\/td><td>37\u201340, 45\u201351, 56\u201362<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">How to use this handbook<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This handbook is deliberately progressive:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Foundations<\/strong>\u00a0\u2014 learn the execution model, YAML, triggers, jobs, steps, runners, variables, contexts, expressions, secrets, and tokens.<\/li>\n\n\n\n<li><strong>Essentials<\/strong>\u00a0\u2014 build production CI\/CD pipelines with matrices, caches, artifacts, environments, reusable workflows, containers, and concurrency.<\/li>\n\n\n\n<li><strong>Advanced engineering<\/strong>\u00a0\u2014 custom actions, security, OIDC, attestations, Docker, Kubernetes, cloud deployments, IaC, ARC, monorepos, cross-repository workflows, APIs, and governance.<\/li>\n\n\n\n<li><strong>Production operations<\/strong>\u00a0\u2014 performance, cost, observability, cancellation, testing, maintenance, limits, migration, anti-patterns, and enterprise administration.<\/li>\n\n\n\n<li><strong>End-to-end labs<\/strong>\u00a0\u2014 reusable examples that can be adapted to real repositories.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Conventions used<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Marker<\/th><th class=\"has-text-align-left\" data-align=\"left\">Meaning<\/th><\/tr><\/thead><tbody><tr><td><strong>Mental model<\/strong><\/td><td>The simplest way to think about a concept<\/td><\/tr><tr><td><strong>Use case<\/strong><\/td><td>A situation where the feature is useful<\/td><\/tr><tr><td><strong>Production note<\/strong><\/td><td>An operational recommendation<\/td><\/tr><tr><td><strong>Security note<\/strong><\/td><td>A security-sensitive behavior<\/td><\/tr><tr><td><strong>Common mistake<\/strong><\/td><td>A frequent source of workflow failures<\/td><\/tr><tr><td><strong>Lab<\/strong><\/td><td>A hands-on exercise<\/td><\/tr><tr><td><strong>2026 note<\/strong><\/td><td>Version-sensitive behavior verified against current docs<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">A note on action versions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Examples use contemporary major versions such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/checkout@v6\n- uses: actions\/setup-node@v7\n- uses: actions\/cache@v4\n- uses: actions\/upload-artifact@v4\n- uses: actions\/download-artifact@v5\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For production, security-sensitive third-party actions should be pinned to a reviewed&nbsp;<strong>full commit SHA<\/strong>. Major tags are easier to read in teaching examples but are mutable references.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Part I \u2014 Foundations<\/h1>\n\n\n\n<h1 class=\"wp-block-heading\">1. BASIC \u2014 GitHub Actions Foundations<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">1.1 What is GitHub Actions?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions is GitHub&#8217;s built-in automation platform. It reacts to events in or around a repository and runs one or more automated tasks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The most familiar use is CI\/CD:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>compile code;<\/li>\n\n\n\n<li>run tests;<\/li>\n\n\n\n<li>lint and format;<\/li>\n\n\n\n<li>build packages or container images;<\/li>\n\n\n\n<li>deploy applications;<\/li>\n\n\n\n<li>publish releases.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">But GitHub Actions is broader than CI\/CD. It can also automate repository administration, issue management, security scanning, scheduled maintenance, release notes, infrastructure changes, or API-driven orchestration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Mental model<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Think of GitHub Actions as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Event -&gt; Workflow -&gt; Jobs -&gt; Steps -&gt; Commands\/Actions -&gt; Result\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A repository event occurs. GitHub finds matching workflow files. Each workflow creates a&nbsp;<strong>workflow run<\/strong>. Jobs are scheduled to runners. Each runner executes steps.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;GitHub or external event] --&gt; B&#91;Workflow trigger]\n    B --&gt; C&#91;Workflow run]\n    C --&gt; D1&#91;Job: lint]\n    C --&gt; D2&#91;Job: test]\n    D1 --&gt; E1&#91;Runner]\n    D2 --&gt; E2&#91;Runner]\n    E1 --&gt; F1&#91;Steps]\n    E2 --&gt; F2&#91;Steps]\n    F1 --&gt; G&#91;Conclusion]\n    F2 --&gt; G\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">1.2 CI, Continuous Delivery, and Continuous Deployment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">These terms are related but not identical.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Practice<\/th><th class=\"has-text-align-left\" data-align=\"left\">Main question<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical GitHub Actions behavior<\/th><\/tr><\/thead><tbody><tr><td>Continuous Integration<\/td><td>&#8220;Is this change safe to merge?&#8221;<\/td><td>Build, lint, unit test, integration test<\/td><\/tr><tr><td>Continuous Delivery<\/td><td>&#8220;Is a release ready to deploy?&#8221;<\/td><td>Build, package, publish, stage, wait for approval<\/td><\/tr><tr><td>Continuous Deployment<\/td><td>&#8220;Can every validated change automatically reach production?&#8221;<\/td><td>CI + automatic production deployment<\/td><\/tr><tr><td>Repository automation<\/td><td>&#8220;Can repetitive GitHub work be automated?&#8221;<\/td><td>Labels, issues, PR comments, releases, housekeeping<\/td><\/tr><tr><td>DevOps automation<\/td><td>&#8220;Can code and operations be connected?&#8221;<\/td><td>IaC, cloud deployment, security scans, notifications<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Example: smallest useful CI workflow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.github\/workflows\/ci.yml\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>name: CI\n\non:\n  push:\n  pull_request:\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repository\n        uses: actions\/checkout@v6\n\n      - name: Run a command\n        run: echo \"CI is running\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That file contains the core of GitHub Actions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>on<\/code>\u00a0\u2014 when to run;<\/li>\n\n\n\n<li><code>jobs<\/code>\u00a0\u2014 what units of work exist;<\/li>\n\n\n\n<li><code>runs-on<\/code>\u00a0\u2014 where a job runs;<\/li>\n\n\n\n<li><code>steps<\/code>\u00a0\u2014 ordered tasks inside a job;<\/li>\n\n\n\n<li><code>uses<\/code>\u00a0\u2014 invoke an action;<\/li>\n\n\n\n<li><code>run<\/code>\u00a0\u2014 execute a shell command.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1.3 Core terminology<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Term<\/th><th class=\"has-text-align-left\" data-align=\"left\">Meaning<\/th><\/tr><\/thead><tbody><tr><td>Workflow<\/td><td>YAML-defined automation stored in&nbsp;<code>.github\/workflows<\/code><\/td><\/tr><tr><td>Workflow run<\/td><td>One execution instance of a workflow<\/td><\/tr><tr><td>Event<\/td><td>Activity that can trigger a workflow<\/td><\/tr><tr><td>Trigger<\/td><td>Workflow configuration matching an event<\/td><\/tr><tr><td>Job<\/td><td>Independently scheduled unit of work<\/td><\/tr><tr><td>Step<\/td><td>Ordered task inside a job<\/td><\/tr><tr><td>Action<\/td><td>Reusable executable automation component<\/td><\/tr><tr><td>Runner<\/td><td>Machine executing a job<\/td><\/tr><tr><td>Command<\/td><td>Shell statement executed by a&nbsp;<code>run<\/code>&nbsp;step<\/td><\/tr><tr><td>Artifact<\/td><td>File retained from a workflow<\/td><\/tr><tr><td>Cache<\/td><td>Reusable dependency\/build data intended to speed later runs<\/td><\/tr><tr><td>Context<\/td><td>Structured runtime information, such as&nbsp;<code>github<\/code>&nbsp;or&nbsp;<code>matrix<\/code><\/td><\/tr><tr><td>Expression<\/td><td><code>${{ ... }}<\/code>&nbsp;logic evaluated by GitHub<\/td><\/tr><tr><td>Secret<\/td><td>Protected sensitive value<\/td><\/tr><tr><td>Variable<\/td><td>Non-sensitive configuration value<\/td><\/tr><tr><td>Environment<\/td><td>Named deployment target with variables, secrets, and protections<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">1.4 Architecture<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions has two broad planes:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Control plane<\/strong>\u00a0\u2014 GitHub receives events, evaluates workflows, creates runs, schedules jobs, manages permissions, logs, artifacts, caches, and API objects.<\/li>\n\n\n\n<li><strong>Execution plane<\/strong>\u00a0\u2014 runners execute jobs.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    subgraph GitHub&#91;\"GitHub control plane\"]\n        R&#91;Repository]\n        E&#91;Event]\n        W&#91;Workflow parser]\n        S&#91;Scheduler]\n        T&#91;Token \/ permissions]\n        L&#91;Logs, cache, artifacts]\n        R --&gt; E --&gt; W --&gt; S\n        T --&gt; S\n        S --&gt; L\n    end\n\n    subgraph Exec&#91;\"Execution plane\"]\n        H1&#91;GitHub-hosted runner]\n        H2&#91;Larger runner]\n        H3&#91;Self-hosted runner]\n        H4&#91;ARC ephemeral runner pod]\n    end\n\n    S --&gt; H1\n    S --&gt; H2\n    S --&gt; H3\n    S --&gt; H4\n    H1 --&gt; L\n    H2 --&gt; L\n    H3 --&gt; L\n    H4 --&gt; L\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">1.5 Workflow lifecycle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A simplified lifecycle is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Event occurs] --&gt; B{Workflow trigger matches?}\n    B -- No --&gt; Z&#91;Nothing runs]\n    B -- Yes --&gt; C&#91;Create workflow run]\n    C --&gt; D&#91;Evaluate workflow-level expressions]\n    D --&gt; E&#91;Create eligible jobs]\n    E --&gt; F{Job dependencies satisfied?}\n    F -- No --&gt; G&#91;Wait \/ skip]\n    F -- Yes --&gt; H&#91;Queue job]\n    H --&gt; I&#91;Assign compatible runner]\n    I --&gt; J&#91;Prepare job]\n    J --&gt; K&#91;Execute steps]\n    K --&gt; L&#91;Post-job cleanup]\n    L --&gt; M&#91;Upload logs\/results]\n    M --&gt; N&#91;Calculate job conclusion]\n    N --&gt; O&#91;Calculate workflow conclusion]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A workflow can be:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>queued;<\/li>\n\n\n\n<li>in progress;<\/li>\n\n\n\n<li>waiting for an environment approval;<\/li>\n\n\n\n<li>completed;<\/li>\n\n\n\n<li>cancelled.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Jobs and steps have their own statuses and conclusions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1.6 Workflow files<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Workflow files must be placed under:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.github\/workflows\/\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Both extensions are valid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ci.yml\nrelease.yaml\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A repository can contain many workflows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.github\/\n\u2514\u2500\u2500 workflows\/\n    \u251c\u2500\u2500 ci.yml\n    \u251c\u2500\u2500 security.yml\n    \u251c\u2500\u2500 release.yml\n    \u251c\u2500\u2500 deploy-staging.yml\n    \u2514\u2500\u2500 deploy-production.yml\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Naming recommendation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use a filename for humans and a&nbsp;<code>name<\/code>&nbsp;for the UI:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Pull Request CI\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Good filenames:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pull-request-ci.yml\nrelease.yml\ndeploy-production.yml\nterraform-plan.yml\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>workflow1.yml\nnew.yml\ntest2.yml\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">1.7 Basic workflow structure<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Application CI\n\nrun-name: &gt;-\n  CI for ${{ github.ref_name }} by @${{ github.actor }}\n\non:\n  push:\n    branches: &#91;main]\n  pull_request:\n\npermissions:\n  contents: read\n\nenv:\n  NODE_ENV: test\n\ndefaults:\n  run:\n    shell: bash\n\nconcurrency:\n  group: ci-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    name: Test\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n\n    steps:\n      - name: Checkout\n        uses: actions\/checkout@v6\n\n      - name: Setup Node\n        uses: actions\/setup-node@v7\n        with:\n          node-version: \"24\"\n\n      - name: Install\n        run: npm ci\n\n      - name: Test\n        run: npm test\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Key hierarchy<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Workflow\n\u251c\u2500\u2500 metadata\/configuration\n\u2502   \u251c\u2500\u2500 name\n\u2502   \u251c\u2500\u2500 run-name\n\u2502   \u251c\u2500\u2500 on\n\u2502   \u251c\u2500\u2500 permissions\n\u2502   \u251c\u2500\u2500 env\n\u2502   \u251c\u2500\u2500 defaults\n\u2502   \u2514\u2500\u2500 concurrency\n\u2514\u2500\u2500 jobs\n    \u2514\u2500\u2500 job_id\n        \u251c\u2500\u2500 runs-on\n        \u251c\u2500\u2500 permissions\n        \u251c\u2500\u2500 needs\n        \u251c\u2500\u2500 if\n        \u251c\u2500\u2500 environment\n        \u251c\u2500\u2500 strategy\n        \u2514\u2500\u2500 steps\n            \u251c\u2500\u2500 uses\n            \u2514\u2500\u2500 run\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. FUNDAMENTALS \u2014 Workflow Syntax and Execution<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">2.1 YAML fundamentals for GitHub Actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">YAML is whitespace-sensitive. Indentation represents structure.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Mappings<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>name: CI\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>name<\/code>&nbsp;is a key and&nbsp;<code>CI<\/code>&nbsp;is its value.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Nested mappings<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Sequences<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>branches:\n  - main\n  - release\/**\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Short form:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>branches: &#91;main, release\/**]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Multiline strings<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Literal style preserves line breaks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>run: |\n  echo \"first\"\n  echo \"second\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Folded style joins lines where possible:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>run-name: &gt;-\n  Deploy ${{ github.ref_name }}\n  by ${{ github.actor }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Strings and quoting<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When ambiguity is possible, quote values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node-version: \"24\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Quoting is especially useful for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>version numbers;<\/li>\n\n\n\n<li>strings containing\u00a0<code>:<\/code>,\u00a0<code>#<\/code>,\u00a0<code>{<\/code>,\u00a0<code>}<\/code>;<\/li>\n\n\n\n<li>values that YAML might interpret as another type.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Comments<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><em># Run only on main<\/em>\non:\n  push:\n    branches: &#91;main]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Common YAML mistake: indentation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Wrong:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\nbuild:\n  runs-on: ubuntu-latest\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Correct:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  build:\n    runs-on: ubuntu-latest\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.2 YAML anchors and aliases<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Modern GitHub Actions supports YAML anchors for reuse.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>env: &amp;common-env\n  NODE_ENV: test\n  CI: \"true\"\n\njobs:\n  unit:\n    runs-on: ubuntu-latest\n    env: *common-env\n    steps:\n      - run: npm test\n\n  integration:\n    runs-on: ubuntu-latest\n    env: *common-env\n    steps:\n      - run: npm run test:integration\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use anchors for small static YAML reuse. Use&nbsp;<strong>reusable workflows<\/strong>&nbsp;or&nbsp;<strong>composite actions<\/strong>&nbsp;for meaningful automation reuse.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Reuse tool<\/th><th class=\"has-text-align-left\" data-align=\"left\">Best for<\/th><\/tr><\/thead><tbody><tr><td>YAML anchor<\/td><td>Repeating small mappings\/lists<\/td><\/tr><tr><td>Composite action<\/td><td>Repeating a sequence of steps<\/td><\/tr><tr><td>Reusable workflow<\/td><td>Repeating one or more complete jobs<\/td><\/tr><tr><td>Workflow template<\/td><td>Standard starting point for many repositories<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">2.3 Workflow-level syntax<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Important top-level keys:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Key<\/th><th class=\"has-text-align-left\" data-align=\"left\">Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>name<\/code><\/td><td>Workflow display name<\/td><\/tr><tr><td><code>run-name<\/code><\/td><td>Dynamic run display name<\/td><\/tr><tr><td><code>on<\/code><\/td><td>Triggers<\/td><\/tr><tr><td><code>permissions<\/code><\/td><td>Default&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;permissions<\/td><\/tr><tr><td><code>env<\/code><\/td><td>Workflow-wide environment variables<\/td><\/tr><tr><td><code>defaults<\/code><\/td><td>Default shell or working directory<\/td><\/tr><tr><td><code>concurrency<\/code><\/td><td>Serialize\/cancel related runs<\/td><\/tr><tr><td><code>cache-mode<\/code><\/td><td>Control workflow cache read\/write access<\/td><\/tr><tr><td><code>jobs<\/code><\/td><td>Job definitions<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><code>defaults.run<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>defaults:\n  run:\n    shell: bash\n    working-directory: .\/app\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This applies to&nbsp;<code>run<\/code>&nbsp;steps unless overridden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2.4 Job-level syntax<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A job is identified by its map key:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  unit_tests:\n    name: Unit Tests\n    runs-on: ubuntu-latest\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Useful job-level keys:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  deploy:\n    name: Deploy\n    needs: build\n    if: github.ref == 'refs\/heads\/main'\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n      id-token: write\n\n    environment:\n      name: production\n      url: https:\/\/example.com\n\n    timeout-minutes: 30\n\n    concurrency:\n      group: production\n      queue: max\n\n    env:\n      APP_NAME: demo\n\n    steps:\n      - run: .\/deploy.sh\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.5 Step-level syntax<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A step either invokes an action or runs a command.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Action step:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Checkout\n  uses: actions\/checkout@v6\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Shell step:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Test\n  run: npm test\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Configured action:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Setup Node\n  uses: actions\/setup-node@v7\n  with:\n    node-version: \"24\"\n    cache: npm\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Conditional step:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Publish\n  if: github.ref == 'refs\/heads\/main'\n  run: npm publish\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Step-specific environment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Integration tests\n  env:\n    API_URL: https:\/\/staging.example.com\n  run: npm run test:integration\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.6 Execution boundaries matter<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each job normally gets its own runner. Therefore:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>files are\u00a0<strong>not automatically shared between jobs<\/strong>;<\/li>\n\n\n\n<li>environment variables are\u00a0<strong>not automatically shared between jobs<\/strong>;<\/li>\n\n\n\n<li>processes are\u00a0<strong>not shared between jobs<\/strong>;<\/li>\n\n\n\n<li>use outputs, artifacts, external stores, or reusable workflow outputs to pass data.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Inside a job, steps share the same filesystem, but each&nbsp;<code>run<\/code>&nbsp;step starts its own process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This does&nbsp;<strong>not<\/strong>&nbsp;persist:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: export VERSION=1.2.3\n- run: echo \"$VERSION\"   <em># empty<\/em>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This does persist to later steps:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: echo \"VERSION=1.2.3\" &gt;&gt; \"$GITHUB_ENV\"\n- run: echo \"$VERSION\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.7 2026 syntax: background and parallel steps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions now supports asynchronous\/background steps and&nbsp;<code>parallel<\/code>&nbsp;step groups.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Background step<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>steps:\n  - name: Start server\n    id: server\n    run: npm start\n    background: true\n\n  - name: Run tests\n    run: npm test\n\n  - name: Stop server\n    cancel: server\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Parallel group<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>steps:\n  - uses: actions\/checkout@v6\n\n  - parallel:\n      - name: Build frontend\n        run: npm run build:frontend\n\n      - name: Build backend\n        run: npm run build:backend\n\n      - name: Build docs\n        run: npm run build:docs\n\n  - name: Test combined result\n    run: npm test\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use job-level parallelism for strong isolation and independent runners. Use step-level parallelism only when the tasks belong in the same job and can safely share the same runner.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. FUNDAMENTALS \u2014 Events and Workflow Triggers<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">3.1 Trigger model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>on<\/code>&nbsp;tells GitHub when a workflow is eligible to run.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on: push\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Multiple triggers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n  pull_request:\n  workflow_dispatch:\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Filtered trigger:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    branches:\n      - main\n      - \"release\/**\"\n    paths:\n      - \"src\/**\"\n      - \"!src\/docs\/**\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.2 Event categories<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Category<\/th><th class=\"has-text-align-left\" data-align=\"left\">Examples<\/th><th class=\"has-text-align-left\" data-align=\"left\">Use cases<\/th><\/tr><\/thead><tbody><tr><td>Code activity<\/td><td><code>push<\/code>,&nbsp;<code>pull_request<\/code><\/td><td>CI<\/td><\/tr><tr><td>Review activity<\/td><td><code>pull_request_review<\/code>,&nbsp;<code>issue_comment<\/code><\/td><td>approval\/automation<\/td><\/tr><tr><td>Release activity<\/td><td><code>release<\/code>,&nbsp;<code>registry_package<\/code><\/td><td>publishing<\/td><\/tr><tr><td>Repository lifecycle<\/td><td><code>create<\/code>,&nbsp;<code>delete<\/code>,&nbsp;<code>fork<\/code>,&nbsp;<code>public<\/code><\/td><td>governance<\/td><\/tr><tr><td>Deployment<\/td><td><code>deployment<\/code>,&nbsp;<code>deployment_status<\/code><\/td><td>CD integration<\/td><\/tr><tr><td>Manual<\/td><td><code>workflow_dispatch<\/code><\/td><td>operator-triggered jobs<\/td><\/tr><tr><td>Schedule<\/td><td><code>schedule<\/code><\/td><td>nightly scans, maintenance<\/td><\/tr><tr><td>External<\/td><td><code>repository_dispatch<\/code><\/td><td>API-driven automation<\/td><\/tr><tr><td>Reuse<\/td><td><code>workflow_call<\/code><\/td><td>reusable workflows<\/td><\/tr><tr><td>Chaining<\/td><td><code>workflow_run<\/code><\/td><td>privileged follow-up workflows<\/td><\/tr><tr><td>Merge queue<\/td><td><code>merge_group<\/code><\/td><td>required checks for merge queues<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">3.3 Important events<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>push<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    branches: &#91;main]\n    tags:\n      - \"v*\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>branch CI;<\/li>\n\n\n\n<li>main-branch builds;<\/li>\n\n\n\n<li>tag-based releases.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><code>pull_request<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  pull_request:\n    types: &#91;opened, synchronize, reopened, ready_for_review]\n    branches: &#91;main]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use for untrusted PR validation. Fork PRs receive restricted tokens and normally do not receive repository secrets.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>pull_request_target<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This event runs in the security context of the base repository.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use it for metadata operations such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>adding labels;<\/li>\n\n\n\n<li>commenting;<\/li>\n\n\n\n<li>assigning reviewers;<\/li>\n\n\n\n<li>triage.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Do&nbsp;<strong>not<\/strong>&nbsp;use it to execute untrusted PR code with privileged credentials.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>workflow_dispatch<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  workflow_dispatch:\n    inputs:\n      environment:\n        description: Deployment environment\n        type: environment\n        required: true\n\n      version:\n        description: Version to deploy\n        type: string\n        required: true\n\n      dry_run:\n        description: Dry run\n        type: boolean\n        default: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Reference inputs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ inputs.environment }}\n${{ inputs.version }}\n${{ inputs.dry_run }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>schedule<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  schedule:\n    - cron: \"15 2 * * 1-5\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Scheduled workflows run from the default branch. Treat cron as UTC unless the current GitHub feature\/documentation for your account explicitly supports otherwise.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Common cron examples:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Schedule<\/th><th class=\"has-text-align-left\" data-align=\"left\">Cron<\/th><\/tr><\/thead><tbody><tr><td>Every hour<\/td><td><code>0 * * * *<\/code><\/td><\/tr><tr><td>Daily 02:00<\/td><td><code>0 2 * * *<\/code><\/td><\/tr><tr><td>Weekdays 09:00<\/td><td><code>0 9 * * 1-5<\/code><\/td><\/tr><tr><td>Sundays midnight<\/td><td><code>0 0 * * 0<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><code>repository_dispatch<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use when another system should trigger a repository workflow.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  repository_dispatch:\n    types: &#91;deploy-request]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Payload:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"event_type\": \"deploy-request\",\n  \"client_payload\": {\n    \"environment\": \"staging\",\n    \"version\": \"1.4.7\"\n  }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ github.event.client_payload.environment }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>workflow_run<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Useful for privilege separation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  workflow_run:\n    workflows: &#91;\"PR CI\"]\n    types: &#91;completed]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A common architecture is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Untrusted PR] --&gt; B&#91;Low-privilege CI workflow]\n    B --&gt; C&#91;Tests + artifact]\n    C --&gt; D&#91;workflow_run]\n    D --&gt; E&#91;Privileged follow-up workflow]\n    E --&gt; F&#91;Comment \/ publish \/ deploy]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The downstream workflow must still treat artifacts and data produced by the untrusted upstream workflow as untrusted input.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.4 Filters<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Branches<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    branches:\n      - main\n      - \"release\/**\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Branch ignore<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    branches-ignore:\n      - \"docs\/**\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Tags<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    tags:\n      - \"v&#91;0-9]+.&#91;0-9]+.&#91;0-9]+\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Paths<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  pull_request:\n    paths:\n      - \"backend\/**\"\n      - \".github\/workflows\/backend-ci.yml\"\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Negative patterns<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>paths:\n  - \"src\/**\"\n  - \"!src\/docs\/**\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Order matters when positive and negative patterns are combined.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.5 Event payloads<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Useful references:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Show event\n  run: |\n    echo \"name=$GITHUB_EVENT_NAME\"\n    cat \"$GITHUB_EVENT_PATH\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expression equivalent:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ github.event }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not dump entire contexts into production logs without considering sensitive values.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. FUNDAMENTALS \u2014 Jobs<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">4.1 Job architecture<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Jobs run in parallel by default.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo lint\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo test\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    W&#91;Workflow] --&gt; L&#91;Lint job]\n    W --&gt; T&#91;Test job]\n    W --&gt; S&#91;Security job]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.2 Sequential jobs with&nbsp;<code>needs<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo build\n\n  test:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo test\n\n  deploy:\n    needs: test\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo deploy\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    B&#91;Build] --&gt; T&#91;Test] --&gt; D&#91;Deploy]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.3 Fan-out and fan-in<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo build\n\n  unit:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo unit\n\n  integration:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo integration\n\n  security:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo security\n\n  deploy:\n    needs: &#91;unit, integration, security]\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo deploy\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    B&#91;Build] --&gt; U&#91;Unit]\n    B --&gt; I&#91;Integration]\n    B --&gt; S&#91;Security]\n    U --&gt; D&#91;Deploy]\n    I --&gt; D\n    S --&gt; D\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.4 Job conditions<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.ref == 'refs\/heads\/main'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Status functions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: success()\nif: failure()\nif: cancelled()\nif: always()\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cleanup pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cleanup:\n  if: always()\n  needs: &#91;test]\n  runs-on: ubuntu-latest\n  steps:\n    - run: .\/cleanup.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: ${{ !cancelled() }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">when you want downstream processing after success\/failure but do not want expensive work after explicit cancellation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4.5 Job outputs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Step \u2192 job \u2192 dependent job:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  version:\n    runs-on: ubuntu-latest\n\n    outputs:\n      release_version: ${{ steps.v.outputs.value }}\n\n    steps:\n      - id: v\n        run: echo \"value=1.8.0\" &gt;&gt; \"$GITHUB_OUTPUT\"\n\n  deploy:\n    needs: version\n    runs-on: ubuntu-latest\n\n    steps:\n      - run: echo \"Deploying ${{ needs.version.outputs.release_version }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    S&#91;Step output] --&gt; J&#91;Job output]\n    J --&gt; N&#91;needs context]\n    N --&gt; D&#91;Dependent job]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.6 Job conclusions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical conclusions include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>success;<\/li>\n\n\n\n<li>failure;<\/li>\n\n\n\n<li>cancelled;<\/li>\n\n\n\n<li>skipped.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A downstream job normally skips if a required dependency fails unless its&nbsp;<code>if<\/code>&nbsp;expression says otherwise.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. FUNDAMENTALS \u2014 Steps<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">5.1 Step types<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Action step<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/checkout@v6\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Command step<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: npm test\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.2 Step IDs and outputs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Calculate version\n  id: version\n  run: echo \"value=2.1.0\" &gt;&gt; \"$GITHUB_OUTPUT\"\n\n- name: Use version\n  run: echo \"${{ steps.version.outputs.value }}\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.3 Step conditions<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Production-only step\n  if: github.ref == 'refs\/heads\/main'\n  run: .\/publish.sh\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.4 Step environment<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Test\n  env:\n    API_URL: https:\/\/api.example.com\n  run: npm test\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.5 Failure and&nbsp;<code>continue-on-error<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Optional scan\n  continue-on-error: true\n  run: .\/non-blocking-scan.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use this carefully. It changes failure semantics and can hide defects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A better pattern is to make the intent visible:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Experimental compatibility test\n  id: experimental\n  continue-on-error: true\n  run: npm run test:experimental\n\n- name: Report experimental result\n  if: always()\n  run: echo \"Experimental result = ${{ steps.experimental.outcome }}\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.6 Timeouts<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: External integration test\n  timeout-minutes: 10\n  run: npm run test:integration\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Also set job timeouts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    timeout-minutes: 20\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Production workflows should generally avoid the default six-hour job timeout unless the workload truly needs it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5.7 Shells<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Common shells:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shell: bash\nshell: sh\nshell: pwsh\nshell: cmd\nshell: python\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Custom shell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shell: python {0}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Python inline script\n  shell: python\n  run: |\n    import platform\n    print(platform.platform())\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.8 Exit codes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For most shells:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>exit\u00a0<code>0<\/code>\u00a0= success;<\/li>\n\n\n\n<li>non-zero = failure.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Bash strict mode is often useful:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Robust script\n  run: |\n    set -euo pipefail\n    .\/build.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But understand what it means:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Option<\/th><th class=\"has-text-align-left\" data-align=\"left\">Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>-e<\/code><\/td><td>stop on failing command<\/td><\/tr><tr><td><code>-u<\/code><\/td><td>error on undefined variables<\/td><\/tr><tr><td><code>-o pipefail<\/code><\/td><td>pipeline fails if any component fails<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. ESSENTIALS \u2014 Runners<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">6.1 What is a runner?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A runner is the machine or compute environment that executes a job.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions supports:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>GitHub-hosted runners;<\/li>\n\n\n\n<li>larger GitHub-hosted runners;<\/li>\n\n\n\n<li>self-hosted runners;<\/li>\n\n\n\n<li>Kubernetes-based ephemeral runners using Actions Runner Controller.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    J&#91;Queued job] --&gt; M{Matching runner?}\n    M --&gt;|GitHub managed| H&#91;Hosted runner]\n    M --&gt;|Large managed| L&#91;Larger runner]\n    M --&gt;|Customer managed| S&#91;Self-hosted runner]\n    M --&gt;|Kubernetes| A&#91;ARC runner pod]\n    H --&gt; X&#91;Execute job]\n    L --&gt; X\n    S --&gt; X\n    A --&gt; X\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6.2&nbsp;<code>runs-on<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Basic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runs-on: ubuntu-latest\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Specific image:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runs-on: ubuntu-26.04\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Multiple self-hosted labels:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runs-on: &#91;self-hosted, linux, x64, prod]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expression:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runs-on: ${{ matrix.os }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6.3 GitHub-hosted runners<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Advantages:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>fresh VM for each job;<\/li>\n\n\n\n<li>no runner maintenance;<\/li>\n\n\n\n<li>preinstalled tooling;<\/li>\n\n\n\n<li>strong isolation between jobs;<\/li>\n\n\n\n<li>simple scaling.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Trade-offs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ephemeral filesystem;<\/li>\n\n\n\n<li>public-network orientation unless private networking is configured;<\/li>\n\n\n\n<li>image contents change over time;<\/li>\n\n\n\n<li>fixed resource classes for standard runners.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2026 note<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Current GitHub-hosted runner labels include modern images such as&nbsp;<code>ubuntu-26.04<\/code>&nbsp;and newer macOS\/Windows images.&nbsp;<code>*-latest<\/code>&nbsp;means GitHub&#8217;s latest stable supported runner image, not necessarily the newest operating-system version published by the OS vendor.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6.4 Larger runners<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use larger runners when you need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>more CPU\/RAM\/disk;<\/li>\n\n\n\n<li>GPU;<\/li>\n\n\n\n<li>autoscaling managed by GitHub;<\/li>\n\n\n\n<li>runner groups;<\/li>\n\n\n\n<li>static IP ranges;<\/li>\n\n\n\n<li>supported private networking options;<\/li>\n\n\n\n<li>custom images where available.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Decision example:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Requirement<\/th><th class=\"has-text-align-right\" data-align=\"right\">Standard hosted<\/th><th class=\"has-text-align-right\" data-align=\"right\">Larger runner<\/th><th class=\"has-text-align-right\" data-align=\"right\">Self-hosted \/ ARC<\/th><\/tr><\/thead><tbody><tr><td>Zero maintenance<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Low<\/td><\/tr><tr><td>Static egress IP<\/td><td class=\"has-text-align-right\" data-align=\"right\">Poor<\/td><td class=\"has-text-align-right\" data-align=\"right\">Strong<\/td><td class=\"has-text-align-right\" data-align=\"right\">Strong<\/td><\/tr><tr><td>Private network<\/td><td class=\"has-text-align-right\" data-align=\"right\">Limited\/options<\/td><td class=\"has-text-align-right\" data-align=\"right\">Stronger options<\/td><td class=\"has-text-align-right\" data-align=\"right\">Full control<\/td><\/tr><tr><td>Custom base image<\/td><td class=\"has-text-align-right\" data-align=\"right\">Limited<\/td><td class=\"has-text-align-right\" data-align=\"right\">Supported in eligible configs<\/td><td class=\"has-text-align-right\" data-align=\"right\">Full<\/td><\/tr><tr><td>GPU<\/td><td class=\"has-text-align-right\" data-align=\"right\">Specialized\/limited<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Strong internal isolation control<\/td><td class=\"has-text-align-right\" data-align=\"right\">Medium<\/td><td class=\"has-text-align-right\" data-align=\"right\">Medium\/High<\/td><td class=\"has-text-align-right\" data-align=\"right\">Highest control<\/td><\/tr><tr><td>Cost predictability<\/td><td class=\"has-text-align-right\" data-align=\"right\">Simple<\/td><td class=\"has-text-align-right\" data-align=\"right\">Per-minute<\/td><td class=\"has-text-align-right\" data-align=\"right\">Infra + operations<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">6.5 Self-hosted runners<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Self-hosted runners are customer-operated machines.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">They can live on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>bare metal;<\/li>\n\n\n\n<li>VM;<\/li>\n\n\n\n<li>cloud instance;<\/li>\n\n\n\n<li>on-premises server;<\/li>\n\n\n\n<li>container\/ephemeral VM;<\/li>\n\n\n\n<li>Kubernetes via ARC.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Architecture<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    G&#91;GitHub Actions service] &lt;--&gt;|Outbound HTTPS| R&#91;Self-hosted runner]\n    R --&gt; C&#91;Repository checkout]\n    R --&gt; P&#91;Private package registry]\n    R --&gt; D&#91;Database]\n    R --&gt; K&#91;Kubernetes\/API]\n    R --&gt; A&#91;Cloud APIs]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Normally, the runner initiates outbound connections to GitHub. You do not generally expose an inbound runner control port to the internet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6.6 Persistent vs ephemeral<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Persistent runner<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The same host runs multiple jobs over time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Risks:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>leftover files;<\/li>\n\n\n\n<li>credentials not cleaned correctly;<\/li>\n\n\n\n<li>modified toolchains;<\/li>\n\n\n\n<li>poisoned caches;<\/li>\n\n\n\n<li>malicious persistence.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Ephemeral runner<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Created for one job and destroyed afterward.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>clean environment;<\/li>\n\n\n\n<li>less state leakage;<\/li>\n\n\n\n<li>easier trust boundaries;<\/li>\n\n\n\n<li>safer autoscaling architecture.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For sensitive production or untrusted workloads, ephemeral runners are usually preferable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6.7 Runner groups<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Runner groups control which repositories can use groups of runners.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example organizational model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Runner Groups\n\u251c\u2500\u2500 public-ci\n\u251c\u2500\u2500 internal-ci\n\u251c\u2500\u2500 production-deploy\n\u2514\u2500\u2500 privileged-infra\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use groups to enforce trust boundaries. A production deployment runner should not normally be available to every repository.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6.8 Private networking<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical methods include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>self-hosted runner in a VPC\/VNet;<\/li>\n\n\n\n<li>larger runner private networking where supported;<\/li>\n\n\n\n<li>hosted runner private-network connectivity patterns such as supported overlay\/API-gateway approaches;<\/li>\n\n\n\n<li>proxy;<\/li>\n\n\n\n<li>VPN\/overlay;<\/li>\n\n\n\n<li>private endpoints.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Use case: private EKS deployment<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    G&#91;GitHub] --&gt; R&#91;Ephemeral self-hosted runner&lt;br\/&gt;private subnet]\n    R --&gt; E&#91;EKS private API]\n    R --&gt; ECR&#91;ECR]\n    R --&gt; S&#91;Secrets manager]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The runner has network reachability. GitHub itself does not need direct access to the private cluster.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6.9 Runner security checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Prefer ephemeral runners.<\/li>\n\n\n\n<li>Segment runner networks by trust level.<\/li>\n\n\n\n<li>Do not allow untrusted public-repository PRs onto privileged self-hosted runners.<\/li>\n\n\n\n<li>Avoid storing long-lived secrets on runner disks.<\/li>\n\n\n\n<li>Patch runner OS and runner application.<\/li>\n\n\n\n<li>Restrict metadata-service\/cloud-instance credentials.<\/li>\n\n\n\n<li>Restrict outbound network access where practical.<\/li>\n\n\n\n<li>Use short-lived cloud credentials via OIDC.<\/li>\n\n\n\n<li>Monitor runner registration and removal.<\/li>\n\n\n\n<li>Remove stale runners.<\/li>\n\n\n\n<li>Use runner groups and repository allowlists.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. ESSENTIALS \u2014 Variables and Environment Variables<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">7.1 Three major variable mechanisms<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Mechanism<\/th><th class=\"has-text-align-left\" data-align=\"left\">Syntax<\/th><th class=\"has-text-align-right\" data-align=\"right\">Sensitive?<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical use<\/th><\/tr><\/thead><tbody><tr><td>Environment variable<\/td><td><code>env:<\/code>&nbsp;\/&nbsp;<code>$NAME<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td>process configuration<\/td><\/tr><tr><td>Configuration variable<\/td><td><code>${{ vars.NAME }}<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td>reusable repo\/org\/env configuration<\/td><\/tr><tr><td>Secret<\/td><td><code>${{ secrets.NAME }}<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td>tokens, passwords, keys<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">7.2&nbsp;<code>env<\/code>&nbsp;scopes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>env:\n  APP_NAME: catalog\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Job:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  build:\n    env:\n      MODE: ci\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Step:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: echo \"$MODE\"\n  env:\n    MODE: debug\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The more specific scope overrides the broader scope.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7.3 Configuration variables<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>env:\n  API_URL: ${{ vars.API_URL }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Variables can be configured at organization, repository, or environment level.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A lower-level configuration variable generally takes precedence over a higher-level one, but remember that environment-level variables become available after the job starts and do not behave exactly like pre-run&nbsp;<code>env<\/code>&nbsp;resolution in every location.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7.4 Default variables<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Useful defaults include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CI\nGITHUB_ACTION\nGITHUB_ACTION_PATH\nGITHUB_ACTION_REPOSITORY\nGITHUB_ACTIONS\nGITHUB_ACTOR\nGITHUB_ACTOR_ID\nGITHUB_API_URL\nGITHUB_BASE_REF\nGITHUB_ENV\nGITHUB_EVENT_NAME\nGITHUB_EVENT_PATH\nGITHUB_GRAPHQL_URL\nGITHUB_HEAD_REF\nGITHUB_JOB\nGITHUB_OUTPUT\nGITHUB_PATH\nGITHUB_REF\nGITHUB_REF_NAME\nGITHUB_REF_PROTECTED\nGITHUB_REF_TYPE\nGITHUB_REPOSITORY\nGITHUB_REPOSITORY_ID\nGITHUB_REPOSITORY_OWNER\nGITHUB_RETENTION_DAYS\nGITHUB_RUN_ATTEMPT\nGITHUB_RUN_ID\nGITHUB_RUN_NUMBER\nGITHUB_SERVER_URL\nGITHUB_SHA\nGITHUB_STEP_SUMMARY\nGITHUB_TRIGGERING_ACTOR\nGITHUB_WORKFLOW\nGITHUB_WORKFLOW_REF\nGITHUB_WORKFLOW_SHA\nGITHUB_WORKSPACE\nRUNNER_ARCH\nRUNNER_ENVIRONMENT\nRUNNER_NAME\nRUNNER_OS\nRUNNER_TEMP\nRUNNER_TOOL_CACHE\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Example<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Metadata\n  run: |\n    echo \"repository=$GITHUB_REPOSITORY\"\n    echo \"sha=$GITHUB_SHA\"\n    echo \"ref=$GITHUB_REF\"\n    echo \"runner=$RUNNER_OS\/$RUNNER_ARCH\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7.5 Runtime-created environment variables<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Calculate\n  run: echo \"RELEASE_VERSION=2.4.0\" &gt;&gt; \"$GITHUB_ENV\"\n\n- name: Use\n  run: echo \"$RELEASE_VERSION\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The creating step does not receive the newly written value; later steps do.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">8. ESSENTIALS \u2014 Contexts<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">8.1 Contexts vs environment variables<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Contexts are evaluated by GitHub during workflow processing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Environment variables are typically expanded by the shell on the runner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: echo \"${{ github.repository }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub expression.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: echo \"$GITHUB_REPOSITORY\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Shell expansion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8.2 Major contexts<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Context<\/th><th class=\"has-text-align-left\" data-align=\"left\">Contains<\/th><\/tr><\/thead><tbody><tr><td><code>github<\/code><\/td><td>event, repository, ref, SHA, actor, workflow metadata<\/td><\/tr><tr><td><code>env<\/code><\/td><td>workflow\/job\/step&nbsp;<code>env<\/code>&nbsp;values<\/td><\/tr><tr><td><code>vars<\/code><\/td><td>configuration variables<\/td><\/tr><tr><td><code>job<\/code><\/td><td>current job status\/container\/service metadata<\/td><\/tr><tr><td><code>steps<\/code><\/td><td>step outcomes and outputs<\/td><\/tr><tr><td><code>runner<\/code><\/td><td>runner OS, arch, temp, tool cache<\/td><\/tr><tr><td><code>secrets<\/code><\/td><td>secrets available to the job<\/td><\/tr><tr><td><code>strategy<\/code><\/td><td>matrix strategy metadata<\/td><\/tr><tr><td><code>matrix<\/code><\/td><td>current matrix combination<\/td><\/tr><tr><td><code>needs<\/code><\/td><td>dependency job results and outputs<\/td><\/tr><tr><td><code>inputs<\/code><\/td><td>manual\/reusable workflow inputs<\/td><\/tr><tr><td><code>jobs<\/code><\/td><td>called-workflow job outputs in supported reusable-workflow contexts<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">8.3 Property access<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dot notation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ github.repository }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Index notation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ github&#91;'repository'] }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Index notation is useful when keys are dynamic or contain characters unsuitable for dot notation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8.4 Context debugging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Safer targeted debugging:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Debug selected GitHub values\n  run: |\n    echo \"event=${{ github.event_name }}\"\n    echo \"ref=${{ github.ref }}\"\n    echo \"sha=${{ github.sha }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid casually dumping:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ toJSON(github) }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">because the&nbsp;<code>github<\/code>&nbsp;context can contain sensitive information such as tokens.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8.5 Context availability<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Not every context is available in every workflow key.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>matrix<\/code>\u00a0exists only for matrix jobs;<\/li>\n\n\n\n<li><code>needs<\/code>\u00a0exists when jobs declare dependencies;<\/li>\n\n\n\n<li><code>steps<\/code>\u00a0exists during step execution;<\/li>\n\n\n\n<li>secrets are unavailable in some untrusted\/fork contexts.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">When GitHub reports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Unrecognized named-value\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">check whether the context is legal at that point in workflow evaluation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">9. ESSENTIALS \u2014 Expressions<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">9.1 Syntax<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ expression }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: ${{ github.ref == 'refs\/heads\/main' }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For many&nbsp;<code>if:<\/code>&nbsp;clauses, the outer&nbsp;<code>${{ }}<\/code>&nbsp;is optional:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.ref == 'refs\/heads\/main'\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">9.2 Operators<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Common operators:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>!\n&gt;\n&gt;=\n&lt;\n&lt;=\n==\n!=\n&amp;&amp;\n||\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.ref == 'refs\/heads\/main' &amp;&amp; !cancelled()\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">9.3 Useful functions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>contains<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>if: contains(github.event.pull_request.labels.*.name, 'safe-to-deploy')\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>startsWith<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>if: startsWith(github.ref, 'refs\/tags\/v')\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>endsWith<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>if: endsWith(github.event.pull_request.title, '&#91;release]')\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>format<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>env:\n  IMAGE: ${{ format('{0}:{1}', vars.REGISTRY, github.sha) }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>join<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ join(matrix.targets, ',') }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>toJSON<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>env:\n  MATRIX_JSON: ${{ toJSON(matrix) }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>fromJSON<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Turn JSON text into structured data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>hashFiles<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>key: npm-${{ runner.os }}-${{ hashFiles('**\/package-lock.json') }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">9.4 Status functions<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: success()\nif: failure()\nif: cancelled()\nif: always()\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Cleanup pattern<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Collect diagnostics\n  if: failure()\n  run: .\/collect-diagnostics.sh\n\n- name: Cleanup\n  if: always()\n  run: .\/cleanup.sh\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">9.5 Expression security<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dangerous:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: echo \"${{ github.event.pull_request.title }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A malicious title can become part of generated shell code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Safer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Validate title\n  env:\n    PR_TITLE: ${{ github.event.pull_request.title }}\n  run: |\n    printf '%s\\n' \"$PR_TITLE\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The key rule is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Treat user-controlled event fields as untrusted data, not executable syntax.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">10. ESSENTIALS \u2014 Secrets<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">10.1 Secret scopes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Secrets can exist at:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>organization;<\/li>\n\n\n\n<li>repository;<\/li>\n\n\n\n<li>environment.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Use environment secrets for deployment-specific sensitive values.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10.2 Referencing a secret<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Login\n  env:\n    API_TOKEN: ${{ secrets.API_TOKEN }}\n  run: .\/login.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer passing secrets through environment variables or action inputs rather than interpolating them directly into shell syntax.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10.3 Secret masking<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub attempts to mask known secrets from logs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not assume masking is a data-loss-prevention system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A secret can still leak if:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>transformed\/encoded;<\/li>\n\n\n\n<li>embedded in another value;<\/li>\n\n\n\n<li>written to an artifact;<\/li>\n\n\n\n<li>sent to an external server;<\/li>\n\n\n\n<li>exposed by malicious code;<\/li>\n\n\n\n<li>stored on a persistent self-hosted runner.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">10.4 Fork and Dependabot behavior<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Untrusted pull requests and Dependabot-triggered workflows are intentionally restricted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not design CI that requires powerful write credentials merely to test arbitrary PR code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Architect separate trust zones:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Untrusted code] --&gt; B&#91;Read-only CI]\n    B --&gt; C&#91;Test result \/ sanitized artifact]\n    C --&gt; D&#91;Trusted workflow]\n    D --&gt; E&#91;Privileged operation]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">10.5 Secret rotation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A practical rotation pattern:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>issue new credential;<\/li>\n\n\n\n<li>add\/update GitHub secret;<\/li>\n\n\n\n<li>validate a workflow;<\/li>\n\n\n\n<li>revoke old credential;<\/li>\n\n\n\n<li>review audit logs;<\/li>\n\n\n\n<li>document rotation date\/owner.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Better still, eliminate many long-lived cloud secrets with OIDC.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10.6 Structured and large secrets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If an application needs structured secret material, prefer a secret manager or carefully encoded file material.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Restore certificate\n  env:\n    CERT_B64: ${{ secrets.CERT_B64 }}\n  run: |\n    printf '%s' \"$CERT_B64\" | base64 --decode &gt; certificate.p12\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Delete sensitive files during cleanup, especially on self-hosted runners.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">11. ESSENTIALS \u2014&nbsp;<code>GITHUB_TOKEN<\/code><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">11.1 What is&nbsp;<code>GITHUB_TOKEN<\/code>?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For each job, GitHub automatically creates a short-lived token that can authenticate to GitHub APIs and repository resources within the permissions granted to that job.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You do not create or rotate this token manually.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Show authenticated user\n  env:\n    GH_TOKEN: ${{ github.token }}\n  run: gh api user\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In many cases, actions access it implicitly through:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>${{ github.token }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">11.2 Permission model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start with minimum privileges:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add only what is needed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example: create a GitHub Release:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example: cloud OIDC:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  id-token: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example: artifact attestation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  id-token: write\n  attestations: write\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Common permissions<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Permission<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical reason<\/th><\/tr><\/thead><tbody><tr><td><code>contents<\/code><\/td><td>clone\/read\/write repository contents and releases<\/td><\/tr><tr><td><code>actions<\/code><\/td><td>work with workflow runs\/actions<\/td><\/tr><tr><td><code>checks<\/code><\/td><td>create\/update checks<\/td><\/tr><tr><td><code>deployments<\/code><\/td><td>deployment objects<\/td><\/tr><tr><td><code>discussions<\/code><\/td><td>discussions<\/td><\/tr><tr><td><code>id-token<\/code><\/td><td>request OIDC token<\/td><\/tr><tr><td><code>issues<\/code><\/td><td>issues<\/td><\/tr><tr><td><code>packages<\/code><\/td><td>GitHub Packages \/ GHCR<\/td><\/tr><tr><td><code>pages<\/code><\/td><td>GitHub Pages<\/td><\/tr><tr><td><code>pull-requests<\/code><\/td><td>PR metadata\/comments<\/td><\/tr><tr><td><code>security-events<\/code><\/td><td>upload security results<\/td><\/tr><tr><td><code>statuses<\/code><\/td><td>commit statuses<\/td><\/tr><tr><td><code>attestations<\/code><\/td><td>artifact attestations<\/td><\/tr><tr><td><code>artifact-metadata<\/code><\/td><td>linked\/artifact metadata capabilities where used<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">11.3 Workflow vs job permissions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Workflow default:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm test\n\n  release:\n    permissions:\n      contents: write\n    runs-on: ubuntu-latest\n    steps:\n      - run: gh release create ...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The release job gets extra permissions without granting them to the test job.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">11.4 Read-all \/ write-all<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub supports broad forms such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions: read-all\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions: write-all\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">unless there is an unusually strong reason.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">11.5 Token lifetime<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The token is tied to a job and is short-lived. Do not treat it as a long-term credential or persist it outside the workflow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">11.6 Token-triggered workflow behavior<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub deliberately prevents many actions performed with the repository&#8217;s&nbsp;<code>GITHUB_TOKEN<\/code>&nbsp;from recursively creating uncontrolled workflow chains.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you need deliberate cross-repository or broader automation, consider:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub App token;<\/li>\n\n\n\n<li>fine-grained PAT;<\/li>\n\n\n\n<li><code>repository_dispatch<\/code>;<\/li>\n\n\n\n<li>reusable workflows;<\/li>\n\n\n\n<li><code>workflow_run<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Choose the narrowest identity and permissions that solve the problem.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">12. ESSENTIALS \u2014 Workflow Commands<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Workflow commands let a running step communicate structured information back to GitHub Actions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">12.1 Environment files<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Modern workflows use environment files instead of deprecated output commands.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">File variable<\/th><th class=\"has-text-align-left\" data-align=\"left\">Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>GITHUB_ENV<\/code><\/td><td>values for later steps<\/td><\/tr><tr><td><code>GITHUB_OUTPUT<\/code><\/td><td>step outputs<\/td><\/tr><tr><td><code>GITHUB_PATH<\/code><\/td><td>add directories to&nbsp;<code>PATH<\/code><\/td><\/tr><tr><td><code>GITHUB_STEP_SUMMARY<\/code><\/td><td>Markdown job summary<\/td><\/tr><tr><td><code>GITHUB_STATE<\/code><\/td><td>state between action phases<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">12.2&nbsp;<code>GITHUB_ENV<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Set runtime value\n  run: echo \"BUILD_CHANNEL=stable\" &gt;&gt; \"$GITHUB_ENV\"\n\n- name: Use runtime value\n  run: echo \"$BUILD_CHANNEL\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">12.3&nbsp;<code>GITHUB_OUTPUT<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Calculate digest\n  id: digest\n  run: echo \"sha=$(sha256sum app.tar.gz | cut -d' ' -f1)\" &gt;&gt; \"$GITHUB_OUTPUT\"\n\n- name: Print digest\n  run: echo \"${{ steps.digest.outputs.sha }}\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">12.4&nbsp;<code>GITHUB_PATH<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Install internal tools\n  run: |\n    mkdir -p \"$HOME\/bin\"\n    cp .\/tools\/mycli \"$HOME\/bin\/\"\n    echo \"$HOME\/bin\" &gt;&gt; \"$GITHUB_PATH\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Later steps can execute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: mycli --version\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">12.5&nbsp;<code>GITHUB_STEP_SUMMARY<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Publish test summary\n  run: |\n    {\n      echo \"## Test Summary\"\n      echo \"\"\n      echo \"| Suite | Result |\"\n      echo \"|---|---|\"\n      echo \"| Unit | \u2705 Passed |\"\n      echo \"| Integration | \u2705 Passed |\"\n    } &gt;&gt; \"$GITHUB_STEP_SUMMARY\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This creates a human-readable summary in the Actions UI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">12.6 Annotations<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: echo \"::notice title=Build::Compilation started\"\n- run: echo \"::warning file=app.js,line=8::Deprecated API\"\n- run: echo \"::error file=app.js,line=42::Validation failed\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use annotations to make actionable errors visible near code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">12.7 Log grouping<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Grouped diagnostics\n  run: |\n    echo \"::group::Environment\"\n    env | sort\n    echo \"::endgroup::\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Be careful not to print secrets.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">12.8 Masking<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Mask dynamic value\n  run: |\n    TOKEN=\"$(.\/generate-temporary-token.sh)\"\n    echo \"::add-mask::$TOKEN\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Mask before printing or passing a dynamically generated sensitive value.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">13. ESSENTIALS \u2014 Matrix Builds<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">13.1 Why matrices exist<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A matrix expands one job definition into multiple jobs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use cases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>multiple OSs;<\/li>\n\n\n\n<li>multiple runtime versions;<\/li>\n\n\n\n<li>multiple architectures;<\/li>\n\n\n\n<li>multiple components;<\/li>\n\n\n\n<li>test permutations.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">13.2 Basic matrix<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    strategy:\n      matrix:\n        node: &#91;22, 24]\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: ${{ matrix.node }}\n\n      - run: npm ci\n      - run: npm test\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Produces two jobs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">13.3 Multi-dimensional matrix<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix:\n    os: &#91;ubuntu-latest, windows-latest]\n    node: &#91;22, 24]\n\nruns-on: ${{ matrix.os }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expansion:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Job<\/th><th class=\"has-text-align-left\" data-align=\"left\">OS<\/th><th class=\"has-text-align-right\" data-align=\"right\">Node<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td>Ubuntu<\/td><td class=\"has-text-align-right\" data-align=\"right\">22<\/td><\/tr><tr><td>2<\/td><td>Ubuntu<\/td><td class=\"has-text-align-right\" data-align=\"right\">24<\/td><\/tr><tr><td>3<\/td><td>Windows<\/td><td class=\"has-text-align-right\" data-align=\"right\">22<\/td><\/tr><tr><td>4<\/td><td>Windows<\/td><td class=\"has-text-align-right\" data-align=\"right\">24<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">13.4 Include<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Add special combinations:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix:\n    node: &#91;22, 24]\n    include:\n      - node: 25\n        experimental: true\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">13.5 Exclude<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix:\n    os: &#91;ubuntu-latest, windows-latest]\n    node: &#91;20, 22, 24]\n    exclude:\n      - os: windows-latest\n        node: 20\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">13.6&nbsp;<code>fail-fast<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  fail-fast: false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>false<\/code>&nbsp;when you want all matrix results even after one fails.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>true<\/code>&nbsp;when later combinations are not worth running after a mandatory combination fails.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">13.7 Experimental combinations<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    continue-on-error: ${{ matrix.experimental }}\n\n    strategy:\n      fail-fast: true\n      matrix:\n        node: &#91;22, 24]\n        experimental: &#91;false]\n        include:\n          - node: 25\n            experimental: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Stable failures block the workflow. Experimental failures do not.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">13.8&nbsp;<code>max-parallel<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  max-parallel: 4\n  matrix:\n    shard: &#91;1, 2, 3, 4, 5, 6, 7, 8]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Useful when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>test infrastructure can handle only limited concurrency;<\/li>\n\n\n\n<li>licenses are scarce;<\/li>\n\n\n\n<li>a database would overload;<\/li>\n\n\n\n<li>cost needs control.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">13.9 Dynamic matrix<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Producer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  prepare:\n    runs-on: ubuntu-latest\n\n    outputs:\n      matrix: ${{ steps.m.outputs.matrix }}\n\n    steps:\n      - id: m\n        run: |\n          echo 'matrix={\"service\":&#91;\"api\",\"worker\",\"frontend\"]}' &gt;&gt; \"$GITHUB_OUTPUT\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Consumer:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code> build:\n    needs: prepare\n\n    strategy:\n      matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - run: echo \"Building ${{ matrix.service }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is a powerful monorepo pattern.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">14. ESSENTIALS \u2014 Caching<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">14.1 Cache vs artifact<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A cache is a performance optimization.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An artifact is a retained workflow output.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Characteristic<\/th><th class=\"has-text-align-left\" data-align=\"left\">Cache<\/th><th class=\"has-text-align-left\" data-align=\"left\">Artifact<\/th><\/tr><\/thead><tbody><tr><td>Main purpose<\/td><td>speed<\/td><td>transfer\/retain files<\/td><\/tr><tr><td>Correctness dependency<\/td><td>should be optional<\/td><td>may be part of pipeline<\/td><\/tr><tr><td>Key lookup<\/td><td>yes<\/td><td>name\/ID<\/td><\/tr><tr><td>Typical content<\/td><td>package manager cache<\/td><td>binaries, reports, plans<\/td><\/tr><tr><td>Mutable<\/td><td>effectively replaced with new keys<\/td><td>upload produces artifact<\/td><\/tr><tr><td>Security concern<\/td><td>poisoning<\/td><td>sensitive data \/ provenance<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Golden rule:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Your build should remain correct if every cache disappears.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">14.2 Package-manager caching<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Simple Node example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/setup-node@v7\n  with:\n    node-version: \"24\"\n    cache: npm\n\n- run: npm ci\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For many ecosystems, setup actions provide automatic dependency caching.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">14.3&nbsp;<code>actions\/cache<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Cache npm data\n  uses: actions\/cache@v4\n  with:\n    path: ~\/.npm\n    key: npm-${{ runner.os }}-${{ hashFiles('**\/package-lock.json') }}\n    restore-keys: |\n      npm-${{ runner.os }}-\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">14.4 Cache-key design<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A good key encodes the inputs that make cache contents valid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ecosystem + OS + architecture + toolchain + dependency lock hash\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>key: &gt;-\n  npm-${{ runner.os }}-${{ runner.arch }}-node24-\n  ${{ hashFiles('**\/package-lock.json') }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">14.5 Cache poisoning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If untrusted code can write to a cache later consumed by a privileged workflow, an attacker may plant malicious content.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mitigations:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>do not let low-trust workflows write privileged caches;<\/li>\n\n\n\n<li>use restore-only cache in untrusted workflows;<\/li>\n\n\n\n<li>use trust-separated cache keys;<\/li>\n\n\n\n<li>do not cache executable security-sensitive state unnecessarily.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">14.6&nbsp;<code>cache-mode<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Current GitHub Actions supports:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Mode<\/th><th class=\"has-text-align-right\" data-align=\"right\">Restore<\/th><th class=\"has-text-align-right\" data-align=\"right\">Save<\/th><\/tr><\/thead><tbody><tr><td><code>read<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><\/tr><tr><td><code>write<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td><code>write-only<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td><code>none<\/code><\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cache-mode: read\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v6\n      - uses: actions\/cache@v4\n        with:\n          path: ~\/.npm\n          key: npm-${{ hashFiles('**\/package-lock.json') }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>read<\/code>&nbsp;for low-trust PR validation unless there is a deliberate reason to save.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">15. ESSENTIALS \u2014 Artifacts<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">15.1 What is an artifact?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Artifacts are files produced by workflows and stored by GitHub for later use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>compiled binary;<\/li>\n\n\n\n<li>package;<\/li>\n\n\n\n<li>Terraform plan;<\/li>\n\n\n\n<li>test report;<\/li>\n\n\n\n<li>coverage;<\/li>\n\n\n\n<li>logs;<\/li>\n\n\n\n<li>SBOM;<\/li>\n\n\n\n<li>deployment bundle.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">15.2 Upload<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Upload package\n  uses: actions\/upload-artifact@v4\n  with:\n    name: app-package\n    path: dist\/\n    retention-days: 14\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">15.3 Download<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Download package\n  uses: actions\/download-artifact@v5\n  with:\n    name: app-package\n    path: dist\/\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">15.4 Sharing between jobs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    B&#91;Build job] --&gt;|upload| A&#91;(Artifact)]\n    A --&gt;|download| T&#91;Test job]\n    A --&gt;|download| D&#91;Deploy job]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v6\n      - run: npm ci\n      - run: npm run build\n      - uses: actions\/upload-artifact@v4\n        with:\n          name: web-dist\n          path: dist\/\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/download-artifact@v5\n        with:\n          name: web-dist\n          path: dist\/\n      - run: .\/deploy.sh dist\/\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">15.5 Build once, deploy many<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A mature pipeline builds one immutable artifact and promotes that exact artifact.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Commit] --&gt; B&#91;Build once]\n    B --&gt; A&#91;(Immutable artifact)]\n    A --&gt; S&#91;Staging]\n    S --&gt; U&#91;UAT]\n    U --&gt; P&#91;Production]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not rebuild from source separately for each environment unless that behavior is intentionally part of the release model.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">15.6 Retention<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Set retention based on purpose:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Artifact<\/th><th class=\"has-text-align-right\" data-align=\"right\">Example retention<\/th><\/tr><\/thead><tbody><tr><td>temporary test logs<\/td><td class=\"has-text-align-right\" data-align=\"right\">3\u20137 days<\/td><\/tr><tr><td>PR build<\/td><td class=\"has-text-align-right\" data-align=\"right\">7\u201314 days<\/td><\/tr><tr><td>release candidate<\/td><td class=\"has-text-align-right\" data-align=\"right\">30\u201390 days<\/td><\/tr><tr><td>formal release<\/td><td class=\"has-text-align-right\" data-align=\"right\">preferably durable package\/release registry<\/td><\/tr><tr><td>audit\/provenance evidence<\/td><td class=\"has-text-align-right\" data-align=\"right\">per compliance requirements<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">16. ESSENTIALS \u2014 Concurrency<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrency prevents conflicting or wasteful simultaneous runs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">16.1 Cancel stale CI<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: ci-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If a developer pushes three commits quickly, old CI can be cancelled.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">16.2 Serialize production deployments<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: production\n  queue: max\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This keeps one production deployment active while allowing pending deployment runs to queue.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2026 note<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Current syntax supports&nbsp;<code>queue: max<\/code>&nbsp;to retain multiple pending runs in the same concurrency group. It cannot be combined with&nbsp;<code>cancel-in-progress: true<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">16.3 Environment-specific groups<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: deploy-${{ inputs.environment }}\n  queue: max\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Staging and production can progress independently, while each environment remains serialized.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">16.4 Avoid accidental cross-workflow cancellation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: main\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two different workflows can share the same repository-level concurrency group.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Better:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">17. ESSENTIALS \u2014 Containers<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">17.1 Container jobs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A job can run inside a container:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    runs-on: ubuntu-latest\n\n    container:\n      image: node:24-bookworm\n\n    steps:\n      - uses: actions\/checkout@v6\n      - run: node --version\n      - run: npm ci\n      - run: npm test\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This makes the job environment more deterministic.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">17.2 Container credentials<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>container:\n  image: ghcr.io\/my-org\/ci-image:2026.09\n  credentials:\n    username: ${{ github.actor }}\n    password: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">17.3 Environment, volumes, ports, options<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>container:\n  image: ubuntu:24.04\n  env:\n    LANG: C.UTF-8\n  volumes:\n    - ci-cache:\/cache\n  options: --cpus 2\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use only runner\/container options supported by GitHub&#8217;s runner\/container implementation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">17.4 Service containers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Service containers are companion services available during a job.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  integration:\n    runs-on: ubuntu-latest\n\n    services:\n      postgres:\n        image: postgres:17\n        env:\n          POSTGRES_USER: app\n          POSTGRES_PASSWORD: test-password\n          POSTGRES_DB: app_test\n        ports:\n          - 5432:5432\n        options: &gt;-\n          --health-cmd \"pg_isready -U app -d app_test\"\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Integration tests\n        env:\n          DATABASE_URL: postgresql:\/\/app:test-password@localhost:5432\/app_test\n        run: npm run test:integration\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Other common services:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Redis\nMySQL\nMongoDB\nRabbitMQ\nElasticsearch\/OpenSearch\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">17.5 Container networking mental model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If the job itself runs directly on the runner and services expose ports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>job process -&gt; localhost:mapped-port -&gt; service\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the job runs in a job container, service containers are typically addressable through their service labels on the shared container network.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">18. ESSENTIALS \u2014 Using Actions<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">18.1 What is an action?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An action is a reusable unit that runs inside a step.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/checkout@v6\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sources include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub official actions;<\/li>\n\n\n\n<li>Marketplace\/community actions;<\/li>\n\n\n\n<li>private actions;<\/li>\n\n\n\n<li>actions in the same repository.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">18.2 Action references<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Major tag:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uses: actions\/checkout@v6\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Specific tag:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uses: some-org\/some-action@v2.3.1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Commit SHA:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uses: some-org\/some-action@0123456789abcdef0123456789abcdef01234567\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For third-party production actions, a reviewed full SHA is the strongest common pinning strategy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">18.3 Local actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Modern same-repository reference:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: $\/.github\/actions\/setup-project\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Traditional relative form:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/checkout@v6\n- uses: .\/.github\/actions\/setup-project\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<code>$<\/code>-rooted repository action reference avoids depending on the checked-out workspace path in supported GitHub.com workflows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">18.4 Third-party action review checklist<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before approving an action:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Who maintains it?<\/li>\n\n\n\n<li>Is the source public and reviewable?<\/li>\n\n\n\n<li>Is it widely used?<\/li>\n\n\n\n<li>Is release provenance credible?<\/li>\n\n\n\n<li>What permissions does it need?<\/li>\n\n\n\n<li>Does it execute downloaded code?<\/li>\n\n\n\n<li>Does it send data externally?<\/li>\n\n\n\n<li>Is it pinned?<\/li>\n\n\n\n<li>Are dependencies maintained?<\/li>\n\n\n\n<li>Does it handle secrets?<\/li>\n\n\n\n<li>Is there an equivalent first-party action?<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">18.5 Dependabot for Actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A typical configuration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>version: 2\n\nupdates:\n  - package-ecosystem: github-actions\n    directory: \/\n    schedule:\n      interval: weekly\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This helps keep action references current. Security review is still required.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">19. ESSENTIALS \u2014 Reusable Automation<\/h1>\n\n\n\n<h1 class=\"wp-block-heading\">19.1 Reusable Workflows<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Reusable workflows centralize full jobs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Called workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.github\/workflows\/reusable-ci.yml\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Reusable CI\n\non:\n  workflow_call:\n    inputs:\n      node-version:\n        type: string\n        required: false\n        default: \"24\"\n\n    secrets:\n      npm-token:\n        required: false\n\n    outputs:\n      package-name:\n        description: Package artifact name\n        value: ${{ jobs.build.outputs.package-name }}\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    outputs:\n      package-name: ${{ steps.meta.outputs.name }}\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: ${{ inputs.node-version }}\n          cache: npm\n\n      - run: npm ci\n      - run: npm test\n      - run: npm run build\n\n      - id: meta\n        run: echo \"name=web-${{ github.sha }}\" &gt;&gt; \"$GITHUB_OUTPUT\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Caller:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  ci:\n    uses: my-org\/platform-workflows\/.github\/workflows\/reusable-ci.yml@v3\n    with:\n      node-version: \"24\"\n    secrets: inherit\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">19.2 Reusable workflow design<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    R1&#91;Repo A] --&gt; W&#91;Central reusable workflow]\n    R2&#91;Repo B] --&gt; W\n    R3&#91;Repo C] --&gt; W\n    W --&gt; S&#91;Standard CI behavior]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Good candidates:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>language CI standards;<\/li>\n\n\n\n<li>security scanning;<\/li>\n\n\n\n<li>container build\/publish;<\/li>\n\n\n\n<li>Terraform plan\/apply;<\/li>\n\n\n\n<li>deployment logic;<\/li>\n\n\n\n<li>compliance steps.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">19.3 Inputs and secrets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer explicit inputs for behavior:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>inputs:\n  environment:\n    type: string\n    required: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use explicit secret contracts when possible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>secrets: inherit<\/code>&nbsp;is convenient, but it gives the called workflow access to all caller secrets available under the inheritance rules. Use it deliberately.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">19.4 Outputs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reusable workflows can expose workflow outputs so the caller can consume computed data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use them for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>artifact name;<\/li>\n\n\n\n<li>image digest;<\/li>\n\n\n\n<li>release version;<\/li>\n\n\n\n<li>deployment URL;<\/li>\n\n\n\n<li>plan status.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">19.5 Nesting<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reusable workflows can call reusable workflows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not create deeply tangled call graphs merely because nesting is allowed. Keep ownership and privilege boundaries obvious.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2026 note<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Current GitHub documentation allows up to&nbsp;<strong>10 levels<\/strong>&nbsp;of connected reusable workflows and up to&nbsp;<strong>50 unique reusable workflows<\/strong>&nbsp;reachable from a top-level workflow file. Treat limits as version-sensitive and check current documentation when designing very large platforms.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">19.6 Composite actions vs reusable workflows<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Need<\/th><th class=\"has-text-align-right\" data-align=\"right\">Composite action<\/th><th class=\"has-text-align-right\" data-align=\"right\">Reusable workflow<\/th><\/tr><\/thead><tbody><tr><td>Reuse steps<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Indirectly<\/td><\/tr><tr><td>Reuse entire jobs<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Choose runner<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Job permissions<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Environment gates<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Matrix jobs<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Simple setup routine<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Overkill<\/td><\/tr><tr><td>Full CI standard<\/td><td class=\"has-text-align-right\" data-align=\"right\">Limited<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">19.7 Workflow templates<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Organization workflow templates help repositories bootstrap approved workflow structures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use templates when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>repositories need a standardized starting point;<\/li>\n\n\n\n<li>teams may customize afterward;<\/li>\n\n\n\n<li>a central reusable workflow is not enough by itself.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">20. ESSENTIALS \u2014 CI Pipelines<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">20.1 A production CI pipeline<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A mature CI pipeline commonly performs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Checkout] --&gt; I&#91;Install]\n    I --&gt; L&#91;Lint]\n    I --&gt; U&#91;Unit tests]\n    I --&gt; S&#91;Security]\n    L --&gt; B&#91;Build]\n    U --&gt; B\n    S --&gt; B\n    B --&gt; A&#91;Artifact]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">20.2 Node.js example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Pull Request CI\n\non:\n  pull_request:\n  push:\n    branches: &#91;main]\n\npermissions:\n  contents: read\n\ncache-mode: read\n\nconcurrency:\n  group: ci-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  quality:\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: \"24\"\n          cache: npm\n\n      - run: npm ci\n\n      - name: Lint\n        run: npm run lint\n\n      - name: Unit tests\n        run: npm test -- --ci\n\n      - name: Build\n        run: npm run build\n\n      - name: Upload build\n        uses: actions\/upload-artifact@v4\n        with:\n          name: web-${{ github.sha }}\n          path: dist\/\n          retention-days: 7\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">20.3 Integration-test job<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code> integration:\n    needs: quality\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n\n    services:\n      postgres:\n        image: postgres:17\n        env:\n          POSTGRES_USER: app\n          POSTGRES_PASSWORD: app\n          POSTGRES_DB: test\n        ports:\n          - 5432:5432\n        options: &gt;-\n          --health-cmd \"pg_isready -U app -d test\"\n          --health-interval 5s\n          --health-timeout 5s\n          --health-retries 10\n\n    steps:\n      - uses: actions\/checkout@v6\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: \"24\"\n          cache: npm\n      - run: npm ci\n      - run: npm run test:integration\n        env:\n          DATABASE_URL: postgresql:\/\/app:app@localhost:5432\/test\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">20.4 Required checks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Configure branch protection\/rulesets so critical CI jobs must succeed before merge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lint\nunit\nintegration\nsecurity\nbuild\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The exact enforcement belongs in repository rules\/rulesets\u2014not merely in workflow YAML.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">21. ESSENTIALS \u2014 CD Pipelines<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">21.1 Delivery vs deployment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A good CD design separates:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>build<\/strong>\u00a0\u2014 create deployable artifact;<\/li>\n\n\n\n<li><strong>verify<\/strong>\u00a0\u2014 prove artifact quality;<\/li>\n\n\n\n<li><strong>promote<\/strong>\u00a0\u2014 choose where it goes;<\/li>\n\n\n\n<li><strong>deploy<\/strong>\u00a0\u2014 apply change;<\/li>\n\n\n\n<li><strong>verify deployment<\/strong>\u00a0\u2014 prove runtime health;<\/li>\n\n\n\n<li><strong>rollback<\/strong>\u00a0\u2014 recover if needed.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Commit] --&gt; B&#91;Build]\n    B --&gt; T&#91;Test]\n    T --&gt; A&#91;(Artifact)]\n    A --&gt; D&#91;Development]\n    D --&gt; S&#91;Staging]\n    S --&gt; G{Approval \/ protection}\n    G --&gt; P&#91;Production]\n    P --&gt; V&#91;Smoke \/ health tests]\n    V --&gt;|Fail| R&#91;Rollback]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">21.2 Basic staged deployment<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v6\n      - run: .\/build.sh\n      - uses: actions\/upload-artifact@v4\n        with:\n          name: release\n          path: dist\/\n\n  staging:\n    needs: build\n    runs-on: ubuntu-latest\n    environment: staging\n    steps:\n      - uses: actions\/download-artifact@v5\n        with:\n          name: release\n      - run: .\/deploy.sh staging\n\n  production:\n    needs: staging\n    runs-on: ubuntu-latest\n    environment: production\n    concurrency:\n      group: production\n      queue: max\n    steps:\n      - uses: actions\/download-artifact@v5\n        with:\n          name: release\n      - run: .\/deploy.sh production\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Environment settings, not the YAML alone, should enforce production approval\/protection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">21.3 Rollback patterns<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Rollback can mean:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>redeploy previous artifact;<\/li>\n\n\n\n<li>revert Git commit;<\/li>\n\n\n\n<li>restore Kubernetes ReplicaSet;<\/li>\n\n\n\n<li>switch blue\/green target;<\/li>\n\n\n\n<li>reduce canary traffic to zero;<\/li>\n\n\n\n<li>revert Terraform\/IaC change with a reviewed plan.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The rollback design must match the deployment technology. &#8220;Run deploy again&#8221; is not a rollback strategy by itself.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">22. ESSENTIALS \u2014 Environments and Deployments<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">22.1 What is a GitHub environment?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An environment models a deployment target such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>development\nstaging\nuat\nproduction\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It can hold:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>environment variables;<\/li>\n\n\n\n<li>environment secrets;<\/li>\n\n\n\n<li>allowed branches\/tags;<\/li>\n\n\n\n<li>required reviewers;<\/li>\n\n\n\n<li>wait timers;<\/li>\n\n\n\n<li>custom deployment protection rules;<\/li>\n\n\n\n<li>deployment history.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">22.2 Reference an environment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Simple:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>environment: production\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With URL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>environment:\n  name: production\n  url: https:\/\/app.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Dynamic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>environment:\n  name: ${{ inputs.environment }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">22.3 Environment secrets<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  deploy:\n    environment: production\n    runs-on: ubuntu-latest\n\n    steps:\n      - env:\n          API_KEY: ${{ secrets.API_KEY }}\n        run: .\/deploy.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Environment secrets do not become available to the job until the applicable environment protections are satisfied.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">22.4 Required reviewers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A production environment can require human approval.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    J&#91;Deployment job] --&gt; W&#91;Waiting]\n    W --&gt; A{Approved?}\n    A -- No --&gt; X&#91;Rejected \/ timeout]\n    A -- Yes --&gt; R&#91;Runner starts]\n    R --&gt; S&#91;Environment secrets available]\n    S --&gt; D&#91;Deploy]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">22.5 Wait timers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A wait timer can delay deployment after triggering.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use cases:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>controlled release window;<\/li>\n\n\n\n<li>soak period;<\/li>\n\n\n\n<li>progressive delivery delay.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">22.6 Branch\/tag restrictions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Allow only appropriate refs to deploy:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>production:\n  protected branches only\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">or selected patterns according to repository policy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">22.7 Custom deployment protection rules<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Apps can gate a deployment using external systems such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>observability;<\/li>\n\n\n\n<li>change-management approval;<\/li>\n\n\n\n<li>security systems;<\/li>\n\n\n\n<li>quality gates.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A sophisticated flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Deploy requested] --&gt; G&#91;GitHub environment gate]\n    G --&gt; O&#91;Observability \/ ITSM \/ security app]\n    O --&gt;|Approve| D&#91;Deployment]\n    O --&gt;|Reject| F&#91;Stop]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">22.8 Environment without deployment object<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Current workflow syntax can use an environment&#8217;s secrets\/variables and protections without creating a deployment record:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>environment:\n  name: staging\n  deployment: false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This can be useful for CI or tests that need protected environment configuration but are not actual deployments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Custom GitHub-App deployment protection rules require a deployment object, so they are not compatible with&nbsp;<code>deployment: false<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">22.9 Environment concurrency<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Environments do not automatically solve every deployment race. Add concurrency:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: deploy-${{ inputs.environment }}\n  queue: max\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">22.10 Recommended environment model<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Environment<\/th><th class=\"has-text-align-left\" data-align=\"left\">Approval<\/th><th class=\"has-text-align-left\" data-align=\"left\">Secrets<\/th><th class=\"has-text-align-left\" data-align=\"left\">Concurrency<\/th><th class=\"has-text-align-left\" data-align=\"left\">Typical source<\/th><\/tr><\/thead><tbody><tr><td>development<\/td><td>no<\/td><td>low privilege<\/td><td>optional<\/td><td>feature\/main<\/td><\/tr><tr><td>staging<\/td><td>optional<\/td><td>staging-only<\/td><td>serialize<\/td><td>main<\/td><\/tr><tr><td>UAT<\/td><td>often<\/td><td>UAT-only<\/td><td>serialize<\/td><td>promoted artifact<\/td><\/tr><tr><td>production<\/td><td>yes\/protection rule<\/td><td>production-only<\/td><td>serialize<\/td><td>approved release<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Part II \u2014 Advanced Engineering<\/h1>\n\n\n\n<h1 class=\"wp-block-heading\">23. ADVANCED \u2014 Custom Actions<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Custom actions package repeatable behavior behind a stable interface.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use a custom action when repeated workflow steps represent a reusable capability rather than a full pipeline.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">23.1 Types of custom actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub supports three major custom-action models:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Type<\/th><th class=\"has-text-align-left\" data-align=\"left\">Runs on<\/th><th class=\"has-text-align-left\" data-align=\"left\">Strengths<\/th><th class=\"has-text-align-left\" data-align=\"left\">Trade-offs<\/th><\/tr><\/thead><tbody><tr><td>JavaScript action<\/td><td>Linux\/macOS\/Windows<\/td><td>Fast startup, cross-platform<\/td><td>Must package dependencies<\/td><\/tr><tr><td>Docker action<\/td><td>Linux<\/td><td>Complete runtime control<\/td><td>Container startup, Linux only<\/td><\/tr><tr><td>Composite action<\/td><td>Runner shell\/actions<\/td><td>Easy step reuse<\/td><td>Less control than JS\/Docker<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">23.2 Action metadata<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">An action directory contains:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.github\/actions\/my-action\/\n\u251c\u2500\u2500 action.yml\n\u2514\u2500\u2500 ...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Typical metadata:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Setup Application\ndescription: Install and prepare the application\n\ninputs:\n  node-version:\n    description: Node.js version\n    required: false\n    default: \"24\"\n\noutputs:\n  cache-key:\n    description: Effective dependency cache key\n    value: ${{ steps.cache-key.outputs.value }}\n\nruns:\n  using: composite\n  steps:\n    - uses: actions\/setup-node@v7\n      with:\n        node-version: ${{ inputs.node-version }}\n\n    - id: cache-key\n      shell: bash\n      run: |\n        echo \"value=npm-${{ runner.os }}-${{ hashFiles('**\/package-lock.json') }}\" \\\n          &gt;&gt; \"$GITHUB_OUTPUT\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: $\/.github\/actions\/setup-application\n  with:\n    node-version: \"24\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">23.3 JavaScript action<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Project:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>hello-action\/\n\u251c\u2500\u2500 action.yml\n\u251c\u2500\u2500 package.json\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 index.js\n\u2514\u2500\u2500 dist\/\n    \u2514\u2500\u2500 index.js\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>action.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Hello Action\ndescription: Demonstrates a JavaScript action\n\ninputs:\n  who:\n    description: Person to greet\n    required: true\n\noutputs:\n  message:\n    description: Generated greeting\n\nruns:\n  using: node24\n  main: dist\/index.js\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>src\/index.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const core = require(\"@actions\/core\");\n\ntry {\n  const who = core.getInput(\"who\", { required: true });\n  const message = `Hello, ${who}!`;\n\n  core.info(message);\n  core.setOutput(\"message\", message);\n} catch (error) {\n  core.setFailed(error.message);\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Package the dependency tree:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install\nnpx @vercel\/ncc build src\/index.js -o dist\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The committed&nbsp;<code>dist\/<\/code>&nbsp;bundle makes the action executable without running&nbsp;<code>npm install<\/code>&nbsp;in the consumer workflow.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Toolkit modules<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Common packages include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@actions\/core\n@actions\/github\n@actions\/exec\n@actions\/io\n@actions\/tool-cache\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>@actions\/github<\/code>&nbsp;for authenticated GitHub API access and Octokit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">23.4 Pre\/main\/post actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Actions can have setup and cleanup phases.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    P&#91;pre] --&gt; M&#91;main] --&gt; O&#91;post]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use state when the post phase needs information created earlier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>start\/stop a service;<\/li>\n\n\n\n<li>mount\/unmount;<\/li>\n\n\n\n<li>acquire\/release temporary resource;<\/li>\n\n\n\n<li>login\/logout.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">23.5 Docker action<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>action.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Policy Check\ndescription: Run a policy tool in a controlled container\n\ninputs:\n  path:\n    description: Path to scan\n    required: true\n\nruns:\n  using: docker\n  image: Dockerfile\n  args:\n    - ${{ inputs.path }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>Dockerfile<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM alpine:3.22\n\nRUN apk add --no-cache bash\n\nCOPY entrypoint.sh \/entrypoint.sh\nRUN chmod +x \/entrypoint.sh\n\nENTRYPOINT &#91;\"\/entrypoint.sh\"]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>entrypoint.sh<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env bash\nset -euo pipefail\n\ntarget=\"$1\"\necho \"Scanning: $target\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A Docker action should write files that later steps need under the GitHub workspace mount.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">23.6 Composite action<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Composite actions are ideal for setup sequences.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Python Quality\ndescription: Run standardized Python quality checks\n\ninputs:\n  python-version:\n    required: false\n    default: \"3.14\"\n\nruns:\n  using: composite\n  steps:\n    - uses: actions\/setup-python@v5\n      with:\n        python-version: ${{ inputs.python-version }}\n\n    - shell: bash\n      run: pip install -r requirements-dev.txt\n\n    - shell: bash\n      run: ruff check .\n\n    - shell: bash\n      run: pytest\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">23.7 Versioning custom actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For a shared action repository:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>v1.0.0  immutable release\nv1.1.0  immutable release\nv1      moving major tag\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Consumers seeking maximum supply-chain assurance can pin the release commit SHA.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">24. ADVANCED \u2014 Workflow Data Flow<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">CI\/CD design is mostly data movement under trust constraints.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">24.1 Data-passing mechanisms<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Mechanism<\/th><th class=\"has-text-align-left\" data-align=\"left\">Scope<\/th><th class=\"has-text-align-left\" data-align=\"left\">Best for<\/th><\/tr><\/thead><tbody><tr><td>Shell variable<\/td><td>current process<\/td><td>temporary command logic<\/td><\/tr><tr><td><code>GITHUB_ENV<\/code><\/td><td>later steps in same job<\/td><td>runtime configuration<\/td><\/tr><tr><td>Step output<\/td><td>later steps\/jobs via mapping<\/td><td>small values<\/td><\/tr><tr><td>Job output<\/td><td>dependent jobs<\/td><td>small values<\/td><\/tr><tr><td>Reusable workflow output<\/td><td>caller<\/td><td>workflow API contract<\/td><\/tr><tr><td>Artifact<\/td><td>jobs\/runs<\/td><td>files, bundles, reports<\/td><\/tr><tr><td>Cache<\/td><td>later runs<\/td><td>performance data<\/td><\/tr><tr><td>External store<\/td><td>arbitrary workflows<\/td><td>durable shared state<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">24.2 Step \u2192 job \u2192 workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Step] --&gt;|GITHUB_OUTPUT| B&#91;Step output]\n    B --&gt; C&#91;Job output]\n    C --&gt;|needs| D&#91;Dependent job]\n    C --&gt; E&#91;Reusable workflow output]\n    E --&gt; F&#91;Caller workflow]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">24.3 JSON as a workflow interchange format<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Producer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- id: components\n  run: |\n    echo 'value=&#91;\"api\",\"worker\",\"frontend\"]' &gt;&gt; \"$GITHUB_OUTPUT\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Consumer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix:\n    component: ${{ fromJSON(needs.detect.outputs.components) }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">24.4 Artifact-based transfer<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use artifacts instead of outputs for large data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not put these into outputs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>binary data;<\/li>\n\n\n\n<li>large JSON documents;<\/li>\n\n\n\n<li>Terraform plan files;<\/li>\n\n\n\n<li>coverage directories;<\/li>\n\n\n\n<li>container tarballs.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">24.5 Trust classification<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before passing data downstream, classify it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>trusted configuration\ntrusted build output\nuntrusted PR metadata\nuntrusted PR artifact\nexternal API response\nuser-supplied workflow_dispatch input\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not treat &#8220;came from another job&#8221; as synonymous with &#8220;trusted.&#8221;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">25. ADVANCED \u2014 Conditional Workflows<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">25.1 Event condition<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.event_name == 'push'\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.2 Branch condition<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.ref == 'refs\/heads\/main'\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.3 Tag condition<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: startsWith(github.ref, 'refs\/tags\/v')\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.4 Actor condition<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.actor == 'release-bot'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use actor checks only as one signal. Do not treat username comparison as a substitute for permissions or environment protection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">25.5 Pull-request condition<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.event.pull_request.draft == false\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.6 Matrix condition<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: matrix.os == 'ubuntu-latest'\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.7 Fork detection<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.event.pull_request.head.repo.full_name == github.repository\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This can separate same-repository PR behavior from fork behavior.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">25.8 Failure-aware cleanup<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: .\/test.sh\n\n  collect:\n    needs: test\n    if: failure()\n    runs-on: ubuntu-latest\n    steps:\n      - run: .\/collect-remote-logs.sh\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">25.9 Avoid unreadable conditions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: &gt;\n  (github.event_name == 'push' &amp;&amp; github.ref == 'refs\/heads\/main') ||\n  (github.event_name == 'workflow_dispatch' &amp;&amp; inputs.force == true) ||\n  ...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Better approaches:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>split jobs;<\/li>\n\n\n\n<li>calculate a decision once;<\/li>\n\n\n\n<li>use a reusable workflow;<\/li>\n\n\n\n<li>keep policy in a script with tests.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A workflow should be readable during an incident.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">26. ADVANCED \u2014 Error Handling<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">26.1 Failure hierarchy<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>command exit code\n    \u2193\nstep outcome\/conclusion\n    \u2193\njob conclusion\n    \u2193\ndependent-job eligibility\n    \u2193\nworkflow conclusion\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">26.2 Step failure<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: exit 1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Normally fails the job.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">26.3 Controlled non-blocking failure<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- id: optional-scan\n  continue-on-error: true\n  run: .\/scan.sh\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Later:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- if: steps.optional-scan.outcome == 'failure'\n  run: echo \"Optional scan failed\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">26.4 Matrix failure<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  fail-fast: false\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">when every compatibility result matters.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">26.5 Retry pattern<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions does not mean every operation should be blindly retried.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Good retry candidates:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>transient network request;<\/li>\n\n\n\n<li>eventually consistent cloud API;<\/li>\n\n\n\n<li>temporary package mirror error.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Bad retry candidates:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>deterministic unit-test failure;<\/li>\n\n\n\n<li>compiler error;<\/li>\n\n\n\n<li>policy violation.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Shell example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for attempt in 1 2 3; do\n  if curl --fail --retry 2 https:\/\/service.example\/health; then\n    exit 0\n  fi\n  sleep $((attempt * 5))\ndone\n\nexit 1\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">26.6 Rollback on failure<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>deploy:\n  ...\n\nverify:\n  needs: deploy\n  ...\n\nrollback:\n  needs: &#91;deploy, verify]\n  if: failure() &amp;&amp; needs.deploy.result == 'success'\n  ...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Automated rollback should be used only when rollback itself is well-tested and safe.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">26.7 Cancellation-aware cleanup<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: always()\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">can execute after cancellation in some workflow evaluation paths, depending on where it is used.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For costly downstream jobs, often prefer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: ${{ !cancelled() }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For local resource cleanup that truly must run, use action post steps or carefully designed cleanup steps.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">27. ADVANCED \u2014 Troubleshooting and Debugging<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Troubleshooting GitHub Actions is easier when you identify&nbsp;<strong>which layer failed<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Workflow not behaving] --&gt; B{Did workflow start?}\n    B -- No --&gt; C&#91;Trigger \/ branch \/ path \/ disabled workflow]\n    B -- Yes --&gt; D{Did job start?}\n    D -- No --&gt; E&#91;if \/ needs \/ environment \/ runner queue]\n    D -- Yes --&gt; F{Did step start?}\n    F -- No --&gt; G&#91;step if \/ previous failure]\n    F -- Yes --&gt; H{What failed?}\n    H --&gt; I&#91;command\/action]\n    H --&gt; J&#91;permission\/auth]\n    H --&gt; K&#91;network\/DNS]\n    H --&gt; L&#91;cache\/artifact]\n    H --&gt; M&#91;service\/container]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">27.1 Workflow syntax errors<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Symptoms:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>workflow not listed;<\/li>\n\n\n\n<li>invalid workflow file;<\/li>\n\n\n\n<li>parser error.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>indentation;<\/li>\n\n\n\n<li>valid keys;<\/li>\n\n\n\n<li>expression syntax;<\/li>\n\n\n\n<li>unsupported context in a key;<\/li>\n\n\n\n<li>correct\u00a0<code>.github\/workflows<\/code>\u00a0path.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>actionlint<\/code>&nbsp;during development.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">27.2 Trigger debugging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ask:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Did the event happen?<\/li>\n\n\n\n<li>Is the workflow file on the relevant branch\/default branch?<\/li>\n\n\n\n<li>Does\u00a0<code>on<\/code>\u00a0match the event?<\/li>\n\n\n\n<li>Do branch filters match?<\/li>\n\n\n\n<li>Do path filters match?<\/li>\n\n\n\n<li>Does the event support the expected ref?<\/li>\n\n\n\n<li>Was the workflow disabled?<\/li>\n\n\n\n<li>Was the event created by a token behavior that intentionally does not trigger another workflow?<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">27.3 Context\/expression debugging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Print only the properties you need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: |\n    echo \"event=${{ github.event_name }}\"\n    echo \"ref=${{ github.ref }}\"\n    echo \"head=${{ github.head_ref }}\"\n    echo \"base=${{ github.base_ref }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For structured debugging:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- env:\n    MATRIX: ${{ toJSON(matrix) }}\n  run: printf '%s\\n' \"$MATRIX\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid whole-context dumps when tokens or secrets may be included.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">27.4 Runner debugging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Print:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: |\n    echo \"OS=$RUNNER_OS\"\n    echo \"ARCH=$RUNNER_ARCH\"\n    echo \"ENV=$RUNNER_ENVIRONMENT\"\n    echo \"WORKSPACE=$GITHUB_WORKSPACE\"\n    df -h\n    free -h || true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Self-hosted runner checks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runner online?\nlabels correct?\nrunner group allows repo?\nnetwork egress works?\nrunner version supported?\ndisk full?\nservice running?\nworkspace permissions?\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">27.5 Authentication failures<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Differentiate:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>401 -&gt; identity\/token invalid or absent\n403 -&gt; identity known but permission\/policy denies\n404 -&gt; resource may be hidden by authorization or truly absent\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>workflow\/job\u00a0<code>permissions<\/code>;<\/li>\n\n\n\n<li>environment approval;<\/li>\n\n\n\n<li>fork restrictions;<\/li>\n\n\n\n<li>token audience;<\/li>\n\n\n\n<li>cloud trust policy;<\/li>\n\n\n\n<li>repository access policy.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">27.6 Network troubleshooting<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Useful Linux commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>getent hosts example.com\ncurl -v https:\/\/example.com\nnc -vz host.example 443\nip route\ncat \/etc\/resolv.conf\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On self-hosted runners also check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>security groups\/firewall;<\/li>\n\n\n\n<li>proxy;<\/li>\n\n\n\n<li>private DNS;<\/li>\n\n\n\n<li>NAT;<\/li>\n\n\n\n<li>route tables;<\/li>\n\n\n\n<li>endpoint policies;<\/li>\n\n\n\n<li>TLS interception.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">27.7 Service container failures<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>image starts;<\/li>\n\n\n\n<li>health check;<\/li>\n\n\n\n<li>port mapping;<\/li>\n\n\n\n<li>service hostname;<\/li>\n\n\n\n<li>credentials;<\/li>\n\n\n\n<li>startup time;<\/li>\n\n\n\n<li>runner Docker capability.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">27.8 Cache problems<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ask:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Is the key exactly what you expect?<\/li>\n\n\n\n<li>Did\u00a0<code>hashFiles<\/code>\u00a0match any files?<\/li>\n\n\n\n<li>Is\u00a0<code>cache-mode<\/code>\u00a0allowing restore\/save?<\/li>\n\n\n\n<li>Is the workflow low-trust?<\/li>\n\n\n\n<li>Is the cache scoped to a different branch?<\/li>\n\n\n\n<li>Is the cache stale by design?<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">27.9 Artifact problems<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>upload path exists;<\/li>\n\n\n\n<li>hidden files behavior if relevant;<\/li>\n\n\n\n<li>artifact name;<\/li>\n\n\n\n<li>download path;<\/li>\n\n\n\n<li>retention;<\/li>\n\n\n\n<li>permissions for cross-run\/cross-repo access.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">27.10 Debug logging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Repository\/organization settings can enable runner\/step debug logging. Also use targeted verbose flags in your tools.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not enable excessive debug logging permanently on secret-heavy production workflows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">27.11 Re-runs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Understand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GITHUB_RUN_ID       stable for the run\nGITHUB_RUN_ATTEMPT  increases on re-run\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A re-run can have a different triggering user, but privileges are based on GitHub&#8217;s documented actor behavior. Do not assume &#8220;person clicking re-run&#8221; automatically changes all permission semantics.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">28. ADVANCED \u2014 Security Hardening<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Security is not a final workflow step. It is part of the workflow&#8217;s execution model.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">28.1 Trust boundaries<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Classify workflow inputs and execution:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    U&#91;Untrusted PR \/ issue \/ external input] --&gt; L&#91;Low privilege]\n    T&#91;Trusted branch \/ release] --&gt; H&#91;Higher privilege]\n    L --&gt; V&#91;Validation]\n    H --&gt; D&#91;Deployment \/ publish]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">28.2 Least privilege<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Start:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions: {}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then grant individual jobs what they need.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  test:\n    permissions:\n      contents: read\n\n  publish:\n    permissions:\n      contents: read\n      packages: write\n      id-token: write\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">28.3 Script injection<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dangerous:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- run: |\n    echo \"PR: ${{ github.event.pull_request.title }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">An attacker controls the title.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Safer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- env:\n    PR_TITLE: ${{ github.event.pull_request.title }}\n  run: |\n    printf 'PR: %s\\n' \"$PR_TITLE\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Best when practical: pass untrusted values as data to an action or program that does not interpret them as shell syntax.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">28.4&nbsp;<code>pull_request<\/code>&nbsp;vs&nbsp;<code>pull_request_target<\/code><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Property<\/th><th class=\"has-text-align-left\" data-align=\"left\"><code>pull_request<\/code><\/th><th class=\"has-text-align-left\" data-align=\"left\"><code>pull_request_target<\/code><\/th><\/tr><\/thead><tbody><tr><td>Primary context<\/td><td>PR\/merge code<\/td><td>base repository<\/td><\/tr><tr><td>Fork token<\/td><td>restricted\/read-oriented<\/td><td>privileged base context<\/td><\/tr><tr><td>Secrets for fork PR<\/td><td>withheld<\/td><td>potentially available<\/td><\/tr><tr><td>Good for running PR code<\/td><td>Yes, under low privilege<\/td><td><strong>No<\/strong><\/td><\/tr><tr><td>Good for label\/comment triage<\/td><td>Possible with limitations<\/td><td>Yes<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Dangerous pattern<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  pull_request_target:\n\njobs:\n  unsafe:\n    permissions:\n      contents: write\n    steps:\n      - uses: actions\/checkout@v6\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - run: npm install\n      - run: npm test\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This combines privileged context with attacker-controlled code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use&nbsp;<code>pull_request_target<\/code>&nbsp;only when you understand and preserve the trust boundary.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">28.5 Third-party action supply chain<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A workflow action can:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>read repository files;<\/li>\n\n\n\n<li>read available secrets;<\/li>\n\n\n\n<li>use the job token;<\/li>\n\n\n\n<li>alter outputs;<\/li>\n\n\n\n<li>make network requests.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Treat action code like any other dependency with execution privileges.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Production controls:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>allowlist actions;<\/li>\n\n\n\n<li>pin third-party actions by full SHA;<\/li>\n\n\n\n<li>review source;<\/li>\n\n\n\n<li>automate update PRs;<\/li>\n\n\n\n<li>restrict job permissions;<\/li>\n\n\n\n<li>isolate privileged jobs.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">28.6 Self-hosted runner security<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never place public untrusted PR workloads on a persistent runner with access to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>production networks;<\/li>\n\n\n\n<li>cloud instance role credentials;<\/li>\n\n\n\n<li>internal systems;<\/li>\n\n\n\n<li>signing keys;<\/li>\n\n\n\n<li>persistent Docker sockets;<\/li>\n\n\n\n<li>privileged Kubernetes service accounts.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer ephemeral isolated runners.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">28.7 Cache poisoning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Low-trust workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cache-mode: read\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Privileged trusted workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cache-mode: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Better: separate cache namespaces where trust levels differ.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">28.8 Security baseline<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Secure CI\n\non:\n  pull_request:\n\npermissions:\n  contents: read\n\ncache-mode: read\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Process untrusted title safely\n        env:\n          PR_TITLE: ${{ github.event.pull_request.title }}\n        run: printf '%s\\n' \"$PR_TITLE\"\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">29. ADVANCED \u2014 OpenID Connect<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">OIDC lets GitHub Actions obtain short-lived cloud credentials without storing a long-lived cloud access key in GitHub.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.1 Traditional secret model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Cloud access key] --&gt; S&#91;GitHub secret]\n    S --&gt; W&#91;Workflow]\n    W --&gt; C2&#91;Cloud API]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Problems:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>manual rotation;<\/li>\n\n\n\n<li>duplicated long-lived credential;<\/li>\n\n\n\n<li>blast radius if leaked.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">29.2 OIDC model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>sequenceDiagram\n    participant W as GitHub Actions Job\n    participant G as GitHub OIDC Provider\n    participant C as Cloud STS\/IAM\n    participant R as Cloud Resource\n\n    W-&gt;&gt;G: Request OIDC ID token\n    G--&gt;&gt;W: Signed short-lived ID token\n    W-&gt;&gt;C: Exchange token under trust policy\n    C--&gt;&gt;W: Short-lived cloud credential\n    W-&gt;&gt;R: Authorized request\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">29.3 Workflow permission<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OIDC requires:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  id-token: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>id-token: write<\/code>&nbsp;allows the job to request an ID token. It does&nbsp;<strong>not<\/strong>&nbsp;itself grant cloud resource access. The cloud-side trust policy decides what the token can assume.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.4 Claims<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical claims identify:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>repository;<\/li>\n\n\n\n<li>owner;<\/li>\n\n\n\n<li>branch\/ref;<\/li>\n\n\n\n<li>tag;<\/li>\n\n\n\n<li>environment;<\/li>\n\n\n\n<li>workflow;<\/li>\n\n\n\n<li>audience.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Cloud trust policy should be narrow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.5 AWS OIDC example<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: AWS Deploy\n\non:\n  push:\n    branches: &#91;main]\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  deploy:\n    environment: production\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Configure AWS credentials\n        uses: aws-actions\/configure-aws-credentials@&lt;PINNED_SHA&gt;\n        with:\n          role-to-assume: arn:aws:iam::123456789012:role\/github-production\n          aws-region: ap-northeast-1\n\n      - run: aws sts get-caller-identity\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Illustrative AWS trust condition:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"Effect\": \"Allow\",\n  \"Principal\": {\n    \"Federated\": \"arn:aws:iam::123456789012:oidc-provider\/token.actions.githubusercontent.com\"\n  },\n  \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n  \"Condition\": {\n    \"StringEquals\": {\n      \"token.actions.githubusercontent.com:aud\": \"sts.amazonaws.com\"\n    },\n    \"StringLike\": {\n      \"token.actions.githubusercontent.com:sub\": \"repo:my-org\/my-repo:*\"\n    }\n  }\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tighten the subject further to the exact branch or environment model you use.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">AWS-specific point<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">AWS does not support GitHub OIDC custom claims in the same way some other providers do. Design AWS trust using supported standard claims and GitHub&#8217;s documented subject format.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.6 Azure OIDC<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  id-token: write\n  contents: read\n\nsteps:\n  - uses: azure\/login@&lt;PINNED_SHA&gt;\n    with:\n      client-id: ${{ vars.AZURE_CLIENT_ID }}\n      tenant-id: ${{ vars.AZURE_TENANT_ID }}\n      subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Azure federated credentials map GitHub identity claims to an Entra application or managed identity trust.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.7 Google Cloud Workload Identity Federation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  id-token: write\n\nsteps:\n  - uses: google-github-actions\/auth@&lt;PINNED_SHA&gt;\n    with:\n      workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }}\n      service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GCP validates GitHub OIDC assertions through Workload Identity Federation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.8 Vault and other providers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Any provider that can validate GitHub&#8217;s OIDC issuer and claims can use the model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub identity -&gt; provider trust policy -&gt; short-lived credential\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">29.9 2026 immutable subject claims<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Current GitHub OIDC documentation includes immutable subject formats for newer\/opted-in repositories using numeric owner\/repository identifiers in addition to names. This reduces risks from repository renames\/transfers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When establishing new cloud trust in 2026+, inspect the actual&nbsp;<code>sub<\/code>&nbsp;format emitted for your repository and match the current GitHub OIDC reference rather than copying an old blog&#8217;s trust-policy string.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">29.10 OIDC security checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>grant\u00a0<code>id-token: write<\/code>\u00a0only to jobs that need it;<\/li>\n\n\n\n<li>restrict cloud role subject;<\/li>\n\n\n\n<li>restrict audience;<\/li>\n\n\n\n<li>bind production roles to protected environments where appropriate;<\/li>\n\n\n\n<li>use separate roles per environment;<\/li>\n\n\n\n<li>keep cloud role permissions least-privilege;<\/li>\n\n\n\n<li>use short session durations;<\/li>\n\n\n\n<li>log role assumption;<\/li>\n\n\n\n<li>never fall back to a long-lived key merely because OIDC trust configuration is inconvenient.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">30. ADVANCED \u2014 Artifact Attestations<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Artifact attestations establish verifiable information about where and how software was built.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">30.1 Why provenance matters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A binary can be genuine only if you can answer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Which repository?\nWhich workflow?\nWhich commit?\nWhich identity?\nWhich build process?\nWas the artifact changed afterward?\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">30.2 Supply-chain flow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    S&#91;Source commit] --&gt; B&#91;GitHub Actions build]\n    B --&gt; A&#91;Artifact \/ image]\n    B --&gt; P&#91;Provenance attestation]\n    A --&gt; V&#91;Verifier]\n    P --&gt; V\n    V --&gt; D{Policy satisfied?}\n    D -- Yes --&gt; R&#91;Release \/ deploy]\n    D -- No --&gt; X&#91;Reject]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">30.3 Permissions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Attestation workflows commonly need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  id-token: write\n  attestations: write\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Package\/container publishing also needs the relevant package permission.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">30.4 Container provenance concept<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Build and push image\n  id: push\n  uses: docker\/build-push-action@&lt;PINNED_SHA&gt;\n  with:\n    push: true\n    tags: ghcr.io\/my-org\/my-app:${{ github.sha }}\n\n- name: Attest image\n  uses: actions\/attest-build-provenance@&lt;PINNED_SHA&gt;\n  with:\n    subject-name: ghcr.io\/my-org\/my-app\n    subject-digest: ${{ steps.push.outputs.digest }}\n    push-to-registry: true\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use the exact current action\/interface documented by GitHub when implementing; attestation features evolve.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">30.5 Kubernetes enforcement<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A Kubernetes admission policy can reject images without valid trusted provenance.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sequenceDiagram\n    participant D as Deployment\n    participant A as Admission Controller\n    participant R as Registry\/Attestation Store\n    participant K as Kubernetes API\n\n    D-&gt;&gt;K: Create Pod\n    K-&gt;&gt;A: Admission review\n    A-&gt;&gt;R: Verify image provenance\n    R--&gt;&gt;A: Attestation evidence\n    A--&gt;&gt;K: Allow \/ deny\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub documents Sigstore Policy Controller integration for enforcing GitHub artifact attestations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">30.6 What attestations do not solve<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Attestations do not prove:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>source code is bug-free;<\/li>\n\n\n\n<li>dependencies are safe;<\/li>\n\n\n\n<li>workflow itself is secure;<\/li>\n\n\n\n<li>maintainer intent is good.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">They prove selected provenance\/integrity properties. Pair them with code review, dependency security, pinning, branch protection, and isolated builds.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">31. ADVANCED \u2014 Docker and Container CI\/CD<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">31.1 Container pipeline<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Code] --&gt; T&#91;Test]\n    T --&gt; B&#91;Buildx \/ BuildKit]\n    B --&gt; I&#91;Image]\n    I --&gt; S&#91;Scan]\n    I --&gt; A&#91;Attest\/sign]\n    S --&gt; P&#91;Push registry]\n    A --&gt; P\n    P --&gt; D&#91;Deploy]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">31.2 Buildx example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  image:\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n      packages: write\n      id-token: write\n      attestations: write\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: docker\/setup-buildx-action@&lt;PINNED_SHA&gt;\n\n      - uses: docker\/login-action@&lt;PINNED_SHA&gt;\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - id: build\n        uses: docker\/build-push-action@&lt;PINNED_SHA&gt;\n        with:\n          context: .\n          push: true\n          tags: ghcr.io\/${{ github.repository }}:${{ github.sha }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">31.3 Image tagging<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use immutable tags:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sha-&lt;commit&gt;\nrelease version\nimage digest\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not rely on only:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>latest\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A robust model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ghcr.io\/org\/app:sha-a1b2c3...\nghcr.io\/org\/app:v2.4.1\ndigest: sha256:...\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Deploy by digest for strongest immutability.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">31.4 Multi-platform<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>with:\n  platforms: linux\/amd64,linux\/arm64\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Multi-platform builds may use emulation or native builders. Measure build time and verify architecture-specific tests.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">31.5 Docker layer caching<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">BuildKit cache can use suitable cache exporters\/importers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The exact cache design depends on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>builder topology;<\/li>\n\n\n\n<li>trust model;<\/li>\n\n\n\n<li>registry;<\/li>\n\n\n\n<li>build size;<\/li>\n\n\n\n<li>private dependencies.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Treat build cache as untrusted performance data unless you have a stronger provenance model.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">31.6 Image scanning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A release path often includes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dependency scan\nDockerfile lint\nimage vulnerability scan\nSBOM\npolicy gate\nprovenance\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not fail production simply because &#8220;scanner returned any CVE.&#8221; Define severity, exploitability, exception, and SLA policies.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">32. ADVANCED \u2014 Package Publishing<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions can publish:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>npm;<\/li>\n\n\n\n<li>Maven;<\/li>\n\n\n\n<li>Gradle packages;<\/li>\n\n\n\n<li>NuGet;<\/li>\n\n\n\n<li>RubyGems;<\/li>\n\n\n\n<li>container packages;<\/li>\n\n\n\n<li>other registries.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">32.1 npm publishing model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    T&#91;Tag\/release] --&gt; B&#91;Build\/test]\n    B --&gt; A&#91;Package]\n    A --&gt; P&#91;Registry]\n    P --&gt; C&#91;Consumers]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use trusted release triggers and protected publishing credentials.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">32.2 GitHub Packages permissions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A package job may need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n  packages: write\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">32.3 npm example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Publish npm\n\non:\n  release:\n    types: &#91;published]\n\npermissions:\n  contents: read\n  packages: write\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: \"24\"\n          registry-url: https:\/\/npm.pkg.github.com\n\n      - run: npm ci\n      - run: npm test\n      - run: npm publish\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">External registries may require registry-specific OIDC\/trusted publishing or a secret.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">32.4 Package provenance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Where supported, combine package publishing with provenance\/attestation so consumers can verify build origin.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">33. ADVANCED \u2014 Releases<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">33.1 Tag-driven release<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    tags:\n      - \"v*\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">33.2 Release-event-driven<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  release:\n    types: &#91;published]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Choose based on ownership:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>tag is source of truth;<\/li>\n\n\n\n<li>GitHub Release publication is source of truth;<\/li>\n\n\n\n<li>an external release manager triggers the workflow.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">33.3 Semantic versioning<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MAJOR.MINOR.PATCH\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1.4.3\n2.0.0\n2.1.0-rc.1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A release workflow should validate version format and prevent accidental republishing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">33.4 Release assets<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: write\n\nsteps:\n  - env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    run: |\n      gh release upload \\\n        \"${{ github.ref_name }}\" \\\n        dist\/app-linux-amd64 \\\n        dist\/app-linux-arm64\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">33.5 Automated release architecture<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    M&#91;Merge to main] --&gt; V&#91;Version decision]\n    V --&gt; T&#91;Tag]\n    T --&gt; B&#91;Build once]\n    B --&gt; A&#91;Artifacts]\n    A --&gt; R&#91;GitHub Release]\n    A --&gt; P&#91;Package registry]\n    R --&gt; D&#91;Deployment promotion]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep &#8220;build release artifact&#8221; separate from &#8220;deploy artifact&#8221; when possible.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">34. ADVANCED \u2014 Cloud Deployments<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Cloud deployments combine GitHub identity, cloud identity, network reachability, immutable artifacts, and deployment policy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">34.1 Cloud deployment reference architecture<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    G&#91;GitHub repository] --&gt; W&#91;Workflow]\n    W --&gt; O&#91;OIDC]\n    O --&gt; I&#91;Cloud IAM \/ STS]\n    I --&gt; C&#91;Short-lived credentials]\n    C --&gt; R&#91;Cloud resource]\n    W --&gt; A&#91;(Build artifact\/image)]\n    A --&gt; R\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">34.2 AWS<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Authentication<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Preferred:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub OIDC -&gt; AWS IAM role -&gt; temporary STS credentials\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid static AWS access keys in repository secrets when OIDC is available.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">S3 deployment<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Sync static site\n  run: aws s3 sync dist\/ s3:\/\/my-site-bucket\/ --delete\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Give the role only the required bucket\/object permissions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">EC2 deployment<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Possible patterns:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SSM Run Command;<\/li>\n\n\n\n<li>CodeDeploy;<\/li>\n\n\n\n<li>immutable AMI + Auto Scaling;<\/li>\n\n\n\n<li>instance pull from artifact store.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid opening SSH from GitHub-hosted runner ranges merely to copy files to production.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ECR<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Login to ECR\n  id: ecr\n  uses: aws-actions\/amazon-ecr-login@&lt;PINNED_SHA&gt;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Build\/push, then deploy by immutable digest.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ECS<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Typical flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>build image -&gt; push ECR -&gt; update task definition -&gt; deploy service -&gt; wait stable\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">EKS<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Typical flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>OIDC -&gt; IAM role -&gt; EKS auth -&gt; Helm\/kubectl -&gt; rollout verification\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A private EKS endpoint requires a runner with network reachability into the VPC\/network.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Lambda<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Patterns:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>zip artifact;<\/li>\n\n\n\n<li>container image;<\/li>\n\n\n\n<li>SAM\/CDK\/CloudFormation;<\/li>\n\n\n\n<li>direct CLI for simple use cases.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">CloudFormation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Production flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>validate -&gt; create change set -&gt; review\/gate -&gt; execute -&gt; monitor\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Terraform<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Production flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fmt -&gt; validate -&gt; security checks -&gt; plan -&gt; review\/gate -&gt; apply\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use cloud\/OIDC auth and remote-state controls.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">34.3 Azure<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OIDC architecture:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub OIDC -&gt; Entra federated credential -&gt; Azure identity -&gt; resource\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Targets:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Web Apps;<\/li>\n\n\n\n<li>Container Apps;<\/li>\n\n\n\n<li>AKS;<\/li>\n\n\n\n<li>Functions;<\/li>\n\n\n\n<li>Azure Storage;<\/li>\n\n\n\n<li>Terraform.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Keep production identities\/environment protections separate from test identities.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">34.4 Google Cloud<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Preferred model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub OIDC -&gt; Workload Identity Federation -&gt; service account -&gt; GCP resource\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Targets:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GKE;<\/li>\n\n\n\n<li>Cloud Run;<\/li>\n\n\n\n<li>Artifact Registry;<\/li>\n\n\n\n<li>Cloud Storage;<\/li>\n\n\n\n<li>Terraform.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">34.5 Cloud deployment checklist<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before a production cloud workflow is considered complete:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] no long-lived cloud key where OIDC can replace it;<\/li>\n\n\n\n<li>[ ] cloud role restricted to exact repository\/ref\/environment;<\/li>\n\n\n\n<li>[ ] production uses protected GitHub environment;<\/li>\n\n\n\n<li>[ ] immutable artifact\/image;<\/li>\n\n\n\n<li>[ ] deployment serialized;<\/li>\n\n\n\n<li>[ ] timeout configured;<\/li>\n\n\n\n<li>[ ] smoke\/health verification;<\/li>\n\n\n\n<li>[ ] logs and deployment record;<\/li>\n\n\n\n<li>[ ] rollback path;<\/li>\n\n\n\n<li>[ ] least-privilege cloud IAM;<\/li>\n\n\n\n<li>[ ] private-resource networking solved explicitly;<\/li>\n\n\n\n<li>[ ] third-party actions pinned.<\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">35. ADVANCED \u2014 Kubernetes CI\/CD<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions is commonly used to build immutable images and then deploy them to Kubernetes using&nbsp;<code>kubectl<\/code>, Helm, Kustomize, a GitOps system, or a cloud-specific deployment action.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">35.1 Deployment model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Code] --&gt; T&#91;Test]\n    T --&gt; B&#91;Build image]\n    B --&gt; R&#91;Container registry]\n    R --&gt; D&#91;Deploy manifest\/Helm]\n    D --&gt; K&#91;Kubernetes]\n    K --&gt; V&#91;Rollout verification]\n    V --&gt; S&#91;Smoke tests]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">35.2 Authentication<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not store a long-lived administrator&nbsp;<code>kubeconfig<\/code>&nbsp;in GitHub secrets if a stronger identity model is available.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Preferred patterns include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>cloud OIDC -> cloud IAM -> cluster authentication;<\/li>\n\n\n\n<li>short-lived Kubernetes tokens;<\/li>\n\n\n\n<li>workload identity;<\/li>\n\n\n\n<li>GitOps pull model;<\/li>\n\n\n\n<li>tightly scoped service accounts.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">35.3&nbsp;<code>kubectl<\/code>&nbsp;deployment<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Deploy\n  run: |\n    kubectl -n production set image \\\n      deployment\/catalog \\\n      catalog=\"$IMAGE@$DIGEST\"\n\n    kubectl -n production rollout status \\\n      deployment\/catalog \\\n      --timeout=5m\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use image digests when possible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">35.4 Helm<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Helm upgrade\n  run: |\n    helm upgrade --install catalog .\/charts\/catalog \\\n      --namespace production \\\n      --create-namespace \\\n      --set image.repository=\"$IMAGE_REPOSITORY\" \\\n      --set image.digest=\"$IMAGE_DIGEST\" \\\n      --wait \\\n      --timeout 5m\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">35.5 Kustomize<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kustomize build overlays\/production | kubectl apply -f -\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For GitOps, update an environment repository rather than applying directly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">35.6 Namespace isolation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Separate environments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dev\nstaging\nuat\nproduction\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But namespaces alone are not always enough isolation. Consider:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>separate clusters\/accounts\/subscriptions;<\/li>\n\n\n\n<li>network policies;<\/li>\n\n\n\n<li>RBAC;<\/li>\n\n\n\n<li>secret boundaries;<\/li>\n\n\n\n<li>admission policy;<\/li>\n\n\n\n<li>workload identity.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">35.7 EKS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A secure EKS pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    G&#91;GitHub job] --&gt; O&#91;GitHub OIDC]\n    O --&gt; I&#91;AWS IAM role]\n    I --&gt; E&#91;EKS authentication]\n    G --&gt; R&#91;ECR]\n    E --&gt; K&#91;EKS API]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Private cluster endpoint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub-hosted public runner -&gt; cannot directly reach private endpoint\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Possible solution:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ephemeral self-hosted runner \/ ARC inside VPC -&gt; private EKS endpoint\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">35.8 AKS \/ GKE<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use Azure\/GCP federated identity to obtain short-lived credentials, then obtain cluster credentials with the narrowest required scope.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">35.9 Rollback<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Kubernetes rollback depends on deployment model.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Imperative example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl rollout undo deployment\/catalog -n production\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Helm:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>helm rollback catalog &lt;revision&gt; -n production\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">GitOps:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>revert environment commit -&gt; reconciler restores prior desired state\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">35.10 Deployment verification<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A deployment is not complete when&nbsp;<code>kubectl apply<\/code>&nbsp;returns zero.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verify:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>rollout completed;<\/li>\n\n\n\n<li>desired replicas available;<\/li>\n\n\n\n<li>readiness healthy;<\/li>\n\n\n\n<li>error rate stable;<\/li>\n\n\n\n<li>smoke test passes;<\/li>\n\n\n\n<li>critical dependencies healthy.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">36. ADVANCED \u2014 Infrastructure as Code<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">IaC workflows deserve stricter controls than ordinary application CI because they can alter networks, IAM, databases, and production resources.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">36.1 Terraform pipeline<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Change] --&gt; F&#91;fmt]\n    F --&gt; V&#91;validate]\n    V --&gt; S&#91;Security\/policy]\n    S --&gt; P&#91;plan]\n    P --&gt; A&#91;(Plan artifact)]\n    A --&gt; G{Approval}\n    G --&gt; X&#91;apply]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">36.2 Pull-request plan<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Terraform PR\n\non:\n  pull_request:\n    paths:\n      - \"infra\/**\"\n      - \".github\/workflows\/terraform-pr.yml\"\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  plan:\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n\n    defaults:\n      run:\n        working-directory: infra\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Configure cloud credentials\n        run: echo \"Use provider-specific OIDC setup here\"\n\n      - name: Terraform fmt\n        run: terraform fmt -check -recursive\n\n      - name: Terraform init\n        run: terraform init -input=false\n\n      - name: Terraform validate\n        run: terraform validate\n\n      - name: Terraform plan\n        run: terraform plan -input=false -out=tfplan\n\n      - name: Upload plan\n        uses: actions\/upload-artifact@v4\n        with:\n          name: tfplan-${{ github.sha }}\n          path: infra\/tfplan\n          retention-days: 5\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">36.3 Production apply<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Production apply should normally require:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>trusted source revision;<\/li>\n\n\n\n<li>protected environment;<\/li>\n\n\n\n<li>short-lived cloud identity;<\/li>\n\n\n\n<li>state locking;<\/li>\n\n\n\n<li>serialized execution;<\/li>\n\n\n\n<li>reviewed plan;<\/li>\n\n\n\n<li>timeout;<\/li>\n\n\n\n<li>audit trail.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  apply:\n    environment: production\n\n    concurrency:\n      group: terraform-production\n      queue: max\n\n    permissions:\n      contents: read\n      id-token: write\n\n    runs-on: self-hosted\n\n    steps:\n      - ...\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">36.4 Plan artifact caution<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A saved Terraform plan can contain sensitive values depending on provider\/resource behavior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Treat plan artifacts as potentially sensitive:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>short retention;<\/li>\n\n\n\n<li>restricted repository access;<\/li>\n\n\n\n<li>do not publish publicly;<\/li>\n\n\n\n<li>avoid logging full plan if it exposes secrets.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">36.5 Terraform Cloud<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Two patterns:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub Actions -&gt; Terraform Cloud API -&gt; remote execution\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub Actions runner -&gt; Terraform CLI -&gt; Terraform Cloud remote state\/execution\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use the model that fits governance and network access.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">36.6 OpenTofu<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Most workflow architecture is similar:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fmt -&gt; init -&gt; validate -&gt; plan -&gt; gate -&gt; apply\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use OpenTofu-specific CLI\/provider\/module compatibility guidance.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">36.7 CloudFormation \/ CDK<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lint\/synth\nvalidate\nsecurity\/policy scan\nchange set\nreview\nexecute\nverify\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">36.8 Pulumi<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use stack-specific credentials\/state and protected environments.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">36.9 Drift detection<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Scheduled workflow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  schedule:\n    - cron: \"30 1 * * *\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The job should&nbsp;<strong>report<\/strong>&nbsp;drift, not automatically destroy\/recreate production infrastructure unless that behavior is intentionally governed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">37. ADVANCED \u2014 Self-Hosted Runner Architecture<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A self-hosted runner fleet is infrastructure, not merely &#8220;a VM with the runner installed.&#8221;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">37.1 Fleet goals<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A production fleet should address:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>trust boundaries;<\/li>\n\n\n\n<li>elasticity;<\/li>\n\n\n\n<li>image management;<\/li>\n\n\n\n<li>patching;<\/li>\n\n\n\n<li>credentials;<\/li>\n\n\n\n<li>networking;<\/li>\n\n\n\n<li>observability;<\/li>\n\n\n\n<li>cleanup;<\/li>\n\n\n\n<li>cost;<\/li>\n\n\n\n<li>disaster recovery.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">37.2 Dedicated vs shared<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dedicated:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>one repository\/team -&gt; one runner pool\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Shared:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>many repositories -&gt; common pool\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Shared fleets have better utilization but a larger trust boundary.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">37.3 Reference architecture<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    G&#91;GitHub Actions] --&gt; RG&#91;Runner group]\n    RG --&gt; Q&#91;Job queue]\n\n    Q --&gt; A1&#91;Ephemeral runner A]\n    Q --&gt; A2&#91;Ephemeral runner B]\n    Q --&gt; A3&#91;Ephemeral runner C]\n\n    subgraph PrivateNetwork&#91;Private network]\n      A1\n      A2\n      A3\n      P&#91;Private package registry]\n      K&#91;Kubernetes API]\n      DB&#91;Private database]\n    end\n\n    A1 --&gt; P\n    A2 --&gt; K\n    A3 --&gt; DB\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">37.4 Golden images<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A runner image can preinstall:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>language runtimes;<\/li>\n\n\n\n<li>Terraform\/OpenTofu;<\/li>\n\n\n\n<li>cloud CLIs;<\/li>\n\n\n\n<li>kubectl\/Helm;<\/li>\n\n\n\n<li>organization CA certificates;<\/li>\n\n\n\n<li>security tools.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>faster jobs;<\/li>\n\n\n\n<li>controlled versions;<\/li>\n\n\n\n<li>repeatability.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Risks:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>stale image;<\/li>\n\n\n\n<li>vulnerable tooling;<\/li>\n\n\n\n<li>snowflake modifications.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Version images immutably:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runner-2026.09.1\nrunner-2026.09.2\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">37.5 Just-in-time \/ ephemeral registration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A mature autoscaled system registers runners when capacity is needed and destroys them after one job.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lifecycle:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    Q&#91;Job demand] --&gt; P&#91;Provision compute]\n    P --&gt; R&#91;Register ephemeral runner]\n    R --&gt; J&#91;Execute one job]\n    J --&gt; U&#91;Upload logs\/results]\n    U --&gt; D&#91;Destroy compute]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">37.6 Network access<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Define egress intentionally.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>general CI pool:\n  internet + package registries\n\ninternal CI pool:\n  internal services + controlled internet\n\nproduction deploy pool:\n  production APIs + artifact registry\n  no arbitrary repository access\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">37.7 Cloud metadata risk<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If a self-hosted runner is on a cloud VM with an instance profile\/managed identity, workflow code may be able to obtain those credentials.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not rely on &#8220;the workflow does not know the credential&#8221; as a security boundary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>no ambient privileged role;<\/li>\n\n\n\n<li>short-lived explicit OIDC identity;<\/li>\n\n\n\n<li>metadata restrictions;<\/li>\n\n\n\n<li>isolated ephemeral compute.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">37.8 Runner monitoring<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Monitor:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>registered count;<\/li>\n\n\n\n<li>online\/offline;<\/li>\n\n\n\n<li>busy\/idle;<\/li>\n\n\n\n<li>queue time;<\/li>\n\n\n\n<li>startup latency;<\/li>\n\n\n\n<li>job duration;<\/li>\n\n\n\n<li>failure rate;<\/li>\n\n\n\n<li>disk;<\/li>\n\n\n\n<li>CPU\/memory;<\/li>\n\n\n\n<li>runner version;<\/li>\n\n\n\n<li>image version;<\/li>\n\n\n\n<li>orphaned runners.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">38. ADVANCED \u2014 Actions Runner Controller (ARC)<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Actions Runner Controller is GitHub&#8217;s Kubernetes-oriented runner orchestration solution.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.1 Mental model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    G&#91;GitHub Actions] &lt;--&gt; L&#91;ARC listener]\n    L --&gt; C&#91;Scale set controller]\n    C --&gt; K&#91;Kubernetes API]\n    K --&gt; P1&#91;Ephemeral runner pod]\n    K --&gt; P2&#91;Ephemeral runner pod]\n    K --&gt; P3&#91;Ephemeral runner pod]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A&nbsp;<strong>runner scale set<\/strong>&nbsp;represents a scalable pool of compatible runners.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.2 Core components<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical concepts include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ARC\/controller;<\/li>\n\n\n\n<li>autoscaling runner scale set;<\/li>\n\n\n\n<li>listener;<\/li>\n\n\n\n<li>runner pods;<\/li>\n\n\n\n<li>Kubernetes namespace;<\/li>\n\n\n\n<li>authentication to GitHub;<\/li>\n\n\n\n<li>runner image;<\/li>\n\n\n\n<li>min\/max runners.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">38.3 Why ARC?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use ARC when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Kubernetes is already an operational platform;<\/li>\n\n\n\n<li>workloads need private-network access;<\/li>\n\n\n\n<li>runners should be ephemeral;<\/li>\n\n\n\n<li>demand changes dynamically;<\/li>\n\n\n\n<li>teams need isolated pools;<\/li>\n\n\n\n<li>custom images are useful.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">38.4 High-level installation flow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>1. Prepare Kubernetes cluster.\n2. Install ARC controller\/chart.\n3. Configure GitHub App\/PAT authentication as supported.\n4. Create runner scale set.\n5. Set runner group\/access policy.\n6. Configure runner image and container mode.\n7. Test job routing.\n8. Add monitoring and upgrades.\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use the exact Helm chart\/API versions from the current GitHub ARC documentation; ARC configuration evolves.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.5 GitHub App vs PAT<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For organization-scale production, GitHub App authentication is generally easier to govern and rotate than a broad personal token.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Evaluate:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>permissions;<\/li>\n\n\n\n<li>installation scope;<\/li>\n\n\n\n<li>organization policy;<\/li>\n\n\n\n<li>secret storage;<\/li>\n\n\n\n<li>rotation;<\/li>\n\n\n\n<li>rate limits.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">38.6 Runner namespace isolation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Possible model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>arc-system\nrunners-general\nrunners-infra\nrunners-production\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use Kubernetes RBAC, network policies, pod security, node pools, and cloud identities to separate trust levels.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.7 Custom runner image<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A runner image should be reproducible.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>FROM &lt;supported-runner-base&gt;\n\n<em># Install pinned organization tools.<\/em>\n<em># Avoid baking long-lived secrets into the image.<\/em>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep sensitive credentials external.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.8 Docker-in-Docker<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">DinD can be useful for image builds but introduces:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>privilege concerns;<\/li>\n\n\n\n<li>storage overhead;<\/li>\n\n\n\n<li>daemon lifecycle;<\/li>\n\n\n\n<li>cache management.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Alternatives include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>BuildKit;<\/li>\n\n\n\n<li>rootless builders;<\/li>\n\n\n\n<li>Kubernetes-native build systems;<\/li>\n\n\n\n<li>remote builders.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Choose based on your security model.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.9 Kubernetes container mode<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ARC can support container-oriented job execution patterns depending on the runner scale-set configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Test:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>filesystem behavior;<\/li>\n\n\n\n<li>service containers;<\/li>\n\n\n\n<li>Docker requirements;<\/li>\n\n\n\n<li>volume access;<\/li>\n\n\n\n<li>network policies.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">38.10 Autoscaling<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Set minimum and maximum based on:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>expected concurrency\nstartup latency\ncluster capacity\nbudget\nburst tolerance\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A pool with&nbsp;<code>minRunners: 0<\/code>&nbsp;may scale to zero but has cold-start latency.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A nonzero minimum reduces queue latency at idle cost.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">38.11 ARC observability<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Track:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>queued GitHub jobs\ndesired runners\nactive runner pods\npod startup time\npod failures\nscheduling failures\nlistener health\ncontroller reconciliation\nKubernetes resource pressure\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">38.12 ARC security<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>use ephemeral runner pods;<\/li>\n\n\n\n<li>separate public\/untrusted and privileged pools;<\/li>\n\n\n\n<li>restrict pod service accounts;<\/li>\n\n\n\n<li>restrict network access;<\/li>\n\n\n\n<li>do not mount cluster-admin credentials;<\/li>\n\n\n\n<li>avoid privileged Docker socket exposure when possible;<\/li>\n\n\n\n<li>apply resource limits;<\/li>\n\n\n\n<li>use dedicated nodes for sensitive pools if required;<\/li>\n\n\n\n<li>keep ARC and runner images patched.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">39. ADVANCED \u2014 Workflow Performance<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Fast feedback changes developer behavior. Slow CI is not merely inconvenient.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">39.1 Measure first<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Important timings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>queue time\nrunner startup\ncheckout\ndependency installation\nbuild\ntests\nartifact upload\ndeployment\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">39.2 Critical path<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Checkout 10s] --&gt; B&#91;Install 60s]\n    B --&gt; C&#91;Build 90s]\n    B --&gt; D&#91;Unit 45s]\n    B --&gt; E&#91;Lint 20s]\n    C --&gt; F&#91;Integration 120s]\n    D --&gt; F\n    E --&gt; F\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Optimizing a 20-second lint that is not on the critical path may not improve total latency.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">39.3 Parallelize independent jobs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lint -&gt; unit -&gt; security -&gt; build\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">use:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>      \/-&gt; lint\nsetup -&gt; unit\n       \\-&gt; security\n       \\-&gt; build\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">when the tasks are truly independent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">39.4 Cache dependencies<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Cache package manager&nbsp;<strong>download caches<\/strong>, not blindly the entire installed dependency directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>npm cache;<\/li>\n\n\n\n<li>pip cache;<\/li>\n\n\n\n<li>Maven local repository;<\/li>\n\n\n\n<li>Gradle cache.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">39.5 Checkout optimization<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If full history is unnecessary, default shallow checkout is efficient.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For special cases:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>with:\n  fetch-depth: 0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">only when history\/tags are needed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sparse checkout can reduce large monorepo I\/O.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">39.6 Selective execution<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>trigger\u00a0<code>paths<\/code>;<\/li>\n\n\n\n<li>changed-component detection;<\/li>\n\n\n\n<li>dynamic matrices;<\/li>\n\n\n\n<li>test-impact analysis.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Do not skip cross-cutting validation that protects shared code merely to gain speed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">39.7 Runner sizing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A bigger runner helps only when the task can use the resources.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Measure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CPU saturation?\nmemory pressure?\ndisk I\/O?\nnetwork?\nsingle-threaded bottleneck?\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">39.8 Image pre-baking<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Self-hosted\/larger custom images can preinstall heavyweight tools to reduce repeated setup.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Balance speed against image maintenance complexity.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">40. ADVANCED \u2014 Cost Optimization<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Actions cost is a combination of compute time, storage, and engineering overhead.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">40.1 Cost equation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Simplified:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CI cost =\n  workflow frequency\n  \u00d7 jobs per run\n  \u00d7 average duration\n  \u00d7 runner rate\n  + artifact\/cache storage\n  + self-hosted infrastructure\/operations\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">40.2 Biggest levers<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>cancel stale PR runs;<\/li>\n\n\n\n<li>avoid unnecessary trigger events;<\/li>\n\n\n\n<li>reduce matrix permutations;<\/li>\n\n\n\n<li>cache expensive dependencies;<\/li>\n\n\n\n<li>parallelize to reduce wall clock\u2014but understand total compute may stay equal or increase;<\/li>\n\n\n\n<li>lower artifact retention;<\/li>\n\n\n\n<li>use appropriate runner sizes;<\/li>\n\n\n\n<li>run expensive scans at the right cadence;<\/li>\n\n\n\n<li>use path-aware monorepo workflows;<\/li>\n\n\n\n<li>measure before migrating to self-hosted merely for cost.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">40.3 Matrix cost explosion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>os: &#91;ubuntu, windows, macos]\nnode: &#91;20, 22, 24]\ndb: &#91;postgres, mysql, sqlite]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">creates:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>3 \u00d7 3 \u00d7 3 = 27 jobs\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ask whether every combination provides unique value.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">40.4 Cost-aware compatibility strategy<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">PR:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>primary OS + supported runtime versions\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nightly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>full OS\/runtime compatibility matrix\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Release:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>all required release targets\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">40.5 Self-hosted economics<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Self-hosted is not &#8220;free.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>compute;<\/li>\n\n\n\n<li>idle capacity;<\/li>\n\n\n\n<li>Kubernetes cluster;<\/li>\n\n\n\n<li>storage;<\/li>\n\n\n\n<li>network;<\/li>\n\n\n\n<li>image building;<\/li>\n\n\n\n<li>patching;<\/li>\n\n\n\n<li>security;<\/li>\n\n\n\n<li>on-call;<\/li>\n\n\n\n<li>monitoring;<\/li>\n\n\n\n<li>autoscaling engineering.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">41. ADVANCED \u2014 Monorepo Workflows<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Monorepos require selective execution without losing confidence in shared dependencies.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">41.1 Naive model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>any file changed -&gt; build every service\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Simple but expensive.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">41.2 Path-filter model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  pull_request:\n    paths:\n      - \"services\/api\/**\"\n      - \"packages\/shared\/**\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Good for independent components but limited when dependencies are complex.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">41.3 Changed-component detection<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    D&#91;Git diff] --&gt; C&#91;Changed files]\n    C --&gt; M&#91;Map files -&gt; components]\n    M --&gt; G&#91;Expand dependency graph]\n    G --&gt; J&#91;Dynamic matrix]\n    J --&gt; B&#91;Build affected components]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">41.4 Dynamic component matrix<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prepare job:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>outputs:\n  matrix: ${{ steps.detect.outputs.matrix }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"component\": &#91;\"api\", \"worker\"]\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Build:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>strategy:\n  matrix: ${{ fromJSON(needs.detect.outputs.matrix) }}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">41.5 Shared library impact<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>services\/api -&gt; packages\/core\nservices\/worker -&gt; packages\/core\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">a change to&nbsp;<code>packages\/core<\/code>&nbsp;should build\/test both services.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Path filters alone cannot fully represent a dependency graph unless manually encoded.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">41.6 Independent deployments<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>api change -&gt; deploy API\nfrontend change -&gt; deploy frontend\nshared change -&gt; deploy affected services\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each component can have its own:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>artifact;<\/li>\n\n\n\n<li>image;<\/li>\n\n\n\n<li>version;<\/li>\n\n\n\n<li>environment;<\/li>\n\n\n\n<li>concurrency group.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">41.7 Monorepo cache keys<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Include component identity:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>key: npm-${{ matrix.component }}-${{ hashFiles(format('{0}\/package-lock.json', matrix.component)) }}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid one giant cache that creates contention and invalidates constantly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">42. ADVANCED \u2014 Multi-Repository Workflows<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Organizations often centralize CI policy while keeping application code in separate repositories.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">42.1 Patterns<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Central reusable workflows<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    A&#91;service-a] --&gt; C&#91;central-workflows]\n    B&#91;service-b] --&gt; C\n    D&#91;service-c] --&gt; C\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Cross-repository dispatch<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Repo A -&gt; API \/ repository_dispatch -&gt; Repo B workflow\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Central deployment repository<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>app repositories -&gt; build artifacts\ndeployment repository -&gt; environment promotion\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">42.2&nbsp;<code>repository_dispatch<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Caller using GitHub CLI\/API conceptually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gh api \\\n  --method POST \\\n  repos\/my-org\/deployer\/dispatches \\\n  -f event_type=deploy-request \\\n  -f 'client_payload&#91;service]=catalog' \\\n  -f 'client_payload&#91;version]=2.4.1'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Receiver:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  repository_dispatch:\n    types: &#91;deploy-request]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">42.3 Authentication<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>GITHUB_TOKEN<\/code>&nbsp;is repository-scoped and is intentionally limited for cross-repository actions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use an appropriately scoped:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub App installation token;<\/li>\n\n\n\n<li>fine-grained PAT;<\/li>\n\n\n\n<li>supported organization\/reusable-workflow access policy.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer GitHub Apps for machine-to-machine organization automation when practical.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">42.4 Cross-repo artifacts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Common solutions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>package registry;<\/li>\n\n\n\n<li>container registry;<\/li>\n\n\n\n<li>release assets;<\/li>\n\n\n\n<li>object storage;<\/li>\n\n\n\n<li>APIs.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid fragile &#8220;download another repo&#8217;s workflow artifact by guessing its latest run&#8221; designs unless the run identity is deterministic and verified.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">43. ADVANCED \u2014 Workflow Chaining<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">43.1&nbsp;<code>workflow_run<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use chaining when workflows need different privileges.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  workflow_run:\n    workflows: &#91;\"Build\"]\n    types: &#91;completed]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">43.2 Security boundary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A strong pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;PR workflow&lt;br\/&gt;untrusted, read-only] --&gt; B&#91;Artifact\/result]\n    B --&gt; C&#91;workflow_run&lt;br\/&gt;trusted workflow]\n    C --&gt; D&#91;Privileged publish\/comment]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But the privileged workflow must validate every artifact\/input created by the untrusted workflow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">43.3 Avoid excessive chains<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>workflow A\n -&gt; B\n -&gt; C\n -&gt; D\n -&gt; E\n -&gt; F\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Problems:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>harder traceability;<\/li>\n\n\n\n<li>fragmented logs;<\/li>\n\n\n\n<li>latency;<\/li>\n\n\n\n<li>failure handling complexity;<\/li>\n\n\n\n<li>privilege confusion.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer one workflow with job dependencies when a single trust boundary is sufficient.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">44. ADVANCED \u2014 GitHub API Integration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions can automate GitHub itself using REST, GraphQL,&nbsp;<code>gh<\/code>, Octokit, or&nbsp;<code>github-script<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">44.1&nbsp;<code>gh<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Comment on issue\n  env:\n    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n  run: |\n    gh issue comment \"${{ github.event.issue.number }}\" \\\n      --body \"Automation completed.\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">44.2&nbsp;<code>gh api<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Read repository\n  env:\n    GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n  run: gh api \"repos\/${GITHUB_REPOSITORY}\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">44.3&nbsp;<code>actions\/github-script<\/code><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/github-script@&lt;PINNED_SHA&gt;\n  with:\n    script: |\n      await github.rest.issues.createComment({\n        owner: context.repo.owner,\n        repo: context.repo.repo,\n        issue_number: context.issue.number,\n        body: 'CI completed.'\n      })\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">44.4 REST vs GraphQL<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">REST<\/th><th class=\"has-text-align-left\" data-align=\"left\">GraphQL<\/th><\/tr><\/thead><tbody><tr><td>simple resource operations<\/td><td>query related data efficiently<\/td><\/tr><tr><td>familiar endpoints<\/td><td>select exact fields<\/td><\/tr><tr><td>easy with&nbsp;<code>gh api<\/code><\/td><td>useful for complex org\/report queries<\/td><\/tr><tr><td>pagination by endpoint<\/td><td>query cost model<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">44.5 Checks and status APIs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use when building custom CI integrations, but ordinary Actions jobs already produce checks automatically.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">44.6 Actions API<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Useful automation:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>list runs;<\/li>\n\n\n\n<li>re-run jobs;<\/li>\n\n\n\n<li>cancel runs;<\/li>\n\n\n\n<li>download logs;<\/li>\n\n\n\n<li>manage artifacts\/caches;<\/li>\n\n\n\n<li>inspect runners.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">44.7 Rate limits<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Large organizations should design API automation with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pagination;<\/li>\n\n\n\n<li>conditional requests;<\/li>\n\n\n\n<li>caching;<\/li>\n\n\n\n<li>backoff;<\/li>\n\n\n\n<li>GitHub App installation tokens;<\/li>\n\n\n\n<li>rate-limit monitoring.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">45. ADVANCED \u2014 Repository Governance<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Workflows are enforceable only when repository governance requires them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">45.1 Governance layers<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    R&#91;Repository rules\/rulesets] --&gt; B&#91;Branch\/tag policy]\n    R --&gt; S&#91;Required status checks]\n    R --&gt; M&#91;Merge policy]\n    R --&gt; C&#91;CODEOWNERS\/reviews]\n    R --&gt; A&#91;Actions settings]\n    R --&gt; E&#91;Environment protections]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">45.2 Branch protection \/ rulesets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Typical main-branch policy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>require PR;<\/li>\n\n\n\n<li>require approvals;<\/li>\n\n\n\n<li>require code-owner review;<\/li>\n\n\n\n<li>require status checks;<\/li>\n\n\n\n<li>dismiss stale approvals if desired;<\/li>\n\n\n\n<li>block force pushes;<\/li>\n\n\n\n<li>restrict deletion;<\/li>\n\n\n\n<li>require signed commits where policy demands;<\/li>\n\n\n\n<li>require linear history if desired;<\/li>\n\n\n\n<li>merge queue for high-volume repositories.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">45.3 Required status checks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Names must remain stable enough for rules to target them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Good:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  unit:\n    name: Unit Tests\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not casually rename required jobs across hundreds of repositories without updating governance.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">45.4 Merge queue and&nbsp;<code>merge_group<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Repositories using merge queues should ensure required CI runs against the merge group event.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  pull_request:\n  merge_group:\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Without merge-group support, a required workflow may not validate the exact queued merge combination.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">45.5 CODEOWNERS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">CODEOWNERS decides review ownership. Actions can support it but should not replace branch\/ruleset enforcement with homemade scripts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">45.6 Actions repository settings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Govern:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>allowed actions;<\/li>\n\n\n\n<li>workflow permissions;<\/li>\n\n\n\n<li>fork workflow policy;<\/li>\n\n\n\n<li>reusable workflow access;<\/li>\n\n\n\n<li>runner access.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">45.7 Environments as governance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Production controls belong in both:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>repository source rules\n+\nenvironment deployment rules\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A merge approval and a deployment approval answer different questions.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">46. ADVANCED \u2014 Organization Governance<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Organization-level policy reduces inconsistent security across repositories.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">46.1 Policy areas<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>allowed GitHub Actions;<\/li>\n\n\n\n<li>third-party action restrictions;<\/li>\n\n\n\n<li>SHA pinning policies where available;<\/li>\n\n\n\n<li>reusable workflow access;<\/li>\n\n\n\n<li>organization secrets;<\/li>\n\n\n\n<li>organization variables;<\/li>\n\n\n\n<li>runner groups;<\/li>\n\n\n\n<li>workflow permissions;<\/li>\n\n\n\n<li>retention;<\/li>\n\n\n\n<li>fork workflow policy.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">46.2 Organization action policy<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Possible strategy:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Allow:\n  GitHub-owned actions\n  selected verified actions\n  organization-owned actions\n  explicitly reviewed third-party actions\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is stronger than letting every repository use arbitrary Marketplace code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">46.3 Organization secrets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use org secrets for truly shared credentials, with repository access limited.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not put a powerful enterprise credential into an organization secret visible to all repositories merely for convenience.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">46.4 Organization variables<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Good for non-sensitive standards:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DEFAULT_AWS_REGION\nARTIFACT_RETENTION_DAYS\nINTERNAL_REGISTRY\nPLATFORM_TEAM\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">46.5 Runner groups<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>general-linux\nmobile-macos\ninternal-network\nproduction-deploy\nterraform-private\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Grant repositories to groups intentionally.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">46.6 Central workflow governance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A platform repository can own:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>reusable-ci.yml\ncontainer-build.yml\nterraform-plan.yml\nterraform-apply.yml\ndeploy-kubernetes.yml\nsecurity-scan.yml\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Application repositories call reviewed versions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This creates a reusable&nbsp;<strong>paved road<\/strong>&nbsp;without copying thousands of lines of YAML.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">46.7 Governance principle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Centralize&nbsp;<strong>policy and common capability<\/strong>, not every repository-specific decision.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A reusable workflow should expose intentional inputs rather than becoming a 100-input universal pipeline.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">47. ADVANCED \u2014 Enterprise Governance<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Enterprise governance applies policy consistently across organizations while preserving controlled delegation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">47.1 Enterprise control plane<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    E&#91;Enterprise policy] --&gt; O1&#91;Organization A]\n    E --&gt; O2&#91;Organization B]\n    E --&gt; O3&#91;Organization C]\n\n    O1 --&gt; R1&#91;Repositories]\n    O2 --&gt; R2&#91;Repositories]\n    O3 --&gt; R3&#91;Repositories]\n\n    E --&gt; RG&#91;Enterprise runner groups]\n    E --&gt; AU&#91;Audit \/ usage]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">47.2 Enterprise policy areas<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Common controls:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Actions usage policy;<\/li>\n\n\n\n<li>allowed\/selected actions;<\/li>\n\n\n\n<li>enterprise runner groups;<\/li>\n\n\n\n<li>organization inheritance;<\/li>\n\n\n\n<li>workflow permissions;<\/li>\n\n\n\n<li>fork policies;<\/li>\n\n\n\n<li>reusable workflow strategy;<\/li>\n\n\n\n<li>audit logging;<\/li>\n\n\n\n<li>billing\/usage analysis;<\/li>\n\n\n\n<li>identity and access.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">47.3 Policy inheritance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A lower level should not be able to silently weaken an enterprise security requirement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Think in layers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Enterprise maximum allowed capability\n  \u2193\nOrganization policy\n  \u2193\nRepository policy\n  \u2193\nWorkflow\/job least privilege\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A repository can usually choose to be more restrictive, but should not be able to exceed organization\/enterprise boundaries.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">47.4 Enterprise runner groups<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Useful model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Enterprise runner groups\n\u251c\u2500\u2500 general\n\u251c\u2500\u2500 regulated\n\u251c\u2500\u2500 production\n\u251c\u2500\u2500 mobile\n\u2514\u2500\u2500 high-memory\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each organization gets access only to the groups it needs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">47.5 Audit logs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Audit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>workflow policy changes;<\/li>\n\n\n\n<li>runner changes;<\/li>\n\n\n\n<li>secret\/configuration changes;<\/li>\n\n\n\n<li>repository settings;<\/li>\n\n\n\n<li>environment approvals;<\/li>\n\n\n\n<li>organization\/enterprise Actions settings;<\/li>\n\n\n\n<li>authentication events.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Audit data is useful only when retained, searchable, and connected to incident response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">47.6 Enterprise operating model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A mature model usually separates:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Role<\/th><th class=\"has-text-align-left\" data-align=\"left\">Responsibility<\/th><\/tr><\/thead><tbody><tr><td>Enterprise admins<\/td><td>platform-wide policy<\/td><\/tr><tr><td>Platform team<\/td><td>reusable workflows\/runners<\/td><\/tr><tr><td>Security<\/td><td>controls, review, detection<\/td><\/tr><tr><td>Org owners<\/td><td>organization policy<\/td><\/tr><tr><td>Repo maintainers<\/td><td>repository CI\/CD<\/td><\/tr><tr><td>App teams<\/td><td>application-specific logic<\/td><\/tr><tr><td>Release approvers<\/td><td>production authorization<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">48. ADVANCED \u2014 Workflow Execution Policies<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Execution policy answers:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Which workflows may execute, under which identities, events, repositories, and protections?<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">48.1 Policy dimensions<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>actor;<\/li>\n\n\n\n<li>event;<\/li>\n\n\n\n<li>repository;<\/li>\n\n\n\n<li>action source;<\/li>\n\n\n\n<li>runner group;<\/li>\n\n\n\n<li>permissions;<\/li>\n\n\n\n<li>environment;<\/li>\n\n\n\n<li>fork status;<\/li>\n\n\n\n<li>branch\/tag.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">48.2 Defense in depth<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    E&#91;Event] --&gt; P1&#91;Enterprise\/Org policy]\n    P1 --&gt; P2&#91;Repository Actions policy]\n    P2 --&gt; W&#91;Workflow trigger\/conditions]\n    W --&gt; T&#91;Token permissions]\n    T --&gt; R&#91;Runner access]\n    R --&gt; G&#91;Environment gate]\n    G --&gt; X&#91;Privileged action]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">No single layer should be expected to carry all authorization.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">48.3 Actor restrictions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid implementing sensitive authorization as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.actor == 'alice'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is brittle and not a substitute for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>repository permissions;<\/li>\n\n\n\n<li>teams;<\/li>\n\n\n\n<li>environment reviewers;<\/li>\n\n\n\n<li>GitHub Apps;<\/li>\n\n\n\n<li>protected branches;<\/li>\n\n\n\n<li>cloud IAM.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Actor checks can supplement, not replace, policy.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">48.4 Event restrictions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PR -&gt; test only\npush main -&gt; build\/publish candidate\nrelease -&gt; publish release\nworkflow_dispatch -&gt; controlled operational action\nschedule -&gt; maintenance\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Tie higher privileges to trusted events and protected refs.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">49. ADVANCED \u2014 Metrics and Observability<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A CI\/CD platform should be operated with SLO-like thinking.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">49.1 Core metrics<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow metrics<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>run count\nsuccess rate\nfailure rate\ncancellation rate\nduration\nqueue duration\nre-run rate\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Job metrics<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>duration by job\nfailure by job\nrunner wait time\nmatrix expansion\ntimeout count\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Runner metrics<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>online runners\nbusy runners\nutilization\nqueue depth\nstartup latency\njob pickup latency\nfailure rate\nversion\/image drift\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Cost metrics<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>minutes by repository\nminutes by runner type\nartifact storage\ncache storage\ntop expensive workflows\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">49.2 CI service-level indicators<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Example internal objectives:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">SLI<\/th><th class=\"has-text-align-left\" data-align=\"left\">Example target<\/th><\/tr><\/thead><tbody><tr><td>PR CI p50<\/td><td>&lt; 5 min<\/td><\/tr><tr><td>PR CI p95<\/td><td>&lt; 15 min<\/td><\/tr><tr><td>Queue p95<\/td><td>&lt; 2 min<\/td><\/tr><tr><td>Platform-caused failure<\/td><td>&lt; 1%<\/td><\/tr><tr><td>Production deployment success<\/td><td>&gt; 99%<\/td><\/tr><tr><td>Runner availability<\/td><td>&gt; 99.9%<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">These are examples, not universal targets.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">49.3 Failure classification<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not treat all red workflows the same.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Classify:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>code defect\ntest defect\/flaky test\nworkflow defect\nrunner\/platform\ndependency registry\nnetwork\ncloud provider\npermission\/policy\ndeployment target\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Without classification, teams optimize the wrong problem.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">49.4 Observability dashboard<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    A&#91;Actions APIs \/ audit logs] --&gt; D&#91;Data pipeline]\n    R&#91;Runner metrics] --&gt; D\n    B&#91;Billing\/usage] --&gt; D\n    D --&gt; M&#91;Metrics store]\n    M --&gt; V&#91;Dashboard]\n    M --&gt; AL&#91;Alerts]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">49.5 Alerting<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Good alerts indicate action:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>runner pool unable to accept jobs\nproduction deployment failure\nqueue time above SLO\nreusable workflow failure spike\nauthentication failures\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Bad alerts notify on every ordinary developer test failure to a central on-call.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">50. ADVANCED \u2014 Workflow Notifications<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Notifications should route information to the people who can act.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">50.1 Native notifications<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub can notify users through:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>web;<\/li>\n\n\n\n<li>email;<\/li>\n\n\n\n<li>repository\/activity notification mechanisms.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">50.2 Chat notifications<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Possible integrations:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Slack;<\/li>\n\n\n\n<li>Microsoft Teams;<\/li>\n\n\n\n<li>custom webhook;<\/li>\n\n\n\n<li>incident-management tool.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">50.3 Notification job<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>notify:\n  needs: &#91;build, test, deploy]\n  if: failure()\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Build notification payload\n      env:\n        RUN_URL: &gt;-\n          ${{ github.server_url }}\/${{ github.repository }}\/actions\/runs\/${{ github.run_id }}\n      run: |\n        echo \"Workflow failed: $RUN_URL\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Send via a reviewed integration or webhook.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">50.4 Avoid secret webhooks when apps can use scoped auth<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A long-lived chat webhook is a bearer credential. Store and scope it appropriately.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">50.5 Notification design<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">PR failure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub check is usually enough\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Production failure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GitHub + deployment channel + incident path if impact exists\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Scheduled maintenance failure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>team-owned alert\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">51. ADVANCED \u2014 Workflow Cancellation<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Cancellation is a normal workflow state and should be designed for.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">51.1 Cancellation sources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>user clicks Cancel;<\/li>\n\n\n\n<li>concurrency cancels older run;<\/li>\n\n\n\n<li>API cancellation;<\/li>\n\n\n\n<li>timeout;<\/li>\n\n\n\n<li>organization\/platform policy.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">51.2 Concurrency cancellation<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>concurrency:\n  group: pr-${{ github.event.pull_request.number }}\n  cancel-in-progress: true\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">51.3 Cleanup<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If a workflow creates temporary infrastructure, design cleanup even when cancellation occurs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>action\u00a0<code>post<\/code>\u00a0phase;<\/li>\n\n\n\n<li>cleanup step\/job;<\/li>\n\n\n\n<li>TTL on temporary resources;<\/li>\n\n\n\n<li>external janitor workflow;<\/li>\n\n\n\n<li>idempotent resource naming.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">51.4 Preview environment example<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    P&#91;PR opened\/update] --&gt; C&#91;Create\/update preview]\n    P --&gt; X&#91;Older run cancelled]\n    X --&gt; J&#91;Cleanup\/TTL safety]\n    Q&#91;PR closed] --&gt; D&#91;Destroy preview]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Never rely on only the &#8220;PR closed&#8221; workflow for cleanup. Events can fail; build a TTL\/janitor.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">51.5 Signals<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Processes should handle termination where practical.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Containerized\/server steps should not require an unbounded graceful shutdown.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">52. ADVANCED \u2014 Pull Request CI Patterns<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">PR CI is the highest-volume and often lowest-trust workflow surface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">52.1 Baseline PR workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: PR CI\n\non:\n  pull_request:\n    types: &#91;opened, synchronize, reopened, ready_for_review]\n\npermissions:\n  contents: read\n\ncache-mode: read\n\nconcurrency:\n  group: pr-${{ github.event.pull_request.number }}\n  cancel-in-progress: true\n\njobs:\n  ci:\n    if: github.event.pull_request.draft == false\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n\n    steps:\n      - uses: actions\/checkout@v6\n      - run: .\/ci.sh\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">52.2 Draft PRs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>skip expensive tests until ready;<\/li>\n\n\n\n<li>run only lint\/unit;<\/li>\n\n\n\n<li>run full CI if draft PRs are routinely used for collaboration.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Make behavior explicit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">52.3 Fork PRs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Assume:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>code is untrusted;<\/li>\n\n\n\n<li>repository secrets are not available;<\/li>\n\n\n\n<li>write token is restricted;<\/li>\n\n\n\n<li>self-hosted privileged runners are dangerous.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">52.4 PR comments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not switch the whole test workflow to&nbsp;<code>pull_request_target<\/code>&nbsp;merely to post a comment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Safer pattern:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pull_request -&gt; test -&gt; artifact\/result\nworkflow_run -&gt; trusted commenter\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Validate the result artifact before use.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">52.5 Changed-file workflows<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use path filtering for coarse optimization and dependency-aware detection for complex monorepos.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">52.6 Preview environments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A secure preview system needs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>isolated namespace\/account;<\/li>\n\n\n\n<li>no production secrets;<\/li>\n\n\n\n<li>bounded lifetime;<\/li>\n\n\n\n<li>safe ingress;<\/li>\n\n\n\n<li>predictable DNS;<\/li>\n\n\n\n<li>cleanup;<\/li>\n\n\n\n<li>cost limit;<\/li>\n\n\n\n<li>untrusted input handling.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">52.7 Merge queue<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If required checks are used with merge queue:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  pull_request:\n  merge_group:\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Validate the queued merge group, not only the original PR head.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">53. ADVANCED \u2014 Branch and Release Strategies<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions should implement your source-control\/release strategy, not accidentally define one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">53.1 Trunk-based development<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>feature branch -&gt; PR -&gt; main -&gt; deploy\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Good fit for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>small changes;<\/li>\n\n\n\n<li>strong automation;<\/li>\n\n\n\n<li>feature flags;<\/li>\n\n\n\n<li>frequent release.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">53.2 GitFlow-style<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>feature -&gt; develop -&gt; release -&gt; main\n                    \\-&gt; hotfix\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">More branches and workflow states mean more automation complexity.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use only if the organizational release model actually needs it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">53.3 Release branches<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>main\nrelease\/2.4\nrelease\/2.5\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Actions can validate\/patch supported release lines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">53.4 Environment branches<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A branch-per-environment model can work but couples source history to deployment state.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Alternative:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>immutable artifact + environment promotion metadata\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Often easier to reason about.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">53.5 Tag-based release<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>on:\n  push:\n    tags:\n      - \"v*\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Validate that the tag points to an approved commit lineage if required.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">53.6 Hotfix workflow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A hotfix should still preserve:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>review\ntests\nartifact integrity\nproduction protection\npost-deploy verification\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;Emergency&#8221; should not automatically mean &#8220;no controls.&#8221;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">54. ADVANCED \u2014 Deployment Strategies<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">54.1 Rolling deployment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Replace instances gradually.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    O&#91;Old replicas] --&gt; M&#91;Mixed old\/new]\n    M --&gt; N&#91;All new]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Pros:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>simple;<\/li>\n\n\n\n<li>resource efficient.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Risk:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>mixed versions coexist.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">54.2 Blue\/green<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    U&#91;Users] --&gt; R{Router}\n    R --&gt; B&#91;Blue active]\n    R -. switch .-&gt; G&#91;Green new]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Deploy green, verify, switch traffic. Rollback by switching back while blue remains viable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">54.3 Canary<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    U&#91;Traffic] --&gt; S{Traffic split}\n    S --&gt;|95%| O&#91;Old]\n    S --&gt;|5%| N&#91;New]\n    N --&gt; M&#91;Metrics]\n    M --&gt;|Healthy| I&#91;Increase traffic]\n    M --&gt;|Bad| R&#91;Rollback]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Requires observability and traffic control.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">54.4 Progressive delivery<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Canary is one form. A progression can use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1% -&gt; 5% -&gt; 25% -&gt; 50% -&gt; 100%\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each stage can gate on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>error rate;<\/li>\n\n\n\n<li>latency;<\/li>\n\n\n\n<li>saturation;<\/li>\n\n\n\n<li>business metric;<\/li>\n\n\n\n<li>manual approval.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">54.5 Feature flags<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Deployment and release become separate:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>deploy code disabled\n-&gt; enable feature for internal users\n-&gt; 1%\n-&gt; 10%\n-&gt; all\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Flags need lifecycle management; stale flags are operational debt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">54.6 Immutable deployment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Promote a previously built artifact\/image rather than changing it per environment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Strong model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>same digest -&gt; staging -&gt; UAT -&gt; production\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">54.7 Smoke tests<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After deploy:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Smoke\n  run: |\n    curl --fail --retry 5 --retry-delay 5 \\\n      https:&#47;&#47;app.example.com\/health\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A&nbsp;<code>\/health<\/code>&nbsp;endpoint alone may not represent user-critical functionality. Include targeted synthetic smoke checks when needed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">55. ADVANCED \u2014 Testing GitHub Actions<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Workflows are production code and should be tested.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">55.1 Validation layers<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>YAML parser\nworkflow linter\nshell\/script tests\ncustom action unit tests\nreusable workflow tests\nintegration repository\nstaging deployment\nproduction canary\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">55.2&nbsp;<code>actionlint<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>actionlint<\/code>&nbsp;catches many:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>syntax;<\/li>\n\n\n\n<li>expressions;<\/li>\n\n\n\n<li>shellcheck-related issues;<\/li>\n\n\n\n<li>context mistakes.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example local\/CI use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>actionlint\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">55.3 YAML validation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A generic YAML parser can catch syntax but not GitHub-specific semantic errors.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use both generic syntax validation and Actions-aware linting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">55.4 JavaScript action testing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Separate business logic from Actions runtime adapters.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example architecture:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>src\/\n  logic.js       &lt;- pure\/testable\n  action.js      &lt;- @actions\/core wrapper\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Unit test&nbsp;<code>logic.js<\/code>, then integration-test&nbsp;<code>action.js<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">55.5 Composite action testing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create a small workflow that invokes the action with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>default inputs;<\/li>\n\n\n\n<li>custom inputs;<\/li>\n\n\n\n<li>failing inputs;<\/li>\n\n\n\n<li>expected outputs;<\/li>\n\n\n\n<li>Linux\/Windows\/macOS if supported.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">55.6 Reusable workflow testing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A reusable workflow should have a test caller.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>test repository\/workflow -&gt; call reusable workflow -&gt; assert outputs\/artifacts\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">55.7 Local emulation with&nbsp;<code>act<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>act<\/code>&nbsp;can be useful for fast local feedback but is not an exact GitHub-hosted runner implementation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Differences may include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>images;<\/li>\n\n\n\n<li>permissions;<\/li>\n\n\n\n<li>networking;<\/li>\n\n\n\n<li>services;<\/li>\n\n\n\n<li>GitHub API behavior;<\/li>\n\n\n\n<li>environment protection;<\/li>\n\n\n\n<li>OIDC;<\/li>\n\n\n\n<li>hosted-runner tooling.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Use it as a development aid, not final proof.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">55.8 Staging workflow tests<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For risky platform changes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>branch\/ref of reusable workflow\n-&gt; test repository\n-&gt; staging environment\n-&gt; production version tag\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not immediately move a central&nbsp;<code>v1<\/code>&nbsp;tag across hundreds of repositories without canary validation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">56. ADVANCED \u2014 Workflow Maintenance<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A workflow that was secure and correct two years ago may no longer be.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">56.1 Maintenance inventory<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Review:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>action versions;<\/li>\n\n\n\n<li>deprecated actions;<\/li>\n\n\n\n<li>runner images;<\/li>\n\n\n\n<li>runtime versions;<\/li>\n\n\n\n<li>third-party dependencies;<\/li>\n\n\n\n<li>secrets;<\/li>\n\n\n\n<li>OIDC trust;<\/li>\n\n\n\n<li>runner application;<\/li>\n\n\n\n<li>container images;<\/li>\n\n\n\n<li>reusable workflow versions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">56.2 Dependabot for GitHub Actions<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>version: 2\n\nupdates:\n  - package-ecosystem: github-actions\n    directory: \/\n    schedule:\n      interval: weekly\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use review and test before merging updates.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">56.3 Runner image changes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid relying on accidental preinstalled software.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Explicitly set up critical versions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- uses: actions\/setup-node@v7\n  with:\n    node-version: \"24\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">56.4 Node runtime changes for actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">JavaScript actions target runner-supported Node action runtimes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When GitHub deprecates an action runtime, update custom actions and third-party dependencies.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">56.5 Refactoring triggers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Refactor when you see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>copy\/paste across repositories;<\/li>\n\n\n\n<li>500-line monolithic workflows;<\/li>\n\n\n\n<li>duplicated setup steps;<\/li>\n\n\n\n<li>unclear privilege boundaries;<\/li>\n\n\n\n<li>repeated shell scripts embedded in YAML;<\/li>\n\n\n\n<li>enormous boolean conditions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">56.6 Secret rotation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Track:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>owner\npurpose\ncreated\nlast rotated\nexpires\nrepositories\nreplacement plan\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer OIDC to reduce long-lived cloud secrets.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">56.7 Documentation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each production workflow should make it easy to answer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>What triggers this?\nWhat does it change?\nWhat identity does it use?\nWhat is the artifact?\nWhat protects production?\nHow do I retry?\nHow do I roll back?\nWho owns it?\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">57. ADVANCED \u2014 GitHub Actions Limits<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Limits change over time. Build scalable systems from current GitHub documentation rather than memorizing an old blog.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">57.1 Important current examples<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As of this handbook verification date, GitHub documents limits including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>workflow-run lifetime;<\/li>\n\n\n\n<li>environment approval waiting;<\/li>\n\n\n\n<li>matrix expansion;<\/li>\n\n\n\n<li>concurrency\/queue behavior;<\/li>\n\n\n\n<li>reusable workflow depth\/count;<\/li>\n\n\n\n<li>API limits;<\/li>\n\n\n\n<li>runner\/job limits;<\/li>\n\n\n\n<li>storage limits.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Matrix<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A matrix can generate at most&nbsp;<strong>256 jobs per workflow run<\/strong>&nbsp;in the current documented limit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Workflow run<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Current documentation lists a workflow run maximum lifetime of&nbsp;<strong>35 days<\/strong>, including execution and waiting\/approval time.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Environment approval<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A workflow can wait for environment approval for up to&nbsp;<strong>30 days<\/strong>&nbsp;under the current documented limit.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reusable workflows<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Current reference documents:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>up to 10 levels\nup to 50 unique reusable workflows reachable from one caller\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">57.2 Design principle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not design at the exact hard limit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>250 matrix jobs every PR\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">the issue is probably architectural before it is a quota issue.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">57.3 Common scale failures<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>giant dynamic matrix;<\/li>\n\n\n\n<li>thousands of repos calling one bottleneck service;<\/li>\n\n\n\n<li>API polling without backoff;<\/li>\n\n\n\n<li>artifact retention explosion;<\/li>\n\n\n\n<li>excessive cross-workflow chaining;<\/li>\n\n\n\n<li>too-small runner pool;<\/li>\n\n\n\n<li>limited private endpoint capacity.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">58. ADVANCED \u2014 GitHub Actions Importer<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub Actions Importer helps migrate CI\/CD definitions from supported systems.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">58.1 Migration is not translation only<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;Existing CI] --&gt; I&#91;Inventory]\n    I --&gt; C&#91;Convert]\n    C --&gt; R&#91;Review]\n    R --&gt; T&#91;Test]\n    T --&gt; S&#91;Security redesign]\n    S --&gt; P&#91;Production cutover]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A converted workflow can be syntactically correct but architecturally poor.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">58.2 Typical migration sources<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Examples include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Jenkins;<\/li>\n\n\n\n<li>GitLab CI\/CD;<\/li>\n\n\n\n<li>CircleCI;<\/li>\n\n\n\n<li>Azure DevOps;<\/li>\n\n\n\n<li>Travis CI.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">58.3 Migration inventory<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Record:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pipeline trigger\nsecrets\nservice connections\nagents\/runners\nartifacts\ncaches\nenvironments\napprovals\nshared libraries\nplugins\nnotifications\ndeployment targets\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">58.4 Replace platform-specific concepts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Jenkins shared library -&gt; reusable workflow\/composite action\nJenkins agent label -&gt; runs-on labels\/runner group\nGitLab artifact -&gt; Actions artifact\nAzure environment approval -&gt; GitHub environment protection\nCI cloud service connection -&gt; OIDC + cloud IAM\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">58.5 Migration security opportunity<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not copy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>long-lived cloud keys;<\/li>\n\n\n\n<li>administrator tokens;<\/li>\n\n\n\n<li>shared mutable workers;<\/li>\n\n\n\n<li>insecure shell interpolation;<\/li>\n\n\n\n<li>unpinned third-party code.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Use migration as a security modernization.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">59. ADVANCED \u2014 Common CI\/CD Design Patterns<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">59.1 Build-test-deploy<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    B&#91;Build] --&gt; T&#91;Test] --&gt; D&#91;Deploy]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Simple and appropriate for many applications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">59.2 Build once, deploy many<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Commit] --&gt; B&#91;Build]\n    B --&gt; A&#91;(Artifact)]\n    A --&gt; S&#91;Stage]\n    S --&gt; U&#91;UAT]\n    U --&gt; P&#91;Prod]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Recommended for immutable promotion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">59.3 Fan-out\/fan-in<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    B&#91;Build] --&gt; U&#91;Unit]\n    B --&gt; I&#91;Integration]\n    B --&gt; S&#91;Security]\n    U --&gt; G&#91;Gate]\n    I --&gt; G\n    S --&gt; G\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">59.4 Matrix<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One definition, many permutations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Good for compatibility testing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">59.5 Central reusable pipeline<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>application repo\n  -&gt; central reusable CI\n  -&gt; central security\n  -&gt; central deployment\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Useful for consistent organization standards.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">59.6 Artifact promotion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Do not rebuild between environments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Promote:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>artifact ID\nimage digest\nrelease bundle\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">59.7 Conditional deployment<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>if: github.ref == 'refs\/heads\/main'\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use conditions plus environment protections.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">59.8 Manual approval gates<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Human approval is appropriate when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>regulatory control;<\/li>\n\n\n\n<li>production change review;<\/li>\n\n\n\n<li>high-risk operation;<\/li>\n\n\n\n<li>business timing decision.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">It is not a substitute for automated verification.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">59.9 Event-driven automation<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>release created -&gt; publish\nissue labeled -&gt; triage\ndeployment status -&gt; notify\npackage published -&gt; downstream test\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">59.10 Dynamic pipeline generation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub workflow structure itself remains YAML-defined, but matrices and outputs can dynamically choose work.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid generating arbitrary executable workflow logic from untrusted input.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">60. ADVANCED \u2014 GitHub Actions Anti-Patterns<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">60.1 Duplicate workflow logic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Problem:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>50 repositories \u00d7 copied 200-line workflow\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Fix:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>reusable workflow + small caller\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">60.2 Overprivileged token<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions: write-all\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Fix:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>permissions:\n  contents: read\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and elevate only specific jobs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.3 Long-lived cloud credentials<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AWS_ACCESS_KEY_ID secret\nAWS_SECRET_ACCESS_KEY secret\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">when OIDC is supported.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fix:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>OIDC -&gt; scoped role -&gt; short-lived token\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">60.4 Mutable third-party actions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uses: unknown\/action@main\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Better:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>uses: unknown\/action@&lt;reviewed-full-SHA&gt;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">60.5 Secrets in YAML<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>env:\n  PASSWORD: real-password-here\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">60.6 Secrets in logs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never enable shell tracing around secret-bearing commands without understanding exposure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>set -x\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">for secret-heavy scripts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.7 Untrusted input in shell<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>run: echo \"${{ github.event.issue.title }}\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use environment indirection or safe structured code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.8 Unsafe&nbsp;<code>pull_request_target<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never combine:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>privileged base context\n+\ncheckout attacker-controlled PR\n+\nexecute PR code\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">60.9 Persistent public self-hosted runners<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This creates a persistence and internal-network attack surface.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use GitHub-hosted runners or isolated ephemeral runners for untrusted public workflows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.10 Excessive matrix<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A matrix should represent valuable test dimensions, not every imaginable combination.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.11 Cache misuse<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never make cache availability a correctness requirement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Never let low-trust code poison privileged cache state.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.12 Artifact misuse<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Artifacts are not automatically secure secret stores.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not upload:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>cloud credentials;<\/li>\n\n\n\n<li>signing keys;<\/li>\n\n\n\n<li><code>.env<\/code>\u00a0secrets;<\/li>\n\n\n\n<li>kubeconfigs;<\/li>\n\n\n\n<li>plaintext tokens.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">60.13 Hard-coded environments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bad:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>aws s3 cp ... s3:\/\/prod-account-bucket\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">inside generic CI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use environment-specific variables and identities.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.14 Excessive workflow chaining<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prefer job dependencies unless separate workflow trust boundaries are actually useful.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.15 Monolithic workflow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Symptoms:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>hundreds\/thousands of lines;<\/li>\n\n\n\n<li>unrelated products;<\/li>\n\n\n\n<li>mixed privilege levels;<\/li>\n\n\n\n<li>many nested conditions.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Split by responsibility and reuse stable building blocks.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.16 Missing timeouts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A stuck external call can consume hours.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Set realistic job\/step timeouts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.17 Missing concurrency<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrent production deployments can race.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Add environment-specific serialization.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">60.18 Missing deployment protections<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A YAML condition is not equivalent to protected environment policy.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">61. ADVANCED \u2014 Production Best Practices<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This chapter condenses the handbook into a production baseline.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">61.1 Secure identity<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>least-privilege\u00a0<code>GITHUB_TOKEN<\/code>;<\/li>\n\n\n\n<li>OIDC for cloud;<\/li>\n\n\n\n<li>separate environment roles;<\/li>\n\n\n\n<li>no static cloud keys where federation works;<\/li>\n\n\n\n<li>protected production environments.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">61.2 Supply-chain controls<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pin third-party actions by full SHA;<\/li>\n\n\n\n<li>automate action update PRs;<\/li>\n\n\n\n<li>scan dependencies;<\/li>\n\n\n\n<li>generate SBOM\/provenance as needed;<\/li>\n\n\n\n<li>attest release artifacts;<\/li>\n\n\n\n<li>verify before deployment.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">61.3 Runner controls<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub-hosted for common untrusted CI;<\/li>\n\n\n\n<li>ephemeral self-hosted\/ARC for private workloads;<\/li>\n\n\n\n<li>runner groups;<\/li>\n\n\n\n<li>network segmentation;<\/li>\n\n\n\n<li>patched golden images;<\/li>\n\n\n\n<li>no ambient privileged credentials.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">61.4 Workflow engineering<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>reusable workflows;<\/li>\n\n\n\n<li>composite actions for repeated steps;<\/li>\n\n\n\n<li>job timeouts;<\/li>\n\n\n\n<li>concurrency;<\/li>\n\n\n\n<li>path filtering;<\/li>\n\n\n\n<li>caching;<\/li>\n\n\n\n<li>build once\/deploy many;<\/li>\n\n\n\n<li>clear outputs;<\/li>\n\n\n\n<li>readable conditions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">61.5 Repository governance<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>rulesets\/branch protection;<\/li>\n\n\n\n<li>required checks;<\/li>\n\n\n\n<li>merge queue support;<\/li>\n\n\n\n<li>CODEOWNERS;<\/li>\n\n\n\n<li>restricted Actions policy;<\/li>\n\n\n\n<li>protected environments.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">61.6 Operational readiness<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>workflow metrics;<\/li>\n\n\n\n<li>queue monitoring;<\/li>\n\n\n\n<li>runner monitoring;<\/li>\n\n\n\n<li>cost monitoring;<\/li>\n\n\n\n<li>failure classification;<\/li>\n\n\n\n<li>ownership;<\/li>\n\n\n\n<li>rollback;<\/li>\n\n\n\n<li>runbook;<\/li>\n\n\n\n<li>auditability.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">61.7 Production reference workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Production Release\n\non:\n  push:\n    tags:\n      - \"v*\"\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: release-${{ github.ref }}\n  cancel-in-progress: false\n\njobs:\n  verify:\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n    cache-mode: read\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: \"24\"\n          cache: npm\n\n      - run: npm ci\n      - run: npm run lint\n      - run: npm test\n      - run: npm run build\n\n      - uses: actions\/upload-artifact@v4\n        with:\n          name: release-${{ github.sha }}\n          path: dist\/\n          retention-days: 30\n\n  deploy:\n    needs: verify\n\n    permissions:\n      contents: read\n      id-token: write\n\n    environment:\n      name: production\n      url: https:\/\/app.example.com\n\n    concurrency:\n      group: production\n      queue: max\n\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n\n    steps:\n      - uses: actions\/download-artifact@v5\n        with:\n          name: release-${{ github.sha }}\n          path: dist\/\n\n      - name: Obtain short-lived cloud credentials\n        run: echo \"Use provider OIDC action pinned to a reviewed SHA\"\n\n      - name: Deploy\n        run: .\/deploy.sh dist\/\n\n      - name: Verify\n        run: .\/smoke-test.sh https:\/\/app.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The exact cloud\/auth action should be pinned to a reviewed SHA.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">62. ADVANCED \u2014 Reference and Administration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This section is a practical index of where to look when operating GitHub Actions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">62.1 Workflow reference<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use the official workflow syntax reference for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>valid keys;<\/li>\n\n\n\n<li>contexts allowed in a key;<\/li>\n\n\n\n<li>defaults;<\/li>\n\n\n\n<li>concurrency;<\/li>\n\n\n\n<li>matrices;<\/li>\n\n\n\n<li>containers;<\/li>\n\n\n\n<li>services;<\/li>\n\n\n\n<li>reusable calls;<\/li>\n\n\n\n<li>modern syntax such as cache access and parallel\/background steps.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.2 Events reference<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Check the event reference for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>default activity types;<\/li>\n\n\n\n<li>branch\/ref behavior;<\/li>\n\n\n\n<li>event payload;<\/li>\n\n\n\n<li>default-branch requirements;<\/li>\n\n\n\n<li>fork security behavior;<\/li>\n\n\n\n<li>recursive-trigger limitations.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.3 Workflow commands<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>outputs;<\/li>\n\n\n\n<li>environment variables;<\/li>\n\n\n\n<li>PATH;<\/li>\n\n\n\n<li>step summaries;<\/li>\n\n\n\n<li>annotations;<\/li>\n\n\n\n<li>masking;<\/li>\n\n\n\n<li>action state.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.4 Variables, expressions, and contexts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">These three references answer different questions:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Reference<\/th><th class=\"has-text-align-left\" data-align=\"left\">Question<\/th><\/tr><\/thead><tbody><tr><td>Variables<\/td><td>What environment\/config variables exist?<\/td><\/tr><tr><td>Expressions<\/td><td>What operators\/functions can I evaluate?<\/td><\/tr><tr><td>Contexts<\/td><td>What structured runtime data is available here?<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">62.5 Deployments and environments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>required reviewers;<\/li>\n\n\n\n<li>wait timers;<\/li>\n\n\n\n<li>deployment branches\/tags;<\/li>\n\n\n\n<li>custom protection rules;<\/li>\n\n\n\n<li>environment variables\/secrets;<\/li>\n\n\n\n<li>deployment records.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.6 Runners<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Administrators should know:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>standard hosted runner images;<\/li>\n\n\n\n<li>larger runner capabilities;<\/li>\n\n\n\n<li>self-hosted network requirements;<\/li>\n\n\n\n<li>runner groups;<\/li>\n\n\n\n<li>ARC;<\/li>\n\n\n\n<li>image\/runtime updates.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.7 Security<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Always keep current on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>secure-use reference;<\/li>\n\n\n\n<li><code>pull_request_target<\/code>;<\/li>\n\n\n\n<li>secrets;<\/li>\n\n\n\n<li>OIDC;<\/li>\n\n\n\n<li>action pinning;<\/li>\n\n\n\n<li>fork policy;<\/li>\n\n\n\n<li>runner isolation;<\/li>\n\n\n\n<li>cache security.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.8 Limits<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Check current limits before designing:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>huge matrices;<\/li>\n\n\n\n<li>deep reusable workflows;<\/li>\n\n\n\n<li>long approvals;<\/li>\n\n\n\n<li>extreme run durations;<\/li>\n\n\n\n<li>high API volumes;<\/li>\n\n\n\n<li>large artifacts\/caches.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.9 Billing and usage<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Review:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>included minutes;<\/li>\n\n\n\n<li>hosted runner rates;<\/li>\n\n\n\n<li>larger runner rates;<\/li>\n\n\n\n<li>storage;<\/li>\n\n\n\n<li>organization usage;<\/li>\n\n\n\n<li>repository hot spots.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">62.10 Administrative runbook<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A GitHub Actions administrator should maintain:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Actions policy\nrunner inventory\nrunner group ownership\napproved actions\nreusable workflow catalog\nsecret inventory\nOIDC trust inventory\nenvironment inventory\ncost dashboard\nplatform SLOs\nupgrade cadence\nincident runbook\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Part III \u2014 End-to-End Tutorials and Labs<\/h1>\n\n\n\n<h1 class=\"wp-block-heading\">Lab A \u2014 From Zero to a Production-Quality Node.js CI Workflow<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">A.1 Repository<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>demo-app\/\n\u251c\u2500\u2500 .github\/\n\u2502   \u2514\u2500\u2500 workflows\/\n\u2502       \u2514\u2500\u2500 ci.yml\n\u251c\u2500\u2500 src\/\n\u251c\u2500\u2500 test\/\n\u251c\u2500\u2500 package.json\n\u2514\u2500\u2500 package-lock.json\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">A.2 Requirements<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We want:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>PR and\u00a0<code>main<\/code>\u00a0validation;<\/li>\n\n\n\n<li>least privilege;<\/li>\n\n\n\n<li>stale-run cancellation;<\/li>\n\n\n\n<li>Node 22 and 24 compatibility;<\/li>\n\n\n\n<li>npm cache;<\/li>\n\n\n\n<li>lint;<\/li>\n\n\n\n<li>test;<\/li>\n\n\n\n<li>build;<\/li>\n\n\n\n<li>artifact;<\/li>\n\n\n\n<li>clear summary.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">A.3 Workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Application CI\n\non:\n  pull_request:\n  push:\n    branches: &#91;main]\n\npermissions:\n  contents: read\n\ncache-mode: read\n\nconcurrency:\n  group: ci-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  quality:\n    name: Node ${{ matrix.node }}\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n\n    strategy:\n      fail-fast: false\n      matrix:\n        node: &#91;22, 24]\n\n    steps:\n      - name: Checkout\n        uses: actions\/checkout@v6\n\n      - name: Setup Node\n        uses: actions\/setup-node@v7\n        with:\n          node-version: ${{ matrix.node }}\n          cache: npm\n\n      - name: Install\n        run: npm ci\n\n      - name: Lint\n        run: npm run lint\n\n      - name: Unit tests\n        run: npm test -- --ci\n\n      - name: Build\n        run: npm run build\n\n      - name: Summary\n        if: always()\n        run: |\n          {\n            echo \"## Node ${{ matrix.node }}\"\n            echo \"\"\n            echo \"- Ref: \\`${{ github.ref }}\\`\"\n            echo \"- SHA: \\`${{ github.sha }}\\`\"\n            echo \"- Result: \\`${{ job.status }}\\`\"\n          } &gt;&gt; \"$GITHUB_STEP_SUMMARY\"\n\n      - name: Upload production build\n        if: matrix.node == 24\n        uses: actions\/upload-artifact@v4\n        with:\n          name: web-${{ github.sha }}\n          path: dist\/\n          retention-days: 7\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">A.4 Why each control exists<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Control<\/th><th class=\"has-text-align-left\" data-align=\"left\">Reason<\/th><\/tr><\/thead><tbody><tr><td><code>contents: read<\/code><\/td><td>CI should not need repository write<\/td><\/tr><tr><td><code>cache-mode: read<\/code><\/td><td>PR code does not need to publish cache state<\/td><\/tr><tr><td><code>concurrency<\/code><\/td><td>cancel obsolete CI<\/td><\/tr><tr><td><code>timeout-minutes<\/code><\/td><td>bound failure\/hang cost<\/td><\/tr><tr><td><code>fail-fast: false<\/code><\/td><td>collect compatibility results<\/td><\/tr><tr><td><code>npm ci<\/code><\/td><td>lockfile-respecting deterministic install<\/td><\/tr><tr><td>artifact on Node 24 only<\/td><td>avoid duplicate build artifacts<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Lab B \u2014 Reusable Organization CI<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">B.1 Goal<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ten repositories should use the same quality gate without copying the workflow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">B.2 Central workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Standard Node CI\n\non:\n  workflow_call:\n    inputs:\n      node-version:\n        type: string\n        default: \"24\"\n      working-directory:\n        type: string\n        default: \".\"\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    defaults:\n      run:\n        working-directory: ${{ inputs.working-directory }}\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: actions\/setup-node@v7\n        with:\n          node-version: ${{ inputs.node-version }}\n          cache: npm\n          cache-dependency-path: &gt;-\n            ${{ inputs.working-directory }}\/package-lock.json\n\n      - run: npm ci\n      - run: npm run lint\n      - run: npm test\n      - run: npm run build\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">B.3 Application caller<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: CI\n\non:\n  pull_request:\n  push:\n    branches: &#91;main]\n\npermissions:\n  contents: read\n\njobs:\n  ci:\n    uses: my-org\/platform-workflows\/.github\/workflows\/node-ci.yml@v3\n    with:\n      node-version: \"24\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">B.4 Version promotion<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>feature branch in platform-workflows\n-&gt; test repositories\n-&gt; tag v3.1.0\n-&gt; move reviewed v3 major tag if your organization uses moving major tags\n-&gt; Dependabot\/update consumers\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For high-assurance consumers, pin the called workflow to a full commit SHA.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Lab C \u2014 Secure AWS OIDC Deployment<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">C.1 Architecture<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>sequenceDiagram\n    participant G as GitHub job\n    participant O as GitHub OIDC\n    participant S as AWS STS\n    participant R as IAM Role\n    participant A as AWS Resource\n\n    G-&gt;&gt;O: request ID token\n    O--&gt;&gt;G: signed token\n    G-&gt;&gt;S: AssumeRoleWithWebIdentity\n    S-&gt;&gt;R: evaluate trust conditions\n    R--&gt;&gt;S: allow\n    S--&gt;&gt;G: temporary credentials\n    G-&gt;&gt;A: deploy\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">C.2 AWS trust principles<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The role trust should constrain:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>issuer;<\/li>\n\n\n\n<li>audience;<\/li>\n\n\n\n<li>repository;<\/li>\n\n\n\n<li>branch or GitHub environment;<\/li>\n\n\n\n<li>immutable subject format if applicable to your repository.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The permission policy should constrain the AWS resources\/actions separately.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">C.3 Workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Deploy Production\n\non:\n  workflow_dispatch:\n    inputs:\n      release:\n        type: string\n        required: true\n\npermissions:\n  contents: read\n\njobs:\n  deploy:\n    environment: production\n\n    concurrency:\n      group: production\n      queue: max\n\n    permissions:\n      contents: read\n      id-token: write\n\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Configure AWS credentials\n        uses: aws-actions\/configure-aws-credentials@&lt;PINNED_SHA&gt;\n        with:\n          role-to-assume: arn:aws:iam::123456789012:role\/github-prod-deploy\n          aws-region: ap-northeast-1\n\n      - name: Verify identity\n        run: aws sts get-caller-identity\n\n      - name: Deploy exact release\n        env:\n          RELEASE: ${{ inputs.release }}\n        run: .\/deploy-release.sh \"$RELEASE\"\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Lab D \u2014 Build and Deploy a Container to Kubernetes<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">D.1 Requirements<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>build once;<\/li>\n\n\n\n<li>push to registry;<\/li>\n\n\n\n<li>deploy by digest;<\/li>\n\n\n\n<li>production environment approval;<\/li>\n\n\n\n<li>rollout verification;<\/li>\n\n\n\n<li>smoke test.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">D.2 Flow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    C&#91;Commit\/tag] --&gt; B&#91;Build image]\n    B --&gt; R&#91;(Registry)]\n    R --&gt; I&#91;Image digest]\n    I --&gt; G{Production gate}\n    G --&gt; H&#91;Helm deploy]\n    H --&gt; V&#91;Rollout verify]\n    V --&gt; S&#91;Smoke test]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">D.3 Skeleton workflow<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  image:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n\n    outputs:\n      digest: ${{ steps.build.outputs.digest }}\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - uses: docker\/setup-buildx-action@&lt;PINNED_SHA&gt;\n\n      - uses: docker\/login-action@&lt;PINNED_SHA&gt;\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - id: build\n        uses: docker\/build-push-action@&lt;PINNED_SHA&gt;\n        with:\n          context: .\n          push: true\n          tags: ghcr.io\/${{ github.repository }}:${{ github.sha }}\n\n  deploy:\n    needs: image\n    environment: production\n    concurrency:\n      group: production\n      queue: max\n    runs-on: self-hosted\n    timeout-minutes: 20\n\n    steps:\n      - uses: actions\/checkout@v6\n\n      - name: Deploy immutable digest\n        env:\n          DIGEST: ${{ needs.image.outputs.digest }}\n        run: |\n          helm upgrade --install app .\/chart \\\n            --namespace production \\\n            --set image.digest=\"$DIGEST\" \\\n            --wait \\\n            --timeout 5m\n\n      - name: Smoke test\n        run: curl --fail --retry 5 https:\/\/app.example.com\/health\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A private Kubernetes API is a networking requirement; the self-hosted runner must be intentionally placed and secured for that trust zone.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Lab E \u2014 Terraform Plan \/ Approval \/ Apply<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">E.1 Safe model<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    PR&#91;Pull request] --&gt; P&#91;Plan]\n    P --&gt; R&#91;Review]\n    R --&gt; M&#91;Merge]\n    M --&gt; A&#91;Trusted apply workflow]\n    A --&gt; E&#91;Protected environment]\n    E --&gt; C&#91;Cloud]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not blindly reuse a plan produced by untrusted code in a privileged apply job without validating the exact source revision and trust model.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">E.2 Plan job<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Terraform Plan\n\non:\n  pull_request:\n    paths: &#91;\"infra\/**\"]\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  plan:\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: infra\n\n    steps:\n      - uses: actions\/checkout@v6\n      - run: terraform fmt -check -recursive\n      - run: terraform init -input=false\n      - run: terraform validate\n      - run: terraform plan -input=false\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">E.3 Apply job<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Terraform Apply\n\non:\n  push:\n    branches: &#91;main]\n    paths: &#91;\"infra\/**\"]\n\npermissions:\n  contents: read\n\njobs:\n  apply:\n    environment: production\n\n    permissions:\n      contents: read\n      id-token: write\n\n    concurrency:\n      group: terraform-production\n      queue: max\n\n    runs-on: self-hosted\n    timeout-minutes: 30\n\n    defaults:\n      run:\n        working-directory: infra\n\n    steps:\n      - uses: actions\/checkout@v6\n      - run: terraform init -input=false\n      - run: terraform plan -input=false -out=tfplan\n      - run: terraform apply -input=false tfplan\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In a mature setup, policy\/security checks and plan review happen before apply.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Lab F \u2014 ARC Runner Fleet for Private Infrastructure<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">F.1 Use case<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The organization must deploy to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>private Kubernetes API;<\/li>\n\n\n\n<li>internal artifact registry;<\/li>\n\n\n\n<li>private database migration endpoint.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">GitHub-hosted runners cannot directly reach the resources.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">F.2 Architecture<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TB\n    G&#91;GitHub Actions] &lt;--&gt; L&#91;ARC listener]\n    L --&gt; C&#91;ARC controller]\n\n    subgraph VPC&#91;\"Private VPC \/ Kubernetes\"]\n      C --&gt; P&#91;Ephemeral runner pods]\n      P --&gt; E&#91;EKS private endpoint]\n      P --&gt; R&#91;Private registry]\n      P --&gt; D&#91;Internal services]\n    end\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">F.3 Trust zoning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use separate scale sets:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>arc-general\narc-infra\narc-production\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>arc-production<\/code>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>production repos only;<\/li>\n\n\n\n<li>no public PRs;<\/li>\n\n\n\n<li>dedicated Kubernetes namespace;<\/li>\n\n\n\n<li>restricted service account;<\/li>\n\n\n\n<li>production network access only as required;<\/li>\n\n\n\n<li>ephemeral runners;<\/li>\n\n\n\n<li>audited changes.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Lab G \u2014 Debugging a Workflow Systematically<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose a production deployment job is not starting.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this sequence:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Workflow exists?] --&gt; B&#91;Trigger matched?]\n    B --&gt; C&#91;Job if true?]\n    C --&gt; D&#91;needs succeeded?]\n    D --&gt; E&#91;Environment approved?]\n    E --&gt; F&#91;Runner available?]\n    F --&gt; G&#91;Runner labels\/group correct?]\n    G --&gt; H&#91;Job starts]\n    H --&gt; I&#91;Auth works?]\n    I --&gt; J&#91;Network works?]\n    J --&gt; K&#91;Deploy works?]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not start by randomly changing IAM and firewall rules. Identify the layer first.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Part IV \u2014 Decision Guides and Cheat Sheets<\/h1>\n\n\n\n<h1 class=\"wp-block-heading\">Decision Guide \u2014 Which Reuse Mechanism?<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Requirement<\/th><th class=\"has-text-align-right\" data-align=\"right\">YAML anchor<\/th><th class=\"has-text-align-right\" data-align=\"right\">Composite action<\/th><th class=\"has-text-align-right\" data-align=\"right\">Reusable workflow<\/th><th class=\"has-text-align-right\" data-align=\"right\">Workflow template<\/th><\/tr><\/thead><tbody><tr><td>Reuse static YAML<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Initial only<\/td><\/tr><tr><td>Reuse steps<\/td><td class=\"has-text-align-right\" data-align=\"right\">Limited<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Good<\/td><td class=\"has-text-align-right\" data-align=\"right\">Initial only<\/td><\/tr><tr><td>Reuse jobs<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Initial only<\/td><\/tr><tr><td>Choose runner<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><\/tr><tr><td>Central updates affect callers<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes by version<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes by version<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><\/tr><tr><td>Simple repository bootstrap<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">No<\/td><td class=\"has-text-align-right\" data-align=\"right\">Good<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><\/tr><tr><td>Cross-repository standard<\/td><td class=\"has-text-align-right\" data-align=\"right\">Limited<\/td><td class=\"has-text-align-right\" data-align=\"right\">Yes<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><td class=\"has-text-align-right\" data-align=\"right\">Excellent<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Decision Guide \u2014 Runner Choice<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Need a runner] --&gt; B{Private network required?}\n    B -- No --&gt; C{Special CPU\/RAM\/GPU\/static IP?}\n    C -- No --&gt; H&#91;Standard GitHub-hosted]\n    C -- Yes --&gt; L&#91;Larger GitHub-hosted]\n    B -- Yes --&gt; D{Can supported hosted private networking meet need?}\n    D -- Yes --&gt; L\n    D -- No --&gt; E{Kubernetes platform available?}\n    E -- Yes --&gt; R&#91;ARC ephemeral runners]\n    E -- No --&gt; S&#91;Ephemeral self-hosted VM runners]\n<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Decision Guide \u2014 Cache or Artifact?<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Need to persist data?] --&gt; B{Is it required for correctness \/ promotion?}\n    B -- Yes --&gt; C&#91;Artifact \/ package \/ registry]\n    B -- No --&gt; D{Is it reusable performance data?}\n    D -- Yes --&gt; E&#91;Cache]\n    D -- No --&gt; F&#91;Do not persist]\n<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Decision Guide \u2014&nbsp;<code>pull_request<\/code>&nbsp;or&nbsp;<code>pull_request_target<\/code>?<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Need<\/th><th class=\"has-text-align-left\" data-align=\"left\">Event<\/th><\/tr><\/thead><tbody><tr><td>Build\/test PR code<\/td><td><code>pull_request<\/code><\/td><\/tr><tr><td>Test fork code safely<\/td><td><code>pull_request<\/code><\/td><\/tr><tr><td>Label\/comment using base-repo privileges without running PR code<\/td><td><code>pull_request_target<\/code><\/td><\/tr><tr><td>Privileged follow-up after untrusted CI<\/td><td>often&nbsp;<code>workflow_run<\/code><\/td><\/tr><tr><td>Execute fork code with production secret<\/td><td><strong>Do not design this<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Decision Guide \u2014 Secret or Variable?<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Value<\/th><th class=\"has-text-align-left\" data-align=\"left\">Use<\/th><\/tr><\/thead><tbody><tr><td>API hostname<\/td><td>variable<\/td><\/tr><tr><td>region<\/td><td>variable<\/td><\/tr><tr><td>feature toggle<\/td><td>variable<\/td><\/tr><tr><td>password<\/td><td>secret<\/td><\/tr><tr><td>private key<\/td><td>secret<\/td><\/tr><tr><td>webhook bearer token<\/td><td>secret<\/td><\/tr><tr><td>cloud access key<\/td><td>preferably replace with OIDC<\/td><\/tr><tr><td>environment name<\/td><td>input\/variable<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Cheat Sheet \u2014 Minimal Secure PR CI<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>name: PR CI\n\non:\n  pull_request:\n\npermissions:\n  contents: read\n\ncache-mode: read\n\nconcurrency:\n  group: pr-${{ github.event.pull_request.number }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n\n    steps:\n      - uses: actions\/checkout@v6\n      - run: .\/ci.sh\n<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Cheat Sheet \u2014 Minimal Protected Production Job<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\n  deploy:\n    environment: production\n\n    permissions:\n      contents: read\n      id-token: write\n\n    concurrency:\n      group: production\n      queue: max\n\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n\n    steps:\n      - run: .\/deploy.sh\n<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Cheat Sheet \u2014 Data Passing<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>same command              -&gt; shell variable\nlater step, same job      -&gt; GITHUB_ENV\nsmall computed value      -&gt; GITHUB_OUTPUT\ndependent job             -&gt; job output + needs\nwhole file\/directory      -&gt; artifact\nlater workflow\/run        -&gt; package\/artifact\/external store\nspeed optimization        -&gt; cache\n<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Cheat Sheet \u2014 Troubleshooting<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Symptom<\/th><th class=\"has-text-align-left\" data-align=\"left\">First checks<\/th><\/tr><\/thead><tbody><tr><td>Workflow never starts<\/td><td>event, branch, path, workflow location<\/td><\/tr><tr><td>Job skipped<\/td><td><code>if<\/code>,&nbsp;<code>needs<\/code>, environment<\/td><\/tr><tr><td>Job queued forever<\/td><td>runner labels\/group\/capacity<\/td><\/tr><tr><td><code>403<\/code>&nbsp;GitHub API<\/td><td><code>permissions<\/code>, fork policy<\/td><\/tr><tr><td>Cloud auth fails<\/td><td>OIDC permission, audience, subject, role trust<\/td><\/tr><tr><td>Private endpoint fails<\/td><td>runner network\/DNS\/firewall<\/td><\/tr><tr><td>Cache misses<\/td><td>key\/hash\/scope\/cache-mode<\/td><\/tr><tr><td>Service unavailable<\/td><td>health check\/port\/hostname\/startup<\/td><\/tr><tr><td>Artifact missing<\/td><td>path\/name\/retention\/permissions<\/td><\/tr><tr><td>Works locally, fails in Actions<\/td><td>shell, OS, env, file case, clean checkout<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Part V \u2014 Official Reference Links<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Use official GitHub documentation as the source of truth for version-sensitive syntax and limits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Workflow syntax:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/workflow-syntax\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/workflow-syntax<\/a><\/li>\n\n\n\n<li>Events:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/events-that-trigger-workflows\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/events-that-trigger-workflows<\/a><\/li>\n\n\n\n<li>Workflow commands:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/workflow-commands\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/workflow-commands<\/a><\/li>\n\n\n\n<li>Variables:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/variables\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/variables<\/a><\/li>\n\n\n\n<li>Expressions:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/expressions\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/expressions<\/a><\/li>\n\n\n\n<li>Contexts:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/contexts\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/contexts<\/a><\/li>\n\n\n\n<li>Deployments and environments:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/deployments-and-environments\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/deployments-and-environments<\/a><\/li>\n\n\n\n<li>Dependency caching:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/dependency-caching\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/dependency-caching<\/a><\/li>\n\n\n\n<li>Reusing workflow configurations:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/reusing-workflow-configurations\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/reusing-workflow-configurations<\/a><\/li>\n\n\n\n<li>Metadata syntax:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/metadata-syntax\">https:\/\/docs.github.com\/en\/actions\/reference\/workflows-and-actions\/metadata-syntax<\/a><\/li>\n\n\n\n<li>GitHub-hosted runners:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/runners\/github-hosted-runners\">https:\/\/docs.github.com\/en\/actions\/reference\/runners\/github-hosted-runners<\/a><\/li>\n\n\n\n<li>Larger runners:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/runners\/larger-runners\">https:\/\/docs.github.com\/en\/actions\/reference\/runners\/larger-runners<\/a><\/li>\n\n\n\n<li>Secure use:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/security\/secure-use\">https:\/\/docs.github.com\/en\/actions\/reference\/security\/secure-use<\/a><\/li>\n\n\n\n<li>Secure\u00a0<code>pull_request_target<\/code>:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/security\/securely-using-pull_request_target\">https:\/\/docs.github.com\/en\/actions\/reference\/security\/securely-using-pull_request_target<\/a><\/li>\n\n\n\n<li>OIDC:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/security\/oidc\">https:\/\/docs.github.com\/en\/actions\/reference\/security\/oidc<\/a><\/li>\n\n\n\n<li>Actions limits:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/reference\/limits\">https:\/\/docs.github.com\/en\/actions\/reference\/limits<\/a><\/li>\n\n\n\n<li>ARC concepts:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/concepts\/runners\/actions-runner-controller\">https:\/\/docs.github.com\/en\/actions\/concepts\/runners\/actions-runner-controller<\/a><\/li>\n\n\n\n<li>Artifact attestations:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/how-tos\/secure-your-work\/use-artifact-attestations\">https:\/\/docs.github.com\/en\/actions\/how-tos\/secure-your-work\/use-artifact-attestations<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final Production Checklist<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Before calling a GitHub Actions platform &#8220;production ready&#8221;:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Workflow correctness<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] Triggers are narrow and intentional.<\/li>\n\n\n\n<li>[ ] Job dependencies match the desired DAG.<\/li>\n\n\n\n<li>[ ] Timeouts exist.<\/li>\n\n\n\n<li>[ ] Concurrency exists where races are unsafe.<\/li>\n\n\n\n<li>[ ] Dynamic data uses outputs\/artifacts appropriately.<\/li>\n\n\n\n<li>[ ] CI can succeed without cache.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Security<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ]\u00a0<code>GITHUB_TOKEN<\/code>\u00a0is least privilege.<\/li>\n\n\n\n<li>[ ] Untrusted event data is not interpolated into executable shell syntax.<\/li>\n\n\n\n<li>[ ]\u00a0<code>pull_request_target<\/code>\u00a0is not executing untrusted PR code.<\/li>\n\n\n\n<li>[ ] Third-party actions are reviewed and SHA-pinned.<\/li>\n\n\n\n<li>[ ] Fork PRs do not reach privileged self-hosted runners.<\/li>\n\n\n\n<li>[ ] Production cloud auth uses OIDC where possible.<\/li>\n\n\n\n<li>[ ] Production environment is protected.<\/li>\n\n\n\n<li>[ ] Long-lived secrets are inventoried and rotated.<\/li>\n\n\n\n<li>[ ] Cache trust boundaries are explicit.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Runners<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] Runner type is appropriate.<\/li>\n\n\n\n<li>[ ] Self-hosted runners are patched.<\/li>\n\n\n\n<li>[ ] Sensitive pools are ephemeral.<\/li>\n\n\n\n<li>[ ] Runner groups restrict repositories.<\/li>\n\n\n\n<li>[ ] Private network paths are explicit.<\/li>\n\n\n\n<li>[ ] No unnecessary ambient cloud identity.<\/li>\n\n\n\n<li>[ ] Capacity\/queue metrics exist.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Supply chain<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] Build is reproducible enough for the release model.<\/li>\n\n\n\n<li>[ ] Release artifacts are immutable.<\/li>\n\n\n\n<li>[ ] Build once\/deploy many is used where practical.<\/li>\n\n\n\n<li>[ ] Image\/package provenance is available where needed.<\/li>\n\n\n\n<li>[ ] Artifact retention is intentional.<\/li>\n\n\n\n<li>[ ] Deployment uses exact artifact\/image identity.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Governance<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] Required checks enforced.<\/li>\n\n\n\n<li>[ ] Merge queue workflows support\u00a0<code>merge_group<\/code>\u00a0if merge queue is used.<\/li>\n\n\n\n<li>[ ] Actions policy restricts unreviewed code.<\/li>\n\n\n\n<li>[ ] CODEOWNERS\/review policy is configured.<\/li>\n\n\n\n<li>[ ] Production deployment approvals\/protection are configured.<\/li>\n\n\n\n<li>[ ] Audit and ownership are documented.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Operations<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] Workflow duration\/queue\/failure metrics are visible.<\/li>\n\n\n\n<li>[ ] Cost is measured.<\/li>\n\n\n\n<li>[ ] Common failures have runbooks.<\/li>\n\n\n\n<li>[ ] Rollback is tested.<\/li>\n\n\n\n<li>[ ] Central reusable workflows have staged rollout\/testing.<\/li>\n\n\n\n<li>[ ] Action\/runner\/runtime upgrades have an owner.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Closing Mental Model<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">If you remember only one architecture, remember this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    E&#91;Event] --&gt; W&#91;Workflow]\n    W --&gt; P&#91;Policy + permissions]\n    P --&gt; J&#91;Jobs]\n    J --&gt; R&#91;Runners]\n    R --&gt; S&#91;Steps\/actions]\n    S --&gt; A&#91;Immutable artifacts]\n    A --&gt; G&#91;Environment gates]\n    G --&gt; D&#91;Deployment]\n    D --&gt; V&#91;Verification]\n    V --&gt; O&#91;Observability + audit]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A production GitHub Actions system is not &#8220;some YAML that runs tests.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is an event-driven execution platform with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>identity;<\/li>\n\n\n\n<li>policy;<\/li>\n\n\n\n<li>compute;<\/li>\n\n\n\n<li>data flow;<\/li>\n\n\n\n<li>supply-chain trust;<\/li>\n\n\n\n<li>deployment controls;<\/li>\n\n\n\n<li>observability;<\/li>\n\n\n\n<li>governance.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Design all eight deliberately.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Appendix A \u2014 2026-Sensitive Features Worth Rechecking<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The core GitHub Actions model is stable, but several capabilities in this handbook are newer or especially version-sensitive. Before standardizing them across an enterprise, confirm the current GitHub documentation for your GitHub.com\/GHES version.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Feature<\/th><th class=\"has-text-align-left\" data-align=\"left\">Current handbook behavior<\/th><\/tr><\/thead><tbody><tr><td><code>actions\/checkout<\/code><\/td><td>Examples use&nbsp;<code>@v6<\/code><\/td><\/tr><tr><td><code>actions\/setup-node<\/code><\/td><td>Examples use&nbsp;<code>@v7<\/code><\/td><\/tr><tr><td>Cache access<\/td><td><code>cache-mode: read\/write\/write-only\/none<\/code><\/td><\/tr><tr><td>Concurrency queues<\/td><td><code>queue: max<\/code>&nbsp;can retain multiple pending runs<\/td><\/tr><tr><td>Step concurrency<\/td><td><code>background<\/code>,&nbsp;<code>wait<\/code>,&nbsp;<code>wait-all<\/code>,&nbsp;<code>cancel<\/code>, and&nbsp;<code>parallel<\/code><\/td><\/tr><tr><td>Environment without deployment object<\/td><td><code>environment.deployment: false<\/code><\/td><\/tr><tr><td>Same-repository action reference<\/td><td><code>$\/.github\/actions\/...<\/code>&nbsp;on supported GitHub.com workflows<\/td><\/tr><tr><td>Reusable workflows<\/td><td>Current reference documents 10 nesting levels and 50 unique called workflows<\/td><\/tr><tr><td>OIDC subject<\/td><td>Newer\/opted-in repositories can use immutable owner\/repository-ID subject formats<\/td><\/tr><tr><td>Hosted images<\/td><td>Current GitHub-hosted runner catalog includes newer Ubuntu\/Windows\/macOS\/ARM images<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Treat these as capabilities to&nbsp;<strong>verify<\/strong>, not assumptions to copy from an older installation.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Appendix B \u2014 Complete Source Topic Coverage Audit<\/h1>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">This appendix preserves the exact curriculum vocabulary from the supplied topic map so the handbook is searchable and auditable against the requested scope. The explanations, examples, diagrams, and production guidance live in Chapters 1\u201362; this appendix is a traceability checklist, not a second tutorial.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">1. BASIC \u2014 GitHub Actions Foundations<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 Introduction to GitHub Actions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub Actions overview<\/li>\n\n\n\n<li>[x] CI<\/li>\n\n\n\n<li>[x] Continuous Delivery<\/li>\n\n\n\n<li>[x] Continuous Deployment<\/li>\n\n\n\n<li>[x] Automation<\/li>\n\n\n\n<li>[x] Event-driven automation<\/li>\n\n\n\n<li>[x] Repository automation<\/li>\n\n\n\n<li>[x] DevOps automation<\/li>\n\n\n\n<li>[x] GitHub Actions use cases<\/li>\n\n\n\n<li>[x] GitHub Actions terminology<\/li>\n\n\n\n<li>[x] GitHub Actions architecture<\/li>\n\n\n\n<li>[x] GitHub Actions execution model<\/li>\n\n\n\n<li>[x] Workflow lifecycle<\/li>\n\n\n\n<li>[x] Workflow run lifecycle<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1.2 Core Components<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflows<\/li>\n\n\n\n<li>[x] Workflow runs<\/li>\n\n\n\n<li>[x] Events<\/li>\n\n\n\n<li>[x] Triggers<\/li>\n\n\n\n<li>[x] Jobs<\/li>\n\n\n\n<li>[x] Steps<\/li>\n\n\n\n<li>[x] Actions<\/li>\n\n\n\n<li>[x] Runners<\/li>\n\n\n\n<li>[x] Commands<\/li>\n\n\n\n<li>[x] Scripts<\/li>\n\n\n\n<li>[x] Artifacts<\/li>\n\n\n\n<li>[x] Caches<\/li>\n\n\n\n<li>[x] Variables<\/li>\n\n\n\n<li>[x] Secrets<\/li>\n\n\n\n<li>[x] Contexts<\/li>\n\n\n\n<li>[x] Expressions<\/li>\n\n\n\n<li>[x] Environments<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1.3 Workflow Files<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>.github\/workflows<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>.yml<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>.yaml<\/code><\/li>\n\n\n\n<li>[x] Multiple workflows<\/li>\n\n\n\n<li>[x] Workflow naming<\/li>\n\n\n\n<li>[x] Workflow file naming<\/li>\n\n\n\n<li>[x] Workflow version control<\/li>\n\n\n\n<li>[x] Workflow visibility<\/li>\n\n\n\n<li>[x] Workflow enablement<\/li>\n\n\n\n<li>[x] Workflow disablement<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1.4 Basic Workflow Structure<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>run-name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>on<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>permissions<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>env<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>defaults<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>concurrency<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>jobs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>runs-on<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>steps<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>uses<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>run<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>with<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. FUNDAMENTALS \u2014 Workflow Syntax and Execution<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">2.1 YAML Fundamentals<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] YAML indentation<\/li>\n\n\n\n<li>[x] YAML mappings<\/li>\n\n\n\n<li>[x] YAML sequences<\/li>\n\n\n\n<li>[x] YAML strings<\/li>\n\n\n\n<li>[x] YAML booleans<\/li>\n\n\n\n<li>[x] YAML multiline strings<\/li>\n\n\n\n<li>[x] YAML quoting<\/li>\n\n\n\n<li>[x] YAML comments<\/li>\n\n\n\n<li>[x] YAML anchors<\/li>\n\n\n\n<li>[x] YAML aliases<\/li>\n\n\n\n<li>[x] YAML reusable structures<\/li>\n\n\n\n<li>[x] YAML syntax validation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2.2 Workflow-Level Syntax<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>run-name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>on<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>permissions<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>env<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>defaults<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>defaults.run<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>defaults.run.shell<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>defaults.run.working-directory<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>concurrency<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>jobs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>cache-mode<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2.3 Job-Level Syntax<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>jobs.&lt;job_id><\/code><\/li>\n\n\n\n<li>[x] Job IDs<\/li>\n\n\n\n<li>[x]\u00a0<code>name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>permissions<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>needs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>if<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>runs-on<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>environment<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>concurrency<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>outputs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>env<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>defaults<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>timeout-minutes<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>continue-on-error<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>container<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>services<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>strategy<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>uses<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>with<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>secrets<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>cache-mode<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2.4 Step-Level Syntax<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>steps<\/code><\/li>\n\n\n\n<li>[x] Step IDs<\/li>\n\n\n\n<li>[x]\u00a0<code>id<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>if<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>uses<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>run<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>shell<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>with<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>env<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>continue-on-error<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>timeout-minutes<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>working-directory<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. FUNDAMENTALS \u2014 Events and Workflow Triggers<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 Trigger Concepts<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub events<\/li>\n\n\n\n<li>[x] Webhook events<\/li>\n\n\n\n<li>[x] Activity types<\/li>\n\n\n\n<li>[x] Manual triggers<\/li>\n\n\n\n<li>[x] Scheduled triggers<\/li>\n\n\n\n<li>[x] External triggers<\/li>\n\n\n\n<li>[x] Multiple triggers<\/li>\n\n\n\n<li>[x] Trigger filters<\/li>\n\n\n\n<li>[x] Event payloads<\/li>\n\n\n\n<li>[x]\u00a0<code>github.event<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_EVENT_NAME<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_EVENT_PATH<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.2 GitHub Actions Events<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>branch_protection_rule<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>check_run<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>check_suite<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>create<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>delete<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>deployment<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>deployment_status<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>discussion<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>discussion_comment<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>fork<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>gollum<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>image_version<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>issue_comment<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>issues<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>label<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>merge_group<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>milestone<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>page_build<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>public<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pull_request<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pull_request_review<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pull_request_review_comment<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pull_request_target<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>push<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>registry_package<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>release<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>repository_dispatch<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>schedule<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>status<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>watch<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>workflow_call<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>workflow_dispatch<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>workflow_run<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.3 Trigger Filters<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>types<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>branches<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>branches-ignore<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>tags<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>tags-ignore<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>paths<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>paths-ignore<\/code><\/li>\n\n\n\n<li>[x] Glob patterns<\/li>\n\n\n\n<li>[x] Negative patterns<\/li>\n\n\n\n<li>[x] Branch filtering<\/li>\n\n\n\n<li>[x] Tag filtering<\/li>\n\n\n\n<li>[x] Path filtering<\/li>\n\n\n\n<li>[x] Activity filtering<\/li>\n\n\n\n<li>[x] Combined filters<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.4 Manual Workflows<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>workflow_dispatch<\/code><\/li>\n\n\n\n<li>[x] Manual workflow execution<\/li>\n\n\n\n<li>[x] Workflow inputs<\/li>\n\n\n\n<li>[x] Boolean inputs<\/li>\n\n\n\n<li>[x] Choice inputs<\/li>\n\n\n\n<li>[x] String inputs<\/li>\n\n\n\n<li>[x] Number inputs<\/li>\n\n\n\n<li>[x] Environment inputs<\/li>\n\n\n\n<li>[x] Default input values<\/li>\n\n\n\n<li>[x] Required inputs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.5 Scheduled Workflows<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>schedule<\/code><\/li>\n\n\n\n<li>[x] Cron syntax<\/li>\n\n\n\n<li>[x] Scheduled workflow limitations<\/li>\n\n\n\n<li>[x] Default branch execution<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.6 External Workflow Triggers<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>repository_dispatch<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>client_payload<\/code><\/li>\n\n\n\n<li>[x] REST API triggering<\/li>\n\n\n\n<li>[x] GitHub CLI triggering<\/li>\n\n\n\n<li>[x] Cross-repository triggers<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4. FUNDAMENTALS \u2014 Jobs<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 Job Architecture<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Job definition<\/li>\n\n\n\n<li>[x] Job isolation<\/li>\n\n\n\n<li>[x] Sequential jobs<\/li>\n\n\n\n<li>[x] Parallel jobs<\/li>\n\n\n\n<li>[x] Job dependencies<\/li>\n\n\n\n<li>[x] Job execution status<\/li>\n\n\n\n<li>[x] Job conclusions<\/li>\n\n\n\n<li>[x] Job failure<\/li>\n\n\n\n<li>[x] Job cancellation<\/li>\n\n\n\n<li>[x] Job skipping<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 Job Dependencies<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>needs<\/code><\/li>\n\n\n\n<li>[x] Dependency chains<\/li>\n\n\n\n<li>[x] Fan-out jobs<\/li>\n\n\n\n<li>[x] Fan-in jobs<\/li>\n\n\n\n<li>[x] Dependency outputs<\/li>\n\n\n\n<li>[x] Conditional dependent jobs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.3 Job Conditions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Job-level\u00a0<code>if<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>success()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>failure()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>cancelled()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>always()<\/code><\/li>\n\n\n\n<li>[x] Conditional execution<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.4 Job Outputs<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Job outputs<\/li>\n\n\n\n<li>[x] Step-to-job outputs<\/li>\n\n\n\n<li>[x] Job-to-job outputs<\/li>\n\n\n\n<li>[x]\u00a0<code>needs<\/code>\u00a0context<\/li>\n\n\n\n<li>[x] Output expressions<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5. FUNDAMENTALS \u2014 Steps<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">5.1 Step Execution<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Action steps<\/li>\n\n\n\n<li>[x] Shell steps<\/li>\n\n\n\n<li>[x] Sequential step execution<\/li>\n\n\n\n<li>[x] Step IDs<\/li>\n\n\n\n<li>[x] Step names<\/li>\n\n\n\n<li>[x] Step conditions<\/li>\n\n\n\n<li>[x] Step outputs<\/li>\n\n\n\n<li>[x] Step environment<\/li>\n\n\n\n<li>[x] Step failure<\/li>\n\n\n\n<li>[x] Step timeout<\/li>\n\n\n\n<li>[x] Continue-on-error<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5.2 Shell Commands<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Bash<\/li>\n\n\n\n<li>[x]\u00a0<code>sh<\/code><\/li>\n\n\n\n<li>[x] PowerShell<\/li>\n\n\n\n<li>[x]\u00a0<code>pwsh<\/code><\/li>\n\n\n\n<li>[x] Windows Command Prompt<\/li>\n\n\n\n<li>[x] Python<\/li>\n\n\n\n<li>[x] Custom shells<\/li>\n\n\n\n<li>[x] Shell exit codes<\/li>\n\n\n\n<li>[x] Multiline commands<\/li>\n\n\n\n<li>[x] Working directories<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6. ESSENTIALS \u2014 Runners<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">6.1 Runner Fundamentals<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Runner architecture<\/li>\n\n\n\n<li>[x] Runner lifecycle<\/li>\n\n\n\n<li>[x] Runner assignment<\/li>\n\n\n\n<li>[x] Runner labels<\/li>\n\n\n\n<li>[x] Runner groups<\/li>\n\n\n\n<li>[x] Runner environment<\/li>\n\n\n\n<li>[x] Runner workspace<\/li>\n\n\n\n<li>[x] Runner temporary directory<\/li>\n\n\n\n<li>[x] Runner tool cache<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6.2 GitHub-Hosted Runners<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Ubuntu runners<\/li>\n\n\n\n<li>[x] Windows runners<\/li>\n\n\n\n<li>[x] macOS runners<\/li>\n\n\n\n<li>[x] ARM runners<\/li>\n\n\n\n<li>[x] Runner images<\/li>\n\n\n\n<li>[x] Preinstalled software<\/li>\n\n\n\n<li>[x] Runner image updates<\/li>\n\n\n\n<li>[x] Hardware resources<\/li>\n\n\n\n<li>[x] Runner filesystem<\/li>\n\n\n\n<li>[x] Hosted runner networking<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6.3 Larger Runners<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Larger GitHub-hosted runners<\/li>\n\n\n\n<li>[x] Runner sizes<\/li>\n\n\n\n<li>[x] Runner groups<\/li>\n\n\n\n<li>[x] Static IP addresses<\/li>\n\n\n\n<li>[x] Private networking<\/li>\n\n\n\n<li>[x] Autoscaling<\/li>\n\n\n\n<li>[x] GPU runners<\/li>\n\n\n\n<li>[x] Larger-runner access control<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6.4 Self-Hosted Runners<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Self-hosted runner architecture<\/li>\n\n\n\n<li>[x] Repository-level runners<\/li>\n\n\n\n<li>[x] Organization-level runners<\/li>\n\n\n\n<li>[x] Enterprise-level runners<\/li>\n\n\n\n<li>[x] Runner installation<\/li>\n\n\n\n<li>[x] Runner registration<\/li>\n\n\n\n<li>[x] Runner removal<\/li>\n\n\n\n<li>[x] Runner labels<\/li>\n\n\n\n<li>[x] Custom labels<\/li>\n\n\n\n<li>[x] Runner groups<\/li>\n\n\n\n<li>[x] Runner services<\/li>\n\n\n\n<li>[x] Runner updates<\/li>\n\n\n\n<li>[x] Runner versioning<\/li>\n\n\n\n<li>[x] Runner networking<\/li>\n\n\n\n<li>[x] Proxy configuration<\/li>\n\n\n\n<li>[x] Firewall requirements<\/li>\n\n\n\n<li>[x] Runner security<\/li>\n\n\n\n<li>[x] Persistent runners<\/li>\n\n\n\n<li>[x] Ephemeral runners<\/li>\n\n\n\n<li>[x] Just-in-time runners<\/li>\n\n\n\n<li>[x] Runner autoscaling<\/li>\n\n\n\n<li>[x] Runner routing<\/li>\n\n\n\n<li>[x] Runner monitoring<\/li>\n\n\n\n<li>[x] Runner cleanup<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6.5 Private Networking<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Private networking for hosted runners<\/li>\n\n\n\n<li>[x] VNet\/VPC connectivity<\/li>\n\n\n\n<li>[x] Private resources<\/li>\n\n\n\n<li>[x] Private package registries<\/li>\n\n\n\n<li>[x] Private cloud endpoints<\/li>\n\n\n\n<li>[x] Network allowlists<\/li>\n\n\n\n<li>[x] DNS considerations<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7. ESSENTIALS \u2014 Variables and Environment Variables<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">7.1 Environment Variables<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow-level environment variables<\/li>\n\n\n\n<li>[x] Job-level environment variables<\/li>\n\n\n\n<li>[x] Step-level environment variables<\/li>\n\n\n\n<li>[x] Environment variable precedence<\/li>\n\n\n\n<li>[x] Default environment variables<\/li>\n\n\n\n<li>[x] Custom environment variables<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7.2 Configuration Variables<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Repository variables<\/li>\n\n\n\n<li>[x] Organization variables<\/li>\n\n\n\n<li>[x] Environment variables<\/li>\n\n\n\n<li>[x] Variable scopes<\/li>\n\n\n\n<li>[x] Variable precedence<\/li>\n\n\n\n<li>[x] Variable naming<\/li>\n\n\n\n<li>[x] Variable limits<\/li>\n\n\n\n<li>[x]\u00a0<code>vars<\/code>\u00a0context<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7.3 GitHub Default Variables<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>CI<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ACTION<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ACTION_PATH<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ACTION_REPOSITORY<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ACTIONS<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ACTOR<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ACTOR_ID<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_API_URL<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_BASE_REF<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ENV<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_EVENT_NAME<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_EVENT_PATH<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_GRAPHQL_URL<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_HEAD_REF<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_JOB<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_OUTPUT<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_PATH<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REF<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REF_NAME<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REF_PROTECTED<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REF_TYPE<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REPOSITORY<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REPOSITORY_ID<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_REPOSITORY_OWNER<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_RETENTION_DAYS<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_RUN_ATTEMPT<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_RUN_ID<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_RUN_NUMBER<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_SERVER_URL<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_SHA<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_STEP_SUMMARY<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_TRIGGERING_ACTOR<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_WORKFLOW<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_WORKFLOW_REF<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_WORKFLOW_SHA<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_WORKSPACE<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>RUNNER_ARCH<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>RUNNER_ENVIRONMENT<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>RUNNER_NAME<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>RUNNER_OS<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>RUNNER_TEMP<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>RUNNER_TOOL_CACHE<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">8. ESSENTIALS \u2014 Contexts<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>github<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>env<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>vars<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>job<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>jobs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>steps<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>runner<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>secrets<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>strategy<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>matrix<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>needs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>inputs<\/code><\/li>\n\n\n\n<li>[x] Context availability<\/li>\n\n\n\n<li>[x] Context property access<\/li>\n\n\n\n<li>[x] Property dereference syntax<\/li>\n\n\n\n<li>[x] Index syntax<\/li>\n\n\n\n<li>[x] Context security<\/li>\n\n\n\n<li>[x] Context debugging<\/li>\n\n\n\n<li>[x] Context dumping<\/li>\n\n\n\n<li>[x] Context availability by workflow key<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9. ESSENTIALS \u2014 Expressions<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">9.1 Expression Syntax<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>${{ }}<\/code><\/li>\n\n\n\n<li>[x] Literals<\/li>\n\n\n\n<li>[x] Booleans<\/li>\n\n\n\n<li>[x] Null<\/li>\n\n\n\n<li>[x] Numbers<\/li>\n\n\n\n<li>[x] Strings<\/li>\n\n\n\n<li>[x] Operators<\/li>\n\n\n\n<li>[x] Operator precedence<\/li>\n\n\n\n<li>[x] Property access<\/li>\n\n\n\n<li>[x] Object filters<\/li>\n\n\n\n<li>[x] Loose equality<\/li>\n\n\n\n<li>[x] Type coercion<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9.2 Expression Operators<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>!<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>><\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>>=<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>&lt;<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>&lt;=<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>==<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>!=<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>&amp;&amp;<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>||<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9.3 Expression Functions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>contains()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>startsWith()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>endsWith()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>format()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>join()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>toJSON()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>fromJSON()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>hashFiles()<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9.4 Status Functions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>success()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>always()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>cancelled()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>failure()<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">10. ESSENTIALS \u2014 Secrets<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Repository secrets<\/li>\n\n\n\n<li>[x] Organization secrets<\/li>\n\n\n\n<li>[x] Environment secrets<\/li>\n\n\n\n<li>[x] Secret scopes<\/li>\n\n\n\n<li>[x] Secret precedence<\/li>\n\n\n\n<li>[x] Secret naming<\/li>\n\n\n\n<li>[x] Secret limits<\/li>\n\n\n\n<li>[x] Secret access policies<\/li>\n\n\n\n<li>[x] Secret masking<\/li>\n\n\n\n<li>[x] Secret redaction<\/li>\n\n\n\n<li>[x] Secret inheritance<\/li>\n\n\n\n<li>[x] Secrets in reusable workflows<\/li>\n\n\n\n<li>[x] Secrets in forked pull requests<\/li>\n\n\n\n<li>[x] Secrets with Dependabot<\/li>\n\n\n\n<li>[x] Secrets with command-line tools<\/li>\n\n\n\n<li>[x] Large secrets<\/li>\n\n\n\n<li>[x] Structured secrets<\/li>\n\n\n\n<li>[x] Secret rotation<\/li>\n\n\n\n<li>[x] Least-privilege secrets<\/li>\n\n\n\n<li>[x]\u00a0<code>secrets<\/code>\u00a0context<\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_TOKEN<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">11. ESSENTIALS \u2014&nbsp;<code>GITHUB_TOKEN<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Automatic token generation<\/li>\n\n\n\n<li>[x] Token lifecycle<\/li>\n\n\n\n<li>[x] Token scope<\/li>\n\n\n\n<li>[x] Token expiration<\/li>\n\n\n\n<li>[x] Repository access<\/li>\n\n\n\n<li>[x]\u00a0<code>permissions<\/code><\/li>\n\n\n\n<li>[x] Workflow-level permissions<\/li>\n\n\n\n<li>[x] Job-level permissions<\/li>\n\n\n\n<li>[x] Read permissions<\/li>\n\n\n\n<li>[x] Write permissions<\/li>\n\n\n\n<li>[x]\u00a0<code>contents<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>actions<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>attestations<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>checks<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>deployments<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>discussions<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>id-token<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>issues<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>models<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>packages<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pages<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pull-requests<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>security-events<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>statuses<\/code><\/li>\n\n\n\n<li>[x] Least privilege<\/li>\n\n\n\n<li>[x] Token-triggered workflow behavior<\/li>\n\n\n\n<li>[x] GitHub API authentication<\/li>\n\n\n\n<li>[x] GitHub CLI authentication<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">12. ESSENTIALS \u2014 Workflow Commands<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow command syntax<\/li>\n\n\n\n<li>[x] Environment files<\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_ENV<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_OUTPUT<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_PATH<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_STEP_SUMMARY<\/code><\/li>\n\n\n\n<li>[x] Setting environment variables<\/li>\n\n\n\n<li>[x] Setting outputs<\/li>\n\n\n\n<li>[x] Adding system paths<\/li>\n\n\n\n<li>[x] Job summaries<\/li>\n\n\n\n<li>[x] Error annotations<\/li>\n\n\n\n<li>[x] Warning annotations<\/li>\n\n\n\n<li>[x] Notice annotations<\/li>\n\n\n\n<li>[x] Debug messages<\/li>\n\n\n\n<li>[x] Log grouping<\/li>\n\n\n\n<li>[x]\u00a0<code>group<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>endgroup<\/code><\/li>\n\n\n\n<li>[x] Secret masking<\/li>\n\n\n\n<li>[x]\u00a0<code>add-mask<\/code><\/li>\n\n\n\n<li>[x] Stopping workflow commands<\/li>\n\n\n\n<li>[x] Action state<\/li>\n\n\n\n<li>[x]\u00a0<code>GITHUB_STATE<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">13. ESSENTIALS \u2014 Matrix Builds<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Matrix strategy<\/li>\n\n\n\n<li>[x]\u00a0<code>strategy.matrix<\/code><\/li>\n\n\n\n<li>[x] Multi-dimensional matrices<\/li>\n\n\n\n<li>[x] Operating-system matrices<\/li>\n\n\n\n<li>[x] Runtime-version matrices<\/li>\n\n\n\n<li>[x] Include<\/li>\n\n\n\n<li>[x] Exclude<\/li>\n\n\n\n<li>[x] Matrix expansion<\/li>\n\n\n\n<li>[x] Dynamic matrices<\/li>\n\n\n\n<li>[x] Matrix from JSON<\/li>\n\n\n\n<li>[x] Matrix outputs<\/li>\n\n\n\n<li>[x] Matrix job dependencies<\/li>\n\n\n\n<li>[x]\u00a0<code>max-parallel<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>fail-fast<\/code><\/li>\n\n\n\n<li>[x] Matrix\u00a0<code>continue-on-error<\/code><\/li>\n\n\n\n<li>[x] Experimental matrix jobs<\/li>\n\n\n\n<li>[x] Matrix limits<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">14. ESSENTIALS \u2014 Caching<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Dependency caching<\/li>\n\n\n\n<li>[x]\u00a0<code>actions\/cache<\/code><\/li>\n\n\n\n<li>[x] Automatic dependency caching<\/li>\n\n\n\n<li>[x] Cache keys<\/li>\n\n\n\n<li>[x] Restore keys<\/li>\n\n\n\n<li>[x] Cache matching<\/li>\n\n\n\n<li>[x] Cache scope<\/li>\n\n\n\n<li>[x] Cache version<\/li>\n\n\n\n<li>[x] Cache hits<\/li>\n\n\n\n<li>[x] Cache misses<\/li>\n\n\n\n<li>[x] Cross-OS cache<\/li>\n\n\n\n<li>[x] Cache eviction<\/li>\n\n\n\n<li>[x] Cache retention<\/li>\n\n\n\n<li>[x] Cache limits<\/li>\n\n\n\n<li>[x] Cache security<\/li>\n\n\n\n<li>[x] Cache poisoning<\/li>\n\n\n\n<li>[x] Cache access restrictions<\/li>\n\n\n\n<li>[x]\u00a0<code>cache-mode<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>read<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>write<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>write-only<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>none<\/code><\/li>\n\n\n\n<li>[x] Trigger-sensitive cache permissions<\/li>\n\n\n\n<li>[x] Package-manager caches<\/li>\n\n\n\n<li>[x] npm caching<\/li>\n\n\n\n<li>[x] Yarn caching<\/li>\n\n\n\n<li>[x] pnpm caching<\/li>\n\n\n\n<li>[x] Maven caching<\/li>\n\n\n\n<li>[x] Gradle caching<\/li>\n\n\n\n<li>[x] pip caching<\/li>\n\n\n\n<li>[x] Bundler caching<\/li>\n\n\n\n<li>[x] NuGet caching<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">15. ESSENTIALS \u2014 Artifacts<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow artifacts<\/li>\n\n\n\n<li>[x] Artifact upload<\/li>\n\n\n\n<li>[x] Artifact download<\/li>\n\n\n\n<li>[x] Artifact naming<\/li>\n\n\n\n<li>[x] Artifact paths<\/li>\n\n\n\n<li>[x] Artifact compression<\/li>\n\n\n\n<li>[x] Artifact retention<\/li>\n\n\n\n<li>[x] Artifact expiration<\/li>\n\n\n\n<li>[x] Artifact IDs<\/li>\n\n\n\n<li>[x] Artifact URLs<\/li>\n\n\n\n<li>[x] Artifact digests<\/li>\n\n\n\n<li>[x] Artifact sharing between jobs<\/li>\n\n\n\n<li>[x] Artifact sharing between workflow runs<\/li>\n\n\n\n<li>[x] Artifact permissions<\/li>\n\n\n\n<li>[x] Artifact deletion<\/li>\n\n\n\n<li>[x] Artifact API<\/li>\n\n\n\n<li>[x] Cache vs artifacts<\/li>\n\n\n\n<li>[x] Build artifacts<\/li>\n\n\n\n<li>[x] Test artifacts<\/li>\n\n\n\n<li>[x] Log artifacts<\/li>\n\n\n\n<li>[x] Deployment artifacts<\/li>\n\n\n\n<li>[x] Artifact attestations<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">16. ESSENTIALS \u2014 Concurrency<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow concurrency<\/li>\n\n\n\n<li>[x] Job concurrency<\/li>\n\n\n\n<li>[x] Concurrency groups<\/li>\n\n\n\n<li>[x] Dynamic concurrency groups<\/li>\n\n\n\n<li>[x]\u00a0<code>cancel-in-progress<\/code><\/li>\n\n\n\n<li>[x] Pending runs<\/li>\n\n\n\n<li>[x] Running runs<\/li>\n\n\n\n<li>[x] Deployment serialization<\/li>\n\n\n\n<li>[x] Branch-specific concurrency<\/li>\n\n\n\n<li>[x] Environment-specific concurrency<\/li>\n\n\n\n<li>[x] Workflow cancellation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">17. ESSENTIALS \u2014 Containers<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">17.1 Container Jobs<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>container<\/code><\/li>\n\n\n\n<li>[x] Container images<\/li>\n\n\n\n<li>[x] Container credentials<\/li>\n\n\n\n<li>[x] Container environment variables<\/li>\n\n\n\n<li>[x] Container ports<\/li>\n\n\n\n<li>[x] Container volumes<\/li>\n\n\n\n<li>[x] Container options<\/li>\n\n\n\n<li>[x] Docker Hub<\/li>\n\n\n\n<li>[x] GitHub Container Registry<\/li>\n\n\n\n<li>[x] Private container registries<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">17.2 Service Containers<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>services<\/code><\/li>\n\n\n\n<li>[x] Database services<\/li>\n\n\n\n<li>[x] Redis<\/li>\n\n\n\n<li>[x] PostgreSQL<\/li>\n\n\n\n<li>[x] MySQL<\/li>\n\n\n\n<li>[x] MongoDB<\/li>\n\n\n\n<li>[x] Service networking<\/li>\n\n\n\n<li>[x] Port mappings<\/li>\n\n\n\n<li>[x] Health checks<\/li>\n\n\n\n<li>[x] Service credentials<\/li>\n\n\n\n<li>[x] Container-to-container communication<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">18. ESSENTIALS \u2014 Using Actions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub Marketplace<\/li>\n\n\n\n<li>[x] Official GitHub actions<\/li>\n\n\n\n<li>[x] Community actions<\/li>\n\n\n\n<li>[x] Public actions<\/li>\n\n\n\n<li>[x] Private actions<\/li>\n\n\n\n<li>[x] Local repository actions<\/li>\n\n\n\n<li>[x] Referencing actions<\/li>\n\n\n\n<li>[x] Action inputs<\/li>\n\n\n\n<li>[x] Action outputs<\/li>\n\n\n\n<li>[x] Action versions<\/li>\n\n\n\n<li>[x] Tags<\/li>\n\n\n\n<li>[x] Branch references<\/li>\n\n\n\n<li>[x] Commit SHA pinning<\/li>\n\n\n\n<li>[x] Semantic versioning<\/li>\n\n\n\n<li>[x] Action updates<\/li>\n\n\n\n<li>[x] Action dependency management<\/li>\n\n\n\n<li>[x] Action trust<\/li>\n\n\n\n<li>[x] Third-party action review<\/li>\n\n\n\n<li>[x] Deprecated actions<\/li>\n\n\n\n<li>[x] Node runtime migrations<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">19. ESSENTIALS \u2014 Reusable Automation<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">19.1 Reusable Workflows<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>workflow_call<\/code><\/li>\n\n\n\n<li>[x] Called workflows<\/li>\n\n\n\n<li>[x] Caller workflows<\/li>\n\n\n\n<li>[x] Reusable workflow inputs<\/li>\n\n\n\n<li>[x] Reusable workflow secrets<\/li>\n\n\n\n<li>[x]\u00a0<code>secrets: inherit<\/code><\/li>\n\n\n\n<li>[x] Reusable workflow outputs<\/li>\n\n\n\n<li>[x] Job outputs<\/li>\n\n\n\n<li>[x] Nested reusable workflows<\/li>\n\n\n\n<li>[x] Reusable workflow permissions<\/li>\n\n\n\n<li>[x] Reusable workflow environments<\/li>\n\n\n\n<li>[x] Reusable workflow matrices<\/li>\n\n\n\n<li>[x] Cross-repository reusable workflows<\/li>\n\n\n\n<li>[x] Private reusable workflows<\/li>\n\n\n\n<li>[x] Workflow versioning<\/li>\n\n\n\n<li>[x] Workflow pinning<\/li>\n\n\n\n<li>[x] Reusable workflow access rules<\/li>\n\n\n\n<li>[x] Reusable workflow limitations<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">19.2 Workflow Templates<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Organization workflow templates<\/li>\n\n\n\n<li>[x] Starter workflows<\/li>\n\n\n\n<li>[x] Workflow template repositories<\/li>\n\n\n\n<li>[x] Template metadata<\/li>\n\n\n\n<li>[x] Organization CI standards<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">19.3 YAML Reuse<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] YAML anchors<\/li>\n\n\n\n<li>[x] YAML aliases<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">20. ESSENTIALS \u2014 CI Pipelines<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Checkout<\/li>\n\n\n\n<li>[x] Dependency installation<\/li>\n\n\n\n<li>[x] Dependency caching<\/li>\n\n\n\n<li>[x] Compilation<\/li>\n\n\n\n<li>[x] Linting<\/li>\n\n\n\n<li>[x] Formatting<\/li>\n\n\n\n<li>[x] Unit testing<\/li>\n\n\n\n<li>[x] Integration testing<\/li>\n\n\n\n<li>[x] Functional testing<\/li>\n\n\n\n<li>[x] End-to-end testing<\/li>\n\n\n\n<li>[x] Security testing<\/li>\n\n\n\n<li>[x] Code coverage<\/li>\n\n\n\n<li>[x] Test reports<\/li>\n\n\n\n<li>[x] Build packaging<\/li>\n\n\n\n<li>[x] Artifact publishing<\/li>\n\n\n\n<li>[x] Pipeline status checks<\/li>\n\n\n\n<li>[x] Pull-request validation<\/li>\n\n\n\n<li>[x] Merge validation<\/li>\n\n\n\n<li>[x] Branch builds<\/li>\n\n\n\n<li>[x] Tag builds<\/li>\n\n\n\n<li>[x] Release builds<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">21. ESSENTIALS \u2014 CD Pipelines<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Continuous delivery<\/li>\n\n\n\n<li>[x] Continuous deployment<\/li>\n\n\n\n<li>[x] Deployment jobs<\/li>\n\n\n\n<li>[x] Deployment workflows<\/li>\n\n\n\n<li>[x] Deployment environments<\/li>\n\n\n\n<li>[x] Development deployments<\/li>\n\n\n\n<li>[x] Staging deployments<\/li>\n\n\n\n<li>[x] UAT deployments<\/li>\n\n\n\n<li>[x] Production deployments<\/li>\n\n\n\n<li>[x] Manual approvals<\/li>\n\n\n\n<li>[x] Deployment gates<\/li>\n\n\n\n<li>[x] Deployment promotion<\/li>\n\n\n\n<li>[x] Deployment rollback<\/li>\n\n\n\n<li>[x] Deployment verification<\/li>\n\n\n\n<li>[x] Post-deployment testing<\/li>\n\n\n\n<li>[x] Deployment status<\/li>\n\n\n\n<li>[x] Deployment history<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">22. ESSENTIALS \u2014 Environments and Deployments<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub environments<\/li>\n\n\n\n<li>[x] Environment names<\/li>\n\n\n\n<li>[x] Environment URLs<\/li>\n\n\n\n<li>[x] Environment secrets<\/li>\n\n\n\n<li>[x] Environment variables<\/li>\n\n\n\n<li>[x] Required reviewers<\/li>\n\n\n\n<li>[x] Deployment protection rules<\/li>\n\n\n\n<li>[x] Custom deployment protection rules<\/li>\n\n\n\n<li>[x] Wait timers<\/li>\n\n\n\n<li>[x] Branch restrictions<\/li>\n\n\n\n<li>[x] Tag restrictions<\/li>\n\n\n\n<li>[x] Environment approvals<\/li>\n\n\n\n<li>[x] Environment deployments<\/li>\n\n\n\n<li>[x] Environment concurrency<\/li>\n\n\n\n<li>[x] Deployment history<\/li>\n\n\n\n<li>[x] Deployment API<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">23. ADVANCED \u2014 Custom Actions<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">23.1 Custom Action Fundamentals<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Custom action architecture<\/li>\n\n\n\n<li>[x] Action repositories<\/li>\n\n\n\n<li>[x] Action metadata<\/li>\n\n\n\n<li>[x]\u00a0<code>action.yml<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>action.yaml<\/code><\/li>\n\n\n\n<li>[x] Inputs<\/li>\n\n\n\n<li>[x] Outputs<\/li>\n\n\n\n<li>[x] Environment variables<\/li>\n\n\n\n<li>[x] Action branding<\/li>\n\n\n\n<li>[x] Action versioning<\/li>\n\n\n\n<li>[x] Action publishing<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">23.2 JavaScript Actions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] JavaScript actions<\/li>\n\n\n\n<li>[x] Node.js runtime<\/li>\n\n\n\n<li>[x]\u00a0<code>@actions\/core<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>@actions\/github<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>@actions\/exec<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>@actions\/io<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>@actions\/tool-cache<\/code><\/li>\n\n\n\n<li>[x] GitHub REST API<\/li>\n\n\n\n<li>[x] Octokit<\/li>\n\n\n\n<li>[x] Action inputs<\/li>\n\n\n\n<li>[x] Action outputs<\/li>\n\n\n\n<li>[x] Action failures<\/li>\n\n\n\n<li>[x] Action logging<\/li>\n\n\n\n<li>[x] Pre-actions<\/li>\n\n\n\n<li>[x] Post-actions<\/li>\n\n\n\n<li>[x] State management<\/li>\n\n\n\n<li>[x] Packaging dependencies<\/li>\n\n\n\n<li>[x]\u00a0<code>ncc<\/code><\/li>\n\n\n\n<li>[x] JavaScript action releases<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">23.3 Docker Container Actions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Docker actions<\/li>\n\n\n\n<li>[x]\u00a0<code>Dockerfile<\/code><\/li>\n\n\n\n<li>[x] Docker action metadata<\/li>\n\n\n\n<li>[x] Docker action inputs<\/li>\n\n\n\n<li>[x] Docker action arguments<\/li>\n\n\n\n<li>[x] Docker entrypoints<\/li>\n\n\n\n<li>[x] Container environment<\/li>\n\n\n\n<li>[x] Container filesystem<\/li>\n\n\n\n<li>[x] Container exit codes<\/li>\n\n\n\n<li>[x] Docker action publishing<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">23.4 Composite Actions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Composite actions<\/li>\n\n\n\n<li>[x] Composite steps<\/li>\n\n\n\n<li>[x] Composite inputs<\/li>\n\n\n\n<li>[x] Composite outputs<\/li>\n\n\n\n<li>[x] Composite environment handling<\/li>\n\n\n\n<li>[x] Nested actions<\/li>\n\n\n\n<li>[x] Composite action limitations<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">23.5 Action Metadata Syntax<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>name<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>author<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>description<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>inputs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>outputs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>runs<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>using<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>main<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pre<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>post<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>pre-if<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>post-if<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>image<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>entrypoint<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>args<\/code><\/li>\n\n\n\n<li>[x] Composite\u00a0<code>steps<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>branding<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">24. ADVANCED \u2014 Workflow Data Flow<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Step outputs<\/li>\n\n\n\n<li>[x] Job outputs<\/li>\n\n\n\n<li>[x] Workflow outputs<\/li>\n\n\n\n<li>[x] Reusable workflow outputs<\/li>\n\n\n\n<li>[x] Environment files<\/li>\n\n\n\n<li>[x] Context-based data passing<\/li>\n\n\n\n<li>[x] JSON serialization<\/li>\n\n\n\n<li>[x]\u00a0<code>toJSON<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>fromJSON<\/code><\/li>\n\n\n\n<li>[x] Artifact-based data transfer<\/li>\n\n\n\n<li>[x] Output-based data transfer<\/li>\n\n\n\n<li>[x] Matrix-generated data<\/li>\n\n\n\n<li>[x] Dynamic workflow data<\/li>\n\n\n\n<li>[x] State between pre\/main\/post actions<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">25. ADVANCED \u2014 Conditional Workflows<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Conditional jobs<\/li>\n\n\n\n<li>[x] Conditional steps<\/li>\n\n\n\n<li>[x] Event-based conditions<\/li>\n\n\n\n<li>[x] Branch conditions<\/li>\n\n\n\n<li>[x] Tag conditions<\/li>\n\n\n\n<li>[x] Actor conditions<\/li>\n\n\n\n<li>[x] Repository conditions<\/li>\n\n\n\n<li>[x] Environment conditions<\/li>\n\n\n\n<li>[x] Matrix conditions<\/li>\n\n\n\n<li>[x] Changed-path conditions<\/li>\n\n\n\n<li>[x] Pull-request conditions<\/li>\n\n\n\n<li>[x] Fork conditions<\/li>\n\n\n\n<li>[x] Failure handling<\/li>\n\n\n\n<li>[x] Cleanup jobs<\/li>\n\n\n\n<li>[x] Always-run jobs<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">26. ADVANCED \u2014 Error Handling<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Exit codes<\/li>\n\n\n\n<li>[x] Step failures<\/li>\n\n\n\n<li>[x] Job failures<\/li>\n\n\n\n<li>[x] Workflow failures<\/li>\n\n\n\n<li>[x]\u00a0<code>continue-on-error<\/code><\/li>\n\n\n\n<li>[x] Matrix experimental failures<\/li>\n\n\n\n<li>[x]\u00a0<code>fail-fast<\/code><\/li>\n\n\n\n<li>[x] Retry patterns<\/li>\n\n\n\n<li>[x] Conditional retry<\/li>\n\n\n\n<li>[x] Cleanup on failure<\/li>\n\n\n\n<li>[x] Rollback on failure<\/li>\n\n\n\n<li>[x]\u00a0<code>success()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>failure()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>cancelled()<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>always()<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">27. ADVANCED \u2014 Troubleshooting and Debugging<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow syntax errors<\/li>\n\n\n\n<li>[x] YAML errors<\/li>\n\n\n\n<li>[x] Trigger debugging<\/li>\n\n\n\n<li>[x] Context debugging<\/li>\n\n\n\n<li>[x] Expression debugging<\/li>\n\n\n\n<li>[x] Runner debugging<\/li>\n\n\n\n<li>[x] Action debugging<\/li>\n\n\n\n<li>[x] Container debugging<\/li>\n\n\n\n<li>[x] Service-container debugging<\/li>\n\n\n\n<li>[x] Permissions errors<\/li>\n\n\n\n<li>[x] Authentication errors<\/li>\n\n\n\n<li>[x] Secret errors<\/li>\n\n\n\n<li>[x] Environment errors<\/li>\n\n\n\n<li>[x] Cache errors<\/li>\n\n\n\n<li>[x] Artifact errors<\/li>\n\n\n\n<li>[x] Matrix errors<\/li>\n\n\n\n<li>[x] Reusable workflow errors<\/li>\n\n\n\n<li>[x] Deployment errors<\/li>\n\n\n\n<li>[x] Timeout troubleshooting<\/li>\n\n\n\n<li>[x] Network troubleshooting<\/li>\n\n\n\n<li>[x] DNS troubleshooting<\/li>\n\n\n\n<li>[x] Workflow logs<\/li>\n\n\n\n<li>[x] Job logs<\/li>\n\n\n\n<li>[x] Step logs<\/li>\n\n\n\n<li>[x] Log search<\/li>\n\n\n\n<li>[x] Log download<\/li>\n\n\n\n<li>[x] Debug logging<\/li>\n\n\n\n<li>[x] Runner diagnostic logging<\/li>\n\n\n\n<li>[x] Re-running workflows<\/li>\n\n\n\n<li>[x] Re-running failed jobs<\/li>\n\n\n\n<li>[x] Re-running individual jobs<\/li>\n\n\n\n<li>[x] Workflow run attempts<\/li>\n\n\n\n<li>[x] Annotations<\/li>\n\n\n\n<li>[x] Job summaries<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">28. ADVANCED \u2014 Security Hardening<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">28.1 Workflow Security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Least privilege<\/li>\n\n\n\n<li>[x] Workflow permissions<\/li>\n\n\n\n<li>[x] Job permissions<\/li>\n\n\n\n<li>[x] Untrusted input<\/li>\n\n\n\n<li>[x] Script injection<\/li>\n\n\n\n<li>[x] Command injection<\/li>\n\n\n\n<li>[x] Expression injection<\/li>\n\n\n\n<li>[x] Shell injection<\/li>\n\n\n\n<li>[x] Pull-request security<\/li>\n\n\n\n<li>[x] Fork security<\/li>\n\n\n\n<li>[x] Public repository workflow security<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">28.2 Third-Party Action Security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Action provenance<\/li>\n\n\n\n<li>[x] Commit SHA pinning<\/li>\n\n\n\n<li>[x] Mutable tags<\/li>\n\n\n\n<li>[x] Action review<\/li>\n\n\n\n<li>[x] Dependency review<\/li>\n\n\n\n<li>[x] Supply-chain attacks<\/li>\n\n\n\n<li>[x] Compromised actions<\/li>\n\n\n\n<li>[x] Action allowlists<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">28.3&nbsp;<code>pull_request<\/code>&nbsp;Security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Fork pull requests<\/li>\n\n\n\n<li>[x] Read-only tokens<\/li>\n\n\n\n<li>[x] Secret restrictions<\/li>\n\n\n\n<li>[x] Workflow approval<\/li>\n\n\n\n<li>[x] Untrusted code execution<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">28.4&nbsp;<code>pull_request_target<\/code>&nbsp;Security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>pull_request_target<\/code><\/li>\n\n\n\n<li>[x] Privileged workflow context<\/li>\n\n\n\n<li>[x] Untrusted checkout risks<\/li>\n\n\n\n<li>[x] Secret exposure<\/li>\n\n\n\n<li>[x] Token exposure<\/li>\n\n\n\n<li>[x] Cache poisoning<\/li>\n\n\n\n<li>[x] Secure\u00a0<code>pull_request_target<\/code>\u00a0patterns<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">28.5 Runner Security<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Hosted runner isolation<\/li>\n\n\n\n<li>[x] Self-hosted runner risks<\/li>\n\n\n\n<li>[x] Persistent runner risks<\/li>\n\n\n\n<li>[x] Ephemeral runners<\/li>\n\n\n\n<li>[x] Runner compromise<\/li>\n\n\n\n<li>[x] Runner credential cleanup<\/li>\n\n\n\n<li>[x] Network isolation<\/li>\n\n\n\n<li>[x] Runner trust boundaries<\/li>\n\n\n\n<li>[x] Public repository runner risks<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">29. ADVANCED \u2014 OpenID Connect<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] OIDC<\/li>\n\n\n\n<li>[x] Federated authentication<\/li>\n\n\n\n<li>[x] Short-lived credentials<\/li>\n\n\n\n<li>[x] ID tokens<\/li>\n\n\n\n<li>[x]\u00a0<code>id-token: write<\/code><\/li>\n\n\n\n<li>[x] OIDC token claims<\/li>\n\n\n\n<li>[x] OIDC subject claims<\/li>\n\n\n\n<li>[x] Custom subject claims<\/li>\n\n\n\n<li>[x] Audience claims<\/li>\n\n\n\n<li>[x] Repository claims<\/li>\n\n\n\n<li>[x] Branch claims<\/li>\n\n\n\n<li>[x] Tag claims<\/li>\n\n\n\n<li>[x] Environment claims<\/li>\n\n\n\n<li>[x] Cloud trust policies<\/li>\n\n\n\n<li>[x] AWS OIDC<\/li>\n\n\n\n<li>[x] Azure OIDC<\/li>\n\n\n\n<li>[x] Google Cloud OIDC<\/li>\n\n\n\n<li>[x] HashiCorp Vault OIDC<\/li>\n\n\n\n<li>[x] Cloud role assumption<\/li>\n\n\n\n<li>[x] Credential-less cloud deployments<\/li>\n\n\n\n<li>[x] OIDC security<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">30. ADVANCED \u2014 Artifact Attestations<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Artifact provenance<\/li>\n\n\n\n<li>[x] Artifact attestations<\/li>\n\n\n\n<li>[x] Build provenance<\/li>\n\n\n\n<li>[x] Attestation generation<\/li>\n\n\n\n<li>[x] Attestation verification<\/li>\n\n\n\n<li>[x] Signed provenance<\/li>\n\n\n\n<li>[x] Software supply-chain integrity<\/li>\n\n\n\n<li>[x] Container image attestations<\/li>\n\n\n\n<li>[x] Binary attestations<\/li>\n\n\n\n<li>[x] Package attestations<\/li>\n\n\n\n<li>[x] Kubernetes attestation enforcement<\/li>\n\n\n\n<li>[x] Kubernetes admission controller<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">31. ADVANCED \u2014 Docker and Container CI\/CD<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Docker builds<\/li>\n\n\n\n<li>[x] Docker Buildx<\/li>\n\n\n\n<li>[x] BuildKit<\/li>\n\n\n\n<li>[x] Multi-platform builds<\/li>\n\n\n\n<li>[x] Docker layer caching<\/li>\n\n\n\n<li>[x] Registry authentication<\/li>\n\n\n\n<li>[x] GitHub Container Registry<\/li>\n\n\n\n<li>[x] Container tagging<\/li>\n\n\n\n<li>[x] Container metadata<\/li>\n\n\n\n<li>[x] Image publishing<\/li>\n\n\n\n<li>[x] Image signing<\/li>\n\n\n\n<li>[x] Image attestations<\/li>\n\n\n\n<li>[x] Image scanning<\/li>\n\n\n\n<li>[x] Multi-architecture images<\/li>\n\n\n\n<li>[x] Release images<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">32. ADVANCED \u2014 Package Publishing<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub Packages<\/li>\n\n\n\n<li>[x] npm packages<\/li>\n\n\n\n<li>[x] Maven packages<\/li>\n\n\n\n<li>[x] Gradle packages<\/li>\n\n\n\n<li>[x] NuGet packages<\/li>\n\n\n\n<li>[x] RubyGems<\/li>\n\n\n\n<li>[x] Container packages<\/li>\n\n\n\n<li>[x] Package authentication<\/li>\n\n\n\n<li>[x] Package permissions<\/li>\n\n\n\n<li>[x] Package publishing<\/li>\n\n\n\n<li>[x] Package installation<\/li>\n\n\n\n<li>[x] Package versioning<\/li>\n\n\n\n<li>[x] Package provenance<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">33. ADVANCED \u2014 Releases<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Release workflows<\/li>\n\n\n\n<li>[x] Git tags<\/li>\n\n\n\n<li>[x] Release events<\/li>\n\n\n\n<li>[x] GitHub Releases<\/li>\n\n\n\n<li>[x] Release assets<\/li>\n\n\n\n<li>[x] Release notes<\/li>\n\n\n\n<li>[x] Automated releases<\/li>\n\n\n\n<li>[x] Semantic versioning<\/li>\n\n\n\n<li>[x] Pre-releases<\/li>\n\n\n\n<li>[x] Draft releases<\/li>\n\n\n\n<li>[x] Release artifact publishing<\/li>\n\n\n\n<li>[x] Release promotion<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">34. ADVANCED \u2014 Cloud Deployments<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">34.1 AWS<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] AWS authentication<\/li>\n\n\n\n<li>[x] AWS OIDC<\/li>\n\n\n\n<li>[x] IAM roles<\/li>\n\n\n\n<li>[x] STS AssumeRole<\/li>\n\n\n\n<li>[x] S3 deployments<\/li>\n\n\n\n<li>[x] EC2 deployments<\/li>\n\n\n\n<li>[x] ECS deployments<\/li>\n\n\n\n<li>[x] ECR publishing<\/li>\n\n\n\n<li>[x] EKS deployments<\/li>\n\n\n\n<li>[x] Lambda deployments<\/li>\n\n\n\n<li>[x] CloudFormation deployments<\/li>\n\n\n\n<li>[x] Terraform deployments<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">34.2 Azure<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Azure authentication<\/li>\n\n\n\n<li>[x] Azure OIDC<\/li>\n\n\n\n<li>[x] Azure Web Apps<\/li>\n\n\n\n<li>[x] Azure Container Apps<\/li>\n\n\n\n<li>[x] AKS<\/li>\n\n\n\n<li>[x] Azure Functions<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">34.3 Google Cloud<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Google Cloud authentication<\/li>\n\n\n\n<li>[x] Workload Identity Federation<\/li>\n\n\n\n<li>[x] GKE<\/li>\n\n\n\n<li>[x] Cloud Run<\/li>\n\n\n\n<li>[x] Artifact Registry<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">35. ADVANCED \u2014 Kubernetes CI\/CD<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Kubernetes authentication<\/li>\n\n\n\n<li>[x] kubeconfig management<\/li>\n\n\n\n<li>[x] OIDC authentication<\/li>\n\n\n\n<li>[x] Kubernetes manifests<\/li>\n\n\n\n<li>[x] Helm<\/li>\n\n\n\n<li>[x] Kustomize<\/li>\n\n\n\n<li>[x] kubectl<\/li>\n\n\n\n<li>[x] Namespace deployments<\/li>\n\n\n\n<li>[x] Deployment rollout<\/li>\n\n\n\n<li>[x] Deployment verification<\/li>\n\n\n\n<li>[x] Rollback<\/li>\n\n\n\n<li>[x] EKS<\/li>\n\n\n\n<li>[x] AKS<\/li>\n\n\n\n<li>[x] GKE<\/li>\n\n\n\n<li>[x] Container registry integration<\/li>\n\n\n\n<li>[x] GitOps integration<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">36. ADVANCED \u2014 Infrastructure as Code<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Terraform workflows<\/li>\n\n\n\n<li>[x] Terraform formatting<\/li>\n\n\n\n<li>[x] Terraform validation<\/li>\n\n\n\n<li>[x] Terraform plan<\/li>\n\n\n\n<li>[x] Terraform apply<\/li>\n\n\n\n<li>[x] Terraform destroy<\/li>\n\n\n\n<li>[x] Terraform state<\/li>\n\n\n\n<li>[x] Terraform workspaces<\/li>\n\n\n\n<li>[x] Terraform Cloud<\/li>\n\n\n\n<li>[x] OpenTofu<\/li>\n\n\n\n<li>[x] CloudFormation<\/li>\n\n\n\n<li>[x] CDK<\/li>\n\n\n\n<li>[x] Pulumi<\/li>\n\n\n\n<li>[x] Infrastructure approvals<\/li>\n\n\n\n<li>[x] Plan artifacts<\/li>\n\n\n\n<li>[x] IaC security scanning<\/li>\n\n\n\n<li>[x] Drift detection<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">37. ADVANCED \u2014 Self-Hosted Runner Architecture<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Runner fleet architecture<\/li>\n\n\n\n<li>[x] Dedicated runners<\/li>\n\n\n\n<li>[x] Shared runners<\/li>\n\n\n\n<li>[x] Ephemeral runners<\/li>\n\n\n\n<li>[x] Autoscaling runners<\/li>\n\n\n\n<li>[x] JIT runners<\/li>\n\n\n\n<li>[x] Runner groups<\/li>\n\n\n\n<li>[x] Custom runner labels<\/li>\n\n\n\n<li>[x] Repository runner isolation<\/li>\n\n\n\n<li>[x] Organization runner isolation<\/li>\n\n\n\n<li>[x] Enterprise runner isolation<\/li>\n\n\n\n<li>[x] Runner images<\/li>\n\n\n\n<li>[x] Golden runner images<\/li>\n\n\n\n<li>[x] Runner patching<\/li>\n\n\n\n<li>[x] Runner upgrades<\/li>\n\n\n\n<li>[x] Runner network access<\/li>\n\n\n\n<li>[x] Private subnet runners<\/li>\n\n\n\n<li>[x] Proxy runners<\/li>\n\n\n\n<li>[x] Runner observability<\/li>\n\n\n\n<li>[x] Runner credential management<\/li>\n\n\n\n<li>[x] Runner lifecycle automation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">38. ADVANCED \u2014 Actions Runner Controller<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Actions Runner Controller<\/li>\n\n\n\n<li>[x] ARC architecture<\/li>\n\n\n\n<li>[x] Kubernetes-based runners<\/li>\n\n\n\n<li>[x] Runner scale sets<\/li>\n\n\n\n<li>[x] Autoscaling runner scale sets<\/li>\n\n\n\n<li>[x] Scale-set controller<\/li>\n\n\n\n<li>[x] Listener<\/li>\n\n\n\n<li>[x] Runner pods<\/li>\n\n\n\n<li>[x] Ephemeral runner pods<\/li>\n\n\n\n<li>[x] ARC Helm charts<\/li>\n\n\n\n<li>[x] GitHub App authentication<\/li>\n\n\n\n<li>[x] PAT authentication<\/li>\n\n\n\n<li>[x] Kubernetes RBAC<\/li>\n\n\n\n<li>[x] Runner namespaces<\/li>\n\n\n\n<li>[x] Custom runner images<\/li>\n\n\n\n<li>[x] Docker-in-Docker<\/li>\n\n\n\n<li>[x] Kubernetes container mode<\/li>\n\n\n\n<li>[x] Runner storage<\/li>\n\n\n\n<li>[x] Runner networking<\/li>\n\n\n\n<li>[x] ARC autoscaling<\/li>\n\n\n\n<li>[x] Minimum runners<\/li>\n\n\n\n<li>[x] Maximum runners<\/li>\n\n\n\n<li>[x] Runner groups<\/li>\n\n\n\n<li>[x] ARC upgrades<\/li>\n\n\n\n<li>[x] ARC monitoring<\/li>\n\n\n\n<li>[x] ARC troubleshooting<\/li>\n\n\n\n<li>[x] ARC security<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">39. ADVANCED \u2014 Workflow Performance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow runtime optimization<\/li>\n\n\n\n<li>[x] Parallel jobs<\/li>\n\n\n\n<li>[x] Matrix parallelism<\/li>\n\n\n\n<li>[x] Dependency caching<\/li>\n\n\n\n<li>[x] Build caching<\/li>\n\n\n\n<li>[x] Docker caching<\/li>\n\n\n\n<li>[x] Artifact optimization<\/li>\n\n\n\n<li>[x] Checkout optimization<\/li>\n\n\n\n<li>[x] Shallow clones<\/li>\n\n\n\n<li>[x] Sparse checkout<\/li>\n\n\n\n<li>[x] Dependency reuse<\/li>\n\n\n\n<li>[x] Selective execution<\/li>\n\n\n\n<li>[x] Path filtering<\/li>\n\n\n\n<li>[x] Job dependency optimization<\/li>\n\n\n\n<li>[x] Runner selection<\/li>\n\n\n\n<li>[x] Larger runners<\/li>\n\n\n\n<li>[x] Self-hosted runner performance<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">40. ADVANCED \u2014 Cost Optimization<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub Actions minutes<\/li>\n\n\n\n<li>[x] Storage usage<\/li>\n\n\n\n<li>[x] Artifact storage<\/li>\n\n\n\n<li>[x] Cache storage<\/li>\n\n\n\n<li>[x] Hosted runner billing<\/li>\n\n\n\n<li>[x] Larger runner billing<\/li>\n\n\n\n<li>[x] Private repository usage<\/li>\n\n\n\n<li>[x] Public repository usage<\/li>\n\n\n\n<li>[x] Workflow runtime reduction<\/li>\n\n\n\n<li>[x] Matrix cost control<\/li>\n\n\n\n<li>[x] Retention optimization<\/li>\n\n\n\n<li>[x] Self-hosted runner economics<\/li>\n\n\n\n<li>[x] Usage monitoring<\/li>\n\n\n\n<li>[x] Billing reports<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">41. ADVANCED \u2014 Monorepo Workflows<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Monorepo CI<\/li>\n\n\n\n<li>[x] Path filters<\/li>\n\n\n\n<li>[x] Selective builds<\/li>\n\n\n\n<li>[x] Selective tests<\/li>\n\n\n\n<li>[x] Component matrices<\/li>\n\n\n\n<li>[x] Dynamic matrices<\/li>\n\n\n\n<li>[x] Changed component detection<\/li>\n\n\n\n<li>[x] Independent deployments<\/li>\n\n\n\n<li>[x] Shared workflows<\/li>\n\n\n\n<li>[x] Shared actions<\/li>\n\n\n\n<li>[x] Dependency graph workflows<\/li>\n\n\n\n<li>[x] Fan-out builds<\/li>\n\n\n\n<li>[x] Fan-in validation<\/li>\n\n\n\n<li>[x] Monorepo caching<\/li>\n\n\n\n<li>[x] Monorepo concurrency<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">42. ADVANCED \u2014 Multi-Repository Workflows<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Cross-repository workflows<\/li>\n\n\n\n<li>[x]\u00a0<code>repository_dispatch<\/code><\/li>\n\n\n\n<li>[x] Reusable workflows across repositories<\/li>\n\n\n\n<li>[x] Private reusable workflows<\/li>\n\n\n\n<li>[x] Repository access policies<\/li>\n\n\n\n<li>[x] Cross-repository deployment<\/li>\n\n\n\n<li>[x] Central CI repositories<\/li>\n\n\n\n<li>[x] Central workflow repositories<\/li>\n\n\n\n<li>[x] Organization automation repositories<\/li>\n\n\n\n<li>[x] GitHub App authentication<\/li>\n\n\n\n<li>[x] Fine-grained PAT authentication<\/li>\n\n\n\n<li>[x] Cross-repository artifacts<\/li>\n\n\n\n<li>[x] Cross-repository orchestration<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">43. ADVANCED \u2014 Workflow Chaining<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x]\u00a0<code>workflow_run<\/code><\/li>\n\n\n\n<li>[x] Workflow dependencies<\/li>\n\n\n\n<li>[x] Upstream workflows<\/li>\n\n\n\n<li>[x] Downstream workflows<\/li>\n\n\n\n<li>[x] Multi-stage workflow chains<\/li>\n\n\n\n<li>[x] Artifact passing between workflow runs<\/li>\n\n\n\n<li>[x] Privilege boundaries<\/li>\n\n\n\n<li>[x] Workflow chain security<\/li>\n\n\n\n<li>[x] Workflow chain limits<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">44. ADVANCED \u2014 GitHub API Integration<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] REST API<\/li>\n\n\n\n<li>[x] GraphQL API<\/li>\n\n\n\n<li>[x] GitHub CLI<\/li>\n\n\n\n<li>[x]\u00a0<code>gh<\/code><\/li>\n\n\n\n<li>[x]\u00a0<code>gh api<\/code><\/li>\n\n\n\n<li>[x] Octokit<\/li>\n\n\n\n<li>[x]\u00a0<code>actions\/github-script<\/code><\/li>\n\n\n\n<li>[x] Issues automation<\/li>\n\n\n\n<li>[x] Pull-request automation<\/li>\n\n\n\n<li>[x] Release automation<\/li>\n\n\n\n<li>[x] Repository automation<\/li>\n\n\n\n<li>[x] Deployment API<\/li>\n\n\n\n<li>[x] Checks API<\/li>\n\n\n\n<li>[x] Status API<\/li>\n\n\n\n<li>[x] Actions API<\/li>\n\n\n\n<li>[x] Workflow API<\/li>\n\n\n\n<li>[x] Artifact API<\/li>\n\n\n\n<li>[x] Cache API<\/li>\n\n\n\n<li>[x] Runner API<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">45. ADVANCED \u2014 Repository Governance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Branch protection<\/li>\n\n\n\n<li>[x] Rulesets<\/li>\n\n\n\n<li>[x] Required status checks<\/li>\n\n\n\n<li>[x] Required workflows<\/li>\n\n\n\n<li>[x] Merge queues<\/li>\n\n\n\n<li>[x]\u00a0<code>merge_group<\/code><\/li>\n\n\n\n<li>[x] CODEOWNERS interaction<\/li>\n\n\n\n<li>[x] Pull-request approval<\/li>\n\n\n\n<li>[x] Workflow approval<\/li>\n\n\n\n<li>[x] Deployment approval<\/li>\n\n\n\n<li>[x] Environment protection<\/li>\n\n\n\n<li>[x] Repository Actions settings<\/li>\n\n\n\n<li>[x] Workflow permissions<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">46. ADVANCED \u2014 Organization Governance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Organization Actions policies<\/li>\n\n\n\n<li>[x] Allowed actions<\/li>\n\n\n\n<li>[x] Selected actions<\/li>\n\n\n\n<li>[x] Marketplace action restrictions<\/li>\n\n\n\n<li>[x] SHA pinning policies<\/li>\n\n\n\n<li>[x] Reusable workflow governance<\/li>\n\n\n\n<li>[x] Runner groups<\/li>\n\n\n\n<li>[x] Organization secrets<\/li>\n\n\n\n<li>[x] Organization variables<\/li>\n\n\n\n<li>[x] Secret repository access<\/li>\n\n\n\n<li>[x] Runner repository access<\/li>\n\n\n\n<li>[x] Private action sharing<\/li>\n\n\n\n<li>[x] Workflow retention<\/li>\n\n\n\n<li>[x] Workflow permissions<\/li>\n\n\n\n<li>[x] Fork workflow policies<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">47. ADVANCED \u2014 Enterprise Governance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Enterprise Actions policies<\/li>\n\n\n\n<li>[x] Enterprise runner groups<\/li>\n\n\n\n<li>[x] Enterprise secrets<\/li>\n\n\n\n<li>[x] Enterprise workflow policies<\/li>\n\n\n\n<li>[x] Enterprise action restrictions<\/li>\n\n\n\n<li>[x] Enterprise runner management<\/li>\n\n\n\n<li>[x] Organization policy inheritance<\/li>\n\n\n\n<li>[x] Actions usage policies<\/li>\n\n\n\n<li>[x] Workflow execution policies<\/li>\n\n\n\n<li>[x] Audit logs<\/li>\n\n\n\n<li>[x] Enterprise security controls<\/li>\n\n\n\n<li>[x] Enterprise billing<\/li>\n\n\n\n<li>[x] Enterprise usage monitoring<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">48. ADVANCED \u2014 Workflow Execution Policies<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow execution protections<\/li>\n\n\n\n<li>[x] Workflow execution allowlists<\/li>\n\n\n\n<li>[x] Actor restrictions<\/li>\n\n\n\n<li>[x] Event restrictions<\/li>\n\n\n\n<li>[x] Repository policies<\/li>\n\n\n\n<li>[x] Organization policies<\/li>\n\n\n\n<li>[x] Enterprise policies<\/li>\n\n\n\n<li>[x] Policy inheritance<\/li>\n\n\n\n<li>[x] Policy evaluation<\/li>\n\n\n\n<li>[x] Workflow execution governance<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">49. ADVANCED \u2014 Metrics and Observability<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub Actions metrics<\/li>\n\n\n\n<li>[x] Workflow metrics<\/li>\n\n\n\n<li>[x] Job metrics<\/li>\n\n\n\n<li>[x] Workflow duration<\/li>\n\n\n\n<li>[x] Queue duration<\/li>\n\n\n\n<li>[x] Job duration<\/li>\n\n\n\n<li>[x] Workflow success rate<\/li>\n\n\n\n<li>[x] Workflow failure rate<\/li>\n\n\n\n<li>[x] Runner utilization<\/li>\n\n\n\n<li>[x] Runner availability<\/li>\n\n\n\n<li>[x] Usage metrics<\/li>\n\n\n\n<li>[x] Performance analysis<\/li>\n\n\n\n<li>[x] Cost analysis<\/li>\n\n\n\n<li>[x] Audit logs<\/li>\n\n\n\n<li>[x] Workflow notifications<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">50. ADVANCED \u2014 Workflow Notifications<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow run notifications<\/li>\n\n\n\n<li>[x] Email notifications<\/li>\n\n\n\n<li>[x] Web notifications<\/li>\n\n\n\n<li>[x] Failed workflow notifications<\/li>\n\n\n\n<li>[x] GitHub notifications<\/li>\n\n\n\n<li>[x] Slack integrations<\/li>\n\n\n\n<li>[x] Microsoft Teams integrations<\/li>\n\n\n\n<li>[x] Custom webhook notifications<\/li>\n\n\n\n<li>[x] Deployment notifications<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">51. ADVANCED \u2014 Workflow Cancellation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Manual cancellation<\/li>\n\n\n\n<li>[x] Automatic cancellation<\/li>\n\n\n\n<li>[x] Concurrency cancellation<\/li>\n\n\n\n<li>[x]\u00a0<code>cancel-in-progress<\/code><\/li>\n\n\n\n<li>[x] Job cancellation<\/li>\n\n\n\n<li>[x] Step cancellation<\/li>\n\n\n\n<li>[x] Cancellation signals<\/li>\n\n\n\n<li>[x] Cancellation conditions<\/li>\n\n\n\n<li>[x] Cleanup after cancellation<\/li>\n\n\n\n<li>[x]\u00a0<code>cancelled()<\/code><\/li>\n\n\n\n<li>[x] Workflow cancellation lifecycle<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">52. ADVANCED \u2014 Pull Request CI Patterns<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] PR validation<\/li>\n\n\n\n<li>[x] Fork PR validation<\/li>\n\n\n\n<li>[x] Draft PR workflows<\/li>\n\n\n\n<li>[x] PR activity types<\/li>\n\n\n\n<li>[x] Required checks<\/li>\n\n\n\n<li>[x] Merge queues<\/li>\n\n\n\n<li>[x] Merge-group checks<\/li>\n\n\n\n<li>[x] Changed-file workflows<\/li>\n\n\n\n<li>[x] PR labeling automation<\/li>\n\n\n\n<li>[x] PR commenting<\/li>\n\n\n\n<li>[x] PR test reports<\/li>\n\n\n\n<li>[x] PR preview environments<\/li>\n\n\n\n<li>[x] PR security scanning<\/li>\n\n\n\n<li>[x] PR cleanup workflows<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">53. ADVANCED \u2014 Branch and Release Strategies<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Feature branches<\/li>\n\n\n\n<li>[x] Trunk-based development<\/li>\n\n\n\n<li>[x] GitFlow<\/li>\n\n\n\n<li>[x] Release branches<\/li>\n\n\n\n<li>[x] Environment branches<\/li>\n\n\n\n<li>[x] Branch-based deployments<\/li>\n\n\n\n<li>[x] Tag-based deployments<\/li>\n\n\n\n<li>[x] Semantic version tags<\/li>\n\n\n\n<li>[x] Hotfix workflows<\/li>\n\n\n\n<li>[x] Promotion workflows<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">54. ADVANCED \u2014 Deployment Strategies<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Rolling deployment<\/li>\n\n\n\n<li>[x] Blue\/green deployment<\/li>\n\n\n\n<li>[x] Canary deployment<\/li>\n\n\n\n<li>[x] Progressive delivery<\/li>\n\n\n\n<li>[x] Feature-flag deployment<\/li>\n\n\n\n<li>[x] Environment promotion<\/li>\n\n\n\n<li>[x] Immutable deployment<\/li>\n\n\n\n<li>[x] Manual deployment<\/li>\n\n\n\n<li>[x] Automated deployment<\/li>\n\n\n\n<li>[x] Rollback workflows<\/li>\n\n\n\n<li>[x] Deployment verification<\/li>\n\n\n\n<li>[x] Smoke testing<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">55. ADVANCED \u2014 Testing GitHub Actions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow syntax validation<\/li>\n\n\n\n<li>[x] Workflow linting<\/li>\n\n\n\n<li>[x]\u00a0<code>actionlint<\/code><\/li>\n\n\n\n<li>[x] YAML validation<\/li>\n\n\n\n<li>[x] Custom action unit testing<\/li>\n\n\n\n<li>[x] JavaScript action testing<\/li>\n\n\n\n<li>[x] Docker action testing<\/li>\n\n\n\n<li>[x] Composite action testing<\/li>\n\n\n\n<li>[x] Reusable workflow testing<\/li>\n\n\n\n<li>[x] Integration testing<\/li>\n\n\n\n<li>[x] Staging workflow testing<\/li>\n\n\n\n<li>[x] Local workflow emulation<\/li>\n\n\n\n<li>[x]\u00a0<code>act<\/code><\/li>\n\n\n\n<li>[x] Mock contexts<\/li>\n\n\n\n<li>[x] Mock events<\/li>\n\n\n\n<li>[x] Test repositories<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">56. ADVANCED \u2014 Workflow Maintenance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Action version upgrades<\/li>\n\n\n\n<li>[x] Deprecated action versions<\/li>\n\n\n\n<li>[x] Runner image changes<\/li>\n\n\n\n<li>[x] Node runtime changes<\/li>\n\n\n\n<li>[x] Dependency updates<\/li>\n\n\n\n<li>[x] Dependabot for Actions<\/li>\n\n\n\n<li>[x] Workflow refactoring<\/li>\n\n\n\n<li>[x] Workflow modularization<\/li>\n\n\n\n<li>[x] Reusable workflow adoption<\/li>\n\n\n\n<li>[x] Action pinning maintenance<\/li>\n\n\n\n<li>[x] Secret rotation<\/li>\n\n\n\n<li>[x] Runner upgrades<\/li>\n\n\n\n<li>[x] Workflow documentation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">57. ADVANCED \u2014 GitHub Actions Limits<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow execution limits<\/li>\n\n\n\n<li>[x] Job execution limits<\/li>\n\n\n\n<li>[x] Matrix limits<\/li>\n\n\n\n<li>[x] Concurrency limits<\/li>\n\n\n\n<li>[x] Workflow queue limits<\/li>\n\n\n\n<li>[x] Runner limits<\/li>\n\n\n\n<li>[x] Self-hosted runner limits<\/li>\n\n\n\n<li>[x] Artifact limits<\/li>\n\n\n\n<li>[x] Cache limits<\/li>\n\n\n\n<li>[x] Secret limits<\/li>\n\n\n\n<li>[x] Variable limits<\/li>\n\n\n\n<li>[x] Input limits<\/li>\n\n\n\n<li>[x] Output limits<\/li>\n\n\n\n<li>[x] Reusable workflow limits<\/li>\n\n\n\n<li>[x] Workflow nesting limits<\/li>\n\n\n\n<li>[x] Workflow retention limits<\/li>\n\n\n\n<li>[x] API rate limits<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">58. ADVANCED \u2014 GitHub Actions Importer<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] GitHub Actions Importer<\/li>\n\n\n\n<li>[x] CI migration<\/li>\n\n\n\n<li>[x] Migration planning<\/li>\n\n\n\n<li>[x] Workflow auditing<\/li>\n\n\n\n<li>[x] Workflow conversion<\/li>\n\n\n\n<li>[x] Supplemental arguments<\/li>\n\n\n\n<li>[x] Importer configuration<\/li>\n\n\n\n<li>[x] Custom transformers<\/li>\n\n\n\n<li>[x] Migration validation<\/li>\n\n\n\n<li>[x] Migration from Jenkins<\/li>\n\n\n\n<li>[x] Migration from GitLab CI\/CD<\/li>\n\n\n\n<li>[x] Migration from CircleCI<\/li>\n\n\n\n<li>[x] Migration from Azure DevOps<\/li>\n\n\n\n<li>[x] Migration from Travis CI<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">59. ADVANCED \u2014 Common CI\/CD Design Patterns<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Build-test-deploy<\/li>\n\n\n\n<li>[x] Build once, deploy many<\/li>\n\n\n\n<li>[x] Fan-out\/fan-in<\/li>\n\n\n\n<li>[x] Matrix pipelines<\/li>\n\n\n\n<li>[x] Reusable pipeline architecture<\/li>\n\n\n\n<li>[x] Centralized workflow architecture<\/li>\n\n\n\n<li>[x] Environment promotion<\/li>\n\n\n\n<li>[x] Artifact promotion<\/li>\n\n\n\n<li>[x] Conditional deployment<\/li>\n\n\n\n<li>[x] Manual approval gates<\/li>\n\n\n\n<li>[x] Automated rollback<\/li>\n\n\n\n<li>[x] Scheduled automation<\/li>\n\n\n\n<li>[x] Event-driven automation<\/li>\n\n\n\n<li>[x] Cross-repository automation<\/li>\n\n\n\n<li>[x] Dynamic pipeline generation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">60. ADVANCED \u2014 GitHub Actions Anti-Patterns<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Duplicate workflow logic<\/li>\n\n\n\n<li>[x] Overprivileged\u00a0<code>GITHUB_TOKEN<\/code><\/li>\n\n\n\n<li>[x] Long-lived cloud credentials<\/li>\n\n\n\n<li>[x] Unpinned third-party actions<\/li>\n\n\n\n<li>[x] Mutable action references<\/li>\n\n\n\n<li>[x] Secrets in workflow files<\/li>\n\n\n\n<li>[x] Secrets in logs<\/li>\n\n\n\n<li>[x] Untrusted input in shell commands<\/li>\n\n\n\n<li>[x] Unsafe\u00a0<code>pull_request_target<\/code><\/li>\n\n\n\n<li>[x] Persistent public self-hosted runners<\/li>\n\n\n\n<li>[x] Excessive matrix jobs<\/li>\n\n\n\n<li>[x] Cache misuse<\/li>\n\n\n\n<li>[x] Cache poisoning<\/li>\n\n\n\n<li>[x] Artifact misuse<\/li>\n\n\n\n<li>[x] Hard-coded environment values<\/li>\n\n\n\n<li>[x] Excessive workflow chaining<\/li>\n\n\n\n<li>[x] Monolithic workflows<\/li>\n\n\n\n<li>[x] Missing timeouts<\/li>\n\n\n\n<li>[x] Missing concurrency controls<\/li>\n\n\n\n<li>[x] Missing deployment protections<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">61. ADVANCED \u2014 Production Best Practices<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Least-privilege permissions<\/li>\n\n\n\n<li>[x] OIDC cloud authentication<\/li>\n\n\n\n<li>[x] SHA-pinned actions<\/li>\n\n\n\n<li>[x] Reusable workflows<\/li>\n\n\n\n<li>[x] Composite actions<\/li>\n\n\n\n<li>[x] Environment protection<\/li>\n\n\n\n<li>[x] Required reviewers<\/li>\n\n\n\n<li>[x] Concurrency controls<\/li>\n\n\n\n<li>[x] Job timeouts<\/li>\n\n\n\n<li>[x] Dependency caching<\/li>\n\n\n\n<li>[x] Artifact retention<\/li>\n\n\n\n<li>[x] Ephemeral runners<\/li>\n\n\n\n<li>[x] Runner isolation<\/li>\n\n\n\n<li>[x] Secret rotation<\/li>\n\n\n\n<li>[x] Branch protection<\/li>\n\n\n\n<li>[x] Rulesets<\/li>\n\n\n\n<li>[x] Required checks<\/li>\n\n\n\n<li>[x] Workflow monitoring<\/li>\n\n\n\n<li>[x] Workflow metrics<\/li>\n\n\n\n<li>[x] CI\/CD auditability<\/li>\n\n\n\n<li>[x] Supply-chain security<\/li>\n\n\n\n<li>[x] Artifact provenance<\/li>\n\n\n\n<li>[x] Workflow versioning<\/li>\n\n\n\n<li>[x] Deployment traceability<\/li>\n\n\n\n<li>[x] Rollback readiness<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">62. ADVANCED \u2014 Reference and Administration<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[x] Workflow syntax reference<\/li>\n\n\n\n<li>[x] Events reference<\/li>\n\n\n\n<li>[x] Workflow commands reference<\/li>\n\n\n\n<li>[x] Variables reference<\/li>\n\n\n\n<li>[x] Expressions reference<\/li>\n\n\n\n<li>[x] Contexts reference<\/li>\n\n\n\n<li>[x] Deployments reference<\/li>\n\n\n\n<li>[x] Environments reference<\/li>\n\n\n\n<li>[x] Dependency caching reference<\/li>\n\n\n\n<li>[x] Reusable configurations reference<\/li>\n\n\n\n<li>[x] Action metadata reference<\/li>\n\n\n\n<li>[x] Workflow cancellation reference<\/li>\n\n\n\n<li>[x] Dockerfile support reference<\/li>\n\n\n\n<li>[x] GitHub-hosted runners reference<\/li>\n\n\n\n<li>[x] Larger runners reference<\/li>\n\n\n\n<li>[x] Self-hosted runners reference<\/li>\n\n\n\n<li>[x] Security reference<\/li>\n\n\n\n<li>[x] Secrets reference<\/li>\n\n\n\n<li>[x] OIDC reference<\/li>\n\n\n\n<li>[x] GitHub Actions limits<\/li>\n\n\n\n<li>[x] GitHub Actions Importer reference<\/li>\n\n\n\n<li>[x] Billing and usage<\/li>\n\n\n\n<li>[x] Actions policies<\/li>\n\n\n\n<li>[x] Actions metrics<\/li>\n\n\n\n<li>[x] Runner administration<\/li>\n\n\n\n<li>[x] Workflow administration<\/li>\n\n\n\n<li>[x] Repository administration<\/li>\n\n\n\n<li>[x] Organization administration<\/li>\n\n\n\n<li>[x] Enterprise administration<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Audience:&nbsp;Developers, DevOps engineers, platform engineers, SREs, security engineers, technical leads, and architectsLevel:&nbsp;Beginner \u2192 Intermediate \u2192 Advanced \u2192 EnterpriseLast verified:&nbsp;2026-09-26Primary source:&nbsp;GitHub Actions official documentation, plus the supplied 62-section&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1187","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1187","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1187"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1187\/revisions"}],"predecessor-version":[{"id":1188,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1187\/revisions\/1188"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}