Automation With Github Actions automation has completely transformed how I handle deployments. Back in the old days (which wasn’t actually that long ago), we relied on clunky webhooks on github and custom scripts to get our code from repository to production. While those methods worked, but they were absolutely painful to maintain.
In this Github Actions tutorial, I’m going to show you EXACTLY how to create powerful, flexible deployment workflows that will save you countless hours of manual work. Trust me, once you start automating your deployments, you’ll never want to go back to the old ways.
GitHub Actions is a CI/CD (Continuous Integration/Continuous Deployment) platform built directly into GitHub. It lets you automate your build, test, and deployment pipeline without needing to set up any external systems.
Unlike the old webhook approach that required maintaining separate build scripts and servers, GitHub Actions handles everything within the GitHub ecosystem. This means faster setup, better integration, and less headache.
The best part? Every GitHub repository comes with free build minutes each month – 2,000 minutes for public repositories and 3,000 minutes for private repositories on the free plan. That’s seriously generous, especially for smaller projects.
Tip 💡: level up your productivity with our git commands cheat sheet!
Before diving into the how-to, let’s talk about why you absolutely need to be using GitHub Actions:
Honestly, the benefits are so substantial that I can’t imagine working without automated workflows anymore.
Setting up your first GitHub Action workflow is incredibly straightforward. Let’s break it down step by step:
GitHub Actions workflows are defined in YAML files stored in the .github/workflows directory of your repository. Let’s create a simple deployment workflow:
.github/workflows if it doesn’t already existdeploy.yml in that directoryHere’s a basic workflow to get you started:
name: Deploy Application
on:
push:
branches: [ main ]
workflow_dispatch: # Allows manual triggering
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm install
- name: Build application
run: npm run build
- name: Deploy to server
uses: easingthemes/ssh-deploy@v4.1.8
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
SOURCE: "dist/"
TARGET: "/var/www/html/"Code language: PHP (php) This workflow will trigger whenever someone pushes to the main branch, checkout the code, set up Node.js, install dependencies, build the application, and then deploy it to your server using SSH.
For the deployment step, we need to securely store our SSH credentials. GitHub provides a secure way to store sensitive information using Secrets:
SSH_PRIVATE_KEY, REMOTE_HOST, and REMOTE_USERThese secrets are encrypted and only exposed to the GitHub Actions workflow during run time. They’ll never be visible in logs or to people who don’t have admin access to your repository.
Commit your workflow file and push it to your repository. This will automatically trigger the workflow if you’re pushing to the main branch.
Let’s break down the key components of a GitHub Actions workflow:
The on section defines when your workflow should run:
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * *' # Run daily at midnight
workflow_dispatch: # Manual triggerCode language: PHP (php) You can trigger workflows on pushes, pull requests, on a schedule using cron syntax, or manually using the workflow_dispatch event.
Workflows are made up of one or more jobs that can run in parallel or sequentially:
jobs:
test:
runs-on: ubuntu-latest
steps:
# Test steps here
build:
needs: test # This job will only run after 'test' completes
runs-on: windows-latest
steps:
# Build steps hereCode language: PHP (php) The needs keyword creates dependencies between jobs, ensuring they run in the right order.
The runs-on property specifies the type of machine to run the job on. GitHub provides runners for Linux, Windows, and macOS:
ubuntu-latest (Linux)windows-latest (Windows)macos-latest (macOS)You can also set up self-hosted runners if you need specialized environments.
Each job consists of a sequence of steps:
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Run a command
run: echo "Hello, World!"
- name: Run a script
run: |
echo "This is a multi-line script"
./my-script.sh
echo "Script completed"Code language: PHP (php) Steps can either use pre-built actions (with the uses keyword) or run commands directly (with the run keyword).
Once you’re comfortable with the basics, you can leverage these advanced features:
You can define environment variables at the workflow, job, or step level:
env:
GLOBAL_VAR: "Available to all jobs"
jobs:
build:
env:
JOB_VAR: "Available to all steps in this job"
steps:
- name: Step with env vars
env:
STEP_VAR: "Available only in this step"
run: echo "$GLOBAL_VAR $JOB_VAR $STEP_VAR"Code language: JavaScript (javascript) Matrix builds let you test across multiple configurations simultaneously:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm testCode language: PHP (php) This would run your tests against Node.js 12, 14, and 16 on both Ubuntu and Windows – a total of 6 different combinations.
To speed up your workflows, cache dependencies between runs:
steps:
- uses: actions/checkout@v3
- name: Cache npm dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-
- name: Install dependencies
run: npm installCode language: PHP (php) This can dramatically reduce build times for subsequent runs.
One of the most powerful features of GitHub Actions is the ability to deploy to different environments based on branch or event.
Here’s how to set up a workflow for deploying to both staging and production:
name: Deploy Pipeline
on:
push:
branches: [develop, main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup application
run: |
npm install
npm run build
- name: Deploy to Staging
if: github.ref == 'refs/heads/develop'
uses: easingthemes/ssh-deploy@v4.1.8
with:
SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_KEY }}
REMOTE_HOST: ${{ secrets.STAGING_HOST }}
REMOTE_USER: ${{ secrets.STAGING_USER }}
SOURCE: "dist/"
TARGET: "/var/www/staging/"
- name: Deploy to Production
if: github.ref == 'refs/heads/main'
uses: easingthemes/ssh-deploy@v4.1.8
with:
SSH_PRIVATE_KEY: ${{ secrets.PRODUCTION_SSH_KEY }}
REMOTE_HOST: ${{ secrets.PRODUCTION_HOST }}
REMOTE_USER: ${{ secrets.PRODUCTION_USER }}
SOURCE: "dist/"
TARGET: "/var/www/production/"Code language: JavaScript (javascript) In this workflow, pushing to the develop branch deploys to staging, while pushing to main deploys to production. The if conditions ensure each deployment step only runs for the appropriate branch.
A robust CI/CD pipeline should include testing to prevent deploying broken code. Here’s how to add testing to your workflow:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
deploy:
needs: test # Only deploy if tests pass
runs-on: ubuntu-latest
steps:
# Deployment steps hereCode language: PHP (php) By adding the needs: test line, the deployment job will only run if the test job completes successfully.
Even with automation, things sometimes go wrong. Here are some common issues and how to fix them:
If your deployment fails, first check the workflow logs for error messages. Common problems include:
For complex debugging, you can set up SSH access to the runner:
steps:
- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3 This creates an SSH session to the runner if a previous step fails, allowing you to investigate interactively.
To make the most out of GitHub Actions, follow these best practices:
timeout-minutes to prevent stuck jobs from running foreverconcurrency settingsenvironment configurationsGitHub Actions has completely revolutionized how we approach deployment automation. With its tight integration into GitHub, extensive marketplace of pre-built actions, and powerful workflow capabilities, it’s now easier than ever to create robust CI/CD pipelines.
I’ve personally migrated all my projects from the old webhook-based approach to GitHub Actions, and the difference is night and day. Deployments are now consistent, reliable, and completely hands-off.
If you’re still using manual deployments or older automation tools, I strongly encourage you to give GitHub Actions a try. The time savings alone make it worth the initial setup investment, not to mention the peace of mind that comes from knowing your deployments are handled automatically.
Have you implemented GitHub Actions in your workflow? Let me know about your experiences in the comments below!
Tired of repetitive tasks eating up your time? Python can help you automate the boring stuff — from organizing files to scraping websites and sending…
Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…
You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…
This website uses cookies.