Build a CI Pipeline with Azure Pipelines

Automate builds and tests for a Node.js app using Azure Pipelines.

Introduction

30 Second Summary

Imagine pushing code to your team's project and finding out a week later that something you wrote broke the whole app. Professional development teams avoid this by having a server automatically check every code change the moment it's pushed.

In this project, you will build an automated CI pipeline using Azure Pipelines that builds your Node.js app and runs tests every time you push code to GitHub.

What You'll Build

A fully automated CI pipeline connected to GitHub that installs dependencies, builds your app, runs tests, and publishes a build artifact on every push to main.

By the end of this project, you'll have:

  • A Node.js application with a working test suite.
  • A YAML pipeline definition (azure-pipelines.yml) version-controlled in your GitHub repository.
  • A connected Azure Pipelines pipeline that triggers automatically on every push to main.
  • Secret Mission: Add a multi-stage pipeline with separate Build and Test stages that run in sequence.

Are there any prerequisites?

No prior experience is required. This project includes setup for all tools. You will need to request a free Azure Pipelines parallel job grant, which typically takes 2-3 business days to process. You can continue building your app and writing your pipeline file while you wait.

Set Up Your Accounts and Tools

Before you can build a CI pipeline, you need the right tools on your machine and accounts ready to go.

In this step, you will install Node.js for running JavaScript locally, set up Git and GitHub for version control, and create your Azure DevOps organization where your pipeline will live.

In this step, get ready to:
  • Install Node.js and verify it works.
  • Set up Git and a GitHub account.
  • Create an Azure DevOps organization and request pipeline access.
Install Node.js

Node.js is the JavaScript runtime that lets you run JavaScript outside a browser. You need it to install packages, run tests, and build your app locally.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.
  • Check if Node.js is already installed by running this command:
node --version

✔️ I see a version number

You are good to go. Node.js is already installed on your machine.

  • Confirm npm is also available by running:
npm --version

You should see a version number printed. npm is the package manager that comes bundled with Node.js.

ⓧ Command not found

Node.js is not installed yet. Let's fix that.

  • Open your browser and go to nodejs.org.
  • Click the LTS download button. The site auto-detects your operating system.
  • Run the downloaded installer and accept the default settings.
  • Close your terminal completely, then reopen it.
  • Verify the installation by running:
node --version

You should now see a version number. Then confirm npm works too:

npm --version

Both commands should print a version number.

Still seeing command not found?

Make sure you closed and reopened your terminal after installing. The terminal needs a fresh session to detect newly installed programs.

On Windows, try opening a new Command Prompt window instead of reusing the old one.

Help me troubleshoot my Node.js installation

Set up Git and GitHub

Git is the version control system that tracks changes to your code. GitHub is the cloud platform where you will host your repository so that Azure Pipelines can access it.

  • In your terminal, check if Git is already installed by running:
git --version

✔️ I see a version number

Git is ready. You can move on to creating your GitHub account below.

ⓧ Command not found

Git is not installed yet. Follow the instructions for your operating system.

macOS

  • Run this command to install Git through Xcode Command Line Tools:
xcode-select --install

A dialog box will appear asking you to confirm the installation. Click Install and wait for it to finish.

  • Once complete, verify by running:
git --version

Windows

  • Open your browser and go to gitforwindows.org.
  • Click Download to get the installer.
  • Run the installer and accept all the default settings.
  • Close and reopen your terminal.
  • Verify by running:
git --version

Still not working?

Make sure you restarted your terminal after installing. If you are on Windows, try opening a brand new Command Prompt or PowerShell window.

Help me get Git installed on my machine

Now let's make sure you have a GitHub account.

  • Open your browser and go to github.com.
  • If you already have an account, sign in and move on to the next substep.
  • If you do not have an account, click Sign up and follow the prompts to create a free account.

Why GitHub?

Azure Pipelines connects directly to GitHub repositories to watch for code changes. When you push a commit, GitHub notifies Azure Pipelines to start your build automatically.

