Operations 33 min read

Getting Started with CI/CD for Frontend Projects Using GitHub Actions

This comprehensive guide explains how to set up continuous integration and continuous deployment (CI/CD) for a front‑end Vite‑React‑TypeScript project using GitHub Actions, covering basic concepts, workflow configuration, code linting, testing, release management, deployment to an Nginx server, status badges, email notifications, rollback mechanisms, and end‑to‑end testing.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Getting Started with CI/CD for Frontend Projects Using GitHub Actions

Introduction

This article introduces CI/CD for front‑end development, focusing on GitHub Actions as the automation tool. It outlines four learning goals: mastering GitHub Actions usage, understanding CI and CD concepts, adding a CI/CD pipeline to a project, and extending the pipeline with rollback, testing, and notifications.

Why Choose GitHub Actions

GitHub Actions is highlighted for its strong support of open‑source projects, free usage for public repositories, and seamless integration with GitHub. Alternatives such as GitLab CI, Travis CI, Drone CI, and Jenkins are compared, noting their limitations for the author’s use case.

GitHub Action Basics (Skip if Familiar)

Key concepts are explained: Event triggers a workflow, Job groups steps and runs on a runner, and Step performs an individual action. An example workflow diagram illustrates the relationship between events, jobs, and steps.

# Specify workflow name
name: learn-github-actions
# Trigger on push events
on: [push]
jobs:
  check-bats-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: "14"
      - run: npm install -g bats
      - run: bats -v

Project Initialization

A Vite React TypeScript project is created with yarn create vite cicd-study --template react-ts . The resulting UI is shown, and the reader is invited to explore the project structure on StackBlitz.

Adding a CI Process

CI Concept

Continuous Integration (CI) is defined as frequent integration of code into the main branch, requiring automated testing before merging. The GitHub Flow model (feature branch → pull request → review → merge) is used as the basis.

Implementing CI in the Project

Code scanning is set up using @umijs/fabric (ESLint, Prettier, Stylelint). Configuration files .eslintrc.js , .prettierrc.js , and .stylelintrc.js are provided, and lint scripts are added to package.json .

module.exports = {
  extends: [require.resolve("@umijs/fabric/dist/eslint")],
};
const fabric = require("@umijs/fabric");
module.exports = {
  ...fabric.prettier,
};
const fabric = require("@umijs/fabric");
module.exports = {
  ...fabric.stylelint,
};

The test suite uses ts-jest and @testing-library/react with an example App.test.tsx that checks props and button clicks.

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import App from "./App";

test("props is avaliable", () => {
  const value = "123";
  render(
);
  expect(screen.getByRole("props")).toHaveTextContent(value);
});

test("click of button is avaliable", () => {
  render(
);
  fireEvent.click(screen.getByRole("button"));
  expect(screen.getByRole("button")).toHaveTextContent(`count is: 1`);
});

CI Workflow Configuration

The CI workflow ( .github/workflows/ci.yml ) runs on pull‑request events to the main branch, checks out the code, sets up Node, caches dependencies, installs packages, runs linting and tests.

name: CI
on:
  pull_request:
    branches: main
jobs:
  CI:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "16.x"
      - name: Cache
        id: cache-dependencies
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
          key: ${{ runner.OS }}-${{ hashFiles('**/yarn.lock') }}
      - name: Installing Dependencies
        if: steps.cache-dependencies.outputs.cache-hit != 'true'
        run: yarn install
      - name: Running Lint
        run: yarn lint
      - name: Running Test
        run: yarn test

Running the workflow shows successful execution, with links to detailed logs and step outputs.

Adding a CD Process

CD Concept

Continuous Delivery (CD) and Continuous Deployment are explained, emphasizing the three steps: build artifacts, deploy to a test environment, and optionally deploy to production. The guide focuses on continuous deployment directly to a production server.

Preparing the Deployment Environment

A server with Nginx is prepared, optionally using Docker Compose. SSH key pairs and a GitHub Personal Access Token are generated and stored as repository secrets ( DEPLOY_TOKEN , PROJECT_ACCESS_TOKEN , REMOTE_HOST , REMOTE_USER ).

version: "3"
services:
  pure-nginx:
    image: nginx:latest
    container_name: pure-nginx
    restart: always
    volumes:
      - /data/www:/usr/share/nginx/html
    ports:
      - 80:80

CD Workflow Configuration

The CD workflow ( .github/workflows/cd.yml ) triggers on pushes to main . It checks out code, sets up Node, caches dependencies, reads the version from package.json , builds the project, creates a GitHub release, uploads the artifact, and finally deploys the artifact to the remote server via SSH.

