DigitalConsumer.ISS/azure-pipelines.yml
2026-02-12 12:06:28 -05:00

479 lines
20 KiB
YAML

trigger:
branches:
include: [ develop, release/* ]
paths:
exclude:
- deployment/*
include:
- "*"
pr:
branches:
include: [ '*' ]
paths:
exclude:
- deployment/*
include:
- "*"
resources:
containers:
- container: node
image: ghcr.io/safelite/node-build:20
endpoint: GitHub Docker
- container: awscli
image: ghcr.io/safelite/awscli2-build:3.9
endpoint: GitHub Docker
repositories:
- repository: AzureDevOps
type: github
name: Safelite/AzureDevOps
endpoint: Safelite
ref: refs/tags/t5.5.40
variables:
- group: Digital-Infrastructure
- group: ISS-BuildBranches
- name: dockerImageName
value: 'playwright-tests'
- name: imageTag
value: '$(Build.BuildId)'
- name: totalShards
value: 2
- name: IS_REGRESSION
value: 'false'
stages:
# PR's
- ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
- stage: TestPr
displayName: Run Tests For PullRequest
jobs:
- template: templates/digital/vue-jest-run-unit-tests.yml@AzureDevOps
parameters:
nodeContainer: node
npmLocation: $(Build.SourcesDirectory)
testResultsFile: junit.xml
summaryFileLocation: $(Build.SourcesDirectory)/coverage/cobertura-coverage.xml
- job: playwright_tests
continueOnError: true
strategy:
matrix:
shard1:
shardNumber: 1
shard2:
shardNumber: 2
# shard3:
# shardNumber: 3
# shard4:
# shardNumber: 4
steps:
- task: Docker@2
displayName: 'Build Docker Image'
inputs:
command: build
dockerfile: Dockerfile.playwright
repository: $(dockerImageName)
tags: $(imageTag)
arguments: '--no-cache --pull'
- script: |
branch_name=$(System.PullRequest.SourceBranch)
echo "Retrieved branch name: '$branch_name'"
JIRA_CARD_NUMBER="${branch_name##*/}"
echo "Extracted JIRA Card number: '$JIRA_CARD_NUMBER'"
# Create container and run tests
container_id=$(docker create \
--ipc=host \
-e CCIS_API_AUTH=$(CCIS_API_AUTH) \
-e BASE_URL=$(BASE_URL) \
-e CCIS_API_URL=$(CCIS_API_URL) \
-e ADMIN_SERVICE_API_URL=$(ADMIN_SERVICE_API_URL) \
-e SHARD=$(shardNumber) \
-e CI=true \
-e ENABLE_MOCK_TESTING=true \
-e ENABLE_ACCESSIBILITY_TESTING=true \
-e NODE_ENV=$(NODE_ENV) \
$(dockerImageName):$(imageTag) \
npx concurrently -k -n "server,playwright"\
"sed -i \"s|^process\.env\.VUE_APP_CONSUMER_CF_DISTRO = .*|process\.env\.VUE_APP_CONSUMER_CF_DISTRO='https://digitalapi.test.safelite.io'|\" \"./vue.config.js\" && echo \"Updated config file to use TEST APIs\" && npm run serve -- --port=8080"\
"npx wait-on http://localhost:8080 && npm run test:playwright -- --shard=$(shardNumber)/$(totalShards) --reporter=list,blob --grep \"@$JIRA_CARD_NUMBER\"")
# Start container and stream logs
echo "Starting tests for shard $(shardNumber)..."
docker start -a $container_id
# Create directory for test results
echo "Creating test results directory..."
mkdir -p $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)
# Copy test results from container
echo "Copying test results..."
docker cp $container_id:/app/blob-report/. $(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)/
# Remove container
echo "Cleaning up container..."
docker rm $container_id
# Check if tests failed
if [ $? -ne 0 ]; then
echo "Tests failed in shard $(shardNumber) or tests don't exist for this shard number!"
exit 0 # Suppress error. It will be visible in report.
fi
displayName: 'Run Playwright Tests - Shard $(shardNumber)'
- task: PublishPipelineArtifact@1
displayName: 'Publish Test Reports - Shard $(shardNumber)'
condition: always()
inputs:
targetPath: '$(System.DefaultWorkingDirectory)/blob-reports/shard-$(shardNumber)'
artifact: 'playwright-report-shard-$(shardNumber)'
publishLocation: 'pipeline'
- script: |
docker rmi $(dockerImageName):$(imageTag) -f
displayName: 'Cleanup Docker Image'
condition: always()
- job: download_and_merge_reports
dependsOn: playwright_tests
timeoutInMinutes: 10
cancelTimeoutInMinutes: 10
steps:
- task: DownloadPipelineArtifact@2
inputs:
targetPath: '$(System.DefaultWorkingDirectory)/playwright-reports'
- task: Docker@2
displayName: 'Build Docker Image'
inputs:
command: build
dockerfile: Dockerfile.playwright
repository: $(dockerImageName)
tags: $(imageTag)
arguments: '--no-cache --pull'
- bash: |
branch_name=$(System.PullRequest.SourceBranch)
echo "Retrieved branch name: '$branch_name'"
JIRA_CARD_NUMBER="${branch_name##*/}"
echo "Extracted JIRA Card number: '$JIRA_CARD_NUMBER'"
# Create container for jira writeback
container_id=$(docker create \
--ipc=host \
-e JIRA_SERVER=$(JIRA_SERVER) \
-e JIRA_USERNAME=$(JIRA_USERNAME) \
-e JIRA_API_KEY=$(JIRA_API_KEY) \
-e IS_REGRESSION=$(IS_REGRESSION) \
-e JIRA_CARD_NUMBER="$JIRA_CARD_NUMBER" \
$(dockerImageName):$(imageTag) \
bash -c "echo \"Moving Playwright reports out of subfolders...\" &&
find ./playwright-reports/ -mindepth 2 -type f -exec mv {} ./playwright-reports/ \; &&
echo \"Merging reports...\" &&
export NODE_OPTIONS=--max_old_space_size=4096
PLAYWRIGHT_JUNIT_OUTPUT_DIR='/app/test-results' PLAYWRIGHT_JUNIT_OUTPUT_NAME='junit_results.xml' npx playwright merge-reports --reporter=junit,\"/app/playwright-tests/impl/reporter/JiraWritebackReporter.ts\" ./playwright-reports
echo \"Contents of ortoni-report:\" && ls ./ortoni-report &&
echo 'Current dir: ' && pwd
echo 'Contents of current dir: ' && ls
echo 'Contents of /app/test-results' && ls /app/test-results ")
# Start container and stream logs
echo "Starting merge"
docker start -a $container_id
# Create directory for test results
echo "Creating test results directory..."
mkdir -p $(System.DefaultWorkingDirectory)/ortoni-report
mkdir -p $(System.DefaultWorkingDirectory)/test-results
# Copy test results from container
echo "Copying test results..."
docker cp $container_id:/app/ortoni-report/. $(System.DefaultWorkingDirectory)/ortoni-report
docker cp $container_id:/app/test-results/junit_results.xml $(System.DefaultWorkingDirectory)/test-results
# Remove container
echo "Cleaning up container..."
docker rm $container_id
env:
JIRA_API_KEY: $(JIRA_API_KEY)
displayName: merge_and_publish_results_to_jira
- task: PublishTestResults@2
displayName: 'Publish test results'
inputs:
searchFolder: 'test-results'
testResultsFormat: 'JUnit'
testResultsFiles: 'junit_results.xml'
mergeTestResults: true
failTaskOnFailedTests: false
testRunTitle: 'Playwright Tests'
condition: succeededOrFailed()
- task: PublishPipelineArtifact@1
displayName: 'Publish Merged Report'
condition: always()
inputs:
targetPath: '$(System.DefaultWorkingDirectory)/ortoni-report'
artifact: 'playwright-merged-report'
publishLocation: 'pipeline'
- script: |
docker rmi $(dockerImageName):$(imageTag) -f
displayName: 'Cleanup Docker Image'
condition: always()
- ${{ else }}:
# Dev Build/Deploy
- stage: Dev
condition: eq(variables['Build.SourceBranch'], variables['dev-branch'] )
variables:
- group: ISS-Dev
jobs:
- deployment: devBuildDeployment
displayName: Build and Deploy ISS - Dev
environment: digitalCloud-dev
container: node
workspace:
clean: all
strategy:
runOnce:
deploy:
steps:
- checkout: self
clean: true
- task: CopyFiles@2
inputs:
SourceFolder: 'public/apple-pay/dev'
Contents: '**'
TargetFolder: 'public/.well-known'
CleanTargetFolder: true
- task: DeleteFiles@1
inputs:
SourceFolder: 'public/apple-pay'
Contents: '**'
RemoveSourceFolder: true
- template: templates/digital/step-build-vue.yml@AzureDevOps
parameters:
buildOutputDir: dist
environment: Dev
- template: templates/digital/step-deploy-vue.yml@AzureDevOps
parameters:
artifactName: vueDistDev
awsProfile: $(devDeploymentProfile)
deployBuckets:
safelite-dev-iss-us-east-2:
clearFolder: true
deployFolder: ''
region: us-east-2
safelite-dev-iss-us-east-1:
clearFolder: true
deployFolder: ''
region: us-east-1
appDeployVariables:
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
__VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__)
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
distributionId: $(cfDistributionId)
paths: /*
awsProfile: $(devDeploymentProfile)
# Test Build/Deploy
- stage: Test
condition: eq(variables['Build.SourceBranch'], variables['test-branch'] )
variables:
- group: ISS-Test
jobs:
- deployment: testBuildDeployment
displayName: Build and Deploy ISS - Test
environment: NoApproval-All
container: node
workspace:
clean: all
strategy:
runOnce:
deploy:
steps:
- checkout: self
clean: true
- task: CopyFiles@2
inputs:
SourceFolder: 'public/apple-pay/test'
Contents: '**'
TargetFolder: 'public/.well-known'
CleanTargetFolder: true
- task: DeleteFiles@1
inputs:
SourceFolder: 'public/apple-pay'
Contents: '**'
RemoveSourceFolder: true
- template: templates/digital/step-build-vue.yml@AzureDevOps
parameters:
buildOutputDir: dist
environment: Test
- template: templates/digital/step-deploy-vue.yml@AzureDevOps
parameters:
artifactName: vueDistTest
awsProfile: $(sysDeploymentProfile)
deployBuckets:
safelite-sys-iss-us-east-1:
clearFolder: true
deployFolder: ""
region: us-east-1
safelite-sys-iss-us-east-2:
clearFolder: true
deployFolder: ""
region: us-east-2
appDeployVariables:
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
__VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__)
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
distributionId: $(cfDistributionId)
paths: /*
awsProfile: $(sysDeploymentProfile)
# QA Build/Deploy
- stage: QA
condition: eq(variables['Build.SourceBranch'], variables['qa-branch'] )
variables:
- group: ISS-QA
jobs:
- deployment: qaBuildDeployment
displayName: Build and Deploy ISS - QA
environment: NoApproval-All
container: node
workspace:
clean: all
strategy:
runOnce:
deploy:
steps:
- checkout: self
clean: true
- task: CopyFiles@2
inputs:
SourceFolder: 'public/apple-pay/qa'
Contents: '**'
TargetFolder: 'public/.well-known'
CleanTargetFolder: true
- task: DeleteFiles@1
inputs:
SourceFolder: 'public/apple-pay'
Contents: '**'
RemoveSourceFolder: true
- template: templates/digital/step-build-vue.yml@AzureDevOps
parameters:
buildOutputDir: dist
environment: Qa
- template: templates/digital/step-deploy-vue.yml@AzureDevOps
parameters:
artifactName: vueDistQa
awsProfile: $(qaDeploymentProfile)
deployBuckets:
safelite-qa-iss-us-east-1:
clearFolder: true
deployFolder: ""
region: us-east-1
safelite-qa-iss-us-east-2:
clearFolder: true
deployFolder: ""
region: us-east-2
appDeployVariables:
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
__VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__)
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
distributionId: $(cfDistributionId)
paths: /*
awsProfile: $(qaDeploymentProfile)
# Prod Build/Deploy
- stage: Prod
condition: succeeded('QA')
variables:
- group: ISS-Prod
jobs:
- deployment: prodBuildDeployment
displayName: Build and Deploy ISS - Prod
environment: digitalCloud-prod
container: node
workspace:
clean: all
strategy:
runOnce:
deploy:
steps:
- checkout: self
clean: true
- task: CopyFiles@2
inputs:
SourceFolder: 'public/apple-pay/prod'
Contents: '**'
TargetFolder: 'public/.well-known'
CleanTargetFolder: true
- task: DeleteFiles@1
inputs:
SourceFolder: 'public/apple-pay'
Contents: '**'
RemoveSourceFolder: true
- template: templates/digital/step-build-vue.yml@AzureDevOps
parameters:
buildOutputDir: dist
environment: Prod
- template: templates/digital/step-deploy-vue.yml@AzureDevOps
parameters:
artifactName: vueDistProd
awsProfile: $(prodDeploymentProfile)
deployBuckets:
safelite-prod-iss-us-east-1:
clearFolder: true
deployFolder: ""
region: us-east-1
safelite-prod-iss-us-east-2:
clearFolder: true
deployFolder: ""
region: us-east-2
appDeployVariables:
__VUE_APP_CONSUMER_CF_DISTRO__: $(__VUE_APP_CONSUMER_CF_DISTRO__)
__VUE_APP_CURRENT_ENVIRONMENT__: $(__VUE_APP_CURRENT_ENVIRONMENT__)
__VUE_APP_CUSTOMER_PORTAL_URL__: $(__VUE_APP_CUSTOMER_PORTAL_URL__)
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
__VUE_APP_SAFELITE_HOP__: $(__VUE_APP_SAFELITE_HOP__)
indexDeployVariables:
__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__: $(__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__)
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
__VUE_APP_GOOGLE_MAPS_API_SCRIPT__: $(__VUE_APP_GOOGLE_MAPS_API_SCRIPT__)
- template: templates/digital/invalidate-cloudfront-cache.yml@AzureDevOps
parameters:
awsCliContainer: awscli
distributionId: $(cfDistributionId)
paths: /*
awsProfile: $(prodDeploymentProfile)
- template: templates/digital/auto-tag.yml@AzureDevOps
parameters:
dependsOn: prodBuildDeployment
userName: SafeliteAzureDevops
userEmail: githubazuredevops@safelite.com