Как передать переменные окружения GitHub Actions Secrets на удаленный сервер для docker-compose?

Иногда самые сложные вопросы имеют самые легкие ответы. Когда мы работаем на локальной машине, нам проще всего взять наши переменные окружения (environment variables) из файла .env при работе с docker-compose. Но ведь в таком виде секреты передавать в репозиторий нельзя! Значит, мы в лучшем случае запишем туда .env.template, где перечислим переменные и, возможно, дадим им некоторые значения по умолчанию для примера.

Очевидно, что сами секреты мы заведем в настройках репозитория, в разделе GitHub Secrets:

Но как их дальше передать на сервер? Первое, что хочется сделать — сформировать на сервере .env файл неким скриптом, но есть путь гораздо проще и чище, давайте используем стандартную команду export, которая в командной среде может присваивать переменную окружения и хранить ее прямо в системе, без каких-либо файлов! Добавим GitHub Action следующего вида:

    runs-on: ubuntu-latest
    needs: set_up_env
    steps:
      - name: executing remote ssh commands to set env
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            export DB_ENGINE=${{ secrets.DB_ENGINE }}
            export DB_NAME=${{ secrets.DB_NAME }}
            export POSTGRES_USER=${{ secrets.POSTGRES_USER }}
            export POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}
            export DB_HOST=${{ secrets.DB_HOST }}
            export DB_PORT=${{ secrets.DB_PORT }}
            export SECRET_KEY=${{ secrets.SECRET_KEY }}
            export DJANGO_SUPERUSER_PASSWORD=${{ secrets.DJANGO_SUPERUSER_PASSWORD }}
            export DJANGO_SUPERUSER_EMAIL=${{ secrets.DJANGO_SUPERUSER_EMAIL }}
            export DJANGO_SUPERUSER_USERNAME=${{ secrets.DJANGO_SUPERUSER_USERNAME }}

Также можно на лету генерировать .env файл, если это то, к чему вы больше привыкли:

            echo DB_ENGINE=${{ secrets.DB_ENGINE }} > .env
            echo DB_NAME=${{ secrets.DB_NAME }} >> .env
            echo POSTGRES_USER=${{ secrets.POSTGRES_USER }} >> .env
            echo POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} >> .env
            echo DB_HOST=${{ secrets.DB_HOST }} >> .env
            echo DB_PORT=${{ secrets.DB_PORT }} >> .env
            echo SECRET_KEY=${{ secrets.SECRET_KEY }} >> .env
            echo DJANGO_SUPERUSER_PASSWORD=${{ secrets.DJANGO_SUPERUSER_PASSWORD }} >> .env
            echo DJANGO_SUPERUSER_EMAIL=${{ secrets.DJANGO_SUPERUSER_EMAIL }} >> .env
            echo DJANGO_SUPERUSER_USERNAME=${{ secrets.DJANGO_SUPERUSER_USERNAME }} >> .env

Возможно, здесь много повторяющего кода, и хотелось бы записать это одной строкой с одним экспортом, но так, на мой взгляд, проще и понятнее. А также небольшая формула в экселе позволяет это довольно быстро сгенерировать:

Теперь все переменные будут автоматически подставляться в docker-compose.

env_file:
      - ./.env

Которая, как известно, сканирует файл .env в поиске переменных. А все можно прочитать в документации:

When you set the same environment variable in multiple files, here’s the priority used by Compose to choose which value to use:

1) Compose file
2) Shell environment variables
3) Environment file
4) Dockerfile
5) Variable is not defined

Сначала env файл, а затем уже переменные Shell. Это позволяет нам на компьютере для тестирования использовать один набор переменных, а для деплоя — другой.