Day 3 - Add GitHub Actions CI
Set up continuous integration for a repository without automation. Create a GitHub Actions workflow that runs tests on every push.
Description
Your team has been manually testing code before merging pull requests. This is slow, error-prone, and doesn't scale. You need to set up automated testing that runs on every push.
Task
Add a GitHub Actions workflow that runs tests on every push.
Requirements:
- Trigger on push and pull request events
- Run tests for the application
- Report test results
- Fail the build if tests fail
Target
- ✅ Workflow runs automatically on push
- ✅ All tests execute successfully
- ✅ Clear pass/fail status in GitHub UI
- ✅ Runs in under 2 minutes
Sample App
Simple Node.js App (app.test.js)
// app.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = { add, multiply };
// app.test.js
const { add, multiply } = require('./app');
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3);
});
test('multiplies 2 * 3 to equal 6', () => {
expect(multiply(2, 3)).toBe(6);
});
package.json
{
"name": "advent-ci",
"version": "1.0.0",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^29.0.0"
}
}
View Solution
Solution
GitHub Actions Workflow (.github/workflows/ci.yml)
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Upload coverage reports
uses: codecov/codecov-action@v3
if: matrix.node-version == '20.x'
with:
files: ./coverage/coverage-final.json
fail_ci_if_error: false
lint:
name: Lint Code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
continue-on-error: false
Explanation
Workflow Breakdown
1. Trigger Events
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
- Runs on pushes to main/develop
- Runs on PRs targeting these branches
- Can add more triggers as needed
2. Matrix Strategy
strategy:
matrix:
node-version: [18.x, 20.x]
- Tests against multiple Node.js versions
- Ensures compatibility
- Jobs run in parallel
3. Key Steps
Checkout Code
- uses: actions/checkout@v4
- Fetches repository code
- Required as first step
Setup Node.js
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- Installs specified Node version
- Caches npm dependencies
- Speeds up subsequent runs
Install & Test
- run: npm ci
- run: npm test
npm cifor clean, deterministic installs- Runs test suite
Best Practices Applied
- Use npm ci instead of npm install: Faster, more reliable
- Cache Dependencies: Speeds up workflow significantly
- Matrix Testing: Test multiple versions
- Separate Jobs: Lint and test independently
- Descriptive Names: Clear job and step names
Try to solve the challenge yourself first!
Click "Reveal Solution" when you're ready to see the answer.
Result
After setup, you'll see:
- ✅ Green checkmark on passing commits
- ✅ Red X on failing tests
- ✅ Test results in PR checks
- ✅ Automatic testing on every push
GitHub UI Shows:
✓ CI / Run Tests (18.x) — 1m 23s
✓ CI / Run Tests (20.x) — 1m 19s
✓ CI / Lint Code — 45s
Validation
Test the Workflow
# Clone your repository
git clone <your-repo>
cd <your-repo>
# Make a change
echo "test('example', () => expect(true).toBe(true));" >> app.test.js
# Commit and push
git add .
git commit -m "test: add example test"
git push origin main
# Watch workflow run in GitHub Actions tab
Check Workflow Status
# Using GitHub CLI
gh run list --workflow=ci.yml
# View latest run
gh run view --log
Advanced Features
Add Code Coverage
- name: Generate coverage
run: npm test -- --coverage
- name: Upload to Codecov
uses: codecov/codecov-action@v3
Add Build Caching
- name: Cache build output
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Conditional Steps
- name: Deploy
if: github.ref == 'refs/heads/main' && success()
run: npm run deploy
Links
- GitHub Actions Documentation
- Workflow Syntax
- GitHub Actions Marketplace
- actions/checkout
- actions/setup-node
Share Your Success
Got your CI pipeline running? Share it!
Tag @thedevopsdaily on X with:
- Screenshot of your passing workflow
- Number of tests running
- Workflow execution time
Use hashtags: #AdventOfDevOps #GitHubActions #CI #Day3
Ready to complete this challenge?
Mark this challenge as complete once you've finished the task. We'll track your progress!
Completed this challenge? Share your success!
Tag @thedevopsdaily on X (Twitter) and share your learning journey with the community!
We earn commissions when you shop through the links below.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
Acronis
The most secure backup
Acronis: the most secure backup solution for your data
Pluralsight
Technology skills platform
Expert-led courses in software development, IT ops, data, and cybersecurity
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?