Skip to content

Commit 469b038

Browse files
committed
Merge remote-tracking branch 'origin/dev' into clean
# Conflicts: # src/app.controller.spec.ts # src/user/user.controller.spec.ts # src/user/user.service.spec.ts
2 parents 34211ba + 0ef79b6 commit 469b038

76 files changed

Lines changed: 5365 additions & 445 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 실행 환경 (production | development)
2+
NODE_ENV=development
3+
14
# Required for all modes
25
SLACK_BOT_TOKEN=xoxb-
36

@@ -20,6 +23,8 @@ DB_SYNCHRONIZE=true
2023
GOOGLE_CLIENT_ID=
2124
GOOGLE_CLIENT_SECRET=
2225
GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback
26+
MCP_GOOGLE_REDIRECT_URI=http://localhost:3000/mcp/auth/callback
27+
MCP_BASE_URL=http://localhost:3000
2328

2429
# Google Service Account
2530
GOOGLE_SERVICE_ACCOUNT_EMAIL=example@example.iam.gserviceaccount.com
@@ -43,4 +48,13 @@ CLOUDFLARE_TUNNEL_TOKEN=
4348
ANTHROPIC_API_KEY=
4449

4550
# 관리자 슬랙 아이디
46-
ADMIN_SLACK_ID=
51+
ADMIN_SLACK_ID=
52+
53+
# 모니터링 스택 (docker-compose.monitoring.yml)
54+
GRAFANA_PASSWORD=
55+
# prod EC2에서는 prometheus.prod.yml 사용 (aws_sd_configs로 ECS 자동 탐색)
56+
# PROMETHEUS_CONFIG=./monitoring/prometheus/prometheus.prod.yml
57+
58+
# Loki 로그 수집 (미설정 시 콘솔 출력만)
59+
# 로컬(npm): http://localhost:3100 / 로컬·dev(Docker): http://host.docker.internal:3100 / prod: http://<EC2 IP>:3100
60+
LOKI_URL=

.github/workflows/ci.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
- dev
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
13+
jobs:
14+
ci:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: 22
25+
cache: npm
26+
27+
- name: Install dependencies
28+
run: npm ci
29+
30+
- name: Format check
31+
id: lint
32+
run: npx prettier --check "src/**/*.ts" "**/*.yml"
33+
continue-on-error: true
34+
35+
- name: Build
36+
id: build
37+
run: npm run build
38+
continue-on-error: true
39+
40+
- name: Test
41+
id: test
42+
run: npm run test:cov -- --forceExit
43+
continue-on-error: true
44+
45+
- name: Comment CI result on PR
46+
if: github.event_name == 'pull_request'
47+
uses: actions/github-script@v7
48+
with:
49+
script: |
50+
const emoji = (outcome) => outcome === 'success' ? '✅' : '❌';
51+
52+
const lintOutcome = '${{ steps.lint.outcome }}';
53+
const buildOutcome = '${{ steps.build.outcome }}';
54+
const testOutcome = '${{ steps.test.outcome }}';
55+
56+
const allPassed = [lintOutcome, buildOutcome, testOutcome].every(o => o === 'success');
57+
58+
const body = `#### CI 결과 — ${allPassed ? '✅ 통과' : '❌ 실패'}
59+
60+
| 단계 | 결과 |
61+
|------|------|
62+
| Format (Prettier) | ${emoji(lintOutcome)} ${lintOutcome} |
63+
| Build | ${emoji(buildOutcome)} ${buildOutcome} |
64+
| Test | ${emoji(testOutcome)} ${testOutcome} |`;
65+
66+
github.rest.issues.createComment({
67+
issue_number: context.issue.number,
68+
owner: context.repo.owner,
69+
repo: context.repo.repo,
70+
body,
71+
});
72+
73+
- name: Fail if any step failed
74+
if: steps.lint.outcome == 'failure' || steps.build.outcome == 'failure' || steps.test.outcome == 'failure'
75+
run: exit 1

.github/workflows/deploy-dev.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Deploy to Dev Server
22

33
on:
44
workflow_run:
5-
workflows: ["Build and Push Docker Image"]
5+
workflows: ['Build and Push Docker Image']
66
types: [completed]
77
branches: [dev]
88

@@ -27,6 +27,10 @@ jobs:
2727
working-directory: ${{ vars.DEPLOY_DIR }}
2828
run: make dev
2929