Create your Azure DevOps organization

Azure DevOps is the platform that hosts Azure Pipelines. You need an organization and a project inside it before you can create a pipeline.

  • Open your browser and go to dev.azure.com.
  • Click Start free.
  • Sign in with your Microsoft account. If you do not have one, create a free Microsoft account when prompted.
  • Follow the prompts to create your new Azure DevOps organization.

What is an Azure DevOps organization?

An organization is the top-level container in Azure DevOps. It holds your projects, pipelines, repositories, and team settings. Signing up is free and does not require a credit card.

Before your pipeline can actually run builds, you need to request a free parallel job grant from Microsoft.

The grant takes a few days to process

Microsoft typically processes free parallel job requests in 2-3 business days, but it can take longer. You do not need to wait to continue this project.

You can build your app, write your pipeline YAML, and push everything to GitHub while you wait. You only need the grant when you are ready to run the pipeline in Step 6.

Your machine is ready and your accounts are in place. Next up, you will create the Node.js application that your pipeline will build and test.

Create the Node.js Application

With your accounts and tools ready, it's time to build the application that your CI pipeline will automatically build and test.

In this step, you'll create a simple calculator module with a test suite powered by Jest. This gives your pipeline something meaningful to run on every code push.

In this step, get ready to:
  • Create a project directory and install dependencies.
  • Write a calculator module with add and subtract functions.
  • Add tests and verify they pass locally.
Set up the project directory

Every Node.js project starts with a folder and a package.json file that tracks its dependencies and scripts.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir azure-ci-demo
cd azure-ci-demo
  • Confirm the folder exists by running:
ls

You should see the azure-ci-demo folder listed on your Desktop.

  • Initialize the project by running:
npm init -y

What does this command do?

The -y flag tells npm to accept all the default values and generate a package.json file immediately. This file is the central config for any Node.js project.

You should see a package.json file printed to your terminal, confirming it was created.

  • Install the Jest testing framework by running:
npm install --save-dev jest

What does this command do?

The --save-dev flag installs Jest as a development dependency. This means Jest is only needed while developing and testing your code, not when running it in production.

You should see npm download Jest and its dependencies. A node_modules/ folder now appears in your project directory.

Seeing permission errors or network failures?

Make sure you are inside the azure-ci-demo folder before running the install command.

If you see permission errors on macOS, try prefixing the command with sudo. If you see network errors, check your internet connection and try again.

Help me troubleshoot my npm install error

  • Create a .gitignore file to keep the bulky node_modules/ folder out of your repository by running:
echo "node_modules/" > .gitignore

Why exclude node_modules?

The node_modules/ folder can contain thousands of files. Anyone who clones your repository can regenerate it by running npm install, so there is no reason to store it in Git.

Write the calculator module

Your pipeline needs actual code to build and test. You will create a simple calculator module with two functions.

  • Create a src folder to hold your source code by running:
mkdir src
  • Open the azure-ci-demo folder in your code editor.
  • Create a new file called calculator.js inside the src folder.
  • Paste the following code into src/calculator.js:
// Define a function that adds two numbers
function add(a, b) {
  return a + b;
}

// Define a function that subtracts two numbers
function subtract(a, b) {
  return a - b;
}

// Export both functions so other files can use them
module.exports = { add, subtract };

What does this code do?

This module defines two simple math functions. The module.exports line makes both functions available to other files that require() this module.

  • Save src/calculator.js.

Now you will write the tests that verify these functions work correctly.

  • Create a new file called calculator.test.js inside the src folder.
  • Paste the following code into src/calculator.test.js:
// Import the functions we want to test
const { add, subtract } = require('./calculator');

// Group related tests inside a describe block
describe('Calculator', () => {
  test('adds two numbers correctly', () => {
    expect(add(2, 3)).toBe(5);
  });

  test('adds negative numbers', () => {
    expect(add(-1, -1)).toBe(-2);
  });

  test('subtracts two numbers correctly', () => {
    expect(subtract(5, 3)).toBe(2);
  });

  test('subtracts resulting in negative', () => {
    expect(subtract(3, 5)).toBe(-2);
  });
});

