Skip to content

/github-actions

Use this skill when authoring or debugging GitHub Actions workflows (.github/workflows/*.yml) — e.g. "add CI for this Drupal project on GitHub", "run phpcs/phpstan/phpunit on pull requests", "cache composer dependencies", "deploy over SSH when main is pushed", "why didn't my

shell
$ npx -y skills add siva01c/claude-plugins --skill github-actions --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/github-actions
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use this skill when authoring or debugging GitHub Actions workflows (.github/workflows/*.yml) — e.g. "add CI for this Drupal project on GitHub", "run phpcs/phpstan/phpunit on pull requests", "cache composer dependencies", "deploy over SSH when main is pushed", "why didn't my

SKILL.md

github-actions.SKILL.md
name: github-actions
description: >
  Use this skill when authoring or debugging GitHub Actions workflows
  (.github/workflows/*.yml) — e.g. "add CI for this Drupal project on
  GitHub", "run phpcs/phpstan/phpunit on pull requests", "cache composer
  dependencies", "deploy over SSH when main is pushed", "why didn't my
  workflow trigger", or anything involving jobs, matrices, runners, secrets,
  or reusable workflows.

GitHub Actions Skill

GitHub Actions runs workflows from `.github/workflows/*.yml`. This skill covers workflow authoring with a Drupal project as the working example: linting, static analysis, PHPUnit with a database service, and SSH/drush deployment.

---

Workflow anatomy

name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read            # least privilege — grant more only per job

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true  # newer push cancels the outdated run

jobs:
  phpcs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          tools: composer:v2
      - uses: actions/cache@v4
        with:
          path: ~/.composer/cache
          key: composer-${{ hashFiles('composer.lock') }}
          restore-keys: composer-
      - run: composer install --no-progress
      - run: vendor/bin/phpcs --standard=Drupal,DrupalPractice web/modules/custom

Key defaults to always set:

  • **`permissions:`** — the implicit token is powerful; start from

`contents: read` and widen per job only when needed.

  • **`concurrency:`** — avoids burning runner minutes on superseded pushes.

---

PHPUnit with a database service

  phpunit:
    runs-on: ubuntu-latest
    services:
      db:
        image: mariadb:11.4
        env:
          MARIADB_DATABASE: drupal_test
          MARIADB_ROOT_PASSWORD: root
        ports: ['3306:3306']
        options: >-
          --health-cmd="healthcheck.sh --connect --innodb_initialized"
          --health-interval=10s --health-timeout=5s --health-retries=5
    env:
      SIMPLETEST_DB: mysql://root:root@127.0.0.1:3306/drupal_test
      SIMPLETEST_BASE_URL: http://localhost
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: gd, pdo_mysql
      - run: composer install --no-progress
      - run: vendor/bin/phpunit -c web/core/phpunit.xml.dist web/modules/custom

`services:` containers get health-checked before steps run — no manual wait loops. From the runner they are reachable on `127.0.0.1:<mapped port>`.

Matrix builds

    strategy:
      matrix:
        php: ['8.3', '8.4']
    steps:
      - uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}

---

Deployment job

  deploy:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: [phpcs, phpunit]
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://www.example.com
    steps:
      - uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
      - run: |
          ssh -o StrictHostKeyChecking=accept-new deploy@prod.example.com \
            "cd /var/www/site && git pull --ff-only \
             && composer install --no-dev --optimize-autoloader \
             && vendor/bin/drush deploy -y"
  • `needs:` gates deployment on green checks.
  • `environment:` enables required reviewers and deployment history

(Settings → Environments) — use it for production approval gates.

  • Secrets live in repo/environment settings, referenced via

`${{ secrets.NAME }}`. For cloud providers prefer **OIDC** (`permissions: id-token: write` + the provider's auth action) over long-lived keys.

---

Reuse

  • **Reusable workflow** — a whole workflow callable from others:
  # .github/workflows/drupal-tests.yml
  on:
    workflow_call:
      inputs:
        php-version: { type: string, default: '8.3' }
  # caller
  jobs:
    tests:
      uses: ./.github/workflows/drupal-tests.yml
      with:
        php-version: '8.4'
  • **Composite action** — a reusable step sequence in

`.github/actions/<name>/action.yml` (e.g. "setup PHP + composer install with cache") to deduplicate job boilerplate.

---

Troubleshooting

| Symptom | Fix | |---|---| | Workflow doesn't trigger | Check `on:` filters — `branches:`/`paths:` excluded the ref, or push was from a workflow using the default token (no recursive triggers) | | `Permission denied` on push/comment from job | Widen `permissions:` for that job (e.g. `pull-requests: write`) | | Composer cache never hits | Key on `hashFiles('composer.lock')` and add a `restore-keys:` prefix fallback | | PHPUnit can't reach DB | Use `127.0.0.1` with the mapped port, not the service name (service names only resolve in container jobs) | | Deploy ran before tests finished | Add the test jobs to `needs:` of the deploy job | | Two runs per PR push | `push:` + `pull_request:` both trigger — restrict `push:` to `branches: [main]` |

Read more
Read it on GitHub ↗
Ships withclaude-plugins

A curated collection of Claude Code plugins for Drupal development, security, and deployment. Each plugin covers one topic — Drupal itself, DDEV, Docker, CI/CD, git workflows, and security verification — so you install only what you need.

Get the whole plugin, auto-invoked

Other skills on claude-plugins.