feat: 버전을 0.1.0으로 변경 - 테스트 단계 #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Release | ||
| on: | ||
| push: | ||
| tags: | ||
| - 'v*.*.*' | ||
| permissions: | ||
| contents: write | ||
| jobs: | ||
| build: | ||
| strategy: | ||
| matrix: | ||
| os: [ubuntu-latest, windows-latest, macos-latest] | ||
| runs-on: ${{ matrix.os }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '18' | ||
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@v2 | ||
| with: | ||
| version: latest | ||
| - name: Get pnpm store directory | ||
| shell: bash | ||
| run: | | ||
| echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV | ||
| - name: Cache pnpm dependencies | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: ${{ env.STORE_PATH }} | ||
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-pnpm-store- | ||
| - name: Install dependencies | ||
| run: pnpm install --frozen-lockfile | ||
| - name: Fix electron installation | ||
| run: | | ||
| if [ -f "node_modules/electron/install.js" ]; then | ||
| node node_modules/electron/install.js | ||
| fi | ||
| shell: bash | ||
| - name: Build application | ||
| run: pnpm run build | ||
| - name: Import Apple certificate (macOS only) | ||
| if: matrix.os == 'macos-latest' | ||
| env: | ||
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | ||
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | ||
| APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} | ||
| run: | | ||
| if [ -n "$APPLE_CERTIFICATE" ]; then | ||
| echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12 | ||
| security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" build.keychain | ||
| security default-keychain -s build.keychain | ||
| security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" build.keychain | ||
| security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign | ||
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$APPLE_KEYCHAIN_PASSWORD" build.keychain | ||
| fi | ||
| - name: Package application (Linux) | ||
| if: matrix.os == 'ubuntu-latest' | ||
| run: pnpm run package:linux | ||
| - name: List built files (Linux) | ||
| if: matrix.os == 'ubuntu-latest' | ||
| run: | | ||
| echo "=== Built files for Linux ===" | ||
| find . -type f \( -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" \) -exec ls -la {} \; | ||
| - name: Package application (Windows) | ||
| if: matrix.os == 'windows-latest' | ||
| env: | ||
| CSC_IDENTITY_AUTO_DISCOVERY: false | ||
| CSC_LINK: ${{ secrets.WIN_CSC_LINK }} | ||
| CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} | ||
| run: pnpm run package:win | ||
| - name: List built files (Windows) | ||
| if: matrix.os == 'windows-latest' | ||
| run: | | ||
| echo "=== Built files for Windows ===" | ||
| Get-ChildItem -Recurse -Include "*.exe","*.msi" | ForEach-Object { Write-Host $_.FullName "; Size:" $_.Length } | ||
| shell: powershell | ||
| - name: Package application (macOS) | ||
| if: matrix.os == 'macos-latest' | ||
| env: | ||
| CSC_IDENTITY_AUTO_DISCOVERY: false | ||
| APPLE_ID: ${{ secrets.APPLE_ID }} | ||
| APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} | ||
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | ||
| run: | | ||
| if [ -f "certificate.p12" ]; then | ||
| export CSC_LINK=certificate.p12 | ||
| export CSC_KEY_PASSWORD="${{ secrets.APPLE_CERTIFICATE_PASSWORD }}" | ||
| fi | ||
| pnpm run package:mac | ||
| - name: List built files (macOS) | ||
| if: matrix.os == 'macos-latest' | ||
| run: | | ||
| echo "=== Built files for macOS ===" | ||
| find . -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.app" \) -exec ls -la {} \; | ||
| - name: Upload artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: ${{ matrix.os }}-artifacts | ||
| path: | | ||
| **/*.exe | ||
| **/*.msi | ||
| **/*.dmg | ||
| **/*.zip | ||
| **/*.AppImage | ||
| **/*.deb | ||
| **/*.rpm | ||
| retention-days: 5 | ||
| release: | ||
| needs: build | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| release_files: ${{ steps.get_files.outputs.files }} | ||
| tag_name: ${{ steps.get_tag.outputs.tag_name }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Get tag name | ||
| id: get_tag | ||
| run: echo "tag_name=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | ||
| - name: Download all artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| merge-multiple: true | ||
| - name: List downloaded artifacts | ||
| run: | | ||
| echo "=== Downloaded artifacts ===" | ||
| find . -type f \( -name "*.exe" -o -name "*.msi" -o -name "*.dmg" -o -name "*.zip" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" \) -exec ls -la {} \; | ||
| echo "=== All files in current directory ===" | ||
| ls -la | ||
| - name: Get release files info | ||
| id: get_files | ||
| run: | | ||
| echo "files<<EOF" >> $GITHUB_OUTPUT | ||
| find . -type f \( -name "*.exe" -o -name "*.msi" -o -name "*.dmg" -o -name "*.zip" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" \) | while read file; do | ||
| filename=$(basename "$file") | ||
| size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "0") | ||
| echo "{\"name\":\"$filename\",\"size\":$size}" | ||
| done | jq -s . >> $GITHUB_OUTPUT | ||
| echo "EOF" >> $GITHUB_OUTPUT | ||
| - name: Create Release | ||
| id: create_release | ||
| uses: softprops/action-gh-release@v1 | ||
| with: | ||
| draft: false | ||
| prerelease: false | ||
| generate_release_notes: true | ||
| files: | | ||
| **/*.exe | ||
| **/*.msi | ||
| **/*.dmg | ||
| **/*.zip | ||
| **/*.AppImage | ||
| **/*.deb | ||
| **/*.rpm | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| deploy-pages: | ||
| needs: release | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| pages: write | ||
| id-token: write | ||
| environment: | ||
| name: github-pages | ||
| url: ${{ steps.deployment.outputs.page_url }} | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Pages | ||
| uses: actions/configure-pages@v4 | ||
| - name: Get release info using GitHub API | ||
| id: get_release | ||
| run: | | ||
| echo "tag_name=${{ needs.release.outputs.tag_name }}" >> $GITHUB_OUTPUT | ||
| echo "release_url=https://github.com/${{ github.repository }}/releases/tag/${{ needs.release.outputs.tag_name }}" >> $GITHUB_OUTPUT | ||
| # Get actual release assets from GitHub API | ||
| echo 'assets<<EOF' >> $GITHUB_OUTPUT | ||
| curl -s "https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ needs.release.outputs.tag_name }}" | \ | ||
| jq '.assets | map({name: .name, download_url: .browser_download_url, size: .size, download_count: .download_count})' >> $GITHUB_OUTPUT | ||
| echo 'EOF' >> $GITHUB_OUTPUT | ||
| - name: Generate landing page | ||
| run: | | ||
| mkdir -p _site | ||
| cat > _site/index.html << 'EOF' | ||
| <!DOCTYPE html> | ||
| <html lang="ko"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>RiSA - RSA 암호화 데스크톱 앱</title> | ||
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | ||
| <style> | ||
| * { margin: 0; padding: 0; box-sizing: border-box; } | ||
| body { | ||
| font-family: 'Inter', sans-serif; | ||
| line-height: 1.6; | ||
| color: #333; | ||
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||
| min-height: 100vh; | ||
| } | ||
| .container { | ||
| max-width: 1200px; | ||
| margin: 0 auto; | ||
| padding: 0 20px; | ||
| } | ||
| .header { | ||
| background: rgba(255, 255, 255, 0.1); | ||
| backdrop-filter: blur(10px); | ||
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | ||
| padding: 1rem 0; | ||
| } | ||
| .nav { | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| } | ||
| .logo { | ||
| font-size: 1.5rem; | ||
| font-weight: 700; | ||
| color: white; | ||
| text-decoration: none; | ||
| } | ||
| .nav-links { | ||
| display: flex; | ||
| list-style: none; | ||
| gap: 2rem; | ||
| } | ||
| .nav-links a { | ||
| color: white; | ||
| text-decoration: none; | ||
| font-weight: 500; | ||
| transition: opacity 0.3s; | ||
| } | ||
| .nav-links a:hover { opacity: 0.8; } | ||
| .hero { | ||
| text-align: center; | ||
| padding: 4rem 0; | ||
| color: white; | ||
| } | ||
| .hero h1 { | ||
| font-size: 3.5rem; | ||
| font-weight: 700; | ||
| margin-bottom: 1rem; | ||
| background: linear-gradient(45deg, #fff, #f0f0f0); | ||
| -webkit-background-clip: text; | ||
| -webkit-text-fill-color: transparent; | ||
| background-clip: text; | ||
| } | ||
| .hero p { | ||
| font-size: 1.25rem; | ||
| margin-bottom: 2rem; | ||
| opacity: 0.9; | ||
| } | ||
| .version-badge { | ||
| display: inline-block; | ||
| background: rgba(255, 255, 255, 0.2); | ||
| padding: 0.5rem 1rem; | ||
| border-radius: 50px; | ||
| margin-bottom: 2rem; | ||
| font-weight: 500; | ||
| } | ||
| .downloads { | ||
| background: white; | ||
| margin: 2rem 0; | ||
| border-radius: 20px; | ||
| padding: 3rem; | ||
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | ||
| } | ||
| .downloads h2 { | ||
| text-align: center; | ||
| margin-bottom: 1rem; | ||
| font-size: 2rem; | ||
| color: #333; | ||
| } | ||
| .system-detect { | ||
| text-align: center; | ||
| margin-bottom: 2rem; | ||
| padding: 1rem; | ||
| background: #f0f9ff; | ||
| border-radius: 8px; | ||
| color: #0284c7; | ||
| } | ||
| .download-grid { | ||
| display: grid; | ||
| grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); | ||
| gap: 2rem; | ||
| margin-top: 2rem; | ||
| } | ||
| .download-card { | ||
| border: 2px solid #f0f0f0; | ||
| border-radius: 12px; | ||
| padding: 2rem; | ||
| transition: all 0.3s ease; | ||
| } | ||
| .download-card.recommended { | ||
| border-color: #667eea; | ||
| background: linear-gradient(to bottom, #f0f4ff, #ffffff); | ||
| } | ||
| .download-card:hover { | ||
| border-color: #667eea; | ||
| transform: translateY(-5px); | ||
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2); | ||
| } | ||
| .platform-header { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| margin-bottom: 1.5rem; | ||
| } | ||
| .platform-icon { | ||
| font-size: 2.5rem; | ||
| margin-right: 1rem; | ||
| } | ||
| .platform-name { | ||
| font-size: 1.5rem; | ||
| font-weight: 700; | ||
| color: #333; | ||
| } | ||
| .recommended-badge { | ||
| display: inline-block; | ||
| background: #667eea; | ||
| color: white; | ||
| padding: 0.25rem 0.75rem; | ||
| border-radius: 20px; | ||
| font-size: 0.8rem; | ||
| margin-left: 1rem; | ||
| } | ||
| .download-options { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 1rem; | ||
| } | ||
| .download-option { | ||
| border: 1px solid #e0e0e0; | ||
| border-radius: 8px; | ||
| padding: 1rem; | ||
| background: #fafafa; | ||
| } | ||
| .download-option-header { | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| margin-bottom: 0.5rem; | ||
| } | ||
| .arch-label { | ||
| font-weight: 600; | ||
| color: #333; | ||
| } | ||
| .arch-desc { | ||
| font-size: 0.85rem; | ||
| color: #666; | ||
| } | ||
| .download-btn { | ||
| display: inline-block; | ||
| background: linear-gradient(45deg, #667eea, #764ba2); | ||
| color: white; | ||
| padding: 0.75rem 1.5rem; | ||
| border-radius: 6px; | ||
| text-decoration: none; | ||
| font-weight: 600; | ||
| font-size: 0.9rem; | ||
| margin: 0.25rem; | ||
| transition: all 0.3s ease; | ||
| } | ||
| .download-btn:hover { | ||
| transform: translateY(-2px); | ||
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); | ||
| } | ||
| .download-btn.secondary { | ||
| background: #f3f4f6; | ||
| color: #333; | ||
| } | ||
| .download-btn.secondary:hover { | ||
| background: #e5e7eb; | ||
| } | ||
| .file-size { | ||
| font-size: 0.85rem; | ||
| color: #666; | ||
| margin-left: 0.5rem; | ||
| } | ||
| .features { | ||
| padding: 4rem 0; | ||
| background: rgba(255, 255, 255, 0.1); | ||
| backdrop-filter: blur(10px); | ||
| margin: 2rem 0; | ||
| border-radius: 20px; | ||
| } | ||
| .features h2 { | ||
| text-align: center; | ||
| color: white; | ||
| font-size: 2rem; | ||
| margin-bottom: 3rem; | ||
| } | ||
| .feature-grid { | ||
| display: grid; | ||
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | ||
| gap: 2rem; | ||
| } | ||
| .feature-card { | ||
| text-align: center; | ||
| color: white; | ||
| padding: 2rem; | ||
| } | ||
| .feature-icon { | ||
| font-size: 2.5rem; | ||
| margin-bottom: 1rem; | ||
| } | ||
| .feature-card h3 { | ||
| font-size: 1.25rem; | ||
| margin-bottom: 1rem; | ||
| } | ||
| .footer { | ||
| text-align: center; | ||
| padding: 2rem 0; | ||
| color: rgba(255, 255, 255, 0.8); | ||
| } | ||
| .footer a { | ||
| color: white; | ||
| text-decoration: none; | ||
| } | ||
| @media (max-width: 768px) { | ||
| .hero h1 { font-size: 2.5rem; } | ||
| .hero p { font-size: 1.1rem; } | ||
| .downloads { padding: 2rem 1rem; } | ||
| .nav-links { display: none; } | ||
| .download-grid { grid-template-columns: 1fr; } | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <header class="header"> | ||
| <div class="container"> | ||
| <nav class="nav"> | ||
| <a href="#" class="logo">🔐 RiSA</a> | ||
| <ul class="nav-links"> | ||
| <li><a href="#download">다운로드</a></li> | ||
| <li><a href="#features">기능</a></li> | ||
| <li><a href="https://github.com/0-ROK/RiSA">GitHub</a></li> | ||
| </ul> | ||
| </nav> | ||
| </div> | ||
| </header> | ||
| <section class="hero"> | ||
| <div class="container"> | ||
| <div class="version-badge">최신 버전: ${{ steps.get_release.outputs.tag_name }}</div> | ||
| <h1>RiSA</h1> | ||
| <p>간단하고 직관적인 RSA 암호화 데스크톱 앱</p> | ||
| </div> | ||
| </section> | ||
| <section class="downloads" id="download"> | ||
| <div class="container"> | ||
| <h2>📥 다운로드</h2> | ||
| <div class="system-detect" id="system-detect"> | ||
| 시스템 감지 중... | ||
| </div> | ||
| <div class="download-grid" id="download-grid"> | ||
| <!-- 다운로드 링크가 여기에 동적으로 생성됩니다 --> | ||
| </div> | ||
| </div> | ||
| </section> | ||
| <section class="features" id="features"> | ||
| <div class="container"> | ||
| <h2>✨ 주요 기능</h2> | ||
| <div class="feature-grid"> | ||
| <div class="feature-card"> | ||
| <div class="feature-icon">🔒</div> | ||
| <h3>RSA 암호화</h3> | ||
| <p>안전한 RSA 알고리즘으로 텍스트 암호화/복호화</p> | ||
| </div> | ||
| <div class="feature-card"> | ||
| <div class="feature-icon">🎨</div> | ||
| <h3>직관적인 UI</h3> | ||
| <p>깔끔하고 사용하기 쉬운 데스크톱 인터페이스</p> | ||
| </div> | ||
| <div class="feature-card"> | ||
| <div class="feature-icon">🔧</div> | ||
| <h3>키 관리</h3> | ||
| <p>RSA 키 쌍 생성, 가져오기, 내보내기</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </section> | ||
| <footer class="footer"> | ||
| <div class="container"> | ||
| <p>© 2024 RiSA Team. MIT 라이선스로 제공됩니다.</p> | ||
| <p> | ||
| <a href="https://github.com/0-ROK/RiSA">GitHub</a> | | ||
| <a href="https://github.com/0-ROK/RiSA/issues">버그 신고</a> | | ||
| <a href="https://github.com/0-ROK/RiSA/releases">릴리즈 노트</a> | ||
| </p> | ||
| </div> | ||
| </footer> | ||
| <script> | ||
| // 다운로드 링크 생성 및 시스템 감지 | ||
| const assets = ${{ steps.get_release.outputs.assets }}; | ||
| const tagName = '${{ steps.get_release.outputs.tag_name }}'; | ||
| const downloadGrid = document.getElementById('download-grid'); | ||
| const systemDetect = document.getElementById('system-detect'); | ||
| // 시스템 감지 | ||
| function detectSystem() { | ||
| const platform = navigator.platform.toLowerCase(); | ||
| const userAgent = navigator.userAgent.toLowerCase(); | ||
| if (platform.includes('win') || userAgent.includes('windows')) { | ||
| const is64bit = navigator.userAgent.includes('WOW64') || navigator.userAgent.includes('Win64'); | ||
| return { os: 'windows', arch: is64bit ? 'x64' : 'ia32' }; | ||
| } else if (platform.includes('mac') || userAgent.includes('mac')) { | ||
| // Check for Apple Silicon | ||
| const isAppleSilicon = navigator.userAgent.includes('ARM') || | ||
| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); | ||
| return { os: 'macos', arch: isAppleSilicon ? 'arm64' : 'x64' }; | ||
| } else if (platform.includes('linux') || userAgent.includes('linux')) { | ||
| return { os: 'linux', arch: 'x64' }; | ||
| } | ||
| return { os: 'unknown', arch: 'unknown' }; | ||
| } | ||
| function formatFileSize(bytes) { | ||
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | ||
| if (bytes === 0) return '0 Bytes'; | ||
| const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); | ||
| return Math.round(bytes / Math.pow(1024, i) * 10) / 10 + ' ' + sizes[i]; | ||
| } | ||
| function categorizeAssets(assets) { | ||
| const categorized = { | ||
| windows: { | ||
| x64: [], | ||
| ia32: [], | ||
| portable: [], | ||
| other: [] | ||
| }, | ||
| macos: { | ||
| arm64: { dmg: [], zip: [] }, | ||
| x64: { dmg: [], zip: [] } | ||
| }, | ||
| linux: { | ||
| appimage: [], | ||
| deb: [], | ||
| rpm: [] | ||
| } | ||
| }; | ||
| assets.forEach(asset => { | ||
| const name = asset.name.toLowerCase(); | ||
| if (name.includes('win') || name.includes('windows') || name.endsWith('.exe')) { | ||
| if (name.includes('x64')) { | ||
| categorized.windows.x64.push(asset); | ||
| } else if (name.includes('ia32')) { | ||
| categorized.windows.ia32.push(asset); | ||
| } else if (name.includes('portable')) { | ||
| categorized.windows.portable.push(asset); | ||
| } else { | ||
| categorized.windows.other.push(asset); | ||
| } | ||
| } else if (name.includes('mac') || name.includes('darwin')) { | ||
| if (name.includes('arm64')) { | ||
| if (name.endsWith('.dmg')) { | ||
| categorized.macos.arm64.dmg.push(asset); | ||
| } else if (name.endsWith('.zip')) { | ||
| categorized.macos.arm64.zip.push(asset); | ||
| } | ||
| } else if (name.includes('x64')) { | ||
| if (name.endsWith('.dmg')) { | ||
| categorized.macos.x64.dmg.push(asset); | ||
| } else if (name.endsWith('.zip')) { | ||
| categorized.macos.x64.zip.push(asset); | ||
| } | ||
| } | ||
| } else if (name.includes('linux')) { | ||
| if (name.endsWith('.appimage')) { | ||
| categorized.linux.appimage.push(asset); | ||
| } else if (name.endsWith('.deb')) { | ||
| categorized.linux.deb.push(asset); | ||
| } else if (name.endsWith('.rpm')) { | ||
| categorized.linux.rpm.push(asset); | ||
| } | ||
| } | ||
| }); | ||
| return categorized; | ||
| } | ||
| function createDownloadCard(platform, platformAssets, recommended) { | ||
| const card = document.createElement('div'); | ||
| card.className = 'download-card' + (recommended ? ' recommended' : ''); | ||
| let content = ''; | ||
| if (platform === 'windows') { | ||
| content = ` | ||
| <div class="platform-header"> | ||
| <span class="platform-icon">🪟</span> | ||
| <span class="platform-name">Windows</span> | ||
| ${recommended ? '<span class="recommended-badge">추천</span>' : ''} | ||
| </div> | ||
| <div class="download-options">`; | ||
| if (platformAssets.x64.length > 0) { | ||
| content += ` | ||
| <div class="download-option"> | ||
| <div class="download-option-header"> | ||
| <span class="arch-label">64비트 (x64)</span> | ||
| <span class="arch-desc">대부분의 최신 PC</span> | ||
| </div>`; | ||
| platformAssets.x64.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn"> | ||
| 다운로드<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| content += `</div>`; | ||
| } | ||
| if (platformAssets.ia32.length > 0) { | ||
| content += ` | ||
| <div class="download-option"> | ||
| <div class="download-option-header"> | ||
| <span class="arch-label">32비트 (ia32)</span> | ||
| <span class="arch-desc">구형 PC용</span> | ||
| </div>`; | ||
| platformAssets.ia32.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn secondary"> | ||
| 다운로드<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| content += `</div>`; | ||
| } | ||
| if (platformAssets.other.length > 0) { | ||
| platformAssets.other.forEach(asset => { | ||
| if (!asset.name.toLowerCase().includes('blockmap')) { | ||
| content += `<a href="${asset.download_url}" class="download-btn"> | ||
| ${asset.name}<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| } | ||
| }); | ||
| } | ||
| content += `</div>`; | ||
| } else if (platform === 'macos') { | ||
| content = ` | ||
| <div class="platform-header"> | ||
| <span class="platform-icon">🍎</span> | ||
| <span class="platform-name">macOS</span> | ||
| ${recommended ? '<span class="recommended-badge">추천</span>' : ''} | ||
| </div> | ||
| <div class="download-options">`; | ||
| if (platformAssets.arm64.dmg.length > 0 || platformAssets.arm64.zip.length > 0) { | ||
| content += ` | ||
| <div class="download-option"> | ||
| <div class="download-option-header"> | ||
| <span class="arch-label">Apple Silicon</span> | ||
| <span class="arch-desc">M1/M2/M3 Mac</span> | ||
| </div>`; | ||
| platformAssets.arm64.dmg.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn"> | ||
| DMG<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| platformAssets.arm64.zip.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn secondary"> | ||
| ZIP<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| content += `</div>`; | ||
| } | ||
| if (platformAssets.x64.dmg.length > 0 || platformAssets.x64.zip.length > 0) { | ||
| content += ` | ||
| <div class="download-option"> | ||
| <div class="download-option-header"> | ||
| <span class="arch-label">Intel Mac</span> | ||
| <span class="arch-desc">Intel 프로세서 Mac</span> | ||
| </div>`; | ||
| platformAssets.x64.dmg.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn"> | ||
| DMG<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| platformAssets.x64.zip.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn secondary"> | ||
| ZIP<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| content += `</div>`; | ||
| } | ||
| content += `</div>`; | ||
| } else if (platform === 'linux') { | ||
| content = ` | ||
| <div class="platform-header"> | ||
| <span class="platform-icon">🐧</span> | ||
| <span class="platform-name">Linux</span> | ||
| ${recommended ? '<span class="recommended-badge">추천</span>' : ''} | ||
| </div> | ||
| <div class="download-options">`; | ||
| if (platformAssets.appimage.length > 0) { | ||
| content += ` | ||
| <div class="download-option"> | ||
| <div class="download-option-header"> | ||
| <span class="arch-label">AppImage</span> | ||
| <span class="arch-desc">모든 배포판용</span> | ||
| </div>`; | ||
| platformAssets.appimage.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn"> | ||
| 다운로드<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| content += `</div>`; | ||
| } | ||
| if (platformAssets.deb.length > 0) { | ||
| content += ` | ||
| <div class="download-option"> | ||
| <div class="download-option-header"> | ||
| <span class="arch-label">DEB</span> | ||
| <span class="arch-desc">Ubuntu/Debian</span> | ||
| </div>`; | ||
| platformAssets.deb.forEach(asset => { | ||
| content += `<a href="${asset.download_url}" class="download-btn secondary"> | ||
| 다운로드<span class="file-size">${formatFileSize(asset.size)}</span> | ||
| </a>`; | ||
| }); | ||
| content += `</div>`; | ||
| } | ||
| content += `</div>`; | ||
| } | ||
| card.innerHTML = content; | ||
| return card; | ||
| } | ||
| // 메인 실행 | ||
| const system = detectSystem(); | ||
| // 시스템 감지 메시지 업데이트 | ||
| if (system.os !== 'unknown') { | ||
| const osNames = { | ||
| 'windows': 'Windows', | ||
| 'macos': 'macOS', | ||
| 'linux': 'Linux' | ||
| }; | ||
| systemDetect.innerHTML = `✅ ${osNames[system.os]} 시스템이 감지되었습니다. 아래에서 적합한 버전을 선택하세요.`; | ||
| } else { | ||
| systemDetect.innerHTML = '⚠️ 시스템을 자동으로 감지할 수 없습니다. 아래에서 수동으로 선택해주세요.'; | ||
| } | ||
| // assets가 있는 경우 | ||
| if (assets && assets.length > 0) { | ||
| const categorized = categorizeAssets(assets); | ||
| // Windows 카드 | ||
| if (categorized.windows.x64.length > 0 || categorized.windows.ia32.length > 0 || categorized.windows.other.length > 0) { | ||
| const windowsCard = createDownloadCard('windows', categorized.windows, system.os === 'windows'); | ||
| downloadGrid.appendChild(windowsCard); | ||
| } | ||
| // macOS 카드 | ||
| if (categorized.macos.arm64.dmg.length > 0 || categorized.macos.arm64.zip.length > 0 || | ||
| categorized.macos.x64.dmg.length > 0 || categorized.macos.x64.zip.length > 0) { | ||
| const macCard = createDownloadCard('macos', categorized.macos, system.os === 'macos'); | ||
| downloadGrid.appendChild(macCard); | ||
| } | ||
| // Linux 카드 | ||
| if (categorized.linux.appimage.length > 0 || categorized.linux.deb.length > 0) { | ||
| const linuxCard = createDownloadCard('linux', categorized.linux, system.os === 'linux'); | ||
| downloadGrid.appendChild(linuxCard); | ||
| } | ||
| } else { | ||
| // 폴백: GitHub 릴리즈 페이지로 연결 | ||
| downloadGrid.innerHTML = ` | ||
| <div style="text-align: center; grid-column: 1 / -1;"> | ||
| <p style="margin-bottom: 1rem;">릴리즈를 불러올 수 없습니다.</p> | ||
| <a href="https://github.com/0-ROK/RiSA/releases/latest" class="download-btn"> | ||
| GitHub에서 최신 릴리즈 보기 | ||
| </a> | ||
| </div> | ||
| `; | ||
| } | ||
| </script> | ||
| </body> | ||
| </html> | ||
| EOF | ||
| - name: Upload Pages artifact | ||
| uses: actions/upload-pages-artifact@v3 | ||
| with: | ||
| path: '_site' | ||
| - name: Deploy to GitHub Pages | ||
| id: deployment | ||
| uses: actions/deploy-pages@v4 | ||