Настройка Github Actions для Symfony проекта

28 Jan 2021

Всем привет! Сегодня хотел бы поделиться опытом настройки и использования Github Actions. Интерес и желание попробовать этот инструмент у меня появилось довольно давно. В коммерческих проектах давно уже используется CI/CD, docker контейнеры и т.д. А для своих проектов все как-то руки не доходили настроить.

Основные применения:

  • Упрощение развертывания проекта и локальной разработки
  • Постоянная сборка приложения и запуск тестов
  • Автоматический деплой

Изначально настраивал для этого блога. Но затем, для большей наглядности, также настроил для одного из открытых проектов - placeholder-service.

Docker

Для начала имеет смысл докеризировать приложение для локальной разработки. Докеризировать Symfony приложения можно по этому руководству. Использование docker локально дает некоторые преимущества.

  • Все окружение создается с нуля. Примерно то же самое происходит в Github Actions.
  • Окружение изолировано от локального.
  • Развернуть проект на новой машине можно с помощью нескольких команд.
  • Возможность менять версии дистрибутива, софта и зависимостей.

В результате получился примерно такой docker-compose.yml.

version: '3'
services:
  php-fpm:
    build:
      context: ./docker/php-fpm
    environment:
      - DOCKER_ENVIRONMENT=true
      - APP_SECRET=${APP_SECRET}
    volumes:
      - ./:/var/www
  nginx:
    build:
      context: ./docker/nginx
    volumes:
      - ./:/var/www
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/sites/:/etc/nginx/sites-available
      - ./docker/nginx/conf.d/:/etc/nginx/conf.d
      - ./docker/logs:/var/log
    depends_on:
      - php-fpm
    ports:
      - "16880:80"
      - "16843:443"

Composer скрипты

Затем некоторые команды вынес в composer scripts для более удобного запуска. В результате, получился такой composer.json.

{
    "name": "antonshell/placeholder-service",
    "description": "There is a self hosted service for images placeholders generation",
    ...
    "scripts": {
        ...
        "test": [
            "php bin/phpunit"
        ],
        ...
    }
}

К примеру, для запуска тестов вместо php bin/phpunit можно запускать composer test. В Github Actions мы будем использовать сокращенные команды.

Github actions - тесты

При настройке тестов ориентировался на это руководство. В репозитории создал директорию .github/workflows/ и добавил файл tests.yml.

Файл tests.yml выглядит так:

name: Tests
on: [push]
jobs:
  php-unit-and-functional-tests:
    runs-on: ubuntu-20.04
    strategy:
      fail-fast: true
      matrix:
        php-versions: ['7.4', '8.0']
    steps:
      # —— Setup Github actions —————————————————————————————————————————————
      # https://github.com/actions/checkout (official)
      - name: Git checkout placeholder-service
        uses: actions/checkout@v2
      # https://github.com/shivammathur/setup-php (community)
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          coverage: none
          tools: composer:v2
          extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, dom, filter, gd, iconv, json, mbstring, pdo
        env:
          update: true
      - name: Check PHP Version
        run: php -v
      # —— Composer —————————————————————————————————————————————————————————
      - name: Validate composer.json and composer.lock
        run: composer validate
      - name: Get composer cache directory
        id: composer-cache
        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
      - name: Cache composer dependencies
        uses: actions/cache@v1
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-
      - name: Install Composer dependencies
        run: composer install
      # —— Symfony ——————————————————————————————————————————————————————————
      - name: Check Symfony requirements
        run: vendor/bin/requirements-checker
      - name: Check the Symfony console
        run: bin/console -V
      # —— Code style —— (Based on this package: https://github.com/OskarStark/php-cs-fixer-ga)
      - name: PHP-CS-Fixer
        uses: docker://oskarstark/php-cs-fixer-ga
        with:
          args: --config=.php_cs.dist --diff --dry-run
      ## —— Tests ———————————————————————————————————————————————————————————
      - name: Run functionnal and unit tests
        run: |
          cp .env.ci .env.test
          cp .env.ci .env
          cp phpunit.ci.xml phpunit.xml
          composer test