30+
- name: Restart monitoring stack
31+
working-directory: ${{ vars.DEPLOY_DIR }}
32+
run: make monitoring
33+
3034
- name: Verify app is running
3135
run: |
3236
sleep 10
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: Deploy Monitoring Stack
2+
3+
# workflow_call: docker-publish.yml의 운영 환경 배포(release) 완료 후 자동 호출
4+
on:
5+
workflow_dispatch:
6+
workflow_call:
7+
inputs:
8+
instance_id:
9+
type: string
10+
required: false
11+
12+
jobs:
13+
deploy:
14+
name: Deploy to monitoring EC2
15+
runs-on: ubuntu-latest
16+
permissions:
17+
id-token: write
18+
contents: read
19+
20+
steps:
21+
- name: Configure AWS credentials
22+
uses: aws-actions/configure-aws-credentials@v4
23+
with:
24+
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
25+
aws-region: ap-northeast-2
26+
27+
- name: Resolve instance ID
28+
id: instance
29+
run: |
30+
INSTANCE_ID="${{ inputs.instance_id }}"
31+
if [ -z "$INSTANCE_ID" ] || [ "$INSTANCE_ID" = "null" ]; then
32+
INSTANCE_ID=$(aws ec2 describe-instances \
33+
--filters "Name=tag:Name,Values=bannote-prod-monitoring" "Name=instance-state-name,Values=running" \
34+
--query "Reservations[0].Instances[0].InstanceId" \
35+
--output text)
36+
fi
37+
echo "id=$INSTANCE_ID" >> $GITHUB_OUTPUT
38+
39+
- name: Pull latest code
40+
id: pull
41+
run: |
42+
COMMAND_ID=$(aws ssm send-command \
43+
--instance-id ${{ steps.instance.outputs.id }} \
44+
--document-name "AWS-RunShellScript" \
45+
--parameters '{"commands":["sudo su - ec2-user -c \"cd /home/ec2-user/gsc-slack-app && git pull origin main\""]}' \
46+
--query "Command.CommandId" \
47+
--output text)
48+
echo "command_id=$COMMAND_ID" >> $GITHUB_OUTPUT
49+
50+
- name: Wait and get pull result
51+
run: |
52+
aws ssm wait command-executed \
53+
--command-id ${{ steps.pull.outputs.command_id }} \
54+
--instance-id ${{ steps.instance.outputs.id }} || true
55+
RESULT=$(aws ssm get-command-invocation \
56+
--command-id ${{ steps.pull.outputs.command_id }} \
57+
--instance-id ${{ steps.instance.outputs.id }} --output json)
58+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); print(r['StandardOutputContent']); print(r['StandardErrorContent'])"
59+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); exit(0 if r['Status']=='Success' else 1)"
60+
61+
- name: Write .env file
62+
id: env
63+
run: |
64+
COMMAND_ID=$(aws ssm send-command \
65+
--instance-id ${{ steps.instance.outputs.id }} \
66+
--document-name "AWS-RunShellScript" \
67+
--parameters '{"commands":["GRAFANA_PASSWORD=$(aws secretsmanager get-secret-value --secret-id ${{ secrets.SECRETS_ARN }} --region ap-northeast-2 --query SecretString --output text | python3 -c \"import sys,json; print(json.load(sys.stdin)[\\\"GRAFANA_PASSWORD\\\"])\") && printf \"GRAFANA_PASSWORD=%s\\nPROMETHEUS_CONFIG=./monitoring/prometheus/prometheus.prod.yml\\n\" \"$GRAFANA_PASSWORD\" > /home/ec2-user/gsc-slack-app/.env && echo done"]}' \
68+
--query "Command.CommandId" \
69+
--output text)
70+
echo "command_id=$COMMAND_ID" >> $GITHUB_OUTPUT
71+
72+
- name: Wait and get env result
73+
run: |
74+
aws ssm wait command-executed \
75+
--command-id ${{ steps.env.outputs.command_id }} \
76+
--instance-id ${{ steps.instance.outputs.id }} || true
77+
RESULT=$(aws ssm get-command-invocation \
78+
--command-id ${{ steps.env.outputs.command_id }} \
79+
--instance-id ${{ steps.instance.outputs.id }} --output json)
80+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); print(r['StandardOutputContent']); print(r['StandardErrorContent'])"
81+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); exit(0 if r['Status']=='Success' else 1)"
82+
83+
- name: Ensure docker compose plugin
84+
id: setup
85+
run: |
86+
COMMAND_ID=$(aws ssm send-command \
87+
--instance-id ${{ steps.instance.outputs.id }} \
88+
--document-name "AWS-RunShellScript" \
89+
--parameters '{"commands":["mkdir -p /usr/libexec/docker/cli-plugins && ln -sf /usr/local/bin/docker-compose /usr/libexec/docker/cli-plugins/docker-compose && docker compose version"]}' \
90+
--query "Command.CommandId" \
91+
--output text)
92+
echo "command_id=$COMMAND_ID" >> $GITHUB_OUTPUT
93+
94+
- name: Wait and get setup result
95+
run: |
96+
aws ssm wait command-executed \
97+
--command-id ${{ steps.setup.outputs.command_id }} \
98+
--instance-id ${{ steps.instance.outputs.id }} || true
99+
RESULT=$(aws ssm get-command-invocation \
100+
--command-id ${{ steps.setup.outputs.command_id }} \
101+
--instance-id ${{ steps.instance.outputs.id }} --output json)
102+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); print(r['StandardOutputContent']); print(r['StandardErrorContent'])"
103+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); exit(0 if r['Status']=='Success' else 1)"
104+
105+
- name: Deploy monitoring stack
106+
id: deploy
107+
run: |
108+
COMMAND_ID=$(aws ssm send-command \
109+
--instance-id ${{ steps.instance.outputs.id }} \
110+
--document-name "AWS-RunShellScript" \
111+
--parameters '{"commands":["sudo su - ec2-user -c \"cd /home/ec2-user/gsc-slack-app && make monitoring\""]}' \
112+
--query "Command.CommandId" \
113+
--output text)
114+
echo "command_id=$COMMAND_ID" >> $GITHUB_OUTPUT
115+
116+
- name: Wait and get deploy result
117+
run: |
118+
aws ssm wait command-executed \
119+
--command-id ${{ steps.deploy.outputs.command_id }} \
120+
--instance-id ${{ steps.instance.outputs.id }} || true
121+
RESULT=$(aws ssm get-command-invocation \
122+
--command-id ${{ steps.deploy.outputs.command_id }} \
123+
--instance-id ${{ steps.instance.outputs.id }} --output json)
124+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); print(r['StandardOutputContent']); print(r['StandardErrorContent'])"
125+
echo "$RESULT" | python3 -c "import sys,json; r=json.load(sys.stdin); exit(0 if r['Status']=='Success' else 1)"

