Skip to content

build-android-app

build-android-app #24

# ===================================================================
# Android 테스트용 APK 빌드 워크플로우
# ===================================================================
#
# 이 워크플로우는 test-deploy 브랜치에서 수동으로 실행하여
# 테스트용 Android APK를 빌드합니다.
#
# 주요 특징:
# - workflow_dispatch로 수동 실행만 가능
# - 버전 관리 불필요 (안드로이드는 버전 제약 없음)
# - 브랜치명에서 이슈 번호 자동 추출 (YYYYMMDD_#이슈번호_내용 형식)
# - GitHub API로 이슈 정보 가져와서 빌드 정보에 포함
# - APK 파일만 생성 (Play Store 배포 제거)
# - 아티팩트로 업로드하여 다운로드 가능 (최대 500MB/아티팩트)
#
# 브랜치명 예시:
# - 20251208_#387_내용
# - 20250115_#123_버그수정
#
# 사용 방법:
# 1. test-deploy 브랜치로 체크아웃
# 2. GitHub Actions에서 "ROMROM-ANDROID-TEST-APK" 워크플로우 선택
# 3. "Run workflow" 버튼 클릭
# 4. 빌드 완료 후 아티팩트에서 APK 다운로드
#
# 아티팩트 용량 제한:
# - 무료 플랜: 500MB/아티팩트, 10GB/저장소 전체
# - APK 파일은 보통 50-200MB이므로 무료 플랜으로 충분
#
# ===================================================================
name: ROMROM-ANDROID-TEST-APK
on:
workflow_dispatch:
repository_dispatch:
types: [build-android-app]
env:
FLUTTER_VERSION: "3.35.5"
JAVA_VERSION: "17"
jobs:
prepare-test-build:
name: 테스트 빌드 준비
runs-on: ubuntu-latest
outputs:
issue_url: ${{ steps.issue_info.outputs.issue_url }}
issue_title: ${{ steps.issue_info.outputs.issue_title }}
issue_number: ${{ steps.issue_info.outputs.issue_number }}
branch_name: ${{ steps.issue_info.outputs.branch_name }}
commit_hash: ${{ steps.build_info.outputs.commit_hash }}
pr_number: ${{ steps.build_info.outputs.pr_number }}
build_number: ${{ steps.build_info.outputs.build_number }}
progress_comment_id: ${{ steps.progress_comment.outputs.comment_id }}
progress_start_time: ${{ steps.progress_comment.outputs.start_time }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref }}
- name: Pull latest changes
run: |
BRANCH_NAME="${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
echo "🌿 브랜치: $BRANCH_NAME"
git pull origin "$BRANCH_NAME" || echo "⚠️ Pull 실패 (이미 최신 상태일 수 있음)"
# 브랜치명에서 이슈 번호 추출 및 GitHub API로 이슈 정보 가져오기
- name: 브랜치명에서 이슈 정보 추출
id: issue_info
run: |
# repository_dispatch인 경우 client_payload에서 브랜치명 가져오기
if [ "${{ github.event_name }}" == "repository_dispatch" ]; then
BRANCH_NAME="${{ github.event.client_payload.branch_name }}"
ISSUE_NUMBER="${{ github.event.client_payload.issue_number }}"
else
BRANCH_NAME="${{ github.ref_name }}"
# 브랜치명에서 이슈 번호 추출 (#387 형식) - sed 사용 (Linux/macOS 호환)
ISSUE_NUMBER=$(echo "$BRANCH_NAME" | sed -n 's/.*#\([0-9]*\).*/\1/p')
fi
echo "🌿 브랜치명: $BRANCH_NAME"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
if [ -z "$ISSUE_NUMBER" ]; then
echo "⚠️ 브랜치명에서 이슈 번호를 찾을 수 없습니다."
echo "issue_url=" >> $GITHUB_OUTPUT
echo "issue_title=" >> $GITHUB_OUTPUT
echo "issue_number=" >> $GITHUB_OUTPUT
else
echo "✅ 추출된 이슈 번호: #$ISSUE_NUMBER"
ISSUE_URL="https://github.com/${{ github.repository }}/issues/$ISSUE_NUMBER"
echo "issue_url=$ISSUE_URL" >> $GITHUB_OUTPUT
echo "issue_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
# GitHub API로 이슈 정보 가져오기
echo "🔍 GitHub API로 이슈 정보 조회 중..."
ISSUE_RESPONSE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/issues/$ISSUE_NUMBER")
# 이슈 제목 추출
ISSUE_TITLE=$(echo "$ISSUE_RESPONSE" | jq -r '.title // "이슈 정보 없음"')
# 이슈 상태 확인
ISSUE_STATE=$(echo "$ISSUE_RESPONSE" | jq -r '.state // "unknown"')
if [ "$ISSUE_TITLE" = "null" ] || [ "$ISSUE_TITLE" = "이슈 정보 없음" ]; then
echo "⚠️ 이슈 #$ISSUE_NUMBER를 찾을 수 없습니다."
echo "issue_title=" >> $GITHUB_OUTPUT
else
echo "📋 이슈 제목: $ISSUE_TITLE"
echo "📌 이슈 상태: $ISSUE_STATE"
echo "issue_title=$ISSUE_TITLE" >> $GITHUB_OUTPUT
fi
fi
# 빌드 정보 생성
- name: 빌드 정보 생성
id: build_info
run: |
COMMIT_SHA="${{ github.sha }}"
COMMIT_SHORT=$(echo "$COMMIT_SHA" | cut -c1-7)
BUILD_DATE=$(date '+%Y-%m-%d %H:%M:%S')
# repository_dispatch 이벤트인 경우 PR 번호를 빌드 번호로 사용
if [ "${{ github.event_name }}" == "repository_dispatch" ]; then
BUILD_NUMBER="${{ github.event.client_payload.build_number }}"
PR_NUMBER="${{ github.event.client_payload.pr_number }}"
echo "📋 repository_dispatch 이벤트 감지"
echo " PR 번호: #$PR_NUMBER"
echo " 빌드 번호: $BUILD_NUMBER (PR 번호 사용)"
else
BUILD_NUMBER="${{ github.run_number }}"
PR_NUMBER=""
echo "📋 workflow_dispatch 이벤트 감지"
echo " 빌드 번호: $BUILD_NUMBER (기본값)"
fi
echo "commit_hash=$COMMIT_SHORT" >> $GITHUB_OUTPUT
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "build_date=$BUILD_DATE" >> $GITHUB_OUTPUT
echo "📋 빌드 정보:"
echo " 빌드 번호: #$BUILD_NUMBER"
if [ -n "$PR_NUMBER" ]; then
echo " PR 번호: #$PR_NUMBER"
fi
echo " 커밋 해시: $COMMIT_SHORT"
echo " 빌드 날짜: $BUILD_DATE"
# 진행상황 댓글 생성 (repository_dispatch로 트리거된 경우에만)
- name: 진행상황 댓글 생성
id: progress_comment
if: github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = parseInt('${{ github.event.client_payload.pr_number }}');
const buildNumber = '${{ steps.build_info.outputs.build_number }}';
const branchName = '${{ steps.issue_info.outputs.branch_name }}';
const runId = '${{ github.run_id }}';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const startTime = Date.now();
const body = [
'## 🤖 Android APK 빌드 중...',
'',
'| 단계 | 상태 | 소요 시간 |',
'|------|------|----------|',
'| 🔧 준비 | ⏳ 진행 중... | - |',
'| 🔨 APK 빌드 | ⏸️ 대기 | - |',
'| 📤 업로드 | ⏸️ 대기 | - |',
'',
`📋 **[실시간 로그 보기](${runUrl})**`
].join('\n');
const { data: comment } = await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: body
});
console.log(`✅ 진행상황 댓글 생성 완료 (ID: ${comment.id})`);
core.setOutput('comment_id', comment.id.toString());
core.setOutput('start_time', startTime.toString());
build-android-test:
name: Android 테스트 APK 빌드
runs-on: ubuntu-latest
needs: prepare-test-build
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref }}
- name: Pull latest changes
run: git pull origin ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}
# Debug Keystore 설정
- name: Setup Debug Keystore
run: |
mkdir -p ~/.android
echo "${{ secrets.DEBUG_KEYSTORE }}" | base64 -d > ~/.android/debug.keystore || echo "Base64 decoding failed"
echo "Debug Keystore created (for debugging)"
ls -la ~/.android/
# .env 파일 생성
- name: Create .env file
run: |
echo "${{ secrets.ENV_FILE }}" > .env
echo ".env file created"
# Keystore와 key.properties 설정
- name: Setup Keystore and key.properties
run: |
mkdir -p android/app/keystore
echo "${{ secrets.DEBUG_KEYSTORE }}" | base64 -d > android/app/keystore/key.jks || echo "Base64 decoding failed"
echo "Keystore created from DEBUG_KEYSTORE"
ls -la android/app/keystore
echo "storeFile=keystore/key.jks" > android/key.properties
echo "storePassword=android" >> android/key.properties
echo "keyAlias=androiddebugkey" >> android/key.properties
echo "keyPassword=android" >> android/key.properties
echo "key.properties created"
ls -la android/
# google-services.json 생성
- name: Create google-services.json
shell: bash
run: |
mkdir -p android/app
printf '%s' "${GOOGLE_SERVICES_JSON}" > android/app/google-services.json
env:
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
# Flutter 설정
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Verify Flutter version
run: |
echo "Flutter setup completed"
flutter --version
# Flutter 및 Gradle 캐시
- name: Cache Flutter dependencies
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
restore-keys: ${{ runner.os }}-flutter-pub-
- name: Cache Gradle dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
# 프로젝트 의존성 설치
- name: Install dependencies
run: |
flutter pub get
echo "Dependencies installed"
# Gradle 셋업
- name: Setup Gradle
working-directory: android
run: |
chmod +x gradlew
echo "Gradle wrapper permissions set"
# Java 설정
- name: Set up Java
uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Verify Java version
run: |
echo "Java setup completed"
java -version
# Android SDK 설정
- name: Set up Android SDK
uses: android-actions/setup-android@v3
- name: Verify Android SDK setup
run: echo "Android SDK setup completed"
# Ruby 설정
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4.1"
bundler-cache: true
- name: Verify Ruby version
run: |
echo "Ruby setup completed"
ruby -v
# Fastlane 설치
- name: Install Fastlane
run: |
gem install fastlane
echo "Fastlane installed"
fastlane --version
# 진행상황 - 준비 완료
- name: 진행상황 - 준비 완료
if: github.event_name == 'repository_dispatch' && needs.prepare-test-build.outputs.progress_comment_id != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const commentId = parseInt('${{ needs.prepare-test-build.outputs.progress_comment_id }}');
const startTime = parseInt('${{ needs.prepare-test-build.outputs.progress_start_time }}');
const runId = '${{ github.run_id }}';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const elapsed = Math.round((Date.now() - startTime) / 1000);
const minutes = Math.floor(elapsed / 60);
const seconds = elapsed % 60;
const elapsedStr = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
const body = [
'## 🤖 Android APK 빌드 중...',
'',
'| 단계 | 상태 | 소요 시간 |',
'|------|------|----------|',
`| 🔧 준비 | ✅ 완료 | ${elapsedStr} |`,
'| 🔨 APK 빌드 | ⏳ 진행 중... | - |',
'| 📤 업로드 | ⏸️ 대기 | - |',
'',
`📋 **[실시간 로그 보기](${runUrl})**`
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: body
});
console.log(`✅ 진행상황 업데이트: 준비 완료 (${elapsedStr})`);
# APK 파일명 생성
- name: Generate APK filename
id: apk_filename
run: |
COMMIT_SHORT="${{ needs.prepare-test-build.outputs.commit_hash }}"
BUILD_NUMBER="${{ needs.prepare-test-build.outputs.build_number }}"
APK_NAME="romrom-test-${BUILD_NUMBER}-${COMMIT_SHORT}.apk"
echo "apk_name=$APK_NAME" >> $GITHUB_OUTPUT
echo "📦 APK 파일명: $APK_NAME"
# Fastlane을 이용하여 APK 빌드
- name: Build APK with Fastlane
run: |
cd android
fastlane build --verbose
ls -la ../build/app/outputs/flutter-apk/ || true
echo "APK built with Fastlane"
# APK 파일 이름 변경 및 준비
- name: Rename and Prepare APK
run: |
mkdir -p ./android/app/build/outputs/apk/release/
APK_NAME="${{ steps.apk_filename.outputs.apk_name }}"
mv ./build/app/outputs/flutter-apk/app-release.apk ./android/app/build/outputs/apk/release/${APK_NAME}
echo "APK renamed to ${APK_NAME}"
ls -la ./android/app/build/outputs/apk/release/
# APK 파일 크기 확인
APK_SIZE=$(stat -c%s "./android/app/build/outputs/apk/release/${APK_NAME}" 2>/dev/null || stat -f%z "./android/app/build/outputs/apk/release/${APK_NAME}" 2>/dev/null || echo "0")
APK_SIZE_MB=$(echo "scale=2; $APK_SIZE / 1024 / 1024" | bc)
echo "📦 APK 파일 크기: ${APK_SIZE_MB}MB"
# 아티팩트 용량 제한 확인 (500MB)
if [ "$APK_SIZE" -gt 524288000 ]; then
echo "⚠️ 경고: APK 파일 크기가 500MB를 초과합니다 (${APK_SIZE_MB}MB)"
else
echo "✅ APK 파일 크기가 아티팩트 제한 내입니다 (${APK_SIZE_MB}MB / 500MB)"
fi
# 진행상황 - APK 빌드 완료
- name: 진행상황 - APK 빌드 완료
if: github.event_name == 'repository_dispatch' && needs.prepare-test-build.outputs.progress_comment_id != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const commentId = parseInt('${{ needs.prepare-test-build.outputs.progress_comment_id }}');
const startTime = parseInt('${{ needs.prepare-test-build.outputs.progress_start_time }}');
const runId = '${{ github.run_id }}';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const elapsed = Math.round((Date.now() - startTime) / 1000);
const minutes = Math.floor(elapsed / 60);
const seconds = elapsed % 60;
const elapsedStr = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
const body = [
'## 🤖 Android APK 빌드 중...',
'',
'| 단계 | 상태 | 소요 시간 |',
'|------|------|----------|',
'| 🔧 준비 | ✅ 완료 | - |',
`| 🔨 APK 빌드 | ✅ 완료 | ${elapsedStr} |`,
'| 📤 업로드 | ⏳ 진행 중... | - |',
'',
`📋 **[실시간 로그 보기](${runUrl})**`
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: body
});
console.log(`✅ 진행상황 업데이트: APK 빌드 완료 (${elapsedStr})`);
# 빌드 정보 파일 생성
- name: Create build info file
run: |
BUILD_NUMBER="${{ needs.prepare-test-build.outputs.build_number }}"
PR_NUMBER="${{ needs.prepare-test-build.outputs.pr_number }}"
BRANCH_NAME="${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
COMMIT_SHA="${{ github.sha }}"
COMMIT_SHORT="${{ needs.prepare-test-build.outputs.commit_hash }}"
BUILD_DATE=$(date '+%Y-%m-%d %H:%M:%S')
cat > build-info.txt << EOF
테스트 APK 빌드 정보
빌드 번호: #$BUILD_NUMBER
EOF
if [ -n "$PR_NUMBER" ]; then
cat >> build-info.txt << EOF
PR 번호: #$PR_NUMBER
EOF
fi
cat >> build-info.txt << EOF
브랜치: $BRANCH_NAME
커밋: $COMMIT_SHORT
전체 커밋 해시: $COMMIT_SHA
빌드 날짜: $BUILD_DATE
EOF
# 이슈 정보가 있으면 추가
if [ -n "${{ needs.prepare-test-build.outputs.issue_number }}" ]; then
ISSUE_NUMBER="${{ needs.prepare-test-build.outputs.issue_number }}"
ISSUE_TITLE="${{ needs.prepare-test-build.outputs.issue_title }}"
ISSUE_URL="${{ needs.prepare-test-build.outputs.issue_url }}"
cat >> build-info.txt << EOF
관련 이슈:
- #$ISSUE_NUMBER: $ISSUE_TITLE
- URL: $ISSUE_URL
EOF
fi
echo "📋 빌드 정보 파일 생성 완료:"
cat build-info.txt
# 빌드 메타데이터 파일 생성 (댓글 작성 워크플로우용)
- name: Create build metadata file
run: |
cat > build-metadata.json << EOF
{
"pr_number": "${{ needs.prepare-test-build.outputs.pr_number }}",
"build_number": "${{ needs.prepare-test-build.outputs.build_number }}",
"issue_number": "${{ needs.prepare-test-build.outputs.issue_number }}",
"branch_name": "${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
}
EOF
cat build-metadata.json
# APK와 빌드 정보를 아티팩트로 업로드
- name: Upload APK and build info as Artifact
id: upload_artifact
uses: actions/upload-artifact@v4
with:
name: romrom-test-apk
path: |
./android/app/build/outputs/apk/release/*.apk
build-info.txt
build-metadata.json
retention-days: 7
if-no-files-found: error
# 진행상황 - 빌드 성공 (최종)
- name: 진행상황 - 빌드 성공 (최종)
if: success() && github.event_name == 'repository_dispatch' && needs.prepare-test-build.outputs.progress_comment_id != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const commentId = parseInt('${{ needs.prepare-test-build.outputs.progress_comment_id }}');
const startTime = parseInt('${{ needs.prepare-test-build.outputs.progress_start_time }}');
const prNumber = parseInt('${{ github.event.client_payload.pr_number }}');
const buildNumber = '${{ needs.prepare-test-build.outputs.build_number }}';
const branchName = '${{ needs.prepare-test-build.outputs.branch_name }}';
const commitHash = '${{ needs.prepare-test-build.outputs.commit_hash }}';
const issueNumber = '${{ needs.prepare-test-build.outputs.issue_number }}';
const runId = '${{ github.run_id }}';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
// upload-artifact@v4의 artifact-url output 사용 (직접 다운로드 URL)
const artifactUrl = '${{ steps.upload_artifact.outputs.artifact-url }}' || `${runUrl}#artifacts`;
const elapsed = Math.round((Date.now() - startTime) / 1000);
const minutes = Math.floor(elapsed / 60);
const seconds = elapsed % 60;
const elapsedStr = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
const body = [
'## 🤖 ✅ Android APK 빌드 완료!',
'',
'| 단계 | 상태 | 소요 시간 |',
'|------|------|----------|',
'| 🔧 준비 | ✅ 완료 | - |',
'| 🔨 APK 빌드 | ✅ 완료 | - |',
`| 📤 업로드 | ✅ 완료 | ${elapsedStr} |`,
'',
'### 📦 빌드 결과',
'| 항목 | 값 |',
'|------|-----|',
`| **앱 버전** | \`0.0.0(${buildNumber})\` |`,
`| **브랜치** | \`${branchName}\` |`,
`| **커밋** | \`${commitHash}\` |`,
`| **총 소요 시간** | ${elapsedStr} |`,
'',
`📦 **[아티팩트 다운로드](${artifactUrl})**`,
`🔗 **[워크플로우 실행 로그](${runUrl})**`
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: body
});
console.log(`✅ 진행상황 업데이트: 빌드 성공 (총 ${elapsedStr})`);
// PR과 연결된 이슈가 있고, PR 번호와 다른 경우에만 이슈에도 댓글
if (issueNumber && issueNumber !== '' && parseInt(issueNumber) !== prNumber) {
const issueBody = [
'🤖 ✅ **Android APK 빌드 완료**',
'',
`- 앱 버전: \`0.0.0(${buildNumber})\``,
`- 커밋: \`${commitHash}\``,
'',
`📦 **[아티팩트 다운로드](${artifactUrl})**`
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber),
body: issueBody
});
console.log(`✅ 이슈 #${issueNumber}에도 빌드 성공 댓글 작성 완료`);
}
# 진행상황 - 빌드 실패 (최종)
- name: 진행상황 - 빌드 실패 (최종)
if: failure() && github.event_name == 'repository_dispatch' && needs.prepare-test-build.outputs.progress_comment_id != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const commentId = parseInt('${{ needs.prepare-test-build.outputs.progress_comment_id }}');
const startTime = parseInt('${{ needs.prepare-test-build.outputs.progress_start_time }}');
const prNumber = parseInt('${{ github.event.client_payload.pr_number }}');
const buildNumber = '${{ needs.prepare-test-build.outputs.build_number }}';
const branchName = '${{ needs.prepare-test-build.outputs.branch_name }}';
const issueNumber = '${{ needs.prepare-test-build.outputs.issue_number }}';
const runId = '${{ github.run_id }}';
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
const elapsed = Math.round((Date.now() - startTime) / 1000);
const minutes = Math.floor(elapsed / 60);
const seconds = elapsed % 60;
const elapsedStr = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
const body = [
'## 🤖 ❌ Android APK 빌드 실패',
'',
'| 단계 | 상태 | 소요 시간 |',
'|------|------|----------|',
'| 🔧 준비 | ✅ 완료 | - |',
`| 🔨 APK 빌드 | ❌ 실패 | ${elapsedStr} |`,
'| 📤 업로드 | ⏸️ 건너뜀 | - |',
'',
`- 앱 버전: \`0.0.0(${buildNumber})\``,
`- 브랜치: \`${branchName}\``,
'',
'❌ 빌드 중 오류가 발생했습니다.',
'',
`🔗 **[워크플로우 실행 로그 확인](${runUrl})**`
].join('\n');
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body: body
});
console.log(`❌ 진행상황 업데이트: 빌드 실패 (${elapsedStr})`);
// PR과 연결된 이슈가 있고, PR 번호와 다른 경우에만 이슈에도 댓글
if (issueNumber && issueNumber !== '' && parseInt(issueNumber) !== prNumber) {
const issueBody = [
'🤖 ❌ **Android APK 빌드 실패**',
'',
`- 앱 버전: \`0.0.0(${buildNumber})\``,
'',
'❌ 빌드 중 오류가 발생했습니다.',
'',
`🔗 **[워크플로우 실행 로그 확인](${runUrl})**`
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt(issueNumber),
body: issueBody
});
console.log(`❌ 이슈 #${issueNumber}에도 빌드 실패 댓글 작성 완료`);
}
# 콘솔 알림 (workflow_dispatch용)
- name: Notify Build Success (Console)
if: success()
run: |
echo "✅ Android 테스트 APK 빌드 성공!"
echo "빌드 번호: ${{ needs.prepare-test-build.outputs.build_number }}"
echo "브랜치: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
echo "커밋: ${{ needs.prepare-test-build.outputs.commit_hash }}"
echo "📦 아티팩트에서 APK 파일을 다운로드할 수 있습니다."
- name: Notify on Failure (Console)
if: failure()
run: |
echo "❌ Android 테스트 APK 빌드 실패!"
echo "로그를 확인해주세요."