What does this code do?

  • require('./calculator') imports your functions from the file next door (same folder).
  • describe() groups related tests under a label.
  • Each test() call checks one specific behavior using expect().toBe() assertions.
  • If any assertion fails, Jest reports exactly which test broke and why.
  • Save src/calculator.test.js.

Not sure where to create the files?

Both files live inside the src/ folder you created. Your project structure should look like this: azure-ci-demo/src/calculator.js and azure-ci-demo/src/calculator.test.js.

Help me set up my project file structure

✔️ Awesome, I've got everything!

Great. Double check you've saved both files before moving on.

ⓧ I'd like to double check the full code

Here is src/calculator.js:

function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };

Here is src/calculator.test.js:

const { add, subtract } = require('./calculator');

describe('Calculator', () => {
  test('adds two numbers correctly', () => {
    expect(add(2, 3)).toBe(5);
  });

  test('adds negative numbers', () => {
    expect(add(-1, -1)).toBe(-2);
  });

  test('subtracts two numbers correctly', () => {
    expect(subtract(5, 3)).toBe(2);
  });

  test('subtracts resulting in negative', () => {
    expect(subtract(3, 5)).toBe(-2);
  });
});
Configure build scripts and run tests

Your package.json still has placeholder scripts from the initial setup. You need to point the test script at Jest and add a build script so your pipeline knows how to build and test the project.

  • Open package.json in your editor (it's in the root of your azure-ci-demo folder).
  • Replace the entire contents of the file with this:
{
  "name": "azure-ci-demo",
  "version": "1.0.0",
  "description": "A simple Node.js app to demonstrate Azure Pipelines CI",
  "main": "src/calculator.js",
  "scripts": {
    "test": "jest",
    "build": "echo Build complete"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jest": "^29.7.0"
  }
}

What changed in this file?

  • "test": "jest" tells npm test to run Jest and execute any files ending in .test.js.
  • "build": "echo Build complete" is a placeholder build script. Your pipeline will call this to confirm the build step runs.
  • "main": "src/calculator.js" points to your module as the entry point.
  • The devDependencies section was added automatically when you installed Jest.
  • Save package.json.
  • Run your tests locally to confirm everything works by running:
npm test

You should see Jest discover your test file and report all 4 tests passing with green PASS status.

Tests failing or not found?

  • If you see "no tests found", make sure your test file is named calculator.test.js (not calculator.tests.js or calculatorTest.js).
  • If you see a "Cannot find module" error, check that require('./calculator') in your test file matches the filename calculator.js exactly.
  • Make sure both files are inside the src/ folder, not in the project root.

Help me debug my failing Jest tests

✔️ Awesome, I've got everything!

Great. Double check you've saved all your files before moving on.

ⓧ I'd like to double check the full code

Here is package.json:

{
  "name": "azure-ci-demo",
  "version": "1.0.0",
  "description": "A simple Node.js app to demonstrate Azure Pipelines CI",
  "main": "src/calculator.js",
  "scripts": {
    "test": "jest",
    "build": "echo Build complete"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jest": "^29.7.0"
  }
}

Here is src/calculator.js:

function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };

Here is src/calculator.test.js:

const { add, subtract } = require('./calculator');

describe('Calculator', () => {
  test('adds two numbers correctly', () => {
    expect(add(2, 3)).toBe(5);
  });

  test('adds negative numbers', () => {
    expect(add(-1, -1)).toBe(-2);
  });

  test('subtracts two numbers correctly', () => {
    expect(subtract(5, 3)).toBe(2);
  });

  test('subtracts resulting in negative', () => {
    expect(subtract(3, 5)).toBe(-2);
  });
});

Here is .gitignore:

node_modules/

