project-auto-wizard 0.13.3 → 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
@@ -62,14 +62,14 @@ on:
62
62
 
63
63
  permissions:
64
64
  contents: write
65
- # README-VERSION-UPDATE·배포 워크플로우를 workflow_dispatch로 깨운다
65
+ # Wake README-VERSION-UPDATE and the deploy workflows via workflow_dispatch
66
66
  actions: write
67
- # 릴리스 PR을 누가 병합했는지 읽어 배포를 다시 깨울지 정한다
67
+ # Read who merged the release PR to decide whether deploys must be woken again
68
68
  pull-requests: read
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:
@@ -103,6 +103,7 @@ jobs:
103
103
  - name: Release gate
104
104
  id: gate
105
105
  run: |
106
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
106
107
  MODE="${{ steps.mode.outputs.mode }}"
107
108
  EVENT="${{ github.event_name }}"
108
109
  HEAD_MSG=$(git log -1 --pretty=%B)
@@ -145,13 +146,13 @@ jobs:
145
146
  if [ "$FILE_VERSION" != "$LATEST_VERSION" ] && [ "$HIGHER" = "$FILE_VERSION" ]; then
146
147
  echo "::error::version.yml($FILE_VERSION) is ahead of the newest tag($LATEST_TAG) but no release was published — this version will never reach npm. Recover with: Actions → PROJECT-RELEASE-PUBLISH → Run workflow (workflow_dispatch bypasses this gate)."
147
148
  {
148
- echo "### ⚠️ 릴리스 누락 감지"
149
+ echo "$(m wf_release.drift_heading)"
149
150
  echo ""
150
- echo "- version.yml: \`$FILE_VERSION\`"
151
- echo "- 최신 태그: \`$LATEST_TAG\`"
151
+ echo "$(m wf_release.drift_version_line version="$FILE_VERSION")"
152
+ echo "$(m wf_release.drift_tag_line tag="$LATEST_TAG")"
152
153
  echo ""
153
- echo "릴리스 확정 없이 버전만 올라갔습니다. 이 버전은 npm에 배포되지 않습니다."
154
- echo "복구: Actions → PROJECT-RELEASE-PUBLISH → Run workflow"
154
+ echo "$(m wf_release.drift_body)"
155
+ echo "$(m wf_release.drift_recover)"
155
156
  } >> "$GITHUB_STEP_SUMMARY"
156
157
  exit 1
157
158
  fi
@@ -230,8 +231,9 @@ jobs:
230
231
  DIFF_BASE="4b825dc642cb6eb9a060e54bf8d69288fbee4904" # git empty tree hash
231
232
  fi
232
233
  git log --pretty=%s "$LOG_RANGE" > commits.txt
233
- # 본문 푸터 BREAKING CHANGE:는 제목 목록 뒤에 한 줄씩 덧붙인다 — 스크립트는 한 줄을
234
- # 한 항목으로 읽으므로 본문 전체(%B)를 넣으면 설명 줄까지 변경 항목으로 잡힌다.
234
+ # Body-footer BREAKING CHANGE: lines are appended after the subject list, one per
235
+ # line. The script reads each line as one entry, so passing the whole body (%B)
236
+ # would turn description lines into change entries.
235
237
  git log --pretty=%b "$LOG_RANGE" | grep -E '^BREAKING[ -]CHANGE[[:space:]]*:' >> commits.txt || true
236
238
  git diff --stat "${DIFF_BASE}..HEAD" > _full_diff_stat.txt
237
239
  if [ "$(wc -l < _full_diff_stat.txt)" -gt 49 ]; then
@@ -281,9 +283,9 @@ jobs:
281
283
 
282
284
  rm -f pr_body.md summary.md commits.txt diff_stat.txt
283
285
 
284
- # -A는 러너가 만든 임시 파일·캐시까지 사용자 레포에 커밋한다.
285
- # 버전 파일은 이미 추적 중이라 -u로 충분하고, 첫 릴리스에 새로 생기는
286
- # CHANGELOG만 따로 올린다 (.gitignore에 있으면 건너뛴다).
286
+ # -A would also commit runner temp files and caches into the user's repo.
287
+ # Version files are already tracked, so -u is enough; only a CHANGELOG
288
+ # created by the first release is added separately (skipped if ignored).
287
289
  git add -u
