모노레포의 강점중 하나는 여러 패키지가 한 레포에 있음에도, 멀티 레포의 배포경험을 포기하지 않는 것이 가능하다는 점이 아닐까 싶다.
Git Webhook 을 CircleCi 에서 전달 받을때, trigging 하는 조건을 Tag 를 사용하도록 하여, 여러 배포 조건( 서비스명, 배포할 환경(dev, prod, staging 등), 프로젝트 버전, Commit Hash 등 )을 모두 만족시킬 수 있도록 했다.
Tag 이름이 너무 길어져 만들기 어렵다? 이 또한 자동화가 가능한 Shell Script 를 작성하여 yarn deploy-dev-admin
같은 간단한 명령어 한번만으로 배포가 가능하도록 했다! (물론, pull request 는 별도로 하게 된다)
배포시작 & 종료 시점에 자동 Slack Message 전송
CircleCI Example Script for MonoRepo (config.yml)
// 보안상 실제 사용하는 스크립트에서 일부 제외하고 간략화 했습니다.
// config.yml
version: 2.1
orbs:
node: circleci/[email protected]
aws-cli: circleci/[email protected]
slack: circleci/[email protected]
references:
context_main: &context
context:
- main_context
- slack-secrets
executor_node: &executor
executor:
name: node/default
tag: '16.15.0'
resource_class_medium: &resource_class_medium
resource_class: medium+
restore_cache_dev: &restore_cache_dev
restore_cache:
key: v1-deps-dev-{{ checksum "yarn.lock" }}
save_cache_dev: &save_cache_dev
save_cache:
key: v1-deps-dev-{{ checksum "yarn.lock" }}
paths:
- node_modules
install: &install
run:
name: Authorize NPM & Install Dependency
command: |
yarn config set 'npmScopes["SCOPE"].npmAuthToken' $TOKEN
yarn install --frozen-lockfile
# must before build
preset_env_dev: &preset_env_dev
run:
name: Set Dev Application Envs
command: |
echo 'export REACT_APP_KEY=${REACT_APP_KEY}' >> "$BASH_ENV"
# must before build
preset_env_prod: &preset_env_prod
run:
name: Set Prod Application Envs
command: |
echo 'export REACT_APP_KEY=${REACT_APP_PROD_KEY}' >> "$BASH_ENV"
build: &build
run:
name: Build
command: yarn build-$SERVICE_NAME_PREFIX
# environments ----------
common_envs: &common_envs
environment:
NODE_OPTIONS: --max_old_space_size=4096
CI: 'false'
# Admin Enviroment Variables
admin_envs: &admin_envs
run:
name: Set Admin Envs
command: |
echo 'export PROJECT_NAME=react-admin' >> "$BASH_ENV"
echo 'export SERVICE_NAME_PREFIX=admin' >> "$BASH_ENV"
# Worker Enviroment Variables
worker_envs: &worker_envs
run:
name: Set Worker Envs
command: |
echo 'export PROJECT_NAME=react-worker' >> "$BASH_ENV"
echo 'export SERVICE_NAME_PREFIX=worker' >> "$BASH_ENV"
# Client Enviroment Variables
client_envs: &client_envs
run:
name: Set Client Envs
command: |
echo 'export PROJECT_NAME=react-client' >> "$BASH_ENV"
echo 'export SERVICE_NAME_PREFIX=client' >> "$BASH_ENV"
# ---------- environments end
# aws jobs ----------
aws_cli_setup: &aws_cli_setup
aws-cli/setup:
aws-region: AWS_REGION
session-duration: '3600'
aws_s3_sync_dev_burket: &aws_s3_sync_dev_burket
run:
name: S3 Sync Dev Bucket
command: $SYNC_DEV_COMMAND
aws_s3_sync_prod_burket: &aws_s3_sync_prod_burket
run:
name: S3 Sync Prod Bucket
command: $SYNC_PROD_COMMAND
aws_cloudfront_create_invalidation_dev: &aws_cloudfront_create_invalidation_dev
run:
name: Cloudfront create invalidation
command: $DISTRIBUTION_DEV_COMMAND
aws_cloudfront_create_invalidation_prod: &aws_cloudfront_create_invalidation_prod
run:
name: Cloudfront create invalidation
command: $DISTRIBUTION_PROD_COMMAND
# ---------- aws jobs end
# filters ----------
admin_dev_tag_filter: &admin_dev_tag_filter
filters:
tags:
only: /^(admin-dev-v)\\d+\\.\\d+\\.\\d+[-a-zA-Z0-9]*/ # ex) admin-dev-v1.255.23-hotfix | admin-dev-v1.2.3
branches:
ignore: /.*/
admin_prod_tag_filter: &admin_prod_tag_filter
filters:
tags:
only: /^(admin-prod-v)\\d+\\.\\d+\\.\\d+[-a-zA-Z0-9]*/ # ex) admin-prod-v1.255.23-hotfix | admin-prod-v1.2.3
branches:
ignore: /.*/
worker_dev_tag_filter: &worker_dev_tag_filter
filters:
tags:
only: /^(worker-dev-v)\\d+\\.\\d+\\.\\d+[-a-zA-Z0-9]*/
branches:
ignore: /.*/
worker_prod_tag_filter: &worker_prod_tag_filter
filters:
tags:
only: /^(worker-prod-v)\\d+\\.\\d+\\.\\d+[-a-zA-Z0-9]*/
branches:
ignore: /.*/
client_dev_tag_filter: &client_dev_tag_filter
filters:
tags:
only: /^(client-dev-v)\\d+\\.\\d+\\.\\d+[-a-zA-Z0-9]*/
branches:
ignore: /.*/
client_prod_tag_filter: &client_prod_tag_filter
filters:
tags:
only: /^(client-prod-v)\\d+\\.\\d+\\.\\d+[-a-zA-Z0-9]*/
branches:
ignore: /.*/
# ---------- filters end
# slack-notify ----------
slack_notify_start: &slack_notify_start
slack/notify:
event: always
custom: |
{
"blocks": [
{
"type": "section",
"text": {
"text": "[$CIRCLE_PROJECT_REPONAME][$CIRCLE_PROJECT_USERNAME/$CIRCLE_TAG] Deployment Start!"
}
}
]
}
slack_notify_pass: &slack_notify_pass
slack/notify:
event: pass
custom: |
{
"blocks": [
{
"type": "section",
"text": {
"text": "[$CIRCLE_PROJECT_REPONAME][$CIRCLE_PROJECT_USERNAME/$CIRCLE_TAG] Deployment Successful! 🎉"
}
}
]
}
slack_notify_fail: &slack_notify_fail
slack/notify:
event: fail
custom: |
{
"blocks": [
{
"type": "section",
"text": {
"text": "[$CIRCLE_PROJECT_REPONAME][$CIRCLE_PROJECT_USERNAME/$CIRCLE_TAG] Deployment failed 🚫"
}
}
]
}
# ---------- slack-notify end
jobs:
admin-dev-deploy:
<<: *executor
<<: *resource_class_medium
<<: *common_envs
steps:
- checkout
- 중략...
admin-prod-deploy:
<<: *executor
<<: *resource_class_medium
<<: *common_envs
steps:
- checkout
- 중략...
worker-dev-deploy:
<<: *executor
<<: *resource_class_medium
<<: *common_envs
steps:
- checkout
- 중략...
worker-prod-deploy:
<<: *executor
<<: *resource_class_medium
<<: *common_envs
steps:
- checkout
- 중략...
client-dev-deploy:
<<: *executor
<<: *resource_class_medium
<<: *common_envs
steps:
- checkout
- 중략...
client-prod-deploy:
<<: *executor
<<: *resource_class_medium
<<: *common_envs
steps:
- checkout
- 중략...
workflows:
admin-dev-deploy-workflow:
jobs:
- admin-dev-deploy:
<<: *context
<<: *admin_dev_tag_filter
admin-prod-deploy-workflow:
jobs:
- admin-prod-deploy:
<<: *context
<<: *admin_prod_tag_filter
worker-dev-deploy-workflow:
jobs:
- worker-dev-deploy:
<<: *context
<<: *worker_dev_tag_filter
worker-prod-deploy-workflow:
jobs:
- worker-prod-deploy:
<<: *context
<<: *worker_prod_tag_filter
client-dev-deploy-workflow:
jobs:
- client-dev-deploy:
<<: *context
<<: *client_dev_tag_filter
client-prod-deploy-workflow:
jobs:
- client-prod-deploy:
<<: *context
<<: *client_prod_tag_filter