PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

GitHub Actions – PHP pipeline, test matrix, staging deploy via SSH

by Henryk Tews / Tuesday, 14 September 2021 / Published in Środowiska

GitHub Actions turned CI/CD from a DevOps speciality into something any developer can set up in an afternoon. For PHP projects it means automatic test runs, static analysis, and deployment to staging on every pull request – without maintaining a Jenkins server. I show a practical pipeline for a PHP/Magento project with a test matrix and SSH deployment.

Basic PHP pipeline

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: mbstring, intl, gd, zip, pdo_mysql, bcmath
          tools: composer:v2
          coverage: xdebug

      - name: Cache Composer dependencies
        uses: actions/cache@v4
        with:
          path: vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --no-progress

      - name: Run PHPUnit
        run: vendor/bin/phpunit --coverage-clover coverage.xml

      - name: Run PHPStan
        run: vendor/bin/phpstan analyse --no-progress

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: coverage.xml

Test matrix – multiple PHP versions

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        php-version: ['8.3', '8.4']
        dependency-version: [prefer-stable, prefer-lowest]
      fail-fast: false  # run all matrix combinations even if one fails

    name: PHP ${{ matrix.php-version }} - ${{ matrix.dependency-version }}

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP ${{ matrix.php-version }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-version }}
          extensions: mbstring, intl, pdo_mysql, bcmath

      - name: Install dependencies
        run: composer update --${{ matrix.dependency-version }} --no-interaction

      - name: Run tests
        run: vendor/bin/phpunit

Deploy to staging via SSH

  deploy-staging:
    runs-on: ubuntu-latest
    needs: test          # only deploy after tests pass
    if: github.ref == 'refs/heads/develop'

    steps:
      - uses: actions/checkout@v4

      - name: Deploy to staging
        uses: appleboy/ssh-action@v1
        with:
          host:     ${{ secrets.STAGING_HOST }}
          username: ${{ secrets.STAGING_USER }}
          key:      ${{ secrets.STAGING_SSH_KEY }}
          script: |
            cd /var/www/magento-staging

            # Pull latest code
            git fetch origin
            git reset --hard origin/develop

            # Install dependencies (no dev, optimised autoloader)
            composer install --no-dev --optimize-autoloader --no-interaction

            # Magento deployment
            php bin/magento maintenance:enable
            php bin/magento setup:upgrade --keep-generated
            php bin/magento setup:di:compile
            php bin/magento setup:static-content:deploy pl_PL en_US -f --jobs=4
            php bin/magento cache:flush
            php bin/magento maintenance:disable

            echo "Deploy complete: $(date)"

Secrets management in GitHub Actions

# Store secrets in GitHub repository settings:
# Settings -> Secrets and variables -> Actions -> New repository secret

# STAGING_HOST      - staging server IP or domain
# STAGING_USER      - SSH username (e.g. deploy)
# STAGING_SSH_KEY   - private SSH key content (generate a dedicated deploy key)

# Generate deploy key:
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key -N ""
# Add public key to staging server: ~/.ssh/authorized_keys
# Add private key content to GitHub Secrets as STAGING_SSH_KEY

Conditional jobs and notifications

  notify:
    runs-on: ubuntu-latest
    needs: [test, deploy-staging]
    if: always()   # run even if previous jobs failed

    steps:
      - name: Notify on failure
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          text: "Pipeline failed for ${{ github.ref }} by ${{ github.actor }}"
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Summary

GitHub Actions requires no external infrastructure – the YAML workflow file lives in the repository alongside the code. The test matrix catches regressions across PHP versions in parallel. SSH deployment gives a repeatable, automated process that runs the same commands every time. Start small – a single job with composer install and phpunit – and add steps incrementally. The feedback loop from “push to PR merged” should be under 5 minutes for a typical PHP module.

About Henryk Tews

What you can read next

Blackfire – DDEV setup, HTTP and CLI profiling, CI/CD assertions
OpenTelemetry – distributed tracing, auto-instrumentation, Jaeger in DDEV
DDEV advanced – mutagen, custom services, hooks, shared team configuration

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}