288
290
  git ls-files -z --others --exclude-standard -- CHANGELOG.json CHANGELOG.md | xargs -0 -r git add --
289
291
  if git diff --staged --quiet; then
@@ -400,9 +402,10 @@ jobs:
400
402
  - name: Create GitHub Release
401
403
  if: steps.gate.outputs.proceed == 'true' && steps.version.outputs.release_exists != 'true'
402
404
  env:
403
- # WORKFLOW_PAT으로 발행해야 release 이벤트가 후속 워크플로우(npm 배포 등)를 트리거한다.
404
- # GITHUB_TOKEN이 만든 이벤트는 GitHub 정책상 다른 워크플로우를 깨우지 못한다.
405
- # PAT이 없는 레포에서도 릴리스 자체는 계속 동작해야 하므로 기본 토큰으로 폴백한다.
405
+ # Publishing with WORKFLOW_PAT lets the release event trigger follow-up workflows
406
+ # (npm publish etc.). Events created by GITHUB_TOKEN never wake other workflows
407
+ # (GitHub policy). Repos without a PAT must still release, so fall back to the
408
+ # default token.
406
409
  GH_TOKEN: ${{ secrets.WORKFLOW_PAT || github.token }}
407
410
  run: |
408
411
  VERSION="${{ steps.version.outputs.version }}"
@@ -410,26 +413,31 @@ jobs:
410
413
  echo "GitHub Release v$VERSION published"
411
414
 
412
415
  - name: Trigger README-VERSION-UPDATE
413
- # 봇 토큰(GITHUB_TOKEN)이 만든 push는 README 갱신 워크플로우를 깨우지 못한다 —
414
- # PAT 없는 릴리스 PR 병합, 안전망·trunk-based의 [skip ci] 버전 커밋이 그렇다.
415
- # 릴리스를 새로 발행했을 때 확정된 버전으로 한 번 직접 깨운다 (중복 실행돼도 변경 없으면 커밋 안 함).
416
+ # Pushes made with the bot token (GITHUB_TOKEN) never wake the README update
417
+ # workflow - that covers a release PR merged without a PAT and the [skip ci]
418
+ # version commits of the safety net and trunk-based releases.
419
+ # When a release is newly published, wake it once with the confirmed version
420
+ # (a duplicate run commits nothing if there is no change).
416
421
  if: steps.gate.outputs.proceed == 'true' && steps.version.outputs.release_exists != 'true'
417
422
  env:
418
423
  GH_TOKEN: ${{ github.token }}
419
424
  run: |
425
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
420
426
  if gh workflow run PROJECT-COMMON-README-VERSION-UPDATE.yaml --ref {{MAIN_BRANCH}}; then
421
- echo "README-VERSION-UPDATE workflow_dispatch 트리거 요청 완료"
427
+ echo "$(m wf_release.readme_dispatch_ok)"
422
428
  else
423
- echo "::notice::README-VERSION-UPDATE 워크플로우를 깨우지 못했습니다 (삭제했거나 비활성화됨) — README 버전 갱신을 건너뜁니다"
429
+ echo "::notice::$(m wf_release.readme_dispatch_skip)"
424
430
  fi
425
431
 
426
432
  - name: Trigger deploy workflows