.github/workflows/docker-publish.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ on:
55
branches:
66
- dev
77
release:
8-
types: [ published ]
8+
types: [published]
9+
workflow_dispatch:
910

1011
env:
1112
REGISTRY: ghcr.io
@@ -37,6 +38,7 @@ jobs:
3738
tags: |
3839
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
3940
type=semver,pattern={{version}},enable=${{ github.event_name == 'release' }}
41+
type=raw,value=latest,enable=${{ github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' }}
4042
4143
- name: Build and push Docker image
4244
uses: docker/build-push-action@v6
@@ -50,10 +52,12 @@ jobs:
5052
name: Deploy to ECS
5153
runs-on: ubuntu-latest
5254
needs: build-and-push
53-
if: github.event_name == 'release'
55+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
5456
permissions:
5557
id-token: write
5658
contents: read
59+
outputs:
60+
monitoring_instance_id: ${{ steps.tf_output.outputs.instance_id }}
5761

5862
steps:
5963
- name: Checkout repository
@@ -84,6 +88,11 @@ jobs:
8488
-var="ecs_task_role_arn=${{ secrets.ECS_TASK_ROLE_ARN }}" \
8589
-var="acm_certificate_arn=${{ secrets.ACM_CERTIFICATE_ARN }}"
8690
91+
- name: Get monitoring instance ID
92+
id: tf_output
93+
working-directory: terraform
94+
run: echo "instance_id=$(terraform output -raw monitoring_instance_id)" >> $GITHUB_OUTPUT
95+
8796
- name: Force new ECS deployment
8897
run: |
8998
TASK_DEF_ARN=$(aws ecs describe-task-definition \
@@ -108,3 +117,11 @@ jobs:
108117
--cluster bannote-prod-cluster \
109118
--services bannote-prod-service \
110119
--region ap-northeast-2
120+
121+
deploy-monitoring-stack:
122+
name: Deploy monitoring stack
123+
needs: deploy-prod
124+
uses: ./.github/workflows/deploy-monitoring.yml
125+
with:
126+
instance_id: ${{ needs.deploy-prod.outputs.monitoring_instance_id }}
127+
secrets: inherit

.github/workflows/terraform-plan.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: Terraform Plan
22

33
on:
4+
workflow_dispatch:
45
pull_request:
56
branches:
67
- main
@@ -47,6 +48,7 @@ jobs:
4748
continue-on-error: true
4849

4950
- name: Comment plan on PR
51+
if: github.event_name == 'pull_request'
5052
uses: actions/github-script@v7
5153
with:
5254
script: |

.github/workflows/version-bump.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
if: steps.bump-type.outputs.type != 'none'
4242
uses: actions/setup-node@v4
4343
with:
44-
node-version: "20"
44+
node-version: '20'
4545

4646
- name: Bump version and commit
4747
if: steps.bump-type.outputs.type != 'none'

Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: db local local-d dev prod down-local down-dev down-prod down-db logs logs-app backup restore clean
1+
.PHONY: db local local-d dev prod down-local down-dev down-prod down-db monitoring down-monitoring logs logs-app backup restore clean
22

33
# DB + Redis 실행 (로컬 개발용)
44
db:
@@ -36,6 +36,14 @@ down-prod:
3636
down-db:
3737
docker compose -f docker-compose.yml -f docker-compose.local.yml down
3838

39+
# 모니터링 스택 실행 (Prometheus, Loki, Grafana)
40+
monitoring:
41+
docker compose -f docker-compose.monitoring.yml up -d
42+
43+
# 모니터링 스택 종료
44+
down-monitoring:
45+
docker compose -f docker-compose.monitoring.yml down
46+
3947
# 로그 확인
4048
logs:
4149
docker compose logs -f

0 commit comments

Comments
 (0)