Skip to content

[Feat] - AI 챗봇 api 연결 #110

[Feat] - AI 챗봇 api 연결

[Feat] - AI 챗봇 api 연결 #110

Workflow file for this run

name: Frontend CI
on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
actions: read
jobs:
setup:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
format:
needs: setup
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Format check
run: pnpm format:check
ci:
needs: setup
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- app: web
filter: web
- app: admin
filter: admin
- app: design-system
filter: '@muneo/design-system'
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'pnpm'
- name: Cache Turbo
uses: actions/cache@v4
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ matrix.app }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-${{ matrix.app }}-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Type check
id: type-check
continue-on-error: true
run: pnpm turbo type-check --filter=${{ matrix.filter }}
- name: Lint check
id: lint
continue-on-error: true
run: pnpm turbo lint --filter=${{ matrix.filter }}
- name: Run tests
id: test
continue-on-error: true
run: pnpm turbo test --filter=${{ matrix.filter }}
- name: Build
id: build
continue-on-error: true
run: pnpm turbo build --filter=${{ matrix.filter }}
- name: Write CI result
if: always()
run: |
mkdir -p ci-results
cat > "ci-results/${{ matrix.app }}.json" <<'JSON'
{
"typeCheck": "${{ steps.type-check.outcome }}",
"lint": "${{ steps.lint.outcome }}",
"test": "${{ steps.test.outcome }}",
"build": "${{ steps.build.outcome }}"
}
JSON
- name: Upload CI result
if: always()
uses: actions/upload-artifact@v4
with:
name: ci-result-${{ matrix.app }}
path: ci-results/${{ matrix.app }}.json
if-no-files-found: error
- name: Check results
if: always()
run: |
if [[ "${{ steps.type-check.outcome }}" == "failure" ]] || \
[[ "${{ steps.lint.outcome }}" == "failure" ]] || \
[[ "${{ steps.test.outcome }}" == "failure" ]] || \
[[ "${{ steps.build.outcome }}" == "failure" ]]; then
echo "CI failed"
exit 1
fi
result:
name: ci
needs: [setup, format, ci]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Check all jobs
run: |
if [[ "${{ needs.setup.result }}" != "success" ]] || \
[[ "${{ needs.format.result }}" != "success" ]] || \
[[ "${{ needs.ci.result }}" != "success" ]]; then
exit 1
fi
comment:
needs: [setup, format, ci]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Download CI results
continue-on-error: true
uses: actions/download-artifact@v4
with:
pattern: ci-result-*
path: ci-results
merge-multiple: true
- name: Comment results on PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
});
const formatJob = jobs.jobs.find(j => j.name === 'format');
const getStepResult = (job, stepName) => {
if (!job) return 'skipped';
const step = job.steps?.find(s => s.name === stepName);
return step?.conclusion || 'skipped';
};
const readAppResult = (app) => {
const filePath = path.join('ci-results', `${app}.json`);
if (!fs.existsSync(filePath)) {
return {
typeCheck: 'skipped',
lint: 'skipped',
test: 'skipped',
build: 'skipped',
};
}
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
};
const icon = (status) => {
if (status === 'success') return '✅';
if (status === 'skipped') return '⏭️';
if (status === 'not-applicable') return '➖';
return '❌';
};
const label = (status) => {
if (status === 'success') return '통과';
if (status === 'skipped') return '스킵';
if (status === 'not-applicable') return '해당 없음';
return '실패';
};
const formatResult = getStepResult(formatJob, 'Format check');
const webResult = readAppResult('web');
const adminResult = readAppResult('admin');
const dsResult = readAppResult('design-system');
// 디자인 시스템은 라이브러리라 build 단계가 의도적으로 없음
dsResult.build = 'not-applicable';
const rows = [
['Type', webResult.typeCheck, adminResult.typeCheck, dsResult.typeCheck],
['Prettier', formatResult, formatResult, formatResult],
['Lint', webResult.lint, adminResult.lint, dsResult.lint],
['tests', webResult.test, adminResult.test, dsResult.test],
['Build', webResult.build, adminResult.build, dsResult.build],
];
let comment = '## 🔍 CI 검증 결과\n\n';
comment += '| 검증 항목 | web | admin | design-system |\n';
comment += '|-----------|-----|-------|---------------|\n';
for (const [name, webStatus, adminStatus, dsStatus] of rows) {
comment += `| **${name}** | ${icon(webStatus)} ${label(webStatus)} | ${icon(adminStatus)} ${label(adminStatus)} | ${icon(dsStatus)} ${label(dsStatus)} |\n`;
}
try {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment,
});
} catch (error) {
console.log('Failed to comment (fork PR may have read-only token):', error.message);
}