427
- # 봇 토큰(GITHUB_TOKEN)이 병합한 릴리스 PR의 push는 {{MAIN_BRANCH}}의 배포 워크플로우(on: push)를
428
- # 깨우지 못한다 — PAT 없는 기본 설치에서 서버·스토어 배포가 한 번도 돌지 않는다.
429
- # 그런 병합일 때만 배포 워크플로우를 workflow_dispatch로 직접 깨운다. PAT·사람이 병합했다면
430
- # push 이벤트로 이미 실행됐으므로 다시 깨우지 않는다 (중복 배포 방지).
431
- # 대상: 마법사가 설치한 워크플로우 중 push.branches에 {{MAIN_BRANCH}}가 있고 workflow_dispatch가
432
- # 있는 것. 모노레포 paths 필터는 push와 같은 범위(병합 커밋의 변경 파일)로 평가한다.
433
+ # A release PR merged with the bot token (GITHUB_TOKEN) never wakes the
434
+ # {{MAIN_BRANCH}} deploy workflows (on: push) - on a default install without a PAT,
435
+ # server/store deploys would never run. Only for such merges, wake the deploy
436
+ # workflows directly via workflow_dispatch. A PAT or human merge already fired
437
+ # the push event, so they are not woken again (no double deploy).
438
+ # Targets: installed workflows that list {{MAIN_BRANCH}} in push.branches and
439
+ # have workflow_dispatch. Monorepo paths filters are evaluated against the same
440
+ # scope as the push (files changed by the merge commit).
433
441
  if: >-
434
442
  steps.gate.outputs.proceed == 'true' &&
435
443
  steps.version.outputs.release_exists != 'true' &&
@@ -441,33 +449,35 @@ jobs:
441
449
  RELEASE_VERSION: ${{ steps.version.outputs.version }}
442
450
  MAIN_BRANCH: "{{MAIN_BRANCH}}"
443
451
  run: |
444
- # 이 버전의 릴리스 PR 병합 커밋 — 안전망 bump·수동 재발행처럼 병합이 아닌 릴리스는
445
- # 사람의 push가 이미 배포를 깨웠으므로 건너뛴다.
452
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
453
+ # Merge commit of this version's release PR - releases that are not merges
454
+ # (safety-net bump, manual republish) were already deployed by a human push,
455
+ # so they are skipped.
446
456
  MERGE_LINE=$(git log --first-parent --pretty='%H %s' -n 100 | grep -m1 -F " chore(release): v${RELEASE_VERSION} (PR #" || true)
447
457
  if [ -z "$MERGE_LINE" ]; then
448
- echo "v${RELEASE_VERSION}은 릴리스 PR 병합으로 들어온 릴리스가 아닙니다 — 배포는 push 이벤트가 맡습니다"
458
+ echo "$(m wf_release.not_merge_release version="$RELEASE_VERSION")"
449
459
  exit 0
450
460
  fi
451
461
  MERGE_SHA=${MERGE_LINE%% *}
452
462
  PR_NUMBER=$(printf '%s\n' "$MERGE_LINE" | sed -n 's/.*(PR #\([0-9][0-9]*\)).*/\1/p')
453
463
 
454
- # 병합 주체로 push 이벤트가 떴는지 판정한다. 조회에 실패하면 PAT 등록 여부로 대신 판단한다.
464
+ # Decide from the merger whether a push event fired. If the lookup fails, fall back to whether a PAT is registered.
455
465
  MERGED_BY=""
456
466
  if [ -n "$PR_NUMBER" ]; then
457
467
  MERGED_BY=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" --jq '.merged_by.login // ""' 2>/dev/null || true)
458
468
  fi
459
469
  case "$MERGED_BY" in
460
- # GITHUB_TOKEN 병합자는 정확히 이 로그인이다 — 접두사만 같은 다른 계정은 사람 병합으로 본다
461
- "github-actions[bot]") echo "PR #${PR_NUMBER}을 봇 토큰이 병합했습니다 — push 이벤트가 배포를 깨우지 못했습니다" ;;
470
+ # A GITHUB_TOKEN merger is exactly this login - another account sharing only the prefix counts as a human merge
471
+ "github-actions[bot]") echo "$(m wf_release.bot_merged pr="$PR_NUMBER")" ;;
462
472
  "")
463
473
  if [ "$HAS_WORKFLOW_PAT" = "true" ]; then
464
- echo "병합 주체를 확인하지 못했고 WORKFLOW_PAT이 있습니다 — push 이벤트가 배포를 실행했다고 보고 건너뜁니다"
474
+ echo "$(m wf_release.merger_unknown_pat)"
465
475
  exit 0
466
476
  fi