Your Node.js app is ready with a working test suite. Next up, you'll push this code to GitHub so Azure Pipelines can access it.

Push Your Code to GitHub

Your Node.js app is built and tested locally. Now you need to get it onto GitHub so that Azure Pipelines can access it.

Azure Pipelines connects directly to your GitHub repository. Every time you push code, the pipeline pulls the latest version and runs your build and tests automatically.

In this step, get ready to:
  • Create a new GitHub repository.
  • Push your project code to the main branch.
  • Verify your files are visible on GitHub.
Create a new GitHub repository

You need an empty repository on GitHub to receive your local project files. Creating it without any pre-populated files avoids merge conflicts when you push.

  • Open your browser and go to github.com.
  • In the upper-right corner of the page, click the + dropdown, then click New repository.
  • Type azure-ci-demo as the repository name.
  • Choose either Public or Private visibility.
  • Leave all checkboxes unchecked (no README, no .gitignore, no license). You already have these files locally.
  • Click Create repository.

GitHub shows a Quick Setup page with instructions for pushing an existing repository. Keep this page open because you will need the repository URL in the next substep.

Why leave it empty?

If you initialize the repository with a README or .gitignore, GitHub creates a commit that your local project does not have. When you try to push, Git sees conflicting histories and rejects the push. Starting empty means your first push goes through cleanly.

Initialize Git and push your code

Your project folder is not yet a Git repository. You need to initialize it, commit your files, and push everything to GitHub.

  • Make sure your terminal is inside the azure-ci-demo project folder.
  • Initialize a new Git repository by running this command:
git init
  • Stage all your project files by running:
git add .
  • Create your first commit by running:
git commit -m "Initial commit"
  • Rename the default branch to main by running:
git branch -M main
  • Connect your local repository to GitHub by running:
git remote add origin [[YOUR_REPO_URL="https://github.com/YOUR_USERNAME/azure-ci-demo.git"]]

Where do I find my repository URL?

Copy the URL from the Quick Setup page that GitHub showed after you created the repository. It looks like https://github.com/your-username/azure-ci-demo.git.

  • Push your code to GitHub by running:
git push -u origin main

What did these commands do?

  • git init turned your folder into a Git repository that tracks file changes.
  • git add . staged all files in the folder for the next commit.
  • git commit saved a snapshot of your staged files with a message.
  • git branch -M main ensured your branch is named main (some systems default to master).
  • git remote add origin linked your local repo to the GitHub repository.
  • git push -u origin main uploaded your commit to GitHub and set main as the default upstream branch.

Push rejected or authentication error?

  • If Git asks for a username and password, GitHub no longer accepts passwords for HTTPS pushes. You need a Personal Access Token (PAT) instead. Go to Settings > Developer settings > Personal access tokens in GitHub and generate one with the repo scope. Use it as your password when prompted.
  • If you see "failed to push some refs", make sure you left the repository empty (no README) when creating it on GitHub.
  • help me fix a git push error to GitHub
Verify your code appears on GitHub
  • Go back to your repository page on GitHub. Refresh the page if it still shows the Quick Setup instructions.
  • Confirm you can see these files listed: package.json, src/calculator.js, src/calculator.test.js, and .gitignore.
  • Click on src/calculator.js to verify the file contents match what you wrote locally.

You should see your add and subtract functions displayed in the file viewer.

Don't see your files?

  • Check that the branch selector in the top-left says main. If it says another branch name, click it and switch to main.
  • If the repository still shows Quick Setup instructions, your push did not go through. Go back to your terminal and check for error messages.
  • help me figure out why my files aren't showing on GitHub

Your code is now live on GitHub and ready to be picked up by a CI pipeline. Next up, you will connect this repository to Azure Pipelines and create your first automated pipeline.

Create Your Azure Pipeline

Your calculator app is live on GitHub. Now it's time to connect it to a service that automatically builds and tests your code every time you push a change.

