CI/CD Pipeline
Continuous Integration and Continuous Deployment (CI/CD) automates your testing and deployment workflow. Learn how to set up a complete pipeline for your Amplify application.
CI/CD Overview
🔄 The CI/CD Flow
- Push code to your Git repository
- CI runs tests (lint, unit tests, type checks)
- Build if tests pass
- Deploy to the appropriate environment
- Notify team of success or failure
Built-in Amplify CI/CD
AWS Amplify Hosting includes basic CI/CD out of the box. When you connect your repository, Amplify automatically:
- Triggers builds on every push
- Runs your build commands
- Deploys to the CDN
- Provides build notifications
Adding Tests to the Build
Enhance the default pipeline by adding tests. Update your
amplify.yml build spec:
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
# Run linting
- npm run lint
# Run type checking
- npm run typecheck
# Run unit tests
- npm run test
# Build the application
- npm run build
artifacts:
baseDirectory: .amplify-hosting
files:
- '**/*'
cache:
paths:
- node_modules/**/*
- .nuxt/**/*
Add these scripts to your package.json:
{
"scripts": {
"dev": "nuxt dev",
"build": "nuxt build",
"lint": "eslint .",
"typecheck": "nuxt typecheck",
"test": "vitest run",
"test:watch": "vitest"
}
}
GitHub Actions Integration
For more control, use GitHub Actions alongside Amplify Hosting. Create
.github/workflows/ci.yml:
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
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: Lint
run: npm run lint
- name: Type check
run: npm run typecheck
- name: Run tests
run: npm run test
- name: Build
run: npm run build
GitHub Actions runs on every push, while Amplify Hosting deploys only when tests pass. This provides fast feedback and prevents broken deployments.
Pull Request Previews
Enable pull request previews to test changes before merging:
- Go to Amplify Console → Previews
- Click Enable previews
- Select which branches should generate previews
Each pull request gets a unique URL (e.g.,
pr-123.d1234567890abc.amplifyapp.com) for testing.
Environment-Based Pipelines
Set up different pipelines for different environments:
# .github/workflows/deploy-staging.yml
name: Deploy to Staging
on:
push:
branches: [develop]
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: Deploy to Staging
run: |
# Amplify automatically deploys when you push to connected branches
echo "Staging deployment triggered"
Testing Strategies
Unit Tests
Test individual functions and composables:
// tests/useTodos.test.ts
import { describe, it, expect, vi } from 'vitest';
describe('useTodos', () => {
it('should create a todo', async () => {
// Mock the Amplify client
vi.mock('aws-amplify/data', () => ({
generateClient: () => ({
models: {
Todo: {
create: vi.fn().mockResolvedValue({
data: { id: '1', content: 'Test', completed: false },
}),
},
},
}),
}));
const { createTodo } = useTodos();
const todo = await createTodo({ content: 'Test' });
expect(todo).toBeDefined();
expect(todo?.content).toBe('Test');
});
});
Component Tests
// tests/TodoItem.test.ts
import { mount } from '@vue/test-utils';
import TodoItem from '~/components/TodoItem.vue';
describe('TodoItem', () => {
it('renders todo content', () => {
const wrapper = mount(TodoItem, {
props: {
todo: {
id: '1',
content: 'Buy groceries',
completed: false,
},
},
});
expect(wrapper.text()).toContain('Buy groceries');
});
it('shows completed state', () => {
const wrapper = mount(TodoItem, {
props: {
todo: {
id: '1',
content: 'Done task',
completed: true,
},
},
});
expect(wrapper.find('input[type="checkbox"]').element.checked).toBe(true);
});
});
E2E Tests
Use Playwright or Cypress for end-to-end testing:
// tests/e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
test('user can sign up', async ({ page }) => {
await page.goto('/auth/signup');
await page.fill('input[type="email"]', 'test@example.com');
await page.fill('input[type="password"]', 'SecurePassword123!');
await page.fill('input[name="confirmPassword"]', 'SecurePassword123!');
await page.click('button[type="submit"]');
// Should redirect to confirmation page
await expect(page).toHaveURL('/auth/confirm');
});
Deployment Protection Rules
Prevent accidental deployments with branch protection:
GitHub Branch Protection
- Go to repository Settings → Branches
- Add a branch protection rule for
main - Enable:
- Require pull request reviews
- Require status checks to pass
- Require branches to be up to date
Monitoring and Alerts
Build Notifications
# .github/workflows/notify.yml
name: Notify on Deploy
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Slack Notification
uses: slackapi/slack-github-action@v1
with:
channel-id: 'deployments'
slack-message: |
Deployment ${{ github.event.workflow_run.conclusion }}
Branch: ${{ github.ref_name }}
Commit: ${{ github.sha }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
Complete Pipeline Example
# .github/workflows/pipeline.yml
name: Complete Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# Job 1: Lint and Type Check
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
# Job 2: Unit Tests
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run test
- name: Upload coverage
uses: codecov/codecov-action@v3
# Job 3: Build
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build
path: .output
# Job 4: E2E Tests (on PRs only)
e2e:
if: github.event_name == 'pull_request'
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
Summary
- Amplify provides built-in CI/CD with Git integration
- Add tests to your build spec for automated testing
- Use GitHub Actions for more complex pipelines
- Enable PR previews for testing before merge
- Protect branches to prevent accidental deployments
- Set up notifications to stay informed
Congratulations! You've learned how to build and deploy full-stack applications with AWS Amplify Gen 2 and Nuxt. You now have the skills to:
- Set up Amplify projects with authentication
- Model and manage data with DynamoDB
- Build secure, type-safe APIs
- Deploy to production with CI/CD