github-actions-advanced — quality + safety report
In the Skillier index (antigravity__github-actions-advanced) · scanned 2026-06-03 · engine: builtin+triage
1 heuristic flag to review
Heuristic flags from the builtin scanner, which is known to over-flag (it trips on legitimate env-reading integrations, security skills, and library .eval calls). This is NOT an authoritative malicious verdict — re-scan with SkillSpector for the authoritative result. Run the authoritative scan →
📇 This skill is in the Skillier index (curated · deduped · quality-filtered). Install Skillier to route & load it into your AI client.
Quality notes
About this skill
Design, debug, and harden GitHub Actions CI/CD workflows, including reusable workflows, matrix builds, self-hosted runners, OIDC authentication, caching, environments, secrets, and release automation.
📄 Read the SKILL.md
---
name: github-actions-advanced
description: >
Design, debug, and harden GitHub Actions CI/CD workflows, including reusable
workflows, matrix builds, self-hosted runners, OIDC authentication, caching,
environments, secrets, and release automation.
category: devops
risk: safe
source: community
date_added: "2026-05-30"
---
# GitHub Actions Advanced Skill
Expert guidance for designing, writing, debugging, and securing **production-grade** GitHub Actions workflows.
---
## When to Use This Skill
- User mentions GitHub Actions, `.github/workflows`, CI/CD pipelines, runners, jobs, steps, or actions
- User wants to automate builds, tests, deployments, or releases via GitHub
- User asks about matrix builds, reusable workflows, composite actions, or self-hosted runners
- User needs help with OIDC authentication, caching strategies, or secrets management
- User says "my GitHub pipeline is failing" or "set up CI for my repo"
- User asks about workflow security, hardening, or environment protection rules
## When NOT to Use This Skill
- The user is working with GitLab CI/CD → recommend `gitlab-ci-patterns`
- The user is working with CircleCI, Jenkins, or other CI platforms
- The task is purely about Docker image building without GitHub context → recommend `docker-expert`
- The task is about Kubernetes deployment configuration → recommend `kubernetes-architect`
---
## Step 1: Understand Context Before Responding
When invoked, first gather context:
```bash
# Discover existing workflows in the repo
find .github/workflows -name "*.yml" -o -name "*.yaml" 2>/dev/null | head -20
# Check for composite actions
find .github/actions -name "action.yml" 2>/dev/null
# Detect tech stack (influences runner OS, language setup actions)
ls package.json requirements.txt Gemfile go.mod Cargo.toml pom.xml 2>/dev/null
```
Then adapt recommendations to:
- Existing workflow patterns in the repo
- The tech stack and language runtime
- Whether this is a monorepo or single-project repo
- Whether self-hosted or GitHub-hosted runners are in use
---
## Workflow Structure Reference
```yaml
name: Workflow Name
on: # Triggers (see Triggers section)
push:
branches: [main]
permissions: # Always declare — principle of least privilege
contents: read
env: # Workflow-level env vars
NODE_VERSION: '20'
concurrency: # Prevent duplicate runs
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # Cancel older runs for same branch
jobs:
job-id:
name: Human-readable name
runs-on: ubuntu-24.04 # Pin OS version — never use -latest in prod
timeout-minutes: 15 # Always set — prevents runaway jobs
environment: production # Links to GitHub Environment (approvals/secrets)
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Step name
run: echo "hello"
```
---
## Triggers (`on:`)
### Common Patterns
```yaml
on:
push:
branches: [main, 'release/**']
paths-ignore: ['**.md', 'docs/**'] # Skip docs-only changes
pull_request:
types: [opened, synchronize, reopened]
branches: [main]
workflow_dispatch: # Manual trigger with inputs
inputs:
environment:
description: 'Deploy target'
required: true
type: choice
options: [staging, production]
dry-run:
description: 'Dry run only?'
type: boolean
default: false
schedule:
- cron: '0 2 * * 1' # Monday 2am UTC
workflow_call: # Called by other workflows (reusable)
inputs:
image-tag:
type: string
required: true
secrets:
deploy-token:
required: true
release:
types: [published] # Trigger only on published releases
pull_request_target: # Runs with repo secrets — use with care!
types: [labeled] # Gate with label + author_association check
```
> **Security Warning:** `pull_request_target` runs with repo secrets. Only use after a maintainer labels the PR. Never check out fork code without explicit sandboxing.
---
## Reusable Workflows
Split large pipelines into composable units stored in `.github/workflows/`.
**Convention:** Prefix internal/reusable workflows with `_` (e.g., `_build.yml`).
### Caller (`.github/workflows/deploy.yml`)
```yaml
jobs:
call-build:
uses: ./.github/workflows/_build.yml # Same-repo reusable
# uses: org/repo/.github/workflows/build.yml@main # Cross-repo
with:
image-tag: ${{ github.sha }}
secrets: inherit # Pass all caller secrets down
call-test:
uses: ./.github/workflows/_test.yml
with:
node-version: '20'
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Explicit secret passing
```
### Reusable Workflow (`.github/workflows/_build.yml`)
```yaml
on:
workflow_call:
inputs:
image-tag:
type: string
required: true
push:
type: boolean
default: false
secrets:
registry-token:
required: false
outputs:
digest:
description: "Image digest"
value: ${{ jobs.build.outputs.digest }}
jobs:
build:
runs-on: ubuntu-24.04
timeout-minutes: 20
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- id: build
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
push: ${{ inputs.push }}
tags: myapp:${{ inputs.image-tag }}
```
---
## Matrix Builds
```yaml
jobs:
test:
strategy:
fail-fast: false # Don't cancel others if one fails
max-parallel: 4 # Limit concurrent runners
matrix:
os: [ubuntu-24.04, windows-2022, macos-14]
node: ['18', '20', '22']
exclude:
- os: windows-2022
node: '18'
include:
- os: ubuntu-24.04
node: '22'
experimental: true # Custom matrix variable
runs-on: ${{ matrix.os }}
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- run: npm ci
- run: npm test
continue-on-error: ${{ matrix.experimental == true }}
```
### Dynamic Matrix via Script
```yaml
jobs:
generate-matrix:
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- id: set-matrix
run: |
SERVICES=$(find services -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | jq -R -s -c 'split("\n")[:-1]')
printf 'matrix={"service":%s}\n' "$SERVICES" >> "$GITHUB_OUTPUT"
build:
needs: generate-matrix
strategy:
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
runs-on: ubuntu-24.04
steps:
- env:
SERVICE: ${{ matrix.service }}
run: echo "Building $SERVICE"
```
---
## Caching Strategies
### Language Setup Actions (Preferred — No Extra Step Needed)
```yaml
# Node.js
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: '20'
cache: 'npm' # or 'yarn' or 'pnpm'
# Python
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: '3.12'
cache: 'pip'
# Go
- uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
with:
go-version: '1.23'
cache: true
# Java / Gradle / Maven
- uses: actions/setup-java@7a6d8a8234af8eb26422e24052f73b12b0e46a27 # v4.6.0
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven' # or 'gradle'
```
### Manual Cache (Any Tool)
```yaml
- uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
id: cache-deps
with:
path: |
~/.cache/pip
.venv
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
${{ runner.os }}-pip-
- name: Install deps (only on cache miss)
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pip install -r requirements.txt
```
### Docker Layer Caching
```yaml
- uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
cache-from: type=gha
cache-to: type=gha,mode=max
# For registry-backed cache (cross-branch):
# cache-from: type=registry,ref=ghcr.io/myorg/myapp:buildcache
# cache-to: type=registry,ref=ghcr.io/myorg/myapp:buildcache,mode=max
```
---
## OIDC Authentication (Keyless Cloud Auth)
**Never store long-lived cloud credentials as secrets.** Use OIDC to get short-lived tokens that expire automatically.
### AWS
```yaml
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
role-session-name: GitHubActions-${{ github.run_id }}
# Trust policy on the IAM role must include:
# "token.actions.githubusercontent.com" as OIDC provider
# Condition: "repo:org/repo:ref:refs/heads/main" (restrict to branch)
```
### GCP (Workload Identity Federation)
```yaml
permissions:
id-token: write
contents: read
steps:
- uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7
with:
workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/github-pool/providers/github-provider
service_account: github-actions@my-project.iam.gserviceaccount.com
token_format: access_token # or 'id_token'
```
### Azure (Federated Identity)
```yaml
permissions:
id-token: write
contents: read
steps:
- uses: azure/login@a65d910e8af852a8061c627c456678983e180302 # v2.2.0
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# No client secret needed! Uses OIDC federated credentials
```
---
## Environments & Deployment Protection
```yaml
jobs:
deploy-staging:
environment:
name: staging
url: https://staging.myapp.com
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- run: ./scripts/deploy.sh staging
deploy-production:
needs: deploy-staging
environment:
name: production
url: https://myapp.com # Shown in the GitHub UI deployment panel
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- run: ./scripts/deploy.sh production
```
**Configure in Settings → Environments:**
- **Required reviewers** — manual approval gate before run
- **Wait timer** — delay after approval (e.g., 10-minute buffer)
- **Branch/tag restrictions** — only `main` or `v*` tags can deploy to prod
- **Environment-specific secrets** — override repo-level secrets per environment
- **Deployment branches** — whitelist which branches can target this environment
---
## Secrets Management
```yaml
# Access repo/org/environment secrets
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
# Auto-provided token — no setup needed
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Hierarchy (most specific wins):
# environment secret > repo secret > org secret
```
### Masking Dynamic Values
```yaml
- name: Generate and mask dynamic token
run:
… (truncated)Want a live grade + an embeddable README badge? Run your skill through the free scanner.
Graded independently by Skillproof — nothing to sell the author. Quality is mechanical + corpus-grounded; safety flags are heuristic (builtin+triage), not a malicious verdict.