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!
These amazing companies help us create free, high-quality DevOps content for the community
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
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorFound an issue?