OMDSCloud/azure-pipelines-staging.yml
湯本 開 cf56239da2 Merged PR 669: Pipeline上でdocker-composeを用いてMySQLを起動する方法を調査する
## 概要
[Task3427: Pipeline上でdocker-composeを用いてMySQLを起動する方法を調査する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3427)

- MySQLでUnitTestを実施する仕組みを作成
  - DevContainerにUnitTestからのみアクセスされるMySQLが動くコンテナ( `test_mysql_db` )を追加
  - テストでMySQLを使用し、上記 `test_mysql_db` に対してアクセスするよう構成
  - テストの前処理で `test_mysql_db` の全てのテーブルをTRUNCATEする処理を実行し、データをクリアする
    - 常にテスト用MySQLは起動しているが、テスト実行前にクリアされるので空っぽ前提の状態でテストを実施できる
  - 実際にMySQLを使用して実行されるテストを1つ追加
    - `users.service.spec.ts | UsersService.createUser` が対象
- Pipeline上でDevContainerを起動し、DevContainer内で `npm ci` `(UnitTest用DBへの) migration` `npm run test` を行う仕組みを作成
  - Pipelineを追加
    - **StagingPipelineでテスト・ステップを切り出し**
  - Pipelineテスト用DevContainer(のdocker-compose.yml)を作成
    - 構成は通常の物と同一だが、ネットワーク設定だけ外部のMySQLやRedisと通信しない前提に変更している
  - テストの実行環境を自己ホストではなく、一般で提供されているマシンに変更
    - 自己ホストのマシンにdocker-composeが入ってない/ビルド後のクリア処理が大変等の理由
- テストで使用する環境変数を `.env.test` という別環境変数に切り出し
  - そうすることで、PipelineでKeyVaultへのアクセスをする必要がなくなる
- **migrationの漏れを修正**
  - テストが通らないことで発覚したmigration漏れを修正
- **テストコードを修正**
  - **Date型のミリ秒単位の誤差を setMillseconds(0) を用いることで0にセットするコードを追加(ライセンス付近)**

## レビューポイント
- **WIPレビュー以降の追加の修正で違和感のある箇所はないか**
  - コミット履歴等で追っていただいた方が楽かと思います
  - 直近の`Merge branch 'develop' into feature/3427/poc-mysql-test` 以降がWIP以降に変更されたコードです
- **レビュー通過後、developのPipelineについては手動で同等のコードに変更→PRをdevelopにマージ予定だが進め方として問題はないか**
- [※WIPでレビュー済み] テスト用DevContainerを別途作成したが、現行のDevContainerを使用するよう頑張った方がいいか?
  - [※WIPでレビュー済み] CI/CDの実行速度面を考慮し、使用されないMySQLとRedisのサービスとネットワーク作成が同居しているdocker-compose.ymlの実行を避けたが、管理対象は増えているので議論の余地はある
- [※WIPでレビュー済み] MySQLでUnitTestを実行する際に懸念事項はないか
- [※WIPでレビュー済み] Dockerを起動する関係でCI/CDのスピードが数分遅くなると思われるが、許容可能か
- [※WIPでレビュー済み] `.env.test` に置き換えて問題ないか

## 動作確認状況
- ローカル&Pipelineで実行して確認済
2024-01-23 07:58:42 +00:00

363 lines
12 KiB
YAML

