GitHub Actions CI/CD: Automate Your Development Workflow
Master GitHub Actions for CI/CD pipelines. Learn workflows, jobs, actions, secrets management, and deploy to any cloud platform automatically.
Moshiour Rahman
Advertisement
What is GitHub Actions?
GitHub Actions is a CI/CD platform that automates build, test, and deployment pipelines directly from your GitHub repository. It’s event-driven, meaning workflows trigger on repository events.
Key Concepts
| Concept | Description |
|---|---|
| Workflow | Automated process defined in YAML |
| Job | Set of steps running on same runner |
| Step | Individual task in a job |
| Action | Reusable unit of code |
| Runner | Server that runs workflows |
Getting Started
Basic Workflow
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
Workflow Triggers
on:
# Push events
push:
branches:
- main
- 'releases/**'
tags:
- 'v*'
paths:
- 'src/**'
- '!src/**/*.md'
# Pull request events
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
# Scheduled runs
schedule:
- cron: '0 0 * * *' # Daily at midnight
# Manual trigger
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
# Repository dispatch (API trigger)
repository_dispatch:
types: [deploy]
Jobs and Steps
Multiple Jobs
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run lint
build:
needs: [test, lint] # Wait for both jobs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
deploy:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: echo "Deploying..."
Matrix Strategy
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
exclude:
- os: macos-latest
node: 18
include:
- os: ubuntu-latest
node: 20
experimental: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm test
Job Outputs
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.value }}
steps:
- id: version
run: echo "value=$(cat package.json | jq -r .version)" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Deploying version ${{ needs.build.outputs.version }}"
Environment and Secrets
Using Secrets
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
curl -X POST https://api.example.com/deploy \
-H "Authorization: Bearer $API_KEY"
Environment Variables
env:
NODE_ENV: production
CI: true
jobs:
build:
runs-on: ubuntu-latest
env:
BUILD_PATH: ./dist
steps:
- name: Build
env:
API_URL: https://api.example.com
run: npm run build
Environments
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- run: echo "Deploying to staging"
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- run: echo "Deploying to production"
Caching and Artifacts
Caching Dependencies
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- run: npm run build
Upload/Download Artifacts
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build
path: dist/
retention-days: 7
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build
path: dist/
- run: ls -la dist/
Complete CI/CD Examples
Node.js Application
name: Node.js CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test
env:
DATABASE_URL: postgres://test:test@localhost:5432/testdb
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy to server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
source: "dist/*"
target: "/var/www/app"
Docker Build and Push
name: Docker Build
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
myuser/myapp
ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Deploy to AWS
name: Deploy to AWS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push to ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: my-app
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: my-service
cluster: my-cluster
wait-for-service-stability: true
Deploy to Kubernetes
name: Deploy to Kubernetes
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Configure kubectl
run: |
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig
echo "KUBECONFIG=$(pwd)/kubeconfig" >> $GITHUB_ENV
- name: Deploy to cluster
run: |
kubectl apply -f k8s/
kubectl rollout status deployment/my-app
Reusable Workflows
Create Reusable Workflow
# .github/workflows/reusable-build.yml
name: Reusable Build
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '20'
environment:
required: true
type: string
secrets:
npm-token:
required: true
outputs:
artifact-name:
description: "Name of the uploaded artifact"
value: ${{ jobs.build.outputs.artifact-name }}
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact-name: ${{ steps.artifact.outputs.name }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
registry-url: 'https://registry.npmjs.org'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.npm-token }}
- run: npm run build
- id: artifact
run: echo "name=build-${{ inputs.environment }}" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: build-${{ inputs.environment }}
path: dist/
Use Reusable Workflow
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
build-staging:
uses: ./.github/workflows/reusable-build.yml
with:
environment: staging
secrets:
npm-token: ${{ secrets.NPM_TOKEN }}
build-production:
uses: ./.github/workflows/reusable-build.yml
with:
environment: production
secrets:
npm-token: ${{ secrets.NPM_TOKEN }}
Custom Actions
JavaScript Action
# action.yml
name: 'Hello World'
description: 'Greet someone'
inputs:
who-to-greet:
description: 'Who to greet'
required: true
default: 'World'
outputs:
time:
description: 'The greeting time'
runs:
using: 'node20'
main: 'dist/index.js'
// index.js
const core = require('@actions/core');
try {
const name = core.getInput('who-to-greet');
console.log(`Hello ${name}!`);
const time = new Date().toTimeString();
core.setOutput('time', time);
} catch (error) {
core.setFailed(error.message);
}
Composite Action
# action.yml
name: 'Setup and Test'
description: 'Setup Node.js and run tests'
inputs:
node-version:
description: 'Node.js version'
default: '20'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
shell: bash
- run: npm test
shell: bash
Summary
| Feature | Purpose |
|---|---|
on | Define triggers |
jobs | Parallel execution units |
steps | Sequential tasks |
needs | Job dependencies |
matrix | Multiple configurations |
secrets | Secure credentials |
artifacts | Share build outputs |
cache | Speed up builds |
GitHub Actions provides powerful, flexible CI/CD directly integrated with your repository.
Advertisement
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
GitHub Actions CI/CD Pipeline: Complete Tutorial
Build automated CI/CD pipelines with GitHub Actions. Learn workflows, jobs, actions, and deploy applications automatically with practical examples.
DevOpsKubernetes Helm Charts: Package and Deploy Applications
Master Helm for Kubernetes deployments. Learn chart creation, templates, values, dependencies, and best practices for production applications.
DevOpsArgoCD: GitOps Continuous Delivery for Kubernetes
Master ArgoCD for Kubernetes deployments. Learn GitOps principles, application management, sync strategies, and automate your deployment pipeline.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.