diff --git a/.github/workflows/wrapup.yaml b/.github/workflows/wrapup.yaml new file mode 100644 index 0000000000..69a2aebab0 --- /dev/null +++ b/.github/workflows/wrapup.yaml @@ -0,0 +1,197 @@ +name: Wrapup πŸ“¦ + +on: + schedule: + # λ§€μ£Ό μΌμš”μΌ μ˜€μ „ 10μ‹œ (KST κΈ°μ€€, UTCλ‘œλŠ” μΌμš”μΌ 1μ‹œ) + - cron: "0 1 * * 0" + workflow_dispatch: + inputs: + date: + description: "λ§ˆλ¬΄λ¦¬ν•  주차에 μ†ν•œ λ‚ μ§œ (λΉ„μ›Œλ‘λ©΄ μ–΄μ œ, KST κΈ°μ€€)" + required: false + type: string + default: "" + exclude_prs: + description: 'PR numbers to exclude (comma-separated, e.g., "1972,1973")' + required: false + type: string + default: "" + skip_release: + description: "릴리즈 생성 κ±΄λ„ˆλ›°κΈ°" + required: false + type: boolean + default: false + +jobs: + wrapup: + name: Wrap Up the Week πŸ“¦ + runs-on: ubuntu-latest + permissions: + contents: write # 릴리즈 생성 + + steps: + - name: Resolve target week + id: week + env: + DATE_INPUT: ${{ inputs.date }} + run: | + date="${DATE_INPUT:-$(TZ=Asia/Seoul date -d yesterday +%F)}" + echo "πŸ—“οΈ λŒ€μƒ λ‚ μ§œ: $date" + + response=$(curl -sS -X POST "https://github.dalestudy.com/resolve-iteration" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg date "$date" '{date: $date}')") + + echo "$response" | jq '.' + + if [ "$(echo "$response" | jq -r '.success // false')" != "true" ]; then + echo "::error::μ£Όμ°¨ μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€" + exit 1 + fi + + found=$(echo "$response" | jq -r '.found') + echo "found=$found" >> $GITHUB_OUTPUT + + if [ "$found" != "true" ]; then + { + echo "## πŸ“¦ λ§ˆλ¬΄λ¦¬ν•  μ£Όμ°¨κ°€ μ—†μŠ΅λ‹ˆλ‹€" + echo "" + echo "\`$date\`이(κ°€) μ–΄λŠ 주차에도 μ†ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 기수 사이 νœ΄μ‹κΈ°μž…λ‹ˆλ‹€." + } >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + cohort=$(echo "$response" | jq -r '.cohort') + week=$(echo "$response" | jq -r '.week') + + { + echo "week_label=$(echo "$response" | jq -r '.week_label')" + echo "tag=v${cohort}.${week}.0" + echo "period=$(echo "$response" | jq -r '"\(.start_date) ~ \(.end_date)"')" + } >> $GITHUB_OUTPUT + + - name: Build exclude list + id: excludes + if: steps.week.outputs.found == 'true' + env: + EXCLUDE_PRS: ${{ inputs.exclude_prs }} + run: | + if [ -z "$EXCLUDE_PRS" ]; then + echo "json=[]" >> $GITHUB_OUTPUT + exit 0 + fi + + if ! echo "$EXCLUDE_PRS" | grep -qE '^[0-9]+(,[0-9]+)*$'; then + echo "::error::exclude_prs ν˜•μ‹μ΄ 잘λͺ»λ˜μ—ˆμŠ΅λ‹ˆλ‹€: $EXCLUDE_PRS" + exit 1 + fi + + echo "json=[$EXCLUDE_PRS]" >> $GITHUB_OUTPUT + + - name: Approve PRs + id: approve + if: steps.week.outputs.found == 'true' + env: + WEEK_LABEL: ${{ steps.week.outputs.week_label }} + EXCLUDES: ${{ steps.excludes.outputs.json }} + REPO_NAME: ${{ github.event.repository.name }} + run: | + echo "πŸ‘ ${WEEK_LABEL} PR 승인 쀑..." + + response=$(curl -sS -X POST "https://github.dalestudy.com/approve-prs" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg repo "$REPO_NAME" --arg week "$WEEK_LABEL" \ + --argjson excludes "$EXCLUDES" \ + '{repo_name: $repo, week: $week, excludes: $excludes}')") + + echo "$response" | jq '.' + echo "response=$(echo "$response" | jq -c '.')" >> $GITHUB_OUTPUT + + if [ "$(echo "$response" | jq -r '.success // false')" != "true" ]; then + echo "::error::승인 μš”μ²­μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€" + exit 1 + fi + + - name: Merge PRs + id: merge + if: steps.week.outputs.found == 'true' + env: + WEEK_LABEL: ${{ steps.week.outputs.week_label }} + EXCLUDES: ${{ steps.excludes.outputs.json }} + REPO_NAME: ${{ github.event.repository.name }} + run: | + echo "πŸš€ ${WEEK_LABEL} PR 병합 쀑..." + + response=$(curl -sS -X POST "https://github.dalestudy.com/merge-prs" \ + -H "Content-Type: application/json" \ + -d "$(jq -nc --arg repo "$REPO_NAME" --arg week "$WEEK_LABEL" \ + --argjson excludes "$EXCLUDES" \ + '{repo_name: $repo, week: $week, merge_method: "squash", excludes: $excludes}')") + + echo "$response" | jq '.' + echo "response=$(echo "$response" | jq -c '.')" >> $GITHUB_OUTPUT + + if [ "$(echo "$response" | jq -r '.success // false')" != "true" ]; then + echo "::error::병합 μš”μ²­μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€" + exit 1 + fi + + - name: Create release + id: release + if: steps.week.outputs.found == 'true' && inputs.skip_release != true + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + TAG: ${{ steps.week.outputs.tag }} + run: | + if gh release view "$TAG" --repo "$REPO" > /dev/null 2>&1; then + echo "⏭️ ${TAG} λ¦΄λ¦¬μ¦ˆκ°€ 이미 μžˆμ–΄ κ±΄λ„ˆλœλ‹ˆλ‹€" + exit 0 + fi + + echo "🏷️ ${TAG} 릴리즈 생성 쀑..." + gh release create "$TAG" --repo "$REPO" \ + --target main --title "$TAG" --generate-notes + + - name: Summary + if: steps.week.outputs.found == 'true' + env: + WEEK_LABEL: ${{ steps.week.outputs.week_label }} + PERIOD: ${{ steps.week.outputs.period }} + TAG: ${{ steps.week.outputs.tag }} + APPROVE_RESPONSE: ${{ steps.approve.outputs.response }} + MERGE_RESPONSE: ${{ steps.merge.outputs.response }} + REPO: ${{ github.repository }} + run: | + # μžλ™ 처리 λŒ€μƒμ΄ μ•„λ‹Œ PR은 λ―Έν•΄κ²°λ‘œ μ„Έμ§€ μ•ŠλŠ”λ‹€ + normal_skip='.reason == "draft PR"' + unresolved_filter=".results[]? | select(.merged != true) | select($normal_skip | not)" + + approved=$(echo "$APPROVE_RESPONSE" | jq -r '.approved') + merged=$(echo "$MERGE_RESPONSE" | jq -r '.merged') + matched=$(echo "$MERGE_RESPONSE" | jq -r '.week_matched') + solving=$(echo "$MERGE_RESPONSE" | jq -r '.solving_excluded') + + { + echo "## πŸ“¦ ${WEEK_LABEL} 마무리" + echo "" + echo "- πŸ—“οΈ κΈ°κ°„: ${PERIOD}" + echo "- πŸ“‹ λŒ€μƒ PR: **${matched}**개 (Solving μ œμ™Έ ${solving}개)" + echo "- πŸ‘ 승인: **${approved}**개" + echo "- πŸš€ 병합: **${merged}**개" + echo "- 🏷️ 릴리즈: [${TAG}](https://github.com/${REPO}/releases/tag/${TAG})" + } >> $GITHUB_STEP_SUMMARY + + unresolved=$(echo "$MERGE_RESPONSE" | jq "[$unresolved_filter] | length") + + if [ "$unresolved" -gt 0 ]; then + { + echo "" + echo "### ⚠️ λ³‘ν•©λ˜μ§€ μ•Šμ€ PR" + echo "" + echo "$MERGE_RESPONSE" | jq -r "$unresolved_filter | \"- #\(.pr) \(.title) β€” \(.reason // .error // \"unknown\")\"" + } >> $GITHUB_STEP_SUMMARY + + echo "::error::λ³‘ν•©λ˜μ§€ μ•Šμ€ PR ${unresolved}건 β€” 직접 확인이 ν•„μš”ν•©λ‹ˆλ‹€" + exit 1 + fi