Azure Pipelines is the CI/CD service inside Azure DevOps. In this step, you'll create a new project, connect your GitHub repository, and generate the starter YAML file that defines your pipeline.

In this step, get ready to:
  • Create a new project in Azure DevOps.
  • Connect your GitHub repository to Azure Pipelines.
  • Generate a starter pipeline YAML file.
Create a new project in Azure DevOps

An Azure DevOps project is a container that holds your pipelines, repositories, and work items. You need one project to house your new CI pipeline.

  • Open your browser and go to dev.azure.com.
  • Sign in with the Microsoft account you used to create your Azure DevOps organization.
  • Click New project in the top-right corner.
  • Enter azure-ci-demo as the project name.
  • Leave the visibility set to Private.
  • Click Create.

Why Private visibility?

New Azure DevOps organizations can no longer create public projects. Private is the only visibility option available, so no action is needed here.

Azure DevOps displays your new project's welcome page. You should see the project name at the top and a left sidebar with navigation options.

Connect GitHub as your code source

Now you'll start creating a pipeline and point it at your GitHub repository. This connection lets Azure Pipelines pull your code whenever it needs to build.

  • In the left sidebar, click Pipelines.
  • Click Create Pipeline.
  • On the Where is your code screen, select GitHub.
  • You may be redirected to GitHub to sign in. Enter your GitHub credentials if prompted.
  • If prompted to install the Azure Pipelines app, click Approve & install.
  • On the Select a repository screen, select the repository you pushed your calculator code to.

What is the Azure Pipelines GitHub app?

When you authorize Azure Pipelines, it installs a GitHub app on your account. This app allows Azure Pipelines to receive notifications when you push code and to fetch your source files during a build.

Don't see your repository in the list?

Make sure you authorized the correct GitHub account. If you have multiple GitHub accounts, sign out of GitHub and sign back in with the account that owns your repository.

help me find my repository in the Azure Pipelines setup

Select the starter pipeline template

Azure Pipelines can generate a template YAML file based on your project. You'll pick a basic starter and customize it in the next step.

  • On the Configure your pipeline screen, select Starter pipeline.
  • Azure Pipelines generates an azure-pipelines.yml file and shows it in a YAML editor.

You should see the YAML editor with a starter template containing a few example steps. Do not click Save and run yet. You will replace this starter content in the next step.

What is azure-pipelines.yml?

This is the file that defines your entire CI pipeline as code. It lives in your repository alongside your application code, which means your pipeline definition is version-controlled just like the rest of your project.

The starter template is a placeholder. In the next step, you'll replace it with your own build, test, and publish steps.

Still waiting on your free parallel job grant?

If your free grant request has not been approved yet, you can still write and save your pipeline YAML in the next step. You just won't be able to run the pipeline until the grant is processed.

Your Azure DevOps project is set up and your GitHub repository is connected. Next, you'll replace the starter YAML with a real CI pipeline that installs dependencies, builds your app, runs tests, and publishes an artifact.

Write Your CI Pipeline YAML

Your pipeline needs instructions. Right now, the azure-pipelines.yml file in the editor contains a generic starter template that doesn't know anything about your Node.js project.

In this step, you'll replace that starter YAML with a pipeline definition that installs Node.js, grabs your dependencies, runs your tests, and packages everything into an artifact. This file is the single source of truth for your CI workflow.

In this step, get ready to:
  • Replace the starter pipeline YAML with your own configuration.
  • Add steps to install, build, and test your Node.js app.
  • Publish a build artifact and trigger your first pipeline run.
Replace the starter YAML with your pipeline configuration

A YAML pipeline definition has three main sections: a trigger (when to run), a pool (what machine to run on), and steps (what to do). You'll start by configuring these sections and adding the first task.

  • In the Azure DevOps pipeline editor, select all of the starter YAML content and delete it.
  • Replace it with the following code, which sets up the trigger, pool, variables, and the UseNode@1 task:
trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  nodeVersion: '18.x'

