Set up pipeline with buildspec.yml

In this section, you will learn how to define the buildspec.yml file – the blueprint of the CI/CD process with AWS CodeBuild. We will configure two sample pipelines: one for Frontend (ReactJS) and one for Backend (Java Spring Boot).

Each file performs the main steps such as:

  • Install environment.

  • Analyze source code quality with SonarQube.

  • Build Docker image.

  • Push image to ECR.

  • Automatically update Helm Chart in the configuration Git repo.

Create buildspec.yml file for Frontend ReactJS

Place this file in the root directory of the frontend source code:

version: 0.2

env:
  secrets-manager:
    GITHUB_TOKEN: "workshop-2-shopnow-github-access-token:GITHUB_TOKEN"
    SONAR_TOKEN: "workshop-2-shopnow-sonarqube-access-token:SONARQUBE_TOKEN"

phases:
  install:
    runtime-versions:
      nodejs: latest
    commands:
      - echo "Installing dependencies..."
      - npm install
      - apt-get update && apt-get install -y git curl unzip openjdk-17-jdk
      - echo "Installing sonar-scanner..."
      - wget --tries=3 https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.1.0.4889-linux-x64.zip
      - unzip sonar-scanner-cli-7.1.0.4889-linux-x64.zip
      - mv sonar-scanner-7.1.0.4889-linux-x64 /opt/sonar-scanner
      - export PATH=$PATH:/opt/sonar-scanner/bin

  pre_build:
    commands:
      - echo "Pre-build steps..."
      - |
        GIT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
        GIT_HASH=$(git rev-parse --short HEAD)
        TAG_NAME="${GIT_TAG}_${GIT_HASH}"
        export TAG_NAME
        echo "Using image tag: $TAG_NAME"
      - test -f "package.json" || { echo "Missing package.json"; exit 1; }
      - test -f "Dockerfile" || { echo "Missing Dockerfile"; exit 1; }
      - echo "Running SonarQube analysis..."
      - sonar-scanner -Dsonar.sources="./src/" -Dsonar.projectKey="shopnow-frontend" -Dsonar.projectName="shopnow-frontend" -Dsonar.projectVersion="${TAG_NAME}" -Dsonar.host.url=${SONAR_HOST_URL} -Dsonar.login=${SONAR_TOKEN}
      - echo "Logging into ECR..."
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ECR_LOGIN_URI

  build:
    commands:
      - echo "Building Docker image..."
      - docker build -t $ECR_REPOSITORY_URI:$TAG_NAME .
      - echo "Running smoke test..."
      - docker run --rm -d --name test-frontend -p 80:80 $ECR_REPOSITORY_URI:$TAG_NAME
      - sleep 5 && curl -f http://localhost:80 || echo "Test failed"
      - docker stop test-frontend || true
      - docker rm test-frontend || true

  post_build:
    commands:
      - echo "Pushing image to ECR..."
      - docker push $ECR_REPOSITORY_URI:$TAG_NAME
      - echo "Updating Helm chart values..."
      - mkdir -p config-repo && cd config-repo
      - git clone $CONFIG_REPO_URL . && git config user.email "tranvix.work@gmail.com"
      - git config user.name "Tran Dai Vi - N22DCCI044"
      - >
        sed -i "s/^  tag.*/  tag: \"$TAG_NAME\"/" shopnow-frontend-chart/values.yaml
      - git add shopnow-frontend-chart/values.yaml
      - git commit -m "Update frontend image version to $TAG_NAME"
      - git remote set-url origin https://$GITHUB_TOKEN@github.com/tranvix0910/shopnow-frontend-config.git
      - git push origin main
      - cd ..
      - echo "Frontend build & deploy completed for tag $TAG_NAME"

Create buildspec.yml file for Backend Java Spring Boot

Similarly, place the buildspec.yml file in the root directory of the backend project:

version: 0.2

env:
  secrets-manager:
    GITHUB_TOKEN: "workshop-2-shopnow-github-access-token:GITHUB_TOKEN"
    SONAR_TOKEN: "workshop-2-shopnow-sonarqube-access-token:SONARQUBE_TOKEN"

