Push deploy a Laravel app for free with GitHub Actions
https://ift.tt/2A4dhDC
Push deploy a Laravel app for free with GitHub Actions
For many teams, it makes sense to use services like Ploi or Forge. They provision servers for you, configure push deploys, deal with backups, and many other things.
You can also use a Platform-as-a-Service like Heroku. PaaS is close to serverless in the sense that you don’t think about servers, but the primary difference between PaaS and serverless is that serverless scales automatically, whereas PaaS merely hides the fact that there are servers to deal with. And — speaking of serverless — you can, of course, use a service like Laravel Vapor and get push deploys.
However, if configuring servers is something you’re used to, you might be interested only in the push to deploy part of these services. And as such, you might not want to pay for the services above only to use a single feature.
Luckily, configuring push deploys is super easy to do yourself — and free! — if you’re using GitHub. Specifically, we’re going to be using GitHub Actions.
Prerequisites
This article assumes you know how to configure webservers, and as such it will only guide you through configuring Continuous Deployment part — not the actual webservers.
- A configured webserver running PHP
- A GitHub repository
The git setup
- Some
public/
assets are in.gitignore
, but are built on GitHub master
branch is used for developmentproduction
is pushed and deployeddeploy
is created by the Action, by takingproduction
and adding a commit with the built assets
So the code flows like this: master --> production --> deploy
.
A few notes
We compile front-end assets inside the GitHub Actions — not your computer, nor your server. This means that you don’t have to run npm locally to deploy, and that the server’s downtime is as short as possible.
We run tests locally, not in the CI Action. The reason for this is to allow you to deploy hotfixes even if they break some tests. If you want to run tests inside the Action, then simply look up some GitHub Actions phpunit workflow and copy the necessary steps. You can use this example.
1. Server deployment script
Add this bash script to your repository, and name it server_deploy.sh
.
This script will be executed on the server to pull and deploy the code from GitHub.
#!/bin/sh set -e echo "Deploying application ..." # Enter maintenance mode (php artisan down --message 'The app is being (quickly!) updated. Please try again in a minute.') || true # Update codebase git fetch origin deploy git reset --hard origin/deploy # Install dependencies based on lock file composer install --no-interaction --prefer-dist --optimize-autoloader # Migrate database php artisan migrate --force # Note: If you're using queue workers, this is the place to restart them. # ... # Clear cache php artisan optimize # Reload PHP to update opcache echo "" | sudo -S service php7.4-fpm reload # Exit maintenance mode php artisan up echo "Application deployed!"
The process explained:
- We’re putting the application into maintenance mode and showing a sensible message to the users.
- We’re fetching the
deploy
branch and hard resetting the local branch to the fetched version. - We’re updating composer dependencies based on the lock file. Make sure your
composer.lock
file is in your repository, and not part of your.gitignore
. It makes sure the production environment uses the exact same version of packages as your local environment. - We’re running database migrations.
- We’re updating Laravel & php-fpm caches. If you’re not using PHP 7.4, change the version in that command.
- We’re putting the server back up.
Note that the server is always on the deploy
branch. Also note that we’re putting the server down for the shortest duration possible — only for the composer install
, migrations and cache updating. The app needs to go down to avoid requests coming in when the codebase and database are not in sync — it would be irresponsible to simply run those commands without putting the server into maintenance mode first.
And a final note, the reason we’re wrapping the php artisan down
command in (...) || true
is that deployments sometimes go wrong. And the down command exits with 1
if the application is already down, which would make it impossible to deploy fixes after the previous deployment errored halfway through.
2. Local deployment script
This script is used in your local environment when you want to deploy to production. Ideally, if you work in a team, you’ll also have a CI Action running phpunit
as a safeguard for pull requests targeting the production
branch. For inspiration, see the link to the Action example in the How it works section above and make it run on pull_request
only.
Store the local deployment script as deploy.sh
:
#!/bin/sh set -e vendor/bin/phpunit (git push) || true git checkout production git merge master git push origin production git checkout master
This script is simpler. We’re running tests, pushing changes (if we have not pushed yet; it’s assumed we’re on master
), switching to production
, merging changes from master
and pushing production
. Then we switch back to master
.
3. The GitHub Action
Store this as .github/workflows/main.yml
name: CD on: push: branches: [ production ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: token: $ - name: Set up Node uses: actions/setup-node@v1 with: node-version: '12.x' - run: npm install - run: npm run production - name: Commit built assets run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git checkout -B deploy git add -f public/ git commit -m "Build front-end assets" git push -f origin deploy - name: Deploy to production uses: appleboy/ssh-action@master with: username: YOUR USERNAME GOES HERE host: YOUR SERVER'S HOSTNAME GOES HERE password: $ script: 'cd /var/www/html && ./server_deploy.sh'
Explained:
- We set up Node.
- We build the front-end assets.
- We force-checkout to
deploy
and commit the assets. Thedeploy
branch is temporary and only holds the code deployed on the server. It doesn’t have a linear history — the asset compilation is never part of theproduction
branch history — which is why we always have to force checkout that branch. - We force-push the
deploy
branch toorigin
. - We connect to the server via SSH and execute
server_deploy.sh
in the webserver root.
Note that you need to store two secrets in the repository:
- a Personal Access Token for a GitHub account with write access to the repository
- the SSH password
If you want to use SSH keys instead of usernames & passwords, see the documentation for the SSH action.
Usage
With all this set up, install your Laravel application into /var/www/html
and checkout the deploy
branch. If it doesn’t exist yet, you can do git checkout production && git checkout -b deploy
to create it.
For all subsequent deploys all you need to do is run this command from the master
branch in your local environment:
./deploy.sh
Or, you can merge into production
. But know that it will not run tests unless you configure the action for that, as mentioned above.
Performance and robustness
This approach is robust since it makes it impossible for a request to be processed when the codebase and database are out of sync — thanks to artisan down
.
And it’s also very fast, with the least amount of things happening on the server — only the necessary steps — which results in minimal downtime.
See how this action runs:
The Deploy to production step took only 13 seconds, and the period when the application was down is actually shorter than that — part of the 13 seconds is GitHub setting up the appleboy/ssh-action
action template (before actually touching your server). So usually, the application would be down for less than 10 seconds.
Filed in: News
programming
via Laravel News https://ift.tt/14pzU0d
May 22, 2020 at 12:23PM