# Pipeline側でKeyVaultやDocker、AppService等に対する操作権限を持ったServiceConenctionを作成しておくこと
# また、環境変数 STATIC_DICTATION_DEPLOYMENT_TOKEN の値として静的WebAppsのデプロイトークンを設定しておくこと
trigger:
branches:
include:
- main
tags:
include:
- stage-*
jobs:
- job: initialize
displayName: Initialize
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
persistCredentials: true
- script: |
git fetch origin main:main
if git merge-base --is-ancestor $(Build.SourceVersion) main; then
echo "This commit is in the main branch."
else
echo "This commit is not in the main branch."
exit 1
fi
displayName: 'タグが付けられたCommitがmainブランチに存在するか確認'
- job: backend_test
dependsOn: initialize
condition: succeeded('initialize')
displayName: UnitTest
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: Bash@3
displayName: Bash Script (Test)
inputs:
targetType: inline
workingDirectory: dictation_server/.devcontainer
script: |
docker-compose -f pipeline-docker-compose.yml build
docker-compose -f pipeline-docker-compose.yml up -d
docker-compose exec -T dictation_server sudo npm ci
docker-compose exec -T dictation_server sudo npm run migrate:up:test
docker-compose exec -T dictation_server sudo npm run test
- job: backend_build
dependsOn: backend_test
condition: succeeded('backend_test')
displayName: Build And Push Backend Image
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: Npm@1
displayName: npm ci
inputs:
command: ci
workingDir: dictation_server
verbose: false
- task: Docker@0
displayName: build
inputs:
azureSubscriptionEndpoint: 'omds-service-connection-stg'
azureContainerRegistry: '{"loginServer":"crodmsregistrymaintenance.azurecr.io", "id" : "/subscriptions/108fb131-cdca-4729-a2be-e5bd8c0b3ba7/resourceGroups/maintenance-rg/providers/Microsoft.ContainerRegistry/registries/crOdmsRegistryMaintenance"}'
dockerFile: DockerfileServerDictation.dockerfile
imageName: odmscloud/staging/dictation:$(Build.SourceVersion)
buildArguments: |
BUILD_VERSION=$(Build.SourceVersion)
- task: Docker@0
displayName: push
inputs:
azureSubscriptionEndpoint: 'omds-service-connection-stg'
azureContainerRegistry: '{"loginServer":"crodmsregistrymaintenance.azurecr.io", "id" : "/subscriptions/108fb131-cdca-4729-a2be-e5bd8c0b3ba7/resourceGroups/maintenance-rg/providers/Microsoft.ContainerRegistry/registries/crOdmsRegistryMaintenance"}'
action: Push an image
imageName: odmscloud/staging/dictation:$(Build.SourceVersion)
- job: frontend_build_staging
dependsOn: backend_build
condition: succeeded('backend_build')
displayName: Build Frontend Files(staging)
variables:
storageAccountName: saomdspipeline
environment: staging
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: Npm@1
displayName: npm ci
inputs:
command: ci
workingDir: dictation_client
verbose: false
- task: Bash@3
displayName: Bash Script
inputs:
targetType: inline
script: cd dictation_client && npm run build:stg
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: dictation_client/build
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.SourceVersion).zip'
replaceExistingArchive: true
- task: AzureCLI@2
inputs:
azureSubscription: 'omds-service-connection-stg'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az storage blob upload \
--auth-mode login \
--account-name $(storageAccountName) \
--container-name $(environment) \
--name $(Build.SourceVersion).zip \
--type block \
--overwrite \
--file $(Build.ArtifactStagingDirectory)/$(Build.SourceVersion).zip
- job: frontend_build_production
dependsOn: frontend_build_staging
condition: succeeded('frontend_build_staging')
displayName: Build Frontend Files(production)
variables:
storageAccountName: saomdspipeline
environment: production
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: Npm@1
displayName: npm ci
inputs:
command: ci
workingDir: dictation_client
verbose: false
- task: Bash@3
displayName: Bash Script
inputs:
targetType: inline
script: cd dictation_client && npm run build:prod
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: dictation_client/build
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/$(Build.SourceVersion).zip'
replaceExistingArchive: true
- task: AzureCLI@2
inputs:
azureSubscription: 'omds-service-connection-stg'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az storage blob upload \
--auth-mode login \
--account-name $(storageAccountName) \
--container-name $(environment) \
--name $(Build.SourceVersion).zip \
--type block \
--overwrite \
--file $(Build.ArtifactStagingDirectory)/$(Build.SourceVersion).zip
- job: function_build
dependsOn: frontend_build_production
condition: succeeded('frontend_build_production')
displayName: Build And Push Function Image
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: Npm@1
displayName: npm ci
inputs:
command: ci
workingDir: dictation_function
verbose: false
- task: AzureKeyVault@2
displayName: 'Azure Key Vault: kv-odms-secret-stg'
inputs:
ConnectedServiceName: 'omds-service-connection-stg'
KeyVaultName: kv-odms-secret-stg
SecretsFilter: '*'
- task: Bash@3
displayName: Bash Script (Test)
inputs:
targetType: inline
script: |
cd dictation_function
npm run test
env:
TENANT_NAME: xxxxxxxxxxxx
SIGNIN_FLOW_NAME: xxxxxxxxxxxx
ADB2C_TENANT_ID: $(adb2c-tenant-id)
ADB2C_CLIENT_ID: $(adb2c-client-id)
ADB2C_CLIENT_SECRET: $(adb2c-client-secret)
ADB2C_ORIGIN: xxxxxx
SENDGRID_API_KEY: $(sendgrid-api-key)
MAIL_FROM: xxxxxx
APP_DOMAIN: xxxxxxxxx
REDIS_HOST: xxxxxxxxxxxx
REDIS_PORT: 0
REDIS_PASSWORD: xxxxxxxxxxxx
- task: Docker@0
displayName: build
inputs:
azureSubscriptionEndpoint: 'omds-service-connection-stg'
azureContainerRegistry: '{"loginServer":"crodmsregistrymaintenance.azurecr.io", "id" : "/subscriptions/108fb131-cdca-4729-a2be-e5bd8c0b3ba7/resourceGroups/maintenance-rg/providers/Microsoft.ContainerRegistry/registries/crOdmsRegistryMaintenance"}'
dockerFile: DockerfileFunctionDictation.dockerfile
imageName: odmscloud/staging/dictation_function:$(Build.SourceVersion)
buildArguments: |
BUILD_VERSION=$(Build.SourceVersion)
- task: Docker@0
displayName: push
inputs:
azureSubscriptionEndpoint: 'omds-service-connection-stg'
azureContainerRegistry: '{"loginServer":"crodmsregistrymaintenance.azurecr.io", "id" : "/subscriptions/108fb131-cdca-4729-a2be-e5bd8c0b3ba7/resourceGroups/maintenance-rg/providers/Microsoft.ContainerRegistry/registries/crOdmsRegistryMaintenance"}'
action: Push an image
imageName: odmscloud/staging/dictation_function:$(Build.SourceVersion)
- job: backend_deploy
dependsOn: function_build
condition: succeeded('function_build')
displayName: Backend Deploy
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureWebAppContainer@1
inputs:
azureSubscription: 'omds-service-connection-stg'
appName: 'app-odms-dictation-stg'
deployToSlotOrASE: true
resourceGroupName: 'stg-application-rg'
slotName: 'staging'
containers: 'crodmsregistrymaintenance.azurecr.io/odmscloud/staging/dictation:$(Build.SourceVersion)'
- job: frontend_deploy
dependsOn: backend_deploy
condition: succeeded('backend_deploy')
displayName: Deploy Frontend Files
variables:
storageAccountName: saomdspipeline
environment: staging
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureCLI@2
inputs:
azureSubscription: 'omds-service-connection-stg'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az storage blob download \
--auth-mode login \
--account-name $(storageAccountName) \
--container-name $(environment) \
--name $(Build.SourceVersion).zip \
--file $(Build.SourcesDirectory)/$(Build.SourceVersion).zip
- task: Bash@3
displayName: Bash Script
inputs:
targetType: inline
script: unzip $(Build.SourcesDirectory)/$(Build.SourceVersion).zip -d $(Build.SourcesDirectory)/$(Build.SourceVersion)
- task: AzureStaticWebApp@0
displayName: 'Static Web App: '
inputs:
workingDirectory: '$(Build.SourcesDirectory)'
app_location: '/$(Build.SourceVersion)'
config_file_location: /dictation_client
skip_app_build: true
skip_api_build: true
is_static_export: false
verbose: false
azure_static_web_apps_api_token: $(STATIC_DICTATION_DEPLOYMENT_TOKEN)
- job: function_deploy
dependsOn: frontend_deploy
condition: succeeded('frontend_deploy')
displayName: Function Deploy
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureFunctionAppContainer@1
inputs:
azureSubscription: 'omds-service-connection-stg'
appName: 'func-odms-dictation-stg'
imageName: 'crodmsregistrymaintenance.azurecr.io/odmscloud/staging/dictation_function:$(Build.SourceVersion)'
- job: smoke_test
dependsOn: function_deploy
condition: succeeded('function_deploy')
displayName: 'smoke test'
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
# スモークテスト用にjobを確保
- job: swap_slot
dependsOn: smoke_test
condition: succeeded('smoke_test')
displayName: 'Swap Staging and Production'
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureAppServiceManage@0
displayName: 'Azure App Service Manage: app-odms-dictation-stg'
inputs:
azureSubscription: 'omds-service-connection-stg'
action: 'Swap Slots'
WebAppName: 'app-odms-dictation-stg'
ResourceGroupName: 'stg-application-rg'
SourceSlot: 'staging'
SwapWithProduction: true
- job: migration
dependsOn: swap_slot
condition: succeeded('swap_slot')
displayName: DB migration
pool:
name: odms-deploy-pipeline
steps:
- checkout: self
clean: true
fetchDepth: 1
- task: AzureKeyVault@2
displayName: 'Azure Key Vault: kv-odms-secret-stg'
inputs:
ConnectedServiceName: 'omds-service-connection-stg'
KeyVaultName: kv-odms-secret-stg
- task: CmdLine@2
displayName: migration
inputs:
script: >2
# DB接続情報書き換え
sed -i -e "s/DB_NAME/$(db-name)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PASS/$(admin-db-pass)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_USERNAME/$(admin-db-user)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_PORT/$(db-port)/g" ./dictation_server/db/dbconfig.yml
sed -i -e "s/DB_HOST/$(db-host)/g" ./dictation_server/db/dbconfig.yml
sql-migrate --version
cat ./dictation_server/db/dbconfig.yml
# migration実行
sql-migrate up -config=./dictation_server/db/dbconfig.yml -env=ci