Skip to content

feat: 릴리즈 노트 포맷팅 개선 #55

feat: 릴리즈 노트 포맷팅 개선

feat: 릴리즈 노트 포맷팅 개선 #55

Workflow file for this run

name: Build and Release
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
inputs:
tag_name:
description: "Tag name for release (e.g., v0.1.1)"
required: true
default: "v0.1.1"
env:
# Mask sensitive values in logs
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
# CI and build configuration
CI: true
# Code signing configuration
CSC_IDENTITY_AUTO_DISCOVERY: true
permissions:
contents: write
jobs:
# === 2단계 빌드/배포 아키텍처 ===
# Job 1: Build - 앱 빌드 및 코드사이닝만 수행
build:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- 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
run: |
if [ -n "$APPLE_CERTIFICATE" ]; then
echo "Setting up Apple certificate..."
# Create variables for keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
# Decode certificate from base64 (secure method)
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
# Create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import certificate (GitHub Actions masks the password in logs)
security import certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH login.keychain-db
security default-keychain -s $KEYCHAIN_PATH
# Allow codesign to access the certificate without password prompt
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH >/dev/null 2>&1
echo "Certificate imported successfully"
# Verify certificate (minimal output)
if security find-identity -p codesigning $KEYCHAIN_PATH >/dev/null 2>&1; then
echo "Certificate verification successful"
else
echo "Certificate verification failed"
exit 1
fi
# Clean up certificate file
rm -f certificate.p12
else
echo "No Apple certificate found in secrets"
fi
- name: Validate secrets
run: |
# Apple 인증서 관련 secrets 검증
if [ -z "$APPLE_CERTIFICATE" ]; then
echo "❌ ERROR: APPLE_CERTIFICATE secret이 설정되지 않았습니다."
echo "macOS 코드 사이닝을 위해 APPLE_CERTIFICATE secret을 설정해주세요."
exit 1
fi
if [ -z "$APPLE_CERTIFICATE_PASSWORD" ]; then
echo "❌ ERROR: APPLE_CERTIFICATE_PASSWORD secret이 설정되지 않았습니다."
echo "macOS 코드 사이닝을 위해 APPLE_CERTIFICATE_PASSWORD secret을 설정해주세요."
exit 1
fi
# Apple ID 관련 secrets 검증
if [ -z "$APPLE_ID" ]; then
echo "❌ ERROR: APPLE_ID secret이 설정되지 않았습니다."
echo "macOS 노타리제이션을 위해 APPLE_ID secret을 설정해주세요."
exit 1
fi
if [ -z "$APPLE_APP_SPECIFIC_PASSWORD" ]; then
echo "❌ ERROR: APPLE_APP_SPECIFIC_PASSWORD secret이 설정되지 않았습니다."
echo "macOS 노타리제이션을 위해 APPLE_APP_SPECIFIC_PASSWORD secret을 설정해주세요."
exit 1
fi
if [ -z "$APPLE_TEAM_ID" ]; then
echo "❌ ERROR: APPLE_TEAM_ID secret이 설정되지 않았습니다."
echo "macOS 노타리제이션을 위해 APPLE_TEAM_ID secret을 설정해주세요."
exit 1
fi
echo "✅ macOS 코드 사이닝 및 노타리제이션 secrets가 모두 설정되었습니다."
shell: bash
- name: Package application
run: |
# Check if certificate was imported (suppress sensitive output)
if security find-identity -p codesigning | grep -q "Developer ID Application" 2>/dev/null; then
echo "Certificate found, building with code signing and notarization"
# Debug: Check environment variables before building (completely safe - no values exposed)
echo "Environment variables for notarization:"
if [ -n "$APPLE_ID" ]; then echo " APPLE_ID: ***SET***"; else echo " APPLE_ID: NOT_SET"; fi
if [ -n "$APPLE_APP_SPECIFIC_PASSWORD" ]; then echo " APPLE_APP_SPECIFIC_PASSWORD: ***SET***"; else echo " APPLE_APP_SPECIFIC_PASSWORD: NOT_SET"; fi
if [ -n "$APPLE_TEAM_ID" ]; then echo " APPLE_TEAM_ID: ***SET***"; else echo " APPLE_TEAM_ID: NOT_SET"; fi
echo " CI: $CI"
# === 2단계 빌드/배포 아키텍처 - 1단계: 빌드만 ===
# 빌드만 수행하고 GitHub 배포는 하지 않음 (--publish never)
# 2단계에서 별도 액션(softprops/action-gh-release)이 배포 담당
pnpm run package:gh-action
# Verify signing (minimal output to avoid exposing sensitive info)
echo "Verifying code signature..."
for app in release/**/*.app; do
if [ -e "$app" ]; then
echo "Checking: $(basename "$app")"
if codesign --verify --deep --strict "$app" >/dev/null 2>&1; then
echo "✓ Code signature valid for $(basename "$app")"
else
echo "⚠ Code signature verification failed for $(basename "$app")"
fi
fi
done
else
echo "Unexpected: Certificate not found after validation"
exit 1
fi
- name: List built files
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: macos-artifacts
path: |
release/*.dmg
release/*.zip
release/*.yml
release/*.blockmap
retention-days: 5
# Job 2: Release - 빌드된 파일들을 GitHub Releases에 배포
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 "*.dmg" -o -name "*.zip" -o -name "*.yml" -o -name "*.blockmap" \) -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 "*.dmg" -o -name "*.zip" \) | 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: Check for release notes
id: check_release_notes
run: |
TAG_NAME=${GITHUB_REF#refs/tags/}
RELEASE_NOTES_FILE="release-notes/${TAG_NAME}.md"
if [ -f "$RELEASE_NOTES_FILE" ]; then
echo "release_notes_exists=true" >> $GITHUB_OUTPUT
echo "release_notes_file=$RELEASE_NOTES_FILE" >> $GITHUB_OUTPUT
echo "Found release notes file: $RELEASE_NOTES_FILE"
else
echo "release_notes_exists=false" >> $GITHUB_OUTPUT
echo "No release notes file found for $TAG_NAME, will use auto-generated notes"
fi
# === 2단계 빌드/배포 아키텍처 - 2단계: 배포만 ===
# 1단계에서 생성된 파일들을 GitHub Releases에 배포
# electron-builder 대신 GitHub Actions 전용 액션 사용
# 장점: 더 안정적, 유연한 배포 옵션, 자동 릴리즈 노트 생성
- name: Create Release (with custom release notes)
if: steps.check_release_notes.outputs.release_notes_exists == 'true'
id: create_release_custom
uses: softprops/action-gh-release@v1
with:
draft: false
prerelease: false
body_path: ${{ steps.check_release_notes.outputs.release_notes_file }}
files: |
*.dmg
*.zip
*.yml
*.blockmap
- name: Create Release (with auto-generated notes)
if: steps.check_release_notes.outputs.release_notes_exists == 'false'
id: create_release_auto
uses: softprops/action-gh-release@v1
with:
draft: false
prerelease: false
generate_release_notes: true
files: |
*.dmg
*.zip
*.yml
*.blockmap
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Job 3: Deploy Pages - 다운로드 랜딩 페이지 생성 및 배포
deploy-pages:
if: always()
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: Generate landing page using script
run: |
# Use tag from GitHub ref if release job outputs are not available
if [ -n "${{ needs.release.outputs.tag_name }}" ]; then
TAG_NAME="${{ needs.release.outputs.tag_name }}"
else
TAG_NAME="${GITHUB_REF#refs/tags/}"
fi
# Run the page generation script
./scripts/generate-page.sh "$TAG_NAME" "${{ github.repository }}"
- 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