project-auto-wizard 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +1 -1
  2. package/bin/project-auto-wizard.js +18 -4
  3. package/package.json +2 -2
  4. package/payload/config/breaking-changes.json +28 -14
  5. package/payload/config/wizard-prompts.yml +102 -46
  6. package/payload/flutter-app/android/fastlane/Fastfile.playstore +44 -32
  7. package/payload/flutter-app/ios/ExportOptions.plist +8 -8
  8. package/payload/flutter-app/ios/fastlane/Fastfile +57 -43
  9. package/payload/scripts/changelog_manager.py +296 -257
  10. package/payload/scripts/issue_helper.py +54 -50
  11. package/payload/scripts/messages.py +2279 -0
  12. package/payload/scripts/truncate_release_notes.py +7 -6
  13. package/payload/scripts/version_manager.py +64 -61
  14. package/payload/version.yml.template +2 -1
  15. package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +17 -12
  16. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +59 -34
  17. package/payload/workflows/common/PROJECT-COMMON-ISSUE-HELPER.yaml +17 -15
  18. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +17 -10
  19. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +54 -44
  20. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +24 -17
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +194 -184
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +226 -213
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +46 -42
  24. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +334 -241
  25. package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +152 -128
  26. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +246 -188
  27. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +353 -238
  28. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +96 -85
  29. package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -36
  30. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +945 -508
  31. package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +155 -133
  32. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +76 -60
  33. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +97 -77
  34. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +62 -47
  35. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +942 -505
  36. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +148 -126
  37. package/payload/workflows/react/PROJECT-REACT-CI.yaml +86 -70
  38. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +112 -92
  39. package/payload/workflows/spring/PROJECT-SPRING-CI.yml +112 -82
  40. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +180 -150
  41. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +161 -131
  42. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +970 -549
  43. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +193 -162
  44. package/src/cli/args.js +98 -52
  45. package/src/cli/help.js +8 -41
  46. package/src/commands/doctor.js +85 -89
  47. package/src/commands/dry-run.js +63 -43
  48. package/src/commands/full.js +79 -77
  49. package/src/commands/install-settings.js +16 -15
  50. package/src/commands/interactive-flutter.js +20 -19
  51. package/src/commands/interactive.js +135 -132
  52. package/src/commands/purge.js +47 -42
  53. package/src/commands/status.js +36 -36
  54. package/src/commands/uninstall.js +43 -41
  55. package/src/context.js +12 -11
  56. package/src/core/assets.js +12 -11
  57. package/src/core/baseline.js +27 -27
  58. package/src/core/branches.js +29 -27
  59. package/src/core/branding.js +7 -6
  60. package/src/core/breaking-check.js +22 -21
  61. package/src/core/breaking.js +15 -8
  62. package/src/core/copy/app-files.js +5 -4
  63. package/src/core/copy/flutter-app.js +8 -8
  64. package/src/core/copy/gitignore.js +58 -50
  65. package/src/core/copy/readme.js +45 -35
  66. package/src/core/copy/simple.js +16 -15
  67. package/src/core/copy/workflows.js +131 -124
  68. package/src/core/deploy-style.js +51 -48
  69. package/src/core/detect-fs.js +57 -57
  70. package/src/core/detect.js +67 -65
  71. package/src/core/errors.js +2 -2
  72. package/src/core/flutter-doctor.js +17 -16
  73. package/src/core/flutter-hooks.js +33 -32
  74. package/src/core/flutter-options.js +34 -33
  75. package/src/core/fsutil.js +12 -12
  76. package/src/core/installed-stores.js +4 -4
  77. package/src/core/logger.js +42 -40
  78. package/src/core/paths-resolve.js +88 -86
  79. package/src/core/paths.js +10 -10
  80. package/src/core/release-options.js +8 -8
  81. package/src/core/removal-exec.js +14 -13
  82. package/src/core/removal-plan.js +37 -37
  83. package/src/core/types.js +47 -47
  84. package/src/core/verify.js +44 -38
  85. package/src/core/version-yml.js +76 -68
  86. package/src/core/wizard-env.js +46 -46
  87. package/src/core/wizard-labels.js +63 -41
  88. package/src/i18n/catalog/en/cli.js +50 -0
  89. package/src/i18n/catalog/en/commands.js +187 -0
  90. package/src/i18n/catalog/en/copy.js +19 -0
  91. package/src/i18n/catalog/en/core.js +74 -0
  92. package/src/i18n/catalog/en/core2.js +15 -0
  93. package/src/i18n/catalog/en/ui.js +221 -0
  94. package/src/i18n/catalog/en.js +12 -0
  95. package/src/i18n/catalog/index.js +5 -0
  96. package/src/i18n/catalog/ko/cli.js +50 -0
  97. package/src/i18n/catalog/ko/commands.js +187 -0
  98. package/src/i18n/catalog/ko/copy.js +19 -0
  99. package/src/i18n/catalog/ko/core.js +74 -0
  100. package/src/i18n/catalog/ko/core2.js +15 -0
  101. package/src/i18n/catalog/ko/ui.js +221 -0
  102. package/src/i18n/catalog/ko.js +10 -0
  103. package/src/i18n/index.js +44 -0
  104. package/src/i18n/languages.js +9 -0
  105. package/src/index.js +128 -94
  106. package/src/ui/ansi.js +7 -7
  107. package/src/ui/banner.js +7 -6
  108. package/src/ui/env-plan.js +82 -81
  109. package/src/ui/prompts.js +97 -122
  110. package/src/ui/readline-engine.js +59 -58
  111. package/src/ui/status-cards.js +37 -31
  112. package/src/ui/summary.js +89 -88