name: CD
on:
  push:
    branches: main
jobs:
  CD:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "16.x"
      - name: Cache
        id: cache-dependencies
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
          key: ${{ runner.OS }}-${{ hashFiles('**/yarn.lock') }}
      - name: Installing Dependencies
        if: steps.cache-dependencies.outputs.cache-hit != 'true'
        run: yarn install
      - name: Read Version
        id: version
        uses: ashley-taylor/[email protected]
        with:
          path: ./package.json
          property: version
      - name: Building
        run: |
          yarn build
          zip -r assets ./dist/**
      - name: Create GitHub Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.PROJECT_ACCESS_TOKEN }}
        with:
          tag_name: v${{ steps.version.outputs.value }}
          release_name: v${{ steps.version.outputs.value }}
          draft: false
          prerelease: false
      - name: Update Release Asset
        id: upload-release-asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.PROJECT_ACCESS_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./assets.zip
          asset_name: assets.zip
          asset_content_type: application/zip
      - name: Upload to Deploy Server
        uses: easingthemes/[email protected]
        env:
          SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_TOKEN }}
          ARGS: "-avzr --delete"
          SOURCE: "dist/"
          REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
          REMOTE_USER: ${{ secrets.REMOTE_USER }}
          TARGET: "/data/www"

After merging a pull request, the CD workflow runs, creates a release (e.g., v1.0.0 ), uploads the artifact, and deploys it to the server. The article notes the need to bump the version field for each release to avoid tag conflicts.

Additional Enhancements

Status Badges

Instructions are provided to add CI and CD status badges to the repository README using the standard GitHub badge URL format.

![CI](https://github.com/Hitotsubashi/cicd-study/actions/workflows/ci.yml/badge.svg)

![CD](https://github.com/Hitotsubashi/cicd-study/actions/workflows/cd.yml/badge.svg)

Email Notifications

GitHub account settings can be configured to receive email notifications for workflow failures or for every workflow run.

Rollback Workflow

A manual Rollback workflow ( .github/workflows/rollback.yml ) allows the user to specify a release version and optionally run end‑to‑end tests before redeploying the selected artifact to the server.

name: Rollback
on:
  workflow_dispatch:
    inputs:
      version:
        description: "choose a version to deploy"
        required: true
      E2ETest:
        description: "enable to run e2e test"
        type: boolean
        default: true
jobs:
  Rollback:
    runs-on: ubuntu-latest
    steps:
      - name: Echo Version
        env:
          VERSION: ${{ github.event.inputs.version }}
        run: |
          echo "Version: $VERSION"
      - name: Download Artifact
        uses: dsaltares/fetch-gh-release-asset@master
        with:
          version: "tags/${{ github.event.inputs.version }}"
          file: "assets.zip"
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Unzip
        run: |
          unzip assets.zip
          ls -a ./dist
      - name: Upload to Deploy Server
        uses: easingthemes/[email protected]
        env:
          SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_TOKEN }}
          ARGS: "-avzr --delete"
          SOURCE: "dist/"
          REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
          REMOTE_USER: ${{ secrets.REMOTE_USER }}
          TARGET: "/data/www"
  E2E-Test:
    if: ${{ github.event.inputs.E2ETest }} == true
    uses: ./.github/workflows/e2e-test.yml
    needs: [Rollback]
    secrets: inherit

End‑to‑End (E2E) Testing

E2E tests are built with puppeteer and jest . Configuration ( jest.config.e2e.js ) and a sample test ( src/e2e/App.test.tsx ) are shown. An e2e-test.yml workflow can be triggered manually ( workflow_dispatch ) or called from other workflows ( workflow_call ), running the tests against the deployed server’s public IP.

name: E2E-Test
on: [workflow_call, workflow_dispatch]
jobs:
  E2E-Test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "16.x"
      - name: Cache
        id: cache-dependencies
        uses: actions/cache@v3
        with:
          path: |
            **/node_modules
          key: ${{ runner.OS }}-${{ hashFiles('**/yarn.lock') }}
      - name: Installing Dependencies
        if: steps.cache-dependencies.outputs.cache-hit != 'true'
        run: yarn install
      - name: Running E2E Test
        run: yarn test:e2e --URL=http://${{ secrets.REMOTE_HOST }}/

The CD workflow is updated to call the E2E test job after deployment, and the rollback workflow can optionally run the same tests based on the user’s checkbox.

Conclusion

The article wraps up by encouraging readers to star, bookmark, or comment on the post, and provides a final animated GIF.

frontendDockerCI/CDTestingviteGitHub Actions
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.