467
- echo "병합 주체를 확인하지 못했고 WORKFLOW_PAT이 없습니다 — 봇 토큰 병합으로 봅니다"
477
+ echo "$(m wf_release.merger_unknown_nopat)"
468
478
  ;;
469
479
  *)
470
- echo "PR #${PR_NUMBER}을 ${MERGED_BY}이(가) 병합했습니다 — push 이벤트가 배포를 이미 실행했으므로 건너뜁니다"
480
+ echo "$(m wf_release.human_merged pr="$PR_NUMBER" user="$MERGED_BY")"
471
481
  exit 0
472
482
  ;;
473
483
  esac
@@ -476,7 +486,7 @@ jobs:
476
486
  git diff --name-only "${MERGE_SHA}^1" "$MERGE_SHA" > "$CHANGED_FILES" || rm -f "$CHANGED_FILES"
477
487
  export CHANGED_FILES
478
488
 
479
- # 명령 치환 안의 heredoc은 따옴표 해석이 어긋나므로 파일로 먼저 쓴다
489
+ # A heredoc inside a command substitution misparses quotes, so write it to a file first
480
490
  FIND_DEPLOY="${RUNNER_TEMP:-/tmp}/find_deploy_workflows.py"
481
491
  cat > "$FIND_DEPLOY" <<'PY'
482
492
  import os, re
@@ -488,7 +498,7 @@ jobs:
488
498
  with open(os.environ["CHANGED_FILES"], encoding="utf-8") as f:
489
499
  CHANGED = [l.strip() for l in f if l.strip()]
490
500
 
491
- # YAML 라이브러리 없이 on 블록만 읽는다 — 러너 파이썬에 PyYAML이 있다고 가정하지 않는다.
501
+ # Read only the on block without a YAML library - do not assume the runner's Python has PyYAML.
492
502
  def strip_comment(s):
493
503
  q = None
494
504
  for i, c in enumerate(s):
@@ -504,7 +514,7 @@ jobs:
504
514
  v = v.strip()
505
515
  return v[1:-1] if len(v) >= 2 and v[0] == v[-1] and v[0] in "\"'" else v
506
516
 
507
- # GitHub 필터 문법: ** 는 / 포함 전부, * 와 ? 는 / 를 넘지 않는다. ! 는 뒤 패턴이 앞을 뒤집는다.
517
+ # GitHub filter syntax: ** matches everything including /, * and ? never cross /. A later ! pattern reverses an earlier match.
508
518
  def glob_re(p):
509
519
  out, i = "", 0
510
520
  while i < len(p):
@@ -576,7 +586,7 @@ jobs:
576
586
 
577
587
  root = ".github/workflows"
578
588
  for name in sorted(os.listdir(root)) if os.path.isdir(root) else []:
579
- # 공통 워크플로우(릴리스·README·안전망)는 배포가 아니고, 필요한 것은 각자 따로 깨운다
589
+ # Common workflows (release, README, safety net) are not deploys; each is woken separately where needed
580
590
  if name.startswith("PROJECT-COMMON-") or not re.search(r"\.ya?ml$", name):
581
591
  continue
582
592
  if is_target(os.path.join(root, name)):
@@ -585,18 +595,18 @@ jobs:
585
595
  TARGETS=$(python3 "$FIND_DEPLOY")
586
596
 
587
597
  if [ -z "$TARGETS" ]; then
588
- echo "{{MAIN_BRANCH}} push로 도는 배포 워크플로우가 없습니다 — 건너뜁니다"
598
+ echo "$(m wf_release.no_targets branch="$MAIN_BRANCH")"
589
599
  exit 0
590
600
  fi
591
601
 
592
602
  FAILED=0
593
603
  for WF in $TARGETS; do
594
- # 입력 없이 실행하면 workflow_dispatch 입력 기본값(설치 때 고른 배포 모드 등)을 쓴다
604
+ # Running without inputs uses the workflow_dispatch input defaults (e.g. the deploy mode chosen at install time)
595
605
  if gh workflow run "$WF" --ref {{MAIN_BRANCH}}; then
596
- echo "$WF workflow_dispatch 트리거 요청 완료"
606
+ echo "$(m wf_release.deploy_trigger_ok wf="$WF")"
597
607
  else