@@ -69,7 +69,7 @@ permissions:
69
69
  copilot-requests: write
70
70
 
71
71
  env:
72
- # 스크립트 import가 .github/scripts/__pycache__를 만들어 봇 커밋에 섞이지 않게 한다
72
+ # Keep script imports from creating .github/scripts/__pycache__ so it never ends up in bot commits
73
73
  PYTHONDONTWRITEBYTECODE: "1"
74
74
 
75
75
  jobs:
@@ -96,9 +96,11 @@ jobs:
96
96
  GH_REPO: ${{ github.repository }}
97
97
  run: |
98
98
  PR_NUMBER=${{ github.event.pull_request.number }}
99
- # 파이프라인 도중 {{DEVELOP_BRANCH}}에 push하면 그 실행은 대기열에서 기다렸다가 이미 병합된
100
- # PR을 다시 처리한다 — 빈 요약 댓글, 중복 CHANGELOG 항목, {{DEVELOP_BRANCH}}에만 남는 확정 커밋이 생긴다.
101
- # 이벤트 시점이 아니라 지금 상태를 본다. 조회에 실패하면 릴리스를 막지 않고 진행한다.
99
+ # A push to {{DEVELOP_BRANCH}} mid-pipeline queues a run that later reprocesses the
100
+ # already-merged PR - an empty summary comment, duplicate CHANGELOG entries and a
101
+ # confirm commit that lands only on {{DEVELOP_BRANCH}}.
102
+ # Look at the current state, not the event-time state. If the lookup fails,
103
+ # do not block the release and carry on.
102
104
  STATE=$(gh pr view "$PR_NUMBER" --json state -q .state 2>/dev/null || echo "UNKNOWN")
103
105
  if [ "$STATE" = "MERGED" ] || [ "$STATE" = "CLOSED" ]; then
104
106
  echo "open=false" >> "$GITHUB_OUTPUT"
@@ -152,8 +154,9 @@ jobs:
152
154
  run: |
153
155
  git fetch origin {{MAIN_BRANCH}}
154
156
  git log --pretty=%s "origin/{{MAIN_BRANCH}}..HEAD" > commits.txt
155
- # 본문 푸터 BREAKING CHANGE:는 제목 목록 뒤에 한 줄씩 덧붙인다 — 스크립트는 한 줄을
156
- # 한 항목으로 읽으므로 본문 전체(%B)를 넣으면 설명 줄까지 변경 항목으로 잡힌다.
157
+ # Body-footer BREAKING CHANGE: lines are appended after the subject list, one per
158
+ # line. The script reads each line as one entry, so passing the whole body (%B)
159
+ # would turn description lines into change entries.
157
160
  git log --pretty=%b "origin/{{MAIN_BRANCH}}..HEAD" | grep -E '^BREAKING[ -]CHANGE[[:space:]]*:' >> commits.txt || true
158
161
  git diff --stat "origin/{{MAIN_BRANCH}}...HEAD" > _full_diff_stat.txt
159
162
  if [ "$(wc -l < _full_diff_stat.txt)" -gt 49 ]; then
@@ -170,6 +173,7 @@ jobs:
170
173
  GH_TOKEN: ${{ github.token }}
