Manual checks get forgotten. A CI pipeline (continuous integration) runs lint, tests and the build on every change, so broken code never reaches your main branch. CD (continuous delivery or deployment) then publishes a passing build automatically. GitHub Actions is built into GitHub and free for small projects.
What you will learn
- A workflow that lints, tests and builds on every pull request
- How to run Playwright tests in CI
- How to deploy with secrets and keep releases safe
Note: the commands below (ng test --watch=false, ng build, npx playwright test) were run locally while preparing this course. The workflow files are standard GitHub Actions configuration, but we could not run GitHub's servers from here, so test them in your own repository.
Scripts first
Make sure your package.json has clear scripts, because the pipeline just calls them:
{
"scripts": {
"start": "ng serve",
"build": "ng build",
"test": "ng test --watch=false",
"lint": "ng lint",
"e2e": "playwright test"
}
}
ng lint exists after you run ng add angular-eslint.
The CI workflow
Create .github/workflows/ci.yml:
name: CI
on:
pull_request:
push:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Unit tests
run: npm test
- name: Production build
run: npm run build
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
What it does:
- Runs on every pull request and on pushes to
main. concurrencycancels older runs of the same branch, so you only wait for the latest.- Uses a Node version supported by your Angular version. Check the requirement of your CLI.
npm ciinstalls exactly what the lock file says, which makes builds repeatable.cache: npmspeeds up repeated runs.- The build is stored as an artifact, so the deploy job can reuse the same files that were tested.
Pin action versions (@v4) and review what third-party actions do, since they run with access to your repository.
Add end-to-end tests
e2e:
runs-on: ubuntu-latest
needs: test-and-build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test
env:
CI: true
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
needs makes e2e run only after the fast checks pass. On failure the report is uploaded, so you can open it from the run page.
Deploy
A simple, common deploy copies the built files to a server over SSH. Keep credentials in repository secrets (Settings, Secrets and variables, Actions), never in the file:
deploy:
runs-on: ubuntu-latest
needs: [test-and-build, e2e]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Prepare SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts
- name: Upload files
run: rsync -az --delete dist/<project-name>/browser/ "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/var/www/my-app/"
Replace <project-name> with your project's folder in dist. Other targets (Netlify, Firebase Hosting, Vercel, Azure, S3 with CloudFront) have official actions or CLIs, and many deploy straight from a Git push without a custom workflow.
Safety habits:
- Deploy only from `main`, and protect
mainso changes need a pull request with passing checks. - Use an
environmentsuch asproductionwith required reviewers if you want a manual approval step. - Give the deploy key access to one server and one folder only.
- Keep a way to roll back: keep the previous build, or redeploy an earlier commit.
- Never print secrets in logs.
Other useful checks
npm audit --audit-level=highto catch known vulnerable packages.- Dependabot or Renovate (a small config file) to open pull requests for updates.
- Bundle budgets in
angular.json: the production build fails when bundles grow too large. - Lighthouse CI to track performance and accessibility scores over time.
- Commit message or pull request templates to keep reviews consistent.
Try it yourself
- Add
ci.ymlto a project and open a pull request. Watch the checks run. - Break a test on purpose and see the pipeline fail. Fix it and push again.
- Add a deploy job for your host of choice, using secrets.
Quick check
- Why use
npm ciinstead ofnpm installin CI? - Why does the deploy job only run on
main? - Where should deploy credentials be stored?
Next, continue with Angular Troubleshooting: 404 on Refresh, ExpressionChanged, EventTarget and SSL on Localhost.