598
608
  FAILED=1
599
- echo "::warning::$WF 배포 워크플로우를 깨우지 못했습니다 — Actions 탭에서 Run workflow로 직접 실행하세요"
609
+ echo "::warning::$(m wf_release.deploy_trigger_warn wf="$WF")"
600
610
  fi
601
611
  done
602
612
  exit "$FAILED"
@@ -47,7 +47,7 @@ permissions:
47
47
  actions: write
48
48
 
49
49
  env:
50
- # 스크립트 import가 .github/scripts/__pycache__를 만들어 봇 커밋에 섞이지 않게 한다
50
+ # Keep script imports from creating .github/scripts/__pycache__ so it never ends up in bot commits
51
51
  PYTHONDONTWRITEBYTECODE: "1"
52
52
 
53
53
  jobs:
@@ -139,9 +139,11 @@ jobs:
139
139
  fi
140
140
 
141
141
  - name: Update CHANGELOG for the safety-net release
142
- # 안전망 릴리스도 실제로 들어간 커밋으로 CHANGELOG 항목을 만든다. 항목이 없으면
143
- # RELEASE-PUBLISH가 고정 문구로 릴리스 노트를 채우고 변경 내역이 어디에도 남지 않는다.
144
- # 요약 엔진: 사용자 API(설정된 경우) -> 규칙 기반 (Copilot은 릴리스 PR·trunk 경로에서만 쓴다)
142
+ # Build the CHANGELOG entry from the commits that actually landed, even for a
143
+ # safety-net release. Without an entry RELEASE-PUBLISH fills the notes with fixed
144
+ # text and the change history is recorded nowhere.
145
+ # Summary engine: user API (if configured) -> rule-based (Copilot is used only on
146
+ # the release-PR and trunk paths)
145
147
  if: steps.release_guard.outputs.skip != 'true'
146
148
  env:
147
149
  AI_API_KEY: ${{ secrets.AI_API_KEY }}
@@ -150,12 +152,13 @@ jobs:
150
152
  run: |
151
153
  NEW_VERSION="${{ steps.version.outputs.new_version }}"
152
154
 
153
- # 마지막 릴리스 태그 이후 {{MAIN_BRANCH}}에 직접 들어온 커밋 (태그가 없으면 전체 이력)
155
+ # Commits pushed straight to {{MAIN_BRANCH}} since the last release tag (full history if there is no tag)
154
156
  LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
155
157
  LOG_RANGE="${LAST_TAG:+${LAST_TAG}..}HEAD"
156
158
  git log --pretty=%s "$LOG_RANGE" > commits.txt
157
- # 본문 푸터 BREAKING CHANGE:는 제목 목록 뒤에 한 줄씩 덧붙인다 — 스크립트는 한 줄을
158
- # 한 항목으로 읽으므로 본문 전체(%B)를 넣으면 설명 줄까지 변경 항목으로 잡힌다.
159
+ # Body-footer BREAKING CHANGE: lines are appended after the subject list, one per
160
+ # line. The script reads each line as one entry, so passing the whole body (%B)
161
+ # would turn description lines into change entries.
159
162
  git log --pretty=%b "$LOG_RANGE" | grep -E '^BREAKING[ -]CHANGE[[:space:]]*:' >> commits.txt || true
160
163
  echo "commits since ${LAST_TAG:-the beginning}: $(wc -l < commits.txt)"
161
164
 
@@ -183,9 +186,9 @@ jobs:
183
186
  git config --local user.email "action@github.com"
184
187
  git config --local user.name "GitHub Action"
185
188
 
186
- # -A는 러너가 만든 임시 파일·캐시까지 사용자 레포에 커밋한다.
187
- # 버전 파일은 이미 추적 중이라 -u로 충분하고, 새로 생기는 CHANGELOG만
188
- # 따로 올린다 (.gitignore에 있으면 건너뛴다).
189
+ # -A would also commit runner temp files and caches into the user's repo.
190
+ # Version files are already tracked, so -u is enough; only a newly created
191
+ # CHANGELOG is added separately (skipped if it is in .gitignore).
189
192
  git add -u
