Skip to content

feat: 공통 위젯 시스템 및 디자인 가이드 구축 #6

feat: 공통 위젯 시스템 및 디자인 가이드 구축

feat: 공통 위젯 시스템 및 디자인 가이드 구축 #6

name: Project Flutter CI
# PR과 main/develop 브랜치 push 시 자동 실행
# Android, iOS 플랫폼만 검증 (macOS, Linux, Web 제외)
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: # 수동 실행 허용
permissions:
contents: read
pull-requests: write # PR에 댓글을 달기 위한 권한
jobs:
# 코드 품질 검증 작업
analyze:
name: Code Analysis
runs-on: ubuntu-latest
steps:
# 1. 저장소 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v4
# 2. Flutter SDK 설치
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.35.5'
channel: 'stable'
cache: true
# 3. 의존성 설치
- name: Install dependencies
run: flutter pub get
# 4. 환경 변수 파일 생성 (.env 파일 - analyze 에러 방지)
- name: Create .env file
run: |
cat > .env << 'EOF'
${{ secrets.ENV }}
EOF
# 5. 코드 포맷팅 검사
- name: Check formatting
id: format_check
continue-on-error: true
run: |
dart format --set-exit-if-changed . 2>&1 | tee format_output.txt
exit ${PIPESTATUS[0]}
# 6. 정적 분석
- name: Analyze code
id: analyze_check
continue-on-error: true
run: |
flutter analyze 2>&1 | tee analyze_output.txt
exit ${PIPESTATUS[0]}
# 7. PR에 결과 댓글 달기 (실패 시)
- name: Comment on PR with issues
if: failure() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
let commentBody = '## 🚨 Flutter CI 검사 실패\n\n';
let hasIssues = false;
// 포맷팅 검사 결과
if ('${{ steps.format_check.outcome }}' === 'failure') {
hasIssues = true;
commentBody += '### ❌ 코드 포맷팅 검사 실패\n\n';
commentBody += '다음 명령어로 자동 포맷팅을 적용하세요:\n';
commentBody += '```bash\n';
commentBody += 'dart format .\n';
commentBody += '```\n\n';
try {
const formatOutput = fs.readFileSync('format_output.txt', 'utf8');
if (formatOutput.trim()) {
commentBody += '<details>\n<summary>포맷팅 오류 상세 내용</summary>\n\n';
commentBody += '```\n' + formatOutput.trim() + '\n```\n\n';
commentBody += '</details>\n\n';
}
} catch (e) {
console.log('포맷팅 출력 파일을 읽을 수 없습니다.');
}
}
// 정적 분석 결과
if ('${{ steps.analyze_check.outcome }}' === 'failure') {
hasIssues = true;
commentBody += '### ❌ 정적 분석 검사 실패\n\n';
try {
const analyzeOutput = fs.readFileSync('analyze_output.txt', 'utf8');
if (analyzeOutput.trim()) {
// 오류/경고 라인 추출
const lines = analyzeOutput.split('\n');
const issues = lines.filter(line =>
line.includes('error •') ||
line.includes('warning •') ||
line.includes('info •')
);
if (issues.length > 0) {
commentBody += '발견된 문제:\n\n';
commentBody += '```\n';
commentBody += issues.slice(0, 20).join('\n'); // 최대 20개만 표시
if (issues.length > 20) {
commentBody += '\n... 그 외 ' + (issues.length - 20) + '개의 문제';
}
commentBody += '\n```\n\n';
}
commentBody += '<details>\n<summary>전체 분석 결과 보기</summary>\n\n';
commentBody += '```\n' + analyzeOutput.trim() + '\n```\n\n';
commentBody += '</details>\n\n';
}
} catch (e) {
console.log('분석 출력 파일을 읽을 수 없습니다.');
}
}
if (hasIssues) {
commentBody += '---\n';
commentBody += '💡 **수정 방법:**\n';
commentBody += '1. 로컬에서 `dart format .` 명령어로 포맷팅 적용\n';
commentBody += '2. `flutter analyze` 명령어로 오류 확인\n';
commentBody += '3. 오류를 수정한 후 다시 커밋하세요\n';
// 기존 봇 댓글 찾기
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('🚨 Flutter CI 검사 실패')
);
if (botComment) {
// 기존 댓글 업데이트
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: commentBody
});
} else {
// 새 댓글 작성
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: commentBody
});
}
}
# 8. 최종 결과 확인 (실패 시 워크플로우 실패)
- name: Check final status
if: steps.format_check.outcome == 'failure' || steps.analyze_check.outcome == 'failure'
run: |
echo "❌ CI 검사 실패!"
echo "포맷팅 검사: ${{ steps.format_check.outcome }}"
echo "정적 분석: ${{ steps.analyze_check.outcome }}"
exit 1