phases:
  install:
    runtime-versions:
      java: corretto17
    commands:
      - echo "Installing Maven & sonar-scanner..."
      - apt-get update && apt-get install -y git curl unzip maven
      - wget --tries=3 https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-7.1.0.4889-linux-x64.zip
      - unzip sonar-scanner-cli-7.1.0.4889-linux-x64.zip
      - mv sonar-scanner-7.1.0.4889-linux-x64 /opt/sonar-scanner
      - export PATH=$PATH:/opt/sonar-scanner/bin

  pre_build:
    commands:
      - echo "Pre-build steps..."
      - |
        GIT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
        GIT_HASH=$(git rev-parse --short HEAD)
        TAG_NAME="${GIT_TAG}_${GIT_HASH}"
        export TAG_NAME
        echo "Using image tag: $TAG_NAME"
      - test -f "pom.xml" || { echo "Missing pom.xml"; exit 1; }
      - test -f "Dockerfile" || { echo "Missing Dockerfile"; exit 1; }
      - echo "Building Java app..."
      - mvn clean package -DskipTests
      - echo "Running SonarQube analysis..."
      - sonar-scanner -Dsonar.projectKey="shopnow-user-service" -Dsonar.projectName="shopnow-user-service" -Dsonar.projectVersion="${TAG_NAME}" -Dsonar.sources=src/main/java -Dsonar.java.binaries=target -Dsonar.host.url=${SONAR_HOST_URL} -Dsonar.login=${SONAR_TOKEN}
      - echo "Logging into ECR..."
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ECR_LOGIN_URI

  build:
    commands:
      - echo "Building Docker image..."
      - docker build -t $ECR_REPOSITORY_URI:$TAG_NAME .
      - echo "Running health check..."
      - docker run --rm -d --name user-service -p 5865:5865 $ECR_REPOSITORY_URI:$TAG_NAME
      - sleep 10 && curl -f http://localhost:5865/actuator/health || echo "Health check failed"
      - docker stop user-service || true
      - docker rm user-service || true

  post_build:
    commands:
      - echo "Pushing image to ECR..."
      - docker push $ECR_REPOSITORY_URI:$TAG_NAME
      - echo "Updating Helm chart values..."
      - mkdir -p config-repo && cd config-repo
      - git clone $CONFIG_REPO_URL . && git config user.email "tranvix.work@gmail.com"
      - git config user.name "Tran Dai Vi - N22DCCI044"
      - >
        sed -i "s/^  tag.*/  tag: \"$TAG_NAME\"/" shopnow-user-service-chart/values.yaml
      - git add shopnow-user-service-chart/values.yaml
      - git commit -m "Update Java service image version to $TAG_NAME"
      - git remote set-url origin https://$GITHUB_TOKEN@github.com/tranvix0910/shopnow-backend-config.git
      - git push origin main
      - cd ..
      - echo "Backend build & deploy completed for tag $TAG_NAME"

TAG_NAME: Created from Git tag or commit hash, helps accurately version Docker images.

SONAR_TOKEN: Retrieved from Secrets Manager, used for authentication when sending SonarQube reports.

ECR_REPOSITORY_URI: URI of the Docker repository on Amazon ECR.

CONFIG_REPO_URL: Repository containing the application configuration Helm Chart.

These variables have all been configured in the CodeBuild Environment Variables.

Note: The remaining services in the system can use the same buildspec.yml structure as presented. You just need to adjust a few specific details such as:

  • sonar.projectKey & sonar.projectName: Project name in SonarQube.

  • Service name when running container for testing.

  • Corresponding Helm chart directory name in the configuration repo.

The buildspec.yml files are all pre-configured in their respective repositories. You can refer to the repositories I introduced in the Introduction.

Reusing configuration helps simplify the CI/CD process, increases consistency and ease of maintenance for the entire system.

Thus, you have completed setting up the buildspec.yml file for both frontend and backend services in the project. In the next section, we will proceed to test the entire pipeline using AWS CodeBuild to ensure everything works as expected.