190
193
  git ls-files -z --others --exclude-standard -- CHANGELOG.json CHANGELOG.md | xargs -0 -r git add --
191
194
 
@@ -233,21 +236,25 @@ jobs:
233
236
  fi
234
237
  fi
235
238
 
236
- - name: Trigger RELEASE-PUBLISH (safety-net bump 발행)
239
+ - name: Trigger RELEASE-PUBLISH (publish the safety-net bump)
237
240
  if: steps.release_guard.outputs.skip != 'true' && steps.commit_push.outputs.pushed == 'true'
238
241
  continue-on-error: true
239
242
  env:
240
243
  GH_TOKEN: ${{ github.token }}
241
244
  run: |
245
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
242
246
  gh workflow run PROJECT-COMMON-RELEASE-PUBLISH.yaml --ref {{MAIN_BRANCH}}
243
- echo "RELEASE-PUBLISH workflow_dispatch 트리거 요청 완료 (WORKFLOW_PAT 없이도 동작, 이슈 #61 드리프트 자동 복구)"
247
+ echo "$(m wf_version.release_publish_triggered)"
244
248
 
245
249
  - name: Back-merge the safety-net bump into {{DEVELOP_BRANCH}}
246
- # 안전망 bump는 {{MAIN_BRANCH}}에만 올라간다. {{DEVELOP_BRANCH}}가 이를 모르면 다음 릴리스 PR에서
247
- # 양쪽이 같은 버전 줄을 고쳐 충돌하므로 곧바로 {{DEVELOP_BRANCH}}에도 병합해 둔다.
248
- # 실패(충돌·브랜치 보호)해도 안전망 릴리스는 이미 끝났으므로 경고만 남긴다.
250
+ # The safety-net bump lands only on {{MAIN_BRANCH}}. If {{DEVELOP_BRANCH}} does not know
251
+ # about it, both sides edit the same version line and conflict in the next release
252
+ # PR, so merge it into {{DEVELOP_BRANCH}} right away.
253
+ # On failure (conflict, branch protection) the safety-net release is already done,
254
+ # so only a warning is left.
249
255
  if: steps.release_guard.outputs.skip != 'true' && steps.commit_push.outputs.pushed == 'true'
250
256
  run: |
257
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
251
258
  DEVELOP_BRANCH="{{DEVELOP_BRANCH}}"
252
259
  NEW_VERSION="${{ steps.version.outputs.new_version }}"
253
260
  MANUAL="git checkout $DEVELOP_BRANCH && git pull && git merge origin/{{MAIN_BRANCH}} && git push"
@@ -266,7 +273,7 @@ jobs:
266
273
  fi
267
274
  if ! git merge --no-edit -m "chore(version): merge v${NEW_VERSION} safety-net bump into ${DEVELOP_BRANCH} [skip ci]" "origin/{{MAIN_BRANCH}}"; then
268
275
  git merge --abort 2>/dev/null || true
269
- echo "::warning::{{MAIN_BRANCH}}을 ${DEVELOP_BRANCH}에 자동 병합하지 못했습니다 (충돌). 다음 릴리스 PR 전에 직접 병합하세요: $MANUAL"
276
+ echo "::warning::$(m wf_version.backmerge_conflict prod="{{MAIN_BRANCH}}" dev="$DEVELOP_BRANCH" manual="$MANUAL")"
270
277
  exit 0
271
278
  fi
272
279
  if git push origin "HEAD:$DEVELOP_BRANCH"; then
@@ -276,7 +283,7 @@ jobs:
276
283
  echo "push attempt $ATTEMPT failed — retrying"
277
284
  sleep 5
278
285
  done
279
- echo "::warning::${DEVELOP_BRANCH}에 push하지 못했습니다 (브랜치 보호 등). 다음 릴리스 PR 전에 직접 병합하세요: $MANUAL"
286
+ echo "::warning::$(m wf_version.backmerge_push_failed dev="$DEVELOP_BRANCH" manual="$MANUAL")"
280
287
 
281
288
  - name: Summary
282
289
  if: always()