Testing is half of a pipeline; shipping is the other half. In this lesson you will deploy a site to GitHub Pages, publish a Playwright report, push a Docker image to GitHub Container Registry and build a staging-to-production pipeline with smoke tests and approvals.
📖 What You’ll Learn: CI vs CD, deploying to GitHub Pages, publishing a Playwright HTML report, building and pushing Docker images to ghcr.io on tags, a staged pipeline with environments, smoke tests and required reviewers, OIDC for cloud credentials, and rolling back.
🚀 From CI to CD
So far your workflows test code (Continuous Integration). Continuous Delivery/Deployment (CD) goes further: after the tests pass, the pipeline ships the result, a website, a Docker image, a package or a test report, automatically and the same way every time.
A typical pipeline:
push to main ──▶ build ──▶ test ──▶ deploy to staging ──▶ smoke tests ──▶ (approval) ──▶ deploy to production
This lesson shows three common deployment targets and the safety features around them. It builds on workflow syntax and secrets and environments.
🌐 Example 1: Deploy a Static Site to GitHub Pages
# .github/workflows/pages.yml
name: Deploy site to GitHub Pages
on:
push:
branches: [ main ]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- run: npm ci
- run: npm run build # creates ./dist
- uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
In Settings, Pages, set Source to GitHub Actions. More in GitHub Pages.
Bonus for testers: publish the Playwright HTML report
Replace the build steps with your test run and upload playwright-report/ as the Pages artifact. The team then opens the latest report at a stable URL instead of downloading artifacts.
- run: npx playwright install --with-deps
- run: npx playwright test
continue-on-error: true # publish the report even when tests fail
- uses: actions/upload-pages-artifact@v3
with:
path: ./playwright-report
⚠️ Common mistake: A Pages site from a public repository is public. Do not publish reports that contain sensitive data, screenshots of internal systems or traces with tokens.
🐳 Example 2: Build and Push a Docker Image to GitHub Container Registry
# .github/workflows/docker.yml
name: Docker image
on:
push:
tags: [ 'v*' ] # release when a version tag is pushed
permissions:
contents: read
packages: write
jobs:
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/acme/test-runner:${{ github.ref_name }}
ghcr.io/acme/test-runner:latest
Tagging a release (git tags) with git tag -a v1.2.0 -m "Release 1.2.0" and git push origin v1.2.0 triggers the build. Image names on ghcr.io must be lowercase. Running tests in containers is covered in Playwright with Docker and Kubernetes.
🖥️ Example 3: Staging and Production with Approvals
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [ main ]
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false # never cancel a deployment halfway
jobs:
test:
uses: ./.github/workflows/playwright-tests.yml # reusable test workflow
with:
base-url: https://qa.example.com
secrets: inherit
deploy-staging:
needs: test
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh staging
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
smoke-staging:
needs: deploy-staging
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- run: npm ci && npx playwright install --with-deps chromium
- run: npx playwright test --grep @smoke --project=chromium
env:
BASE_URL: https://staging.example.com
deploy-production:
needs: smoke-staging
runs-on: ubuntu-latest
environment:
name: production # configured with required reviewers
url: https://www.example.com
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh production
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
What makes this safe:
| Feature | Protection |
|---|---|
needs chain | Production only after tests, staging deployment and smoke tests pass |
Environment production with required reviewers | A person approves the production job |
| Environment deployment branches | Only main can deploy to production |
| Environment secrets | Staging and production use different credentials |
concurrency without cancel | Deployments run one at a time and are never cut off |
| Reusable workflow for tests | One test definition for every pipeline |
The deploy script depends on your hosting (cloud CLI, SSH, Kubernetes, a hosting provider’s action). For cloud providers, prefer OpenID Connect (OIDC): permissions: id-token: write plus the provider’s official login action, so no long-lived cloud keys are stored as secrets.
↩️ Rolling Back
- Redeploy the previous version: re-run the deployment workflow for an earlier tag or commit (keep
workflow_dispatchwith areforversioninput). - Or revert the bad commit on
mainwithgit revertand let the pipeline deploy again (undo changes).
📝 Assignment
- Deploy a simple static HTML page from a repository to GitHub Pages with the Pages workflow.
- Publish a Playwright HTML report to Pages from a practice (public, non-sensitive) project.
- Create
stagingandproductionenvironments; add yourself as required reviewer for production and restrict it tomain. - Build the staging → smoke test → production chain with
echodeploy steps and approve the production job. - Push a
v0.1.0tag and build a Docker image to ghcr.io (any small Dockerfile).
❓ FAQ
🤔 How do I deploy with GitHub Actions?
Add a workflow that runs after tests (needs), uses an environment for the target, and runs the deployment step (a Pages action, a Docker push, a cloud CLI or a script) with secrets or OIDC credentials.
🤔 How do I add manual approval before production deployment?
Create a production environment with required reviewers and reference it with environment: production in the deploy job.
🤔 How do I publish a Playwright report with GitHub Actions?
Upload playwright-report/ with actions/upload-pages-artifact and deploy with actions/deploy-pages, or upload it as a normal artifact.
🤔 How do I push a Docker image to GitHub Container Registry?
Log in with docker/login-action to ghcr.io using GITHUB_TOKEN with packages: write permission, then build and push with docker/build-push-action.
🤔 What is OIDC in GitHub Actions?
OpenID Connect lets workflows get short-lived cloud credentials directly from the cloud provider, so you do not store long-lived keys as secrets.
🎯 Next Steps
- 🏋️ GitHub Actions Hands-on Practice Projects
- 🌐 GitHub Pages: Host a Website for Free
- 📘 Git and GitHub Tutorial: full course
🎬 Learn Git and GitHub step by step: watch the series on YouTube!
Subscribe to SqaToolsByDY • Practice on sqatools.in