<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Максим Кузнецов: заметки с тегом инфраструктура</title>
<link>https://maxkuznetsov.ru/tags/infrastruktura/</link>
<description>Простыми словами о веб-разработке</description>
<author>Максим Кузнецов</author>
<language>ru</language>
<generator>E2 (v3559; Aegea)</generator>

<itunes:owner>
<itunes:name>Максим Кузнецов</itunes:name>
<itunes:email></itunes:email>
</itunes:owner>
<itunes:subtitle>Простыми словами о веб-разработке</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>Бесплатный wildcard SSL-сертификат для поддоменов</title>
<guid isPermaLink="false">40</guid>
<link>https://maxkuznetsov.ru/all/nginx-free-wildcard-ssl/</link>
<pubDate>Sun, 02 May 2021 18:05:26 +0300</pubDate>
<author>Максим Кузнецов</author>
<comments>https://maxkuznetsov.ru/all/nginx-free-wildcard-ssl/</comments>
<description>
&lt;p&gt;Для простых веб-приложений достаточно одного домена (example.com). Однако для сложных систем, с разделением на бэкенд и фронтенд части, с лендингами, микросервисами и т. п. нужны поддомены, например: api.example.com, app.example.com, cdn.example.com.&lt;/p&gt;
&lt;p&gt;Вместо того, чтобы выпускать отдельные сертификаты на каждый поддомен, мы можем выпустить один сертификат, покрывающий сразу все кейсы — *.example.com. Такой тип сертификатов называется &lt;i&gt;Wildcard&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;Как и в предыдущей статье про выдачу &lt;a href="https://maxkuznetsov.ru/all/nginx-free-ssl/"&gt;бесплатного SSL-сертификата&lt;/a&gt;, мы воспользуемся утилитой Certbot, который поможет нам выпустить и установить бесплатные SSL-сертификаты для наших поддоменов через центр Let’s encrypt.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Предполагаю, что вы уже настроили ваш сервер, чтобы поддомены были доступны по http (80 порт). Если вы не знаете, как это сделать, поищите статью по вашим ОС и веб-серверу на Digitalocean. Например, вот статья про &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04-ru"&gt;установку и настройку Nginx на Ubuntu 20.04&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Заходим на сайт &lt;a href="https://certbot.eff.org/"&gt;Certbot’а&lt;/a&gt; и выбираем ОС нашего сервиса и веб-сервер. Ниже в примере будет Ubuntu 20.04 и Nginx. Дальше выбираем таб Wildcard.&lt;/p&gt;
&lt;p&gt;Предположим, что нам нужны следующие домены: example.com, api.example.com и app.example.com.&lt;br /&gt;
Запускаем по инструкции первые 8 шагов для установки самой утилиты:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;// Удаляем snap, если он был установлен ранее
$ sudo apt-get remove certbot
// Устанавливаем snap
$ sudo snap install core; sudo snap refresh core
// Устанавливаем certbot
$ sudo snap install --classic certbot
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
$ sudo snap set certbot trust-plugin-with-root=ok

