Introduction: Why do you need CI/CD for JavaScript projects?
\n\nDeveloping modern JavaScript applications on Node.js requires not only writing quality code but also reliably delivering it to end users. Manual testing, building, and deployment are not only slow but also prone to human error. This is where CI/CD (Continuous Integration / Continuous Deployment) comes to the rescue — a methodology for automating all stages of the software lifecycle.
\n\nCI/CD for JavaScript projects allows you to automatically run tests on every push to the repository, build the application, and deploy it to a server or the cloud. One of the most popular tools for implementing this methodology is GitHub Actions — a built-in workflow automation system on the GitHub platform.
\n\nIn this article, we will cover how to set up a full CI/CD pipeline for a JavaScript (Node.js) project using GitHub Actions: from writing the configuration to deployment. You will learn how to speed up development, improve code quality, and forget about routine operations.
\n\nMain Part: Creating a CI/CD pipeline with GitHub Actions
\n\n1. What is GitHub Actions and how does it work?
\n\nGitHub Actions is a platform for automating workflows that runs in response to events in a repository (push, pull request, issue creation, etc.). A workflow is a set of steps executed on virtual machines (runners). Each step can run scripts, install dependencies, or call external services.
\n\nKey concepts:
\n- \n
- Workflow — a YAML configuration file stored in the
.github/workflows/folder. \n - Event — a trigger that starts the workflow (e.g.,
pushorpull_request). \n - Job — a set of steps executed on one runner (dependencies, tests, build). \n
- Step — an individual command or action. \n
For JavaScript projects, we will use the standard Ubuntu runner, as it supports Node.js out of the box.
\n\n2. Basic setup: Testing and linting
\n\nLet's start by creating a simple workflow for testing and linting code. Create a file .github/workflows/ci.yml in the root of your repository.
Example configuration for a Node.js project:
\n\nname: CI for Node.js\n\non:\n push:\n branches: [ main, develop ]\n pull_request:\n branches: [ main ]\n\njobs:\n test:\n runs-on: ubuntu-latest\n\n strategy:\n matrix:\n node-version: [18.x, 20.x]\n\n steps:\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Setup Node.js ${{ matrix.node-version }}\n uses: actions/setup-node@v4\n with:\n node-version: ${{ matrix.node-version }}\n cache: 'npm'\n\n - name: Install dependencies\n run: npm ci\n\n - name: Run linter\n run: npm run lint\n\n - name: Run tests\n run: npm test\n\nLet's break down the key points:
\n- \n
- Matrix strategy — allows running tests on multiple Node.js versions (18.x and 20.x) in parallel. \n
- actions/checkout — a standard action for cloning the repository. \n
- actions/setup-node — installs the required Node.js version and enables caching of
node_modulesfor speed. \n - npm ci — a clean installation of dependencies from
package-lock.json(faster and more reliable thannpm install). \n
After pushing this file to the repository, GitHub Actions will automatically run the tests. You will see the result in the "Actions" section of your repository.
\n\n3. Building and publishing a Docker image
\n\nE