171
174
  GITHUB_TOKEN: ${{ github.token }}
172
175
  run: |
176
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
173
177
  git log --pretty=%H "origin/{{MAIN_BRANCH}}..HEAD" > commit_shas.txt
174
178
 
175
179
  gh pr list --state merged --base {{DEVELOP_BRANCH}} \
@@ -177,7 +181,7 @@ jobs:
177
181
 
178
182
  MERGED_PR_COUNT=$(python3 -c "import json; print(len(json.load(open('merged_prs.json'))))")
179
183
  if [ "$MERGED_PR_COUNT" -ge 200 ]; then
180
- echo "::warning::gh pr list가 --limit 200에 도달했습니다 — 오래된 병합 PR이 누락됐을 수 있습니다 (조회된 PR: $MERGED_PR_COUNT)"
184
+ echo "::warning::$(m wf_changelog.pr_list_limit count="$MERGED_PR_COUNT")"
181
185
  fi
182
186
 
183
187
  ISSUE_NUMBERS=$(python3 .github/scripts/changelog_manager.py collect-issue-closes \
@@ -187,7 +191,7 @@ jobs:
187
191
  rm -f commit_shas.txt merged_prs.json
188
192
 
189
193
  if [ -z "$ISSUE_NUMBERS" ]; then
190
- echo "이번 릴리스에 연결할 이슈 없음 — 건너뜀"
194
+ echo "$(m wf_changelog.no_issues)"
191
195
  exit 0
192
196
  fi
193
197
 
@@ -196,7 +200,7 @@ jobs:
196
200
  --issue-numbers "$ISSUE_NUMBERS" \
197
201
  --replace
198
202
 
199
- echo "이슈 연결: $ISSUE_NUMBERS"
203
+ echo "$(m wf_changelog.issues_linked issues="$ISSUE_NUMBERS")"
200
204
 
201
205
  - name: Confirm release version (bump + sync)
202
206
  id: bump
@@ -207,9 +211,11 @@ jobs:
207
211
  GITHUB_TOKEN: ${{ github.token }}
208
212
  COPILOT_AI: ${{ steps.copilot_options.outputs.copilot_ai }}
209
213
  run: |
210
- # 멱등: 이 PR에서 이미 확정한(아직 발행 전) 버전을 다시 올리지 않는다.
211
- # HEAD 커밋 메시지만 보면 확정 커밋 위에 커밋이 더 쌓일 때({{DEVELOP_BRANCH}} 추가 push,
212
- # {{MAIN_BRANCH}} 역병합) 가드가 풀려 발행되지 않은 버전이 생기므로 버전과 태그로 판정한다.
214
+ # Idempotency: never raise a version this PR already confirmed (not yet published).
215
+ # Looking only at the HEAD commit message lets the guard slip when more commits
216
+ # pile on top of the confirm commit (extra {{DEVELOP_BRANCH}} pushes, a
217
+ # {{MAIN_BRANCH}} back-merge) and an unpublished version appears, so decide by
218
+ # version and tag instead.
213
219
  HEAD_MSG=$(git log -1 --pretty=%s)
214
220
  HEAD_VERSION=$(python3 .github/scripts/version_manager.py get | tail -n 1)
215
221
  MAIN_VERSION=$(git show "origin/{{MAIN_BRANCH}}:version.yml" 2>/dev/null | grep -m1 '^version:' | sed 's/version:[ \t]*"\{0,1\}\([0-9][0-9.]*\)"\{0,1\}.*/\1/' || true)
@@ -224,8 +230,10 @@ jobs:
224
230
  echo "bump level: $BUMP"
225
231
 
226
232
  if [ -n "$MAIN_VERSION" ] && [ -n "$HEAD_VERSION" ] && version_gt "$HEAD_VERSION" "$MAIN_VERSION" && ! is_tagged "$HEAD_VERSION"; then
227
- # 이미 확정된 버전 — {{MAIN_BRANCH}} 버전 기준으로 다시 계산해 더 높아질 때만(예: 확정 뒤 feat 추가) 올린다.
228
- # 버전을 건너뛰지 않고, AI 보조 판정이 재실행마다 달라도 낮아지지 않는다.
233
+ # Already-confirmed version - recompute from the {{MAIN_BRANCH}} version and raise it
234
+ # only when the result is higher (e.g. a feat added after confirming).
235
+ # Versions are never skipped, and they never drop even if an AI-assisted
236
+ # classification differs between re-runs.
229
237
  TARGET=$(python3 -c 'import sys; sys.path.insert(0, ".github/scripts"); from version_manager import increment_version; print(increment_version(sys.argv[1], sys.argv[2]))' "$MAIN_VERSION" "$BUMP")
230
238
  if version_gt "$TARGET" "$HEAD_VERSION"; then
231
239
  NEW_VERSION=$(python3 .github/scripts/version_manager.py set "$TARGET" | tail -n 1)
@@ -235,7 +243,7 @@ jobs:
235
243
  echo "version already confirmed and not yet released — reusing $NEW_VERSION ({{MAIN_BRANCH}} is $MAIN_VERSION)"
236
244
  fi
237
245
  elif [ -z "$MAIN_VERSION" ] && printf '%s\n' "$HEAD_MSG" | grep -q "^chore(version): confirm v.* \[skip ci\]"; then
238
- # {{MAIN_BRANCH}}에 아직 version.yml이 없는 첫 릴리스의 재실행
246
+ # Re-run of a first release where {{MAIN_BRANCH}} has no version.yml yet
239
247
  NEW_VERSION="$HEAD_VERSION"
240
248
  echo "version already confirmed by a previous run — reusing $NEW_VERSION (no re-increment)"
241
249
  else
@@ -270,7 +278,7 @@ jobs:
270
278
  --pr-title "$PR_TITLE" \
271
279
  --diff-stat-file diff_stat.txt | tail -n 1)