// Проверяем, что certbot установлен
$ certbot --version
&amp;gt; certbot 1.14.0&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для того, чтобы выпустить сертификаты, мы должны доказать удостоверяющему центру Let’s Encrypt, что мы действительно владеем данными доменами и сабдоменами. Есть два способа это сделать.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Добавить в DNS специальные txt записи, которые попросит нас добавить Let’s Encrypt, после этого он их проверит, и если записи будут обнаружены, это подтвердит факт владения (или по крайней мере доступа к ним). С чужими доменами так сделать не получится.&lt;/li&gt;
&lt;li&gt;Webroot. Аналогичный способ, но специальные уникальные строки добавляются в файлы, которые потом должны быть доступны через URL, например: &lt;tt&gt;&lt;a href="http://api.example.com/.well-known/acme-challenge/JH1kjoemxaS33d"&gt;http://api.example.com/.well-known/acme-challenge/JH1kjoemxaS33d&lt;/a&gt;&lt;/tt&gt;. Поскольку такой файл сможет добавить только владелец домена, этого тоже достаточно, чтобы Let’s Encrypt удостоверился в факте нашего владения.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Certbot позволяет автоматизировать каждый из способов. Нам не придётся делать это вручную, что критично для будущей поддержки сертификатов — их нужно будет обновлять раз в два месяца. И если доменов и сабдоменов много, то это быстро станет обузой.&lt;/p&gt;
&lt;p&gt;Certbot поддерживает из коробки несколько популярных DNS-регистраторов (те сервисы, где можно купить домен), и тогда можно следовать по инструкции, однако в моём случае регистратором был Godaddy, который не поддерживается. Поэтому я воспользовался вторым способом и он оказался даже проще первого. Кроме того, данный способ надёжнее, так как не зависит, повезёт ли вам с поддержкой вашего регистратора или нет.&lt;/p&gt;
&lt;p&gt;Для верификации доменов по webroot нам нужно настроить каждый домен/поддомен, чтобы он отдавал директорию &lt;tt&gt;&lt;domain&gt;/.well-known/acme-challenge/&lt;/tt&gt;. В эту папку certbot запишет то, что нужно Let’s Encrypt, чтобы мы прошли проверку, поэтому убедитесь, что certbot имеет туда доступ.&lt;/p&gt;
&lt;p&gt;Есть небольшой лайфхак, как упростить работу с этой папкой в конфигах сервера. Для этого мы создадим отдельный файл конфига, который будем подключать для каждого домена/поддомена. Пример для Nginx:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="nginx"&gt;// создаём файл &amp;quot;/etc/nginx/letsencrypt.conf&amp;quot;
location /.well-known/acme-challenge {
    # Вместо `/var/www/acme-challenge` можно указать любую папку.
    # Она может быть недоступна извне, это неважно. Главное, чтобы туда имел доступ ваш текущий юзер.
    alias /var/www/acme-challenge/.well-known/acme-challenge;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Подключаем этот файл в конфигах доменов и поддоменов. Допустим, у вас есть файл &lt;/tt&gt;/etc/nginx/sites-available/api.example.com&lt;/tt&gt; (с остальными аналогично).&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="nginx"&gt;server {
    server_name api.example.com;
    include letsencrypt.conf;
    ...
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Готово. Теперь домен будет все запросы, начинающиеся на &lt;tt&gt;/.well-known/acme-challenge&lt;/tt&gt; будут направлены в папку &lt;tt&gt;/var/www/acme-challenge/.well-known/acme-challenge&lt;/tt&gt;, общую для всех доменов/поддоменов.&lt;/p&gt;
&lt;p&gt;Далее запускаем certbot, чтобы он пообщался с Let’s Encrypt, подтвердил наши домены, выпустил сертификаты и даже настроил наш веб-сервер на их использование.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;certbot run -a webroot -i nginx -w /var/www/acme-challenge -d example.com -d api.example.com -d app.example.com&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Разберём эти конфиги:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;tt&gt;-a webroot&lt;/tt&gt; — это способ аутентификации (подтверждение, что мы владеем доменами)&lt;/li&gt;
&lt;li&gt;&lt;tt&gt;-i nginx&lt;/tt&gt; — один из доступных способов установки сертификатов. В данном случае это nginx, так как именно этот веб-сервер мы используем. Certbot вам задаст несколько вопросов и после этого обновит конфиги. Делает он это очень умно, так что без разницы как эти конфиги у вас написаны. И даже устанавливает редирект http на https.&lt;/li&gt;
&lt;li&gt;&lt;tt&gt;-w /var/www/acme-challenge&lt;/tt&gt; — путь до папки, которая будет использоваться для webroot аутентификации. Обратите внимание, что именно эта папка прописана в &lt;tt&gt;/etc/nginx/letsencrypt.conf&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;&lt;tt&gt;-d example.com -d api.example.com -d app.example.com&lt;/tt&gt; — указываем все необходимые поддомены, обязательно первым указываем основной домен (не поддомен), иначе ничего не получится.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Всё, certbot всё сделал сам, даже перезагрузил веб-сервер. Можно зайти через браузер на поддомены и убедиться, что всё работает.&lt;/p&gt;
&lt;p&gt;&lt;hr&gt;&lt;/p&gt;
&lt;p&gt;p.s. если браузер выдаёт ошибку, что такой домен не найден, то проверьте, что файрволл настроен на выдачу https трафика. Например, в Ubuntu 20.04 это делается через UFW:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="bash"&gt;// Проверяем, есть ли HTTPS в активных правилах
$ sudo ufw status
// Смотрим, какие правила могут быть включены
$ sudo ufw app list
// Включаем Nginx https
$ sudo ufw allow 'Nginx HTTPS'
// Проверяем, что правило появилось в активных
$ sudo ufw status&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Как настроить php-cs-fixer и phpstan на локалке</title>
<guid isPermaLink="false">39</guid>
<link>https://maxkuznetsov.ru/all/code-quality-local/</link>
<pubDate>Tue, 01 Dec 2020 17:34:55 +0300</pubDate>
<author>Максим Кузнецов</author>
<comments>https://maxkuznetsov.ru/all/code-quality-local/</comments>
<description>
&lt;h2&gt;Проблема 1&lt;/h2&gt;
&lt;p&gt;При совместной разработке (от 2х человек) code style становится критичным. Если каждый будет писать в своём стиле, вскоре проект превратится в бардак, где по разному стилю можно будет оценивать возраст кода будто по кольцам на деревьях.&lt;/p&gt;
&lt;p&gt;На собеседованиях я часто слышу от кандидатов, что мы пользуемся проверками, встроенными в IDE. Но это полрешения, так как такие проверки носят рекомендательный, а не обязательный характер. Очень легко не заметить предложение по правке от IDE. А кроме того, приходится синхронизировать настройки IDE между всей командой. А что делать, если у кого-то другой редактор? Результат непредсказуем, а значит и ненадёжен.&lt;/p&gt;
&lt;h2&gt;Проблема 2&lt;/h2&gt;
&lt;p&gt;Чем больше кодовая база проекта, тем проще допустить ошибку или опечатку. Вручную проверка кода перед каждым коммитом возможна, но требует много времени и большой внимательности. Результат опять непредсказуем и ненадёжен.&lt;/p&gt;
&lt;h2&gt;Решение&lt;/h2&gt;
&lt;p&gt;Нужно договориться об общих правилах с командой и автоматизировать процесс проверки кода, сделав её независимой от IDE, платформы, настроения, желания и внимательности человека.&lt;/p&gt;
&lt;p&gt;Такие инструменты уже есть — code style и статические анализаторы кода.&lt;/p&gt;
&lt;h3&gt;Единый стиль кода&lt;/h3&gt;
&lt;p&gt;Поскольку я специализируюсь на Symfony, то для код-стайла выбираю &lt;a href="https://github.com/FriendsOfPHP/PHP-CS-Fixer"&gt;PHP-CS-Fixer&lt;/a&gt;. Он написан при участии создателя Symfony и имеет из коробки поддержку рекомендованного сообществом &lt;a href="https://symfony.com/doc/current/contributing/code/standards.html"&gt;Symfony Code Standards&lt;/a&gt;. Есть ещё &lt;a href="https://github.com/squizlabs/PHP_CodeSniffer"&gt;PHPCodeSniffer&lt;/a&gt;, но он умеет только обнаруживать ошибки, но не исправляет их автоматически, поэтому я рекомендую именно PHP-CS-Fixer.&lt;/p&gt;
&lt;p&gt;Совет: обязательно изучите и следуйте code style и рекомендациям вашего фреймворка. С большой вероятностью в будущих проектах вам встретится именно общепринятый в сообществе фреймворка code style, поэтому будет проще вписаться в новую команду, если вы придёте к этому стилю как можно раньше. К тому же, уже принятый в сообществе стиль поможет избежать ненужных холиваров внутри команды.&lt;/p&gt;
&lt;p&gt;Устанавливаем через composer и настраиваем PHPCSFixer в Symfony-проекте:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;cd /ваш/проект
composer req --dev friendsofphp/php-cs-fixer
# Если используете docker-compose, то устанавливайте в нём
docker-compose exec web composer req --dev friendsofphp/php-cs-fixer&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Создаём в корне проекта файл &lt;tt&gt;.php_cs&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;?php

$finder = PhpCsFixer\Finder::create()
    -&amp;gt;in([
        __DIR__ . '/src',
        __DIR__ . '/tests'
    ])
;

return PhpCsFixer\Config::create()
    -&amp;gt;setRules([
        '@Symfony' =&amp;gt; true,
        'array_syntax' =&amp;gt; ['syntax' =&amp;gt; 'short'],
        'concat_space' =&amp;gt; ['spacing' =&amp;gt; 'one'],
        'increment_style' =&amp;gt; ['style' =&amp;gt; 'post'],
        'no_extra_blank_lines' =&amp;gt; ['tokens' =&amp;gt; [
            'extra',
            'parenthesis_brace_block',
            'square_brace_block',
            'throw',
            'use',
        ]],
        'no_superfluous_phpdoc_tags' =&amp;gt; false,
        'phpdoc_align' =&amp;gt; false,
        'phpdoc_annotation_without_dot' =&amp;gt; false,
        'trailing_comma_in_multiline_array' =&amp;gt; false,
        'yoda_style' =&amp;gt; false
    ])
    -&amp;gt;setFinder($finder)
;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь вы можете запустить проверку всего проекта (или конкретного пути) с помощью команды&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;# только поиск ошибок
./vendor/bin/php-cs-fixer  fix --dry-run --diff ./
# поиск ошибок и их автоматический фикс
./vendor/bin/php-cs-fixer fix ./&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Ошибки и качество кода&lt;/h3&gt;
&lt;p&gt;Для отлова опечаток и даже поиска более сложных ошибок мы воспользуемся статическим анализатором кода. Прелесть анализаторов в том, что они не выполняют код, а просто читают и анализируют — это очень быстро. Кроме того, каждый из них имеет свои уровни строгости, поэтому можно внедрять их в существующий проект постепенно: фиксим ошибки с минимальным уровнем, коммитим, повышаем уровень, фиксим, коммитим, снова повышаем.&lt;/p&gt;
&lt;p&gt;Самые популярные статичиские анализаторы кода:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/phpstan/phpstan"&gt;PHPStan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vimeo/psalm"&gt;Psalm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/phan/phan"&gt;Phan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Я покажу, как настроить и внедрить PHPStan, а вы можете добавить в проект все три по аналогии. Устанавливаем через composer сам PHPStan и несколько полезных плагинов — PHPUnit, Symfony, Doctrine.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;cd /ваш/проект
composer req --dev phpstan/phpstan phpstan/phpstan-doctrine phpstan/phpstan-phpunit phpstan/phpstan-symfony&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Должен создаться в корне проекта файл &lt;tt&gt;.phpstan.neon.dist&lt;/tt&gt;, редактируем его:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;includes:
    - vendor/phpstan/phpstan-doctrine/extension.neon
    - vendor/phpstan/phpstan-phpunit/extension.neon
    - vendor/phpstan/phpstan-phpunit/rules.neon
    - vendor/phpstan/phpstan-symfony/extension.neon
parameters:
    reportUnmatchedIgnoredErrors: false     #to avoid throwing of errors ... if no errors matching the ignored one is raised
    scanFiles:  #we need this for some twig based functions
        - '%rootDir%/../../../vendor/twig/twig/src/Extension/CoreExtension.php'
    bootstrapFiles:
        - '%rootDir%/../../../vendor/bin/.phpunit/phpunit-X.X.X/vendor/autoload.php'
    ignoreErrors:
        - message: '/^Service &amp;quot;[^&amp;quot;]+&amp;quot; is private.$/'
          path: '%rootDir%/../../../tests/'
    level: 0 
    symfony:
        container_xml_path: '%rootDir%/../../../var/cache/development/App_KernelDevelopmentDebugContainer.xml'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Внимательно пройдитесь по этим настройкам и укажите корректные пути. Особое внимание:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;bootstrapFiles — включает путь до текущего phpunit. Если его не используете, то можете закоментить.&lt;/li&gt;
&lt;li&gt;level — уровень строгости проверок &lt;a href="https://phpstan.org/user-guide/rule-levels"&gt;от 0 до 8&lt;/a&gt;. 0 — самый слабый уровень, подходит для первого внедрения.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Теперь вы можете запустить проверку всего проекта (или конкретного пути) с помощью команды&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;./vendor/bin/phpstan analyse src tests&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Советы по внедрению&lt;/h3&gt;
&lt;p&gt;Несколько советов по внедреню PHP-CS-Fixer в существующий проект с большой командой.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Выберите одного ответственного, кто запустит скрипты и пофиксит весь проект у себя на локалке, а потом создаст ПР / смерджит в develop или master.&lt;/li&gt;
&lt;li&gt;Лучшее время — первое утро нового спринта. Обычно после спринта все ветки смёрджены, и это позволит залить все найденные правки в основную ветку проекта без конфликтов.&lt;/li&gt;
&lt;li&gt;В статических анализаторах используйте для начала минимальный уровень строгости проверок. У каждого анализатора для этого своя школа, читайте документацию.&lt;/li&gt;
&lt;li&gt;Первый фикс — самый критичный момент, так как правок может быть очень много. Все последующие фиксы обычно не создают ощутимых конфликтов и гит их смёрдживает самостоятельно.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Чтобы не запоминать длинные пути до скриптов, мы добавим несколько задач (tasks) в composer.json:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;{
    &amp;quot;require&amp;quot;: { ... },
    &amp;quot;require-dev&amp;quot;: { ... },
    ...
    &amp;quot;scripts&amp;quot;: {
        ...
        &amp;quot;csfix&amp;quot;: &amp;quot;./vendor/bin/php-cs-fixer fix&amp;quot;,
        &amp;quot;csfix-validate&amp;quot; : &amp;quot;./vendor/bin/php-cs-fixer fix --dry-run --diff&amp;quot;,
        &amp;quot;phpstan&amp;quot;: &amp;quot;./vendor/bin/phpstan analyse src tests&amp;quot;,
        &amp;quot;code-quality&amp;quot;: [
            &amp;quot;@phpcsfixer-validate&amp;quot;,
            &amp;quot;@phpstan&amp;quot;
        ]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь мы можем запускать проверки в упрощённом формате:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;composer csfix
composer phpstan
composer code-quality&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Обязательный запуск проверок перед коммитом&lt;/h2&gt;
&lt;p&gt;Внедрение этих инструментов позволяет удостовериться, что код соответствует принятому в команде стилю и не имеет очевидных опечаток и синтаксических ошибок. Однако это ещё не решает проблему забывчивости программистов и необязательности проверок.&lt;/p&gt;
&lt;p&gt;Воспользуемся Git-хуками (git hooks) — это скрипты, которые мы можем попросить git выполнить при возникновении какого-то события: момент перед созданием коммита, момент после создания коммита, момент добавления сообщения в коммит и т. п.&lt;/p&gt;
&lt;p&gt;Полный список git-хуков вы можете увидеть у себя в репозитории:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;$ ls -al .git/hooks
-rwxr-xr-x   1 mk  staff   478 Jul 13 13:04 applypatch-msg.sample
-rwxr-xr-x   1 mk  staff   896 Jul 13 13:04 commit-msg.sample
-rwxr-xr-x   1 mk  staff  3327 Jul 13 13:04 fsmonitor-watchman.sample
-rwxr-xr-x   1 mk  staff   189 Jul 13 13:04 post-update.sample
-rwxr-xr-x   1 mk  staff   424 Jul 13 13:04 pre-applypatch.sample
-rwxr-xr-x   1 mk  staff   382 Dec  1 14:46 pre-commit
-rwxr-xr-x   1 mk  staff  1638 Jul 13 13:04 pre-commit.sample
-rwxr-xr-x   1 mk  staff   416 Jul 13 13:04 pre-merge-commit.sample
-rwxr-xr-x   1 mk  staff  1348 Jul 13 13:04 pre-push.sample
-rwxr-xr-x   1 mk  staff  4898 Jul 13 13:04 pre-rebase.sample
-rwxr-xr-x   1 mk  staff   544 Jul 13 13:04 pre-receive.sample
-rwxr-xr-x   1 mk  staff  1492 Jul 13 13:04 prepare-commit-msg.sample
-rwxr-xr-x   1 mk  staff  3610 Jul 13 13:04 update.sample&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Создадим скрипт, который будет запускать проверки каждый раз перед попыткой создать коммит.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;vim .git/hooks/pre-commit&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Добавляем внутрь файла:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;#!/bin/sh
export PATH=/usr/local/bin:$PATH
# Если вы используете docker-compose (рекомендуется), то добавьте
export COMPOSE_INTERACTIVE_NO_CLI=1

if [ -t 1 ]; then
    # If we're in a terminal, redirect stdout and stderr to /dev/tty and
    # read stdin from /dev/tty. Allow interactive mode for CaptainHook.
    exec &amp;gt;/dev/tty 2&amp;gt;/dev/tty &amp;lt;/dev/tty
fi

errors=0

composer code-quality
# или, если вы используете docker-compose (рекомендуется), то
docker-compose exec -T web php bin/composer code-quality

if [ &amp;quot;$?&amp;quot; -ne 0 ]; then
    errors=1
fi

if [ &amp;quot;$errors&amp;quot; -eq 1 ]; then
    echo &amp;quot;Errors detected!&amp;quot;
    exit 1
fi&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь можете попробовать сделать ошибку в .php файле и создать коммит. Git должен ругнуться:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://maxkuznetsov.ru/pictures/Screen-Shot-2020-12-01-at-17.04.25.png" width="1344" height="826" alt="" /&gt;
&lt;/div&gt;
&lt;h2&gt;Устанавливаем git-hook автоматически&lt;/h2&gt;
&lt;p&gt;Финальный шаг — добавить git-hook в .git/hooks/ папку при запуске composer. Тогда ни один разработчик не сможет пропустить запуск проверок по невнимательности. Для этого добавим в composer.json такую строку:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;{
    ...,
    &amp;quot;scripts&amp;quot;: {
        &amp;quot;post-autoload-dump&amp;quot;: &amp;quot;mkdir -p .git/hooks &amp;amp;&amp;amp; cp config/pre-commit .git/hooks/pre-commit&amp;quot;,
        ...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Итог&lt;/h2&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Мы добавили в проект PHPCsFixer для код-стайла и PHPStan для поиска ошибок и опечаток.&lt;/li&gt;
&lt;li&gt;Запускаем их при каждой попытке создать коммит.&lt;/li&gt;
&lt;li&gt;И всё это начинает работать автоматически после первого же &lt;tt&gt;composer install&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
</description>
</item>

<item>
<title>Как подружить docker, localhost и HTTPS</title>
<guid isPermaLink="false">34</guid>
<link>https://maxkuznetsov.ru/all/docker-localhost-and-https/</link>
<pubDate>Tue, 26 May 2020 22:29:08 +0300</pubDate>
<author>Максим Кузнецов</author>
<comments>https://maxkuznetsov.ru/all/docker-localhost-and-https/</comments>
<description>
&lt;p&gt;Когда разрабатываешь веб-сервис, который крутится на проде по HTTPS, есть большой соблазн не запариваться и работать локально по HTTP. Потому что так проще, да и в целом же всё работает плюс-минус одинаково. Если сервис простой, то скорее всего никаких проблем не всплывёт, но если сервис не монолитный и зависит от внешних сервисов или сам предоставляет АПИ клиентам, то проблемы с CORS неизбежны. И обычно эти проблемы обнаруживаются слишком поздно — либо на стейджинге, либо даже после релиза на продакшен.&lt;/p&gt;
&lt;p&gt;Браузер начинает ругаться, что запросы с HTTPS на HTTP или наоборот не безопасны, сторонние сервисы перестают отвечать, так как по HTTP работали только тестовые стенды, с которыми вы и работали.&lt;/p&gt;
&lt;p&gt;Решение простое — нужно не лениться и максимально приблизить локальную среду к боевой. Благо это задача из серии один раз сделал и используешь для всех последующих проектов.&lt;/p&gt;
&lt;h2&gt;Docker на продакшене&lt;/h2&gt;
&lt;p class="remark"&gt;
Если у вас продакшен без Docker, то вам поможет статья — &lt;a href="https://maxkuznetsov.ru/all/nginx-free-ssl/"&gt;Как получить бесплатный SSL-сертификат и установить его на Nginx&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;В этой статье я рассматриваю только локальную сборку через docker-compose, потому что на продакшене, как ни странно, это немного проще. Достаточно выполнить понятную инструкцию &lt;a href="https://certbot.eff.org/lets-encrypt/ubuntuxenial-nginx"&gt;по настройке Certbot&lt;/a&gt; и/или добавить соответствующий  &lt;a href="https://hub.docker.com/r/certbot/certbot/"&gt;докер-образ с Docker Hub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Docker на localhost&lt;/h2&gt;
&lt;p class="remark"&gt;См. как запустить сайт через docker-compose на примере &lt;a href="https://maxkuznetsov.ru/all/egeya-docker/"&gt;докеризации Эгеи&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;На локалхосте Certbot не подойдёт, так как ему нужно реально существующее доменное имя и доступный через интернет сайт. Поэтому прописать локальный адрес в /etc/hosts не поможет. Решение — выпустить свой самоподписанный сертификат. Да, он не будет приниматься никем, кроме нашей системы, но для локальной разработки этого и не нужно.&lt;/p&gt;
&lt;h3&gt;Создаём доменное имя&lt;/h3&gt;
&lt;p&gt;Во-первых, создадим локально доменное имя, чтобы оно было похожим на боевое, а не обычный и скучный localhost. Например, docker.loc для основного сайта и api.docker.loc для API:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;$ sudo vim /etc/hosts
127.0.0.1       localhost
127.0.0.1       docker.loc
127.0.0.1       api.docker.loc
...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Если для вашего сервиса необходимо несколько доменов, то пропишите их ниже по аналогии.&lt;/p&gt;
&lt;h3&gt;Выпускаем свой SSL-сертификат&lt;/h3&gt;
&lt;p&gt;Для реальных сайтов SSL-сертификаты выдаются специальными сертификационными центрами за деньги. За это они потом могут подтвердить верность домена по запросу любого браузера. Мы же выпустим свой сертификат и сами его подтвердим.&lt;/p&gt;
&lt;p&gt;SSL-сертификат создаётся одной командой и состоит из двух частей: публичного сертификата и секретного ключа.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="bash"&gt;openssl req -x509 -out docker.loc.crt -keyout docker.loc.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=docker.loc' -extensions EXT -config &amp;lt;( \
   printf &amp;quot;[dn]\nCN=docker.loc\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:docker.loc\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth&amp;quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Файлы docker.loc.crt и docker.loc.key будут сохранены в той папке, откуда вы запускаете команду.&lt;/p&gt;
&lt;h3&gt;Добавляем сертификаты в ОС&lt;/h3&gt;
&lt;p&gt;Ниже пример, как это сделать на Mac OS X.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Найдите сертификат docker.loc.crt в Finder (или можно в нужной папке консоли ввести «open .»)&lt;/li&gt;
&lt;li&gt;Дважды кликните по нему, появится окно.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://maxkuznetsov.ru/pictures/Screen-Shot-2020-05-26-at-22.04.11.png" width="1294" height="834" alt="" /&gt;
&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Введите пароль администратора&lt;/li&gt;
&lt;li&gt;Теперь нужно найти этот сертификат в Keychain Access и выбрать Trust Always&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://maxkuznetsov.ru/pictures/Screen-Shot-2020-05-26-at-22.09.28.png" width="1870" height="1386" alt="" /&gt;
&lt;/div&gt;
&lt;h3&gt;Добавляем сертификаты в docker-compose сборку&lt;/h3&gt;
&lt;p&gt;Сертификаты нужно положить в контейнер с веб-сервером. Я приведу пример для Nginx и сборки по типу &lt;a href="https://github.com/pluseg/e2-docker."&gt;https://github.com/pluseg/e2-docker.&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Расположение сертификатов и конфигов&lt;/h3&gt;
&lt;p&gt;В этой сборке папки из app/docker/nginx монтируются прямо в nginx-контейнер. Поэтому удобно расположить файлы так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;project root&amp;gt;
- app
--- docker
----- nginx
------- conf.d
--------- docker.loc.conf
------- ssl
--------- docker.loc.crt
--------- docker.loc.key&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Пример docker.loc.conf (проект на PHP + Symfony)&lt;/h3&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="yaml"&gt;# лучше с php соединять через socket
upstream php-upstream { server web:9000; }

# Редиректим HTTP на HTTPS
server {
       listen         80;
       listen    [::]:80;
       server_name    docker.loc;
       return         301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    root /var/www/html/docs;

    server_name            docker.loc;
    ssl_certificate          /etc/nginx/ssl/docker.loc.crt;
    ssl_certificate_key   /etc/nginx/ssl/docker.loc.key;
    ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

    location / {
    	try_files $uri /index.php$is_args$args;
    }

    location ~ ^/(index|app|app_dev|config)\.php(/|$) {
        fastcgi_pass php-upstream;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        fastcgi_read_timeout 300;
    }

    error_log /var/log/nginx/docker.loc-error.log;
    access_log /var/log/nginx/docker.loc-access.log;
}&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Пример docker-compose.yml&lt;/h3&gt;
&lt;p&gt;Добавляем 443 порт и несколько volume с конфигами и сертификатами.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="yaml"&gt;version: '3.2'
services:
    ...
    nginx:
        image: nginx:latest
        ports:
            - 80:80
            - 443:443
        volumes:
            - ./app/docker/nginx/conf.d:/etc/nginx/conf.d
            - ./app/docker/nginx/ssl:/etc/nginx/ssl
            - ./:/var/www/html
            - ./var/logs/nginx:/var/log/nginx
        command: /bin/bash -c &amp;quot;exec nginx -g 'daemon off;'&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Запускаем и проверяем&lt;/h3&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;docker-compose up -d --build&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Как получить бесплатный SSL-сертификат и установить его на Nginx</title>
<guid isPermaLink="false">23</guid>
<link>https://maxkuznetsov.ru/all/nginx-free-ssl/</link>
<pubDate>Fri, 10 Apr 2020 08:39:24 +0300</pubDate>
<author>Максим Кузнецов</author>
<comments>https://maxkuznetsov.ru/all/nginx-free-ssl/</comments>
<description>
&lt;p&gt;Иметь сайт без HTTPS — это небезопасно. Особенно критично для интернет-магазинов и сервисов, где пользователь оставляет свои данные.&lt;/p&gt;
&lt;p&gt;HTTPS — это защищённый брат HTTP (см. статью &lt;a href="https://maxkuznetsov.ru/all/http-basics/"&gt;Что такое HTTP&lt;/a&gt;), который шифрует все пересылаемые от браузера к серверу и обратно данные. Для такого шифрования необходим SSL-сертификат, который позволяет убедиться пользователю, что он видит страницы именно того сайта, на  который он зашёл. Сертификаты бывают разного вида и стоимости в зависимости от уровня проверки владельца сайта, но при этом суть остаётся та же — шифрование пользовательских данных.&lt;/p&gt;
&lt;p&gt;SSL-сертификат нужно получить в центрах сертификации (ЦС). Часто регистраторы доменов и провайдеры хостинга партнёрятся с такими центрами и облегачают задачу выпуска и установки сертификата на ваш сайт.&lt;/p&gt;
&lt;p&gt;За выпуск сертификата ЦС берут плату, ведь им нужно по крайней мере удостовериться, что вы действительно владеете доменом, на который запрашиваете сертификат. У них такой бизнес. Можно выпустить и свой самоподписанный сертификат бесплатно, но браузеры не смогут ему доверять (ведь они не смогут уточнить его оригинальность у независимого ЦС) и  будут показывать предупреждение.&lt;/p&gt;
&lt;h3&gt;Как получить SSL-сертификат бесплатно&lt;/h3&gt;
&lt;p&gt;К счастью, есть ЦС, который выпускает SSL-сертификаты бесплатно, — это &lt;a href="https://letsencrypt.org/ru/about/"&gt;LetsEncrypt&lt;/a&gt;. Они это делают во имя благой цели — безопасного интернета во всём мире. Такой сертификат подойдёт для большинства небольших сайтов и сервисов, но среднему и крупном бизнесу — нет. Им нужны сертификаты &lt;a href="https://www.globalsign.com/ru-ru/ssl-information-center/types-of-ssl-certificate/"&gt;с частичной или полной проверкой компании&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;LetsEncrypt позволяет вам выпустить сертификат &lt;a href="https://letsencrypt.org/ru/docs/client-options/"&gt;множеством способов&lt;/a&gt; для разных операционных систем, веб-серверов, языков программирования. Cам центр сертификации крайне рекомендует использовать &lt;a href="https://certbot.eff.org/"&gt;Certbot&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Пример установки сертификатов с помощью Certbot на Ubuntu&lt;/h3&gt;
&lt;p&gt;Предположим, у нас есть сайты yoursite.ru и yourblog.ru, работающие по HTTP через Nginx. Добавим для них сертификаты и перейдём на HTTPS.&lt;/p&gt;
&lt;p&gt;Заходим на сайт &lt;a href="https://certbot.eff.org/"&gt;Certbot’a&lt;/a&gt; и выбираем Nginx и Ubuntu.&lt;/p&gt;
&lt;p&gt;Добавляем ppa для установки certbot на нашем сервере, обновляем зависимости и устанавливаем саму утилиту.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="terminal"&gt;$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository universe
$ sudo add-apt-repository ppa:certbot/certbot

# крайне важно выполнить эту команду, иначе следующая команда выдаст ошибку о незнакомом пакете.
$ sudo apt-get update

# Устанавливаем сам certbot
$ sudo apt-get install certbot python-certbot-nginx&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Запускаем certbot и проходим через визард, отвечая на простые вопросы.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="terminal"&gt;$ certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: yourblog.ru
2: yoursite.ru
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Указываем через запятую или пробел номера сайтов из списка, для которых хотим выпустить сертификат, — в нашем случае «1,2» — жмём enter.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="bash"&gt;Obtaining a new certificate
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/yourblog.ru
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/yoursite.ru

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Certbot настолько заботлив, что предлагает настроить принудительный редирект HTTP-&gt;HTTPS за нас. Соглашаемся, введя «2».&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="bash"&gt;Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/yourblog.ru
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/yoursite.ru

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://yourblog.ru and
https://yoursite.ru&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Обновление letsencrypt-сертификата&lt;/h3&gt;
&lt;p&gt;Нужно иметь в виду, что такой SSL-сертификат действителен только на 30 дней и через месяц его нужно обновить, снова подтвердив свои данные.&lt;/p&gt;
&lt;p&gt;Но сюрприз! Certbot уже добавил регулярную задачу в systemd или cron и обновит сертификат сам, нам ничего больше делать не нужно.&lt;/p&gt;
&lt;p&gt;—-&lt;br /&gt;
Для других систем и веб-серверов установка будет слегка отличаться, поэтому обратитесь к &lt;a href="https://certbot.eff.org/lets-encrypt/ubuntuxenial-nginx"&gt;официальному сайту certbot’a&lt;/a&gt;.&lt;/p&gt;
</description>
</item>


</channel>
</rss>