Каждый файл в папке workflows описывает определенный процесс. В конфиге мы указываем название workflow, на какой системе он будет запускаться(ubuntu 20.04), и в какой момент(on push).

Затем указываем последовательность действий для запуска подготовки окружения - установка нужной весии PHP, клонирование репозитория, установка зависимостей и т.д.

Когда все готово - проверяем codestyle и запускаем тесты. Тесты автоматически запускаются каждый раз, когда мы заливаем изменения в репозиторий.

Github Actions - деплой

При настройке деплоя использовал это руководство и этот пакет.

1 . Создал отдельное workflow - ssh_deploy.yml.

name: SSH Deploy
on:
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout
        uses: actions/checkout@v1
      # Install PHP
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.0'
          coverage: none
          tools: composer:v2
          extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, dom, filter, gd, iconv, json, mbstring, pdo
        env:
          update: true
      - name: Check PHP Version
        run: php -v
      # Install backend dependencies (Composer)
      - name: Validate composer.json and composer.lock
        run: composer validate
      - name: Get composer cache directory
        id: composer-cache
        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
      - name: Cache composer dependencies
        uses: actions/cache@v1
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-
      - name: Install Composer dependencies
        run: composer install
      # Prepare .env file for production
      - name: Make production envfile
        uses: SpicyPizza/create-envfile@v1
        with:
          envkey_APP_ENV: prod
          envkey_APP_DEBUG: false
          envkey_APP_SECRET: ${{ secrets.APP_SECRET }}
          file_name: .env
      # Copying files and artifacts via SSH
      - name: Copying files to server
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.REMOTE_HOST }}
          username: ${{ secrets.REMOTE_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          passphrase: ''
          rm: true
          source: "./"
          target: ${{ secrets.REMOTE_TARGET }}
      # Run commands on production
      - name: Executing remote ssh commands
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.REMOTE_HOST }}
          username: ${{ secrets.REMOTE_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          passphrase: ''
          script: rsync -a --exclude={'var','temp'} --delete ${{ secrets.REMOTE_TARGET }} ${{ secrets.REMOTE_TARGET_DEPLOY }}

Сначала проект собирается на временном сервере, затем копируется по ssh на основной сервер.

2 . Для хранения секретных данных(адрес сервера, ssh ключ, или пароль) использовал Github Secrets. Подробнее о Github Secrets можно узнать тут. Значения секретных переменных можно использовать таким образом:

host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}

3 . Создал ssh ключ для deployment пользователя и добавил его в Github Secrets. Сохранил содержимое файла id_rsa в переменную SERVER_SSH_KEY.

ssh-keygen -m PEM -t rsa -b 4096 -f ./id_rsa
cat id_rsa
cat id_rsa.pub

4 . На целевом сервере создал отдельного пользователя и добавил ssh ключ. Добавил содержимое id_rsa.pub в файл /home/deployment/.ssh/authorized_keys.

adduser deployment
usermod -aG www-data deployment

mkdir /home/deployment/.ssh/
nano /home/deployment/.ssh/authorized_keys
chown deployment:deployment /home/deployment/.ssh/authorized_keys
chmod 600 /home/deployment/.ssh/authorized_keys

5 . Проверил логин для deployment пользователя. Также это может пригодиться для отладки процесса и запуска команд от имени пользователя deployment.

ssh -i ./id_rsa deployment@{{remote_server_ip}}

6 . Создал директории необходимые для деплоя. Сначала проект копируется во временную директорию. После этого копируется в web-директорию.

cd /var/www
sudo mkdir /var/www/deployment/
sudo mkdir /var/www/deployment/placeholder-service
sudo chown -R deployment:deployment /var/www/deployment/
sudo chmod -R 775 /var/www/deployment/

sudo mkdir /var/www/placeholder-service
sudo chown -R deployment:deployment /var/www/placeholder-service
sudo chmod -R 775 /var/www/antonshell

7 . Деплой запускается вручную с вкладки "Actions"

Github Actions - дополнительно

Здесь я собрал несколько полезных, либо просто интересных дополнений для Github Actions.

Также может быть интересно заглянуть в Github Actions Marketplace и посмотреть расширения для PHP проектов

Тестовый проект доступен на github. На этом пока все. Спасибо за внимание!