272
280
  ENGINE=$(python3 -c 'import json,sys; print(json.loads(sys.argv[1]).get("engine") or "unknown")' "$SUMMARY_RESULT" 2>/dev/null || echo "unknown")
273
- # fallback이면 사유(스크립트가 ::warning::으로도 남김)를 engine 줄에 붙인다 — 잡 로그를 열지 않아도 원인이 보이게
281
+ # On fallback, append the reason (the script also emits it as ::warning::) to the engine line so the cause is visible without opening the job log
274
282
  FALLBACK_REASON=$(python3 -c 'import html,json,sys; print(html.escape((json.loads(sys.argv[1]).get("fallback_reason") or "")[:200]))' "$SUMMARY_RESULT" 2>/dev/null || echo "")
275
283
  ENGINE_LINE="$ENGINE${FALLBACK_REASON:+ ($FALLBACK_REASON)}"
276
284
  echo "summary engine: $ENGINE_LINE"
@@ -289,7 +297,7 @@ jobs:
289
297
  GH_TOKEN: ${{ github.token }}
290
298
  run: |
291
299
  PR_NUMBER=${{ github.event.pull_request.number }}
292
- # 재실행·재동기화 때 댓글이 쌓이지 않도록 이 봇이 남긴 요약 댓글을 표식으로 찾아 교체한다
300
+ # Find this bot's earlier summary comment by its marker and replace it so re-runs and re-syncs do not pile up comments
293
301
  MARKER="<!-- project-auto-wizard:pr-summary -->"
294
302
 