steps:
  - task: UseNode@1
    inputs:
      version: $(nodeVersion)
    displayName: 'Install Node.js'

What does this code do?

  • trigger: - main tells Azure Pipelines to run this pipeline every time code is pushed to the main branch.
  • vmImage: 'ubuntu-latest' runs your pipeline on a fresh Linux virtual machine hosted by Microsoft.
  • variables: defines reusable values. Here, nodeVersion: '18.x' means you can reference $(nodeVersion) anywhere in the pipeline.
  • UseNode@1 is a built-in Azure Pipelines task that installs Node.js and adds it to the system PATH.

You won't see a change yet. The next chunk adds the build and test steps that make this pipeline useful.

Add build and test script steps

With Node.js installed on the pipeline agent, you now need steps that mirror what you do locally: install packages, build the project, and run the tests.

  • In the pipeline editor, place your cursor at the end of the displayName: 'Install Node.js' line.
  • Add the following three script steps below it:
  - script: |
      npm install
    displayName: 'Install dependencies'

  - script: |
      npm run build
    displayName: 'Build project'

  - script: |
      npm test
    displayName: 'Run tests'

What does this code do?

  • Each script: block runs a terminal command on the pipeline agent, just like you would in your own terminal.
  • npm install downloads your project's dependencies (like Jest) on the pipeline machine.
  • npm run build executes the build script from your package.json.
  • npm test runs your Jest test suite. If any test fails, the pipeline stops and reports a failure.
  • displayName gives each step a readable label in the Azure DevOps run log.

You won't see a change yet. The next chunk adds the final publish step, and then you'll save and run.

Publish the build artifact and run your pipeline

A pipeline artifact is a packaged snapshot of your build output. Publishing it means other pipelines (like a deployment pipeline) can pick it up later without rebuilding from scratch.

  • Place your cursor at the end of the displayName: 'Run tests' line.
  • Add the PublishPipelineArtifact@1 task below it:
  - task: PublishPipelineArtifact@1
    inputs:
      artifactName: 'nodejs-app'
      targetPath: '$(Build.SourcesDirectory)'
    displayName: 'Publish pipeline artifact'

What does this code do?

  • PublishPipelineArtifact@1 is a built-in Azure Pipelines task that packages files and stores them as a downloadable artifact.
  • artifactName: 'nodejs-app' gives the artifact a name you can reference later.
  • targetPath: '$(Build.SourcesDirectory)' points to the folder where Azure Pipelines checked out your code. This packages your entire project.
  • Click the Save and run button in the top right of the editor.
  • In the dialog that appears, keep the default commit message and make sure main is selected as the branch.
  • Click Save and run again to confirm.

What does Save and run do?

Clicking Save and run does two things: it commits your azure-pipelines.yml file directly to the main branch of your GitHub repository, and it immediately triggers the pipeline to run.

Azure DevOps takes you to the pipeline run page. You should see your pipeline queued or running with each step listed (Install Node.js, Install dependencies, Build project, Run tests, Publish pipeline artifact).

Pipeline stuck in "queued" state?

If your pipeline stays queued and never starts running, your free parallel job grant may not have been approved yet. New Azure DevOps organizations must request this at https://aka.ms/azpipelines-parallelism-request and it can take 2-3 business days to process.

You can continue to the next step and come back to verify the run once your grant is approved. The pipeline will run automatically when capacity becomes available.

help me figure out why my Azure Pipeline is stuck in queued

✔️ Awesome, I've got everything!

Great. Double check you see all five steps listed in the pipeline run view before moving on.

ⓧ I'd like to double check the full code

Here is the complete azure-pipelines.yml file. Make sure your editor matches this exactly:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  nodeVersion: '18.x'

steps:
  - task: UseNode@1
    inputs:
      version: $(nodeVersion)
    displayName: 'Install Node.js'

  - script: |
      npm install
    displayName: 'Install dependencies'

  - script: |
      npm run build
    displayName: 'Build project'

  - script: |
      npm test
    displayName: 'Run tests'

  - task: PublishPipelineArtifact@1
    inputs:
      artifactName: 'nodejs-app'
      targetPath: '$(Build.SourcesDirectory)'
    displayName: 'Publish pipeline artifact'

Your pipeline is now live and connected to your code. Next up, you'll push a real code change and watch the pipeline trigger automatically.

Trigger CI and Watch It Work

Your pipeline ran once when you saved the YAML file. That was a manual trigger from inside Azure DevOps.

The real power of Continuous Integration is the automatic feedback loop. Every time you push code to main, the pipeline triggers on its own, builds your project, runs your tests, and tells you immediately if something broke.

In this step, get ready to:
  • Push new code and watch the pipeline trigger automatically.
  • Break a test intentionally and observe the pipeline fail.
  • Fix the test and confirm the pipeline goes green again.
Add a new function and trigger the pipeline

To prove your pipeline triggers automatically, you need to push a real code change. You will add a multiply function to your calculator and a test to go with it.

  • Open src/calculator.js in your editor.
  • Add the following multiply function above the module.exports line:
function multiply(a, b) {
  return a * b;
}
  • Update the module.exports line to include multiply, so it looks like this:
module.exports = { add, subtract, multiply };

What does this code do?

The multiply function takes two numbers and returns their product. Exporting it makes it available to your test file.

  • Now open src/calculator.test.js and update the require statement at the top to include multiply:
const { add, subtract, multiply } = require('./calculator');
  • Add a new test inside the describe('Calculator', ...) block, below the existing tests:
  test('multiplies two numbers correctly', () => {
    expect(multiply(3, 4)).toBe(12);
  });

What does this test do?

This test calls multiply(3, 4) and checks that the result equals 12. If the function works correctly, the test passes.

  • Save both files.
  • Confirm your new test passes locally by running:
npm test

You should see 5 passing tests in the output.

  • Commit and push your changes to main by running these commands:
git add .
git commit -m "Add multiply function and test"
git push

Why does pushing trigger the pipeline?

Your azure-pipelines.yml starts with trigger: - main. This tells Azure Pipelines to start a new run every time a commit lands on the main branch. No manual action needed.

Pipeline not triggering?

If your pipeline does not start within a minute or two, check that your free parallel job grant has been approved. New Azure DevOps organizations must request the grant at https://aka.ms/azpipelines-parallelism-request and it can take 2-3 business days to process.

help me figure out why my Azure Pipeline isn't triggering after a push

Watch the pipeline run in real time

Within seconds of your push, Azure Pipelines picks up the commit and starts a new run. You can watch every step execute live.

  • In your Azure DevOps project, click Pipelines in the left sidebar.
  • You should see a new run in progress. Click on it to open the run details.
  • Click on the job name to see each step executing in real time.

You can see a live feed of the console output as each step runs. The agent runs steps sequentially, so you will watch them complete one after another.

What should I see?

Each step shows a green checkmark when it passes. You should see Install Node.js, Install dependencies, Build project, Run tests, and Publish pipeline artifact all pass with green checkmarks.

Expand the Run tests step to see Jest report that all 5 tests passed (including your new multiply test).

  • Wait for the entire run to complete. The overall status should show as succeeded (green).
Break a test and experience CI catching the failure

A green pipeline is nice, but the real value of CI is catching bugs before they reach production. You are going to intentionally break a test, push it, and watch the pipeline turn red.

  • In src/calculator.test.js, find the multiply test you just added.
  • Find this line:
    expect(multiply(3, 4)).toBe(12);
  • Change 12 to 11 so the assertion is wrong:
    expect(multiply(3, 4)).toBe(11);
  • Save the file.
  • Commit and push the broken test by running:
git add .
git commit -m "Break test intentionally"
git push
  • Go back to Pipelines in Azure DevOps and click into the new run.
  • Click on the job to watch it execute.

This time, the Run tests step fails. The overall pipeline run shows a red status. This is CI doing its job.

What does the failure look like?

The Run tests step shows a red X. Expand it to see Jest's output showing that the multiply test expected 11 but received 12.

Steps after the failure (like Publish pipeline artifact) are skipped. A step fails if it ends with a nonzero exit code, and subsequent steps only run if all previous steps succeeded.

Now fix the test and confirm CI recovers.

  • In src/calculator.test.js, change 11 back to 12:
    expect(multiply(3, 4)).toBe(12);
  • Save the file.
  • Commit and push the fix by running:
git add .
git commit -m "Fix broken test"
git push
  • Go back to Pipelines in Azure DevOps and watch the new run.
  • Confirm the pipeline completes with a green status. All steps should pass again.

Check your pipeline history

On the Pipelines page, you can see all your runs listed with their status. You should now have a green run (new feature), a red run (broken test), and another green run (fix). This history is exactly what a real team reviews to track code health.

Pipeline still red after the fix?

Make sure you changed the expected value back to 12 and saved the file before committing. Run npm test locally to confirm all tests pass before pushing.

help me debug why my Azure Pipeline is still failing after I fixed my test

You just experienced the full CI feedback loop. Every push triggers an automatic build and test. Failures surface immediately. This is the safety net that professional teams rely on to ship code confidently.

Secret mission

Add Multi-Stage Pipeline

Your pipeline works, but it runs everything in a single job. Real-world pipelines separate concerns into distinct stages. Can you restructure your YAML into a Build stage and a Test stage that run in sequence?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

This project uses Azure Pipelines' free tier, so your pipeline only runs when you push code. There are no ongoing costs while your pipeline is idle.

Resources you used:

  • Azure DevOps project (contains your pipeline).
  • GitHub repository (stores your code and azure-pipelines.yml).
  • Local project folder on your Desktop.

Keep everything running

No action needed. Choose this if you are still actively building or plan to add the Secret Mission stages to your pipeline.

  • Your Azure DevOps project stays at dev.azure.com. The pipeline only consumes minutes when you push code.
  • Your GitHub repository remains available. No storage cost applies.
  • Your local project folder stays on your Desktop.
  • Node.js and Git remain installed on your machine.

Pause - I'll come back to this later

Shut down running processes to free up resources while keeping your files and accounts intact.

  • Your pipeline only triggers on push, so there is nothing to pause. It will not run until you push again.
  • Leave the Azure DevOps project and GitHub repository as they are. Both remain on the free tier.

Delete - I don't want to use this again

Remove all project resources and start fresh.

Delete the Azure DevOps project:

  • Sign in to your organization at dev.azure.com.
  • Select Organization settings in the bottom-left corner.
  • Select Projects, check the box next to your project, then select Delete.
  • Confirm deletion by typing the project name, then select Delete.

Delete the GitHub repository:

  • Navigate to your repository on GitHub.
  • Click Settings.
  • Scroll down to the Danger Zone section and click Delete this repository.
  • Click I want to delete this repository.
  • Click I have read and understand these effects.
  • Type your repository name to confirm, then click Delete this repository.

Delete the local project folder:

macOS

  • Delete the project folder by running:
rm -rf ~/Desktop/azure-ci-demo

Windows

  • Delete the project folder by running:
rmdir /s /q %USERPROFILE%\Desktop\azure-ci-demo

Nice Work!

Nice Work!

You just built an automated CI pipeline from scratch. Every time you push code to GitHub, Azure Pipelines now builds your project, runs your tests, and produces a deployable artifact without any manual intervention.

You've learned how to:

  • Build a Node.js application with a Jest test suite that validates your code automatically.
  • Write a YAML pipeline definition (azure-pipelines.yml) that lives in your repository alongside your code.
  • Connect GitHub to Azure Pipelines for automatic build triggers, artifact publishing, and real-time feedback on every push to main.
  • Added multi-stage pipeline architecture with sequential Build and Test stages that run in sequence.

Ready to quiz yourself?