295
303
  {
@@ -343,17 +351,18 @@ jobs:
343
351
  # Working files must not land in the repo
344
352
  rm -f pr_body.md summary.md commits.txt diff_stat.txt comment_body.md comment_payload.json commit_shas.txt merged_prs.json
345
353
 
346
- # "Re-run failed jobs"는 precheck 결과를 재사용한다 — 그사이 PR이 병합·종료됐다면
347
- # 확정 커밋을 올리지 않는다 (병합 뒤 {{DEVELOP_BRANCH}}에만 남는 커밋이 된다).
354
+ # "Re-run failed jobs" reuses the precheck result - if the PR was merged or closed
355
+ # in the meantime, do not push the confirm commit (it would exist only on
356
+ # {{DEVELOP_BRANCH}} after the merge).
348
357
  PR_STATE=$(gh pr view "${{ github.event.pull_request.number }}" --json state -q .state 2>/dev/null || echo "UNKNOWN")
349
358
  if [ "$PR_STATE" = "MERGED" ] || [ "$PR_STATE" = "CLOSED" ]; then
350
359
  echo "::notice::PR is already $PR_STATE — not pushing release docs"
351
360
  exit 0
352
361
  fi
353
362
 
354
- # -A는 러너가 만든 임시 파일·캐시까지 사용자 레포에 커밋한다.
355
- # 버전 파일은 이미 추적 중이라 -u로 충분하고, 첫 릴리스에 새로 생기는
356
- # CHANGELOG만 따로 올린다 (.gitignore에 있으면 건너뛴다).
363
+ # -A would also commit runner temp files and caches into the user's repo.
364
+ # Version files are already tracked, so -u is enough; only a CHANGELOG
365
+ # created by the first release is added separately (skipped if ignored).
357
366
  git add -u
358
367
  git ls-files -z --others --exclude-standard -- CHANGELOG.json CHANGELOG.md | xargs -0 -r git add --
359
368
 
@@ -399,6 +408,7 @@ jobs:
399
408
  # (RELEASE-PUBLISH etc.); GITHUB_TOKEN merges do not.
400
409
  GH_TOKEN: ${{ secrets.WORKFLOW_PAT || github.token }}
401
410
  run: |
411
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
402
412
  PR_NUMBER=${{ github.event.pull_request.number }}
403
413
  VERSION="${{ steps.bump.outputs.new_version }}"
404
414
 
@@ -407,8 +417,9 @@ jobs:
407
417
  SUBJECT="chore(release): v${VERSION} (PR #${PR_NUMBER})"
408
418
  ERR_FILE="${RUNNER_TEMP:-/tmp}/merge_err.txt"
409
419
 
410
- # 확정 커밋 push 직후에는 GitHub가 PR head 갱신을 아직 반영하지 못해 "Head branch is
411
- # out of date"·"not mergeable"로 간헐 실패한다. 간격을 늘려 가며 다시 시도한다.
420
+ # Right after the confirm commit is pushed, GitHub may not have picked up the new PR
421
+ # head yet and intermittently fails with "Head branch is out of date" / "not
422
+ # mergeable". Retry with growing intervals.
412
423
  MERGED=false
413
424
  MAX_ATTEMPTS=4
414
425
  for ATTEMPT in $(seq 1 $MAX_ATTEMPTS); do
@@ -430,11 +441,12 @@ jobs:
430
441
  fi
431
442
  cat "$ERR_FILE" >&2
432
443
  [ "$ATTEMPT" -ge "$MAX_ATTEMPTS" ] && break
433
- # 충돌은 기다려도 풀리지 않는다
444
+ # A conflict never resolves by waiting
434
445
  [ "$(gh pr view "$PR_NUMBER" --json mergeable -q .mergeable 2>/dev/null)" = "CONFLICTING" ] && break
435
446
  if grep -qi "out of date" "$ERR_FILE"; then
436
- # 브랜치 보호의 "최신 상태 요구"로 실제로 뒤처졌다면 base를 head에 병합해 따라잡는다
437
- # (단순 반영 지연이면 "새 커밋 없음"으로 거절되고 그대로 재시도한다)
447
+ # If the head really fell behind because of branch protection's "require up to
448
+ # date" rule, merge base into head to catch up (a mere propagation delay is
449
+ # rejected as "no new commits" and simply retried)
438
450
  gh api -X PUT "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/update-branch" > /dev/null 2>&1 \
439
451
  && echo "head branch updated from base" || true
440
452
  fi
@@ -448,10 +460,10 @@ jobs:
448
460
  MERGEABLE=$(gh pr view "$PR_NUMBER" --json mergeable -q .mergeable 2>/dev/null || echo "unknown")
449
461
  ALLOW_MERGE_COMMIT=$(gh api "repos/${GITHUB_REPOSITORY}" --jq '.allow_merge_commit' 2>/dev/null || echo "unknown")
450
462
  if [ "$MERGEABLE" = "CONFLICTING" ]; then
451
- # 주로 {{MAIN_BRANCH}} 직접 push 뒤 안전망 버전 bump가 {{DEVELOP_BRANCH}}에 반영되지 않았을 때다
452
- echo "::error::릴리스 PR이 {{MAIN_BRANCH}}와 충돌합니다 (버전 파일·CHANGELOG 등) — {{MAIN_BRANCH}}에만 있는 커밋을 {{DEVELOP_BRANCH}}에 병합한 뒤 push하세요: git checkout {{DEVELOP_BRANCH}} && git merge origin/{{MAIN_BRANCH}} (버전 줄 충돌은 어느 쪽을 택해도 다음 실행이 알맞게 다시 확정합니다)"
463
+ # Mostly happens when a safety-net version bump after a direct {{MAIN_BRANCH}} push never reached {{DEVELOP_BRANCH}}
464
+ echo "::error::$(m wf_changelog.merge_conflict prod="{{MAIN_BRANCH}}" dev="{{DEVELOP_BRANCH}}")"
453
465
  elif [ "$ALLOW_MERGE_COMMIT" = "false" ]; then
454
- echo "::error::이 레포는 merge commit이 비활성화되어 있습니다 — Settings에서 'Allow merge commits'를 켜세요; 릴리스 감지는 merge commit subject를 사용합니다"
466
+ echo "::error::$(m wf_changelog.merge_commit_disabled)"
455
467
  else
456
468
  echo "::error::PR merge failed after $MAX_ATTEMPTS attempts (allow_merge_commit=$ALLOW_MERGE_COMMIT) — check branch protection rules, required checks, and token permissions"
457
469
  fi
@@ -459,20 +471,32 @@ jobs:
459
471
  fi
460
472
 
461
473
  wait-for-merge-and-trigger-release:
462
- name: Wait for merge and trigger RELEASE-PUBLISH (WORKFLOW_PAT 없이도 동작)
474
+ name: Wait for merge and trigger RELEASE-PUBLISH (works without WORKFLOW_PAT)
463
475
  needs: changelog-and-merge
464
476
  runs-on: ubuntu-latest
465
477
  timeout-minutes: 35
466
478
  permissions:
479
+ # contents: read is needed for the sparse checkout that loads the message catalog
480
+ contents: read
467
481
  pull-requests: read
468
482
  actions: write
469
483
  steps:
484
+ # Only the message catalog and version.yml (language) are needed here
485
+ - name: Checkout message catalog
486
+ uses: actions/checkout@v7
487
+ with:
488
+ sparse-checkout: |
489
+ .github/scripts/messages.py
490
+ version.yml
491
+ sparse-checkout-cone-mode: false
492
+
470
493
  - name: Poll for merge completion
471
494
  id: wait
472
495
  env:
473
496
  GH_TOKEN: ${{ github.token }}
474
497
  GH_REPO: ${{ github.repository }}
475
498
  run: |
499
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
476
500
  PR_NUMBER=${{ github.event.pull_request.number }}
477
501
  MAX_WAIT=1800
478
502
  INTERVAL=20
@@ -483,7 +507,7 @@ jobs:
483
507
  PR_STATE=$(gh pr view "$PR_NUMBER" --json state -q .state 2>/dev/null || echo "")
484
508
  if [ "$PR_STATE" = "MERGED" ]; then
485
509
  MERGED=true
486
- echo "PR #$PR_NUMBER 병합 확인 (경과 ${ELAPSED}s)"
510
+ echo "$(m wf_changelog.merge_confirmed pr="$PR_NUMBER" elapsed="$ELAPSED")"
487
511
  break
488
512
  fi
489
513
  sleep $INTERVAL
@@ -491,7 +515,7 @@ jobs:
491
515
  done
492
516
 
493
517
  if [ "$MERGED" != "true" ]; then
494
- echo "::warning::PR #$PR_NUMBER 병합을 ${MAX_WAIT}s 내에 확인하지 못했습니다 — RELEASE-PUBLISH 자동 트리거를 건너뜁니다. WORKFLOW_PAT이 등록되어 있다면 기존 경로로 정상 진행되며, 없다면 필요 시 RELEASE-PUBLISH를 수동으로 workflow_dispatch 하세요."
518
+ echo "::warning::$(m wf_changelog.merge_timeout pr="$PR_NUMBER" max="$MAX_WAIT")"
495
519
  fi
496
520
 
497
521
  echo "merged=$MERGED" >> "$GITHUB_OUTPUT"
@@ -503,5 +527,6 @@ jobs:
503
527
  GH_TOKEN: ${{ github.token }}
504
528
  GH_REPO: ${{ github.repository }}
505
529
  run: |
530
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
506
531
  gh workflow run PROJECT-COMMON-RELEASE-PUBLISH.yaml --ref {{MAIN_BRANCH}}
507
- echo "RELEASE-PUBLISH workflow_dispatch 트리거 요청 완료 (WORKFLOW_PAT 없이도 동작)"
532
+ echo "$(m wf_changelog.release_publish_triggered)"
@@ -1,15 +1,16 @@
1
1
  # project-auto-wizard:managed-workflow
2
2
  # PROJECT-COMMON-ISSUE-HELPER.yaml
3
- # 이슈 생성/제목 수정 시 브랜치명·커밋 메시지를 댓글로 안내
3
+ # Suggest a branch name and commit message as a comment when an issue is created or retitled
4
4
  #
5
- # 이슈가 열리거나(opened) 제목이 수정되면(edited + 제목 변경),
6
- # 정규화된 브랜치명과 커밋 메시지를 이슈 댓글로 생성/갱신한다.
7
- # ISSUE_HELPER_CREATE_BRANCH=true로 바꾸면 브랜치도 함께 생성한다
8
- # (기본값은 false — 이슈를 열 때마다 브랜치가 자동 생성되는 것을
9
- # 막기 위해 옵트인으로 전환됨).
5
+ # When an issue is opened, or its title is edited (edited + title changed),
6
+ # the normalized branch name and commit message are posted (or updated) as an
7
+ # issue comment.
8
+ # Set ISSUE_HELPER_CREATE_BRANCH=true to also create the branch
9
+ # (default is false - opt-in, so a branch is not created automatically
10
+ # every time an issue is opened).
10
11
  #
11
- # 로직 본체는 .github/scripts/issue_helper.py (Chuseok22/github-issue-helper의
12
- # Python 재작성, 동일 작성자).
12
+ # The logic lives in .github/scripts/issue_helper.py (a Python rewrite of
13
+ # Chuseok22/github-issue-helper, same author).
13
14
 
14
15
  name: PROJECT-ISSUE-HELPER
15
16
 
@@ -17,30 +18,31 @@ on:
17
18
  issues:
18
19
  types: [opened, edited]
19
20
 
20
- # contents는 항상 write로 고정한다. ISSUE_HELPER_CREATE_BRANCH="true"일 때
21
- # git/refs API로 브랜치를 생성하려면 write가 필요한데, 이 값은 @wizard ask로
22
- # 설치 후에도 사용자가 직접 켤 수 있어(마법사 재실행 없이) 그 시점에 맞춰
23
- # permissions를 조건부로 올릴 방법이 없다.
21
+ # contents is always fixed to write. Creating a branch through the git/refs API
22
+ # when ISSUE_HELPER_CREATE_BRANCH="true" needs write, and users can switch that
23
+ # value on themselves after install (via the wizard ask marker, without re-running the
24
+ # wizard), so there is no way to raise permissions conditionally at that point.
24
25
  permissions:
25
26
  issues: write
26
27
  contents: write
27
28
 
28
29
  jobs:
29
30
  issue-helper:
30
- name: 브랜치명 & 커밋 메시지 안내
31
+ name: Branch name & commit message guide
31
32
  if: github.event.action == 'opened' || (github.event.action == 'edited' && github.event.changes.title)
32
33
  runs-on: ubuntu-latest
33
34
  env:
34
35
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35
36
  ISSUE_HELPER_BRANCH_PREFIX: ""
36
37
  ISSUE_HELPER_MAX_BRANCH_LENGTH: "100"
37
- ISSUE_HELPER_COMMIT_TEMPLATE: "${issueTitle} : feat : {변경 사항에 대한 설명} ${issueUrl}"
38
+ # Optional override of the commit message template (the default follows the workflow language):
39
+ # ISSUE_HELPER_COMMIT_TEMPLATE: "${issueTitle} : feat : {description of the change} ${issueUrl}"
38
40
  ISSUE_HELPER_CREATE_BRANCH: "false" # @wizard ask:false
39
41
  ISSUE_HELPER_BASE_BRANCH: "{{MAIN_BRANCH}}"
40
42
  steps:
41
43
  - name: Checkout repository
42
44
  uses: actions/checkout@v7
43
45
 
44
- - name: 브랜치명 & 커밋 메시지 생성
46
+ - name: Generate branch name & commit message
45
47
  id: issue-helper
46
48
  run: python3 .github/scripts/issue_helper.py run
@@ -12,13 +12,12 @@
12
12
  # 4. Commits and pushes the change ([skip ci])
13
13
  #
14
14
  # Supported version line formats (case-insensitive, flexible spacing):
15
- # - ## 최신 버전 : v1.0.0 (2025-08-15)
16
- # - ## 최신버전 : v1.0.0 (2025-08-15)
15
+ # - ## <Korean "latest version", with or without a space> : v1.0.0 (2025-08-15)
17
16
  # - ## Current Version : v1.0.0 (2025-08-15)
18
17
  # - ## Recent Version : v1.0.0 (2025-08-15)
19
18
  # - ## Latest Version : v1.0.0
20
19
  # - ## Version : v1.0.0
21
- # - ## 버전 : v1.0.0
20
+ # - ## <Korean "version"> : v1.0.0
22
21
  #
23
22
  # The marker comment <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
24
23
  # is added automatically above the version line.
@@ -51,7 +50,7 @@ name: README VERSION UPDATE
51
50
  env:
52
51
  # Whether to show the date next to the version (true/false)
53
52
  SHOW_DATE: true
54
- # 스크립트 import가 .github/scripts/__pycache__를 만들어 봇 커밋에 섞이지 않게 한다
53
+ # Keep script imports from creating .github/scripts/__pycache__ so it never ends up in bot commits
55
54
  PYTHONDONTWRITEBYTECODE: "1"
56
55
 
57
56
  concurrency:
@@ -113,10 +112,16 @@ jobs:
113
112
  - name: Update version line in README.md
114
113
  if: steps.precheck.outputs.readme_exists == 'true'
115
114
  run: |
115
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
116
116
  LATEST_VERSION="${{ steps.version_info.outputs.latest_version }}"
117
117
  RELEASE_DATE="${{ steps.version_info.outputs.release_date }}"
118
118
  SHOW_DATE="${{ env.SHOW_DATE }}"
119
119
 
120
+ # Hangul in the Korean heading patterns below is written as UTF-8 byte escapes so
121
+ # this file stays ASCII-only; they match the same README headings byte for byte.
122
+ KO_LATEST=$'\354\265\234\354\213\240' # Korean "latest"
123
+ KO_VERSION=$'\353\262\204\354\240\204' # Korean "version"
124
+
120
125
  # Error message helper
121
126
  print_error_message() {
122
127
  local error_type="$1"
@@ -132,7 +137,7 @@ jobs:
132
137
  echo " new version text: $version_text"
133
138
  echo ""
134
139
  echo "Make sure the README version line matches one of:"
135
- echo " ## 최신 버전 : v1.0.0 (2025-08-15)"
140
+ echo " $(m wf_readme.hint_first_example)"
136
141
  echo " ## Current Version : v1.0.0"
137
142
  echo " ## Version : v1.0.0"
138
143
  echo "Notes:"
@@ -151,12 +156,12 @@ jobs:
151
156
 
152
157
  # Patterns for recognized version lines (case-insensitive, flexible spacing)
153
158
  VERSION_PATTERNS=(
154
- "^##[[:space:]]*최신[[:space:]]*버전[[:space:]]*:"
155
- "^##[[:space:]]*최신버전[[:space:]]*:"
159
+ "^##[[:space:]]*${KO_LATEST}[[:space:]]*${KO_VERSION}[[:space:]]*:"
160
+ "^##[[:space:]]*${KO_LATEST}${KO_VERSION}[[:space:]]*:"
156
161
  "^##[[:space:]]*[Cc]urrent[[:space:]]*[Vv]ersion[[:space:]]*:"
157
162
  "^##[[:space:]]*[Rr]ecent[[:space:]]*[Vv]ersion[[:space:]]*:"
158
163
  "^##[[:space:]]*[Vv]ersion[[:space:]]*:"
159
- "^##[[:space:]]*버전[[:space:]]*:"
164
+ "^##[[:space:]]*${KO_VERSION}[[:space:]]*:"
160
165
  "^##[[:space:]]*[Ll]atest[[:space:]]*[Vv]ersion[[:space:]]*:"
161
166
  )
162
167
 
@@ -175,13 +180,15 @@ jobs:
175
180
  # No version line yet — insert a default one
176
181
  if [ -z "$VERSION_LINE" ]; then
177
182
  echo "no version line found — inserting a new one"
183
+ # Heading wording follows the configured language; an existing line keeps its own wording
184
+ DEFAULT_HEADING=$(m wf_readme.default_heading)
178
185
 
179
186
  if grep -q "^# " README.md; then
180
187
  # right after the first title
181
- sed -i '/^# /a\\n<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->\n## Latest Version : '"$VERSION_TEXT" README.md
188
+ sed -i '/^# /a\\n<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->\n## '"$DEFAULT_HEADING"' : '"$VERSION_TEXT" README.md
182
189
  else
183
190
  # at the very top of the file
184
- sed -i '1i\<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->\n## Latest Version : '"$VERSION_TEXT"'\n' README.md
191
+ sed -i '1i\<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->\n## '"$DEFAULT_HEADING"' : '"$VERSION_TEXT"'\n' README.md
185
192
  fi
186
193
  echo "default version line inserted: $VERSION_TEXT"
187
194
  else