project-auto-wizard 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -17,7 +17,7 @@ npx project-auto-wizard
17
17
  [![node](https://img.shields.io/badge/node-%3E%3D20.12-brightgreen)](package.json)
18
18
 
19
19
  <!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
20
- ## 최신 버전 : v0.4.0 (2026-08-23)
20
+ ## 최신 버전 : v0.5.1 (2026-08-23)
21
21
 
22
22
  [전체 버전 기록 보기](CHANGELOG.md)
23
23
 
@@ -37,17 +37,17 @@ npx project-auto-wizard --mode full --force --type spring,react # CI에서 비
37
37
 
38
38
  | 축 | 내용 |
39
39
  |---|---|
40
- | ① **npx 마법사** | 마커 파일로 프로젝트 타입 자동 감지 — **9타입 + 멀티타입 + 모노레포 경로**까지. 질문은 최소한만 |
40
+ | ① **npx 마법사** | 마커 파일로 프로젝트 타입 자동 감지 — **10타입 + 멀티타입 + 모노레포 경로**까지. 질문은 최소한만 |
41
41
  | ② **GitHub-native AI Release Automation** | 릴리스 PR을 열면: 버전 확정 → **AI가 릴리스 노트 작성** → CHANGELOG 갱신 → automerge → tag + GitHub Release. **API 키 0개** (GitHub Models) |
42
- | ③ **타입별 CI/CD 워크플로우** | Spring(무중단 배포 포함)·Flutter(스토어 배포)·React·Next·Python 등 타입에 맞는 GitHub Actions 자동 배치 |
42
+ | ③ **타입별 CI/CD 워크플로우** | Spring(무중단 배포 포함)·Flutter(스토어 배포)·React·Next·Python·Go 등 타입에 맞는 GitHub Actions 자동 배치 |
43
43
 
44
44
  ### 지원 프로젝트 타입
45
45
 
46
- `spring` `flutter` `react` `next` `node` `python` `react-native` `react-native-expo` `basic`
46
+ `spring` `flutter` `react` `next` `node` `python` `react-native` `react-native-expo` `basic` `go`
47
47
 
48
48
  - **멀티타입**: `--type spring,react,python` — 한 레포에 여러 타입 공존
49
49
  - **모노레포**: `--paths "flutter=app,react=client"` — 타입별 서브폴더 지정 (마커 파일 자동 감지)
50
- - `spring`/`flutter`/`react`/`next`/`python` 5개 타입은 아래 "타입별 워크플로우 구성"처럼 전용 CI/CD가 설치됩니다. `node`/`react-native`/`react-native-expo`/`basic`은 타입 전용 CI 없이 릴리스 자동화(버전 관리·체인지로그·AI 요약)를 담당하는 공통 워크플로우만 설치됩니다 — 빌드/배포 CI는 직접 추가해서 확장할 수 있습니다.
50
+ - `spring`/`flutter`/`react`/`next`/`python`/`go` 6개 타입은 아래 "타입별 워크플로우 구성"처럼 전용 CI/CD가 설치됩니다. `node`/`react-native`/`react-native-expo`/`basic`은 타입 전용 CI 없이 릴리스 자동화(버전 관리·체인지로그·AI 요약)를 담당하는 공통 워크플로우만 설치됩니다 — 빌드/배포 CI는 직접 추가해서 확장할 수 있습니다.
51
51
 
52
52
  ### 질문 문구 커스터마이징
53
53
 
@@ -73,6 +73,7 @@ flutter.APP_ARTIFACT_NAME:
73
73
  - PR 프리뷰는 배포 방식과 무관한 별개 축이라 선택과 관계없이 함께 설치됩니다.
74
74
  - **react/next**: CI와 CI+CD 분리 구성
75
75
  - **python**: CI / PR 프리뷰 / SimpleCICD
76
+ - **go**: CI(Dockerfile 불필요, go test/vet/build/lint) / PR 프리뷰 / SimpleCICD(Dockerfile 있는 프로젝트만 해당)
76
77
 
77
78
  ### 설치 기록 (`.github/.wizard/logs/`)
78
79
 
@@ -175,23 +176,17 @@ npx project-auto-wizard --mode doctor # 환경 진단 (읽기 전용, 규칙
175
176
  [✓] GitHub 로그인 — 레포 설정 조회 권한 인증됨
176
177
  [✓] merge commit 허용 — 릴리스 PR 자동 머지 조건 허용됨
177
178
 
178
- [!] WORKFLOW_PAT — 자동 태그·Release 발행
179
- ✗ secret이 등록되어 있지 않습니다.
180
- PR 자동 머지 뒤 태그·Release 워크플로가 이어지지 않습니다.
181
- (GitHub 정책상 기본 토큰으로 만든 커밋은 다음 워크플로를 깨우지 못합니다)
182
- → 개인 액세스 토큰 발급 (scopes: repo, workflow)
183
- → 레포 Settings → Secrets and variables → Actions
184
- → New repository secret · 이름은 WORKFLOW_PAT
185
- → 자세히: https://github.com/Twin-Fang/project-auto-wizard#post-install
186
-
187
179
  [i] Workflow permissions — 직접 추가한 워크플로우의 기본 권한
188
180
  현재 read 입니다 — 마법사가 설치한 워크플로우는 각자 권한을 선언하므로 그대로 동작합니다.
181
+ [i] WORKFLOW_PAT — 자동 태그·Release 발행
182
+ secret이 없어도 폴백이 자동으로 이어받아 태그·Release까지 진행됩니다 — 실제 병합 후 최대 ~20초 정도 더 걸릴 뿐입니다.
183
+ 속도를 더 원한다면 PAT을 등록할 수 있습니다 — 반드시 개인 계정이 아닌 조직 bot/machine 계정으로 발급하세요 (scopes: repo, workflow).
184
+ 등록: 레포 Settings → Secrets and variables → Actions → New repository secret · 이름은 WORKFLOW_PAT
189
185
  [i] GitHub Models — AI 릴리스 노트 생성
190
186
  조직 정책으로 차단됐는지는 자동으로 확인할 수 없습니다 (Settings → Models).
191
187
  차단돼 있어도 규칙 기반 요약으로 자동 전환되므로 그대로 두셔도 됩니다.
192
188
 
193
- ! 1개 항목에서 문제를 찾았습니다.
194
- 설치 자체는 지금 진행할 수 있고, 위 1개는 나중에 설정해도 됩니다.
189
+ 문제를 찾지 못했습니다.
195
190
  ```
196
191
 
197
192
  > **드리프트 판정 기준**: `--mode status`는 설치된 워크플로우 파일이 "설치 시점 기본값 템플릿"과 바이트 단위로 일치하는지만 비교합니다 — 파일을 직접 편집했는지는 추적하지 않습니다. 대화형 설치에서 `@wizard ask` 질문(예: 배포 포트)에 기본값이 아닌 값으로 응답했다면, 파일을 전혀 수정하지 않았더라도 설치 직후부터 항상 "사용자가 수정한 워크플로우 파일"로 표시됩니다. 정상 동작이며, 파일을 직접 편집했는지 구분하려면 해당 값이 예상한 응답과 일치하는지 직접 확인하세요.
@@ -223,7 +218,7 @@ npx project-auto-wizard --no-semver-auto # 항상 patch+1 (레거시 동작)
223
218
 
224
219
  | 항목 | 내용 |
225
220
  |---|---|
226
- | **`WORKFLOW_PAT` secret** (권장) | automerge 후속 워크플로우(tag/Release)가 이어지려면 PAT가 필요합니다 `GITHUB_TOKEN`으로 머지하면 GitHub 정책상 후속 워크플로우가 트리거되지 않습니다. Settings → Secrets → Actions에 `WORKFLOW_PAT` (scopes: `repo`, `workflow`) 등록. 없으면 `GITHUB_TOKEN`으로 동작하되 Release 발행은 수동 재실행이 필요할 있습니다 |
221
+ | **`WORKFLOW_PAT` secret** (선택 — 속도 최적화용) | 없어도 `GITHUB_TOKEN` 폴백이 automerge부터 Release 발행까지 자동으로 이어갑니다(실제 병합 최대 ~20초 추가). 빠르게 하고 싶다면 Settings → Secrets → Actions에 `WORKFLOW_PAT` (scopes: `repo`, `workflow`) 등록 반드시 개인 계정이 아닌 조직 bot/machine 계정으로 발급하세요 |
227
222
  | **Workflow permissions** | Settings → Actions → Workflow permissions: **Read and write** |
228
223
  | **GitHub Models** | 기본 활성 — 별도 설정 불필요. 조직 정책으로 차단된 경우 자동으로 규칙 fallback |
229
224
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-auto-wizard",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "One command DevOps: npx wizard that installs GitHub-native AI Release Automation into any project",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,6 +32,8 @@ import traceback
32
32
  import urllib.error
33
33
  import urllib.request
34
34
 
35
+ import issue_helper
36
+
35
37
 
36
38
  # ----------------------------- 공통 유틸 -----------------------------
37
39
 
@@ -405,6 +407,35 @@ def _ai_assisted_minor_upgrade(unclassified_lines: list[str]) -> bool:
405
407
  return False
406
408
 
407
409
 
410
+ def filter_release_issue_numbers(commit_shas: set[str], merged_prs: list[dict]) -> list[str]:
411
+ seen: set[str] = set()
412
+ result: list[str] = []
413
+ for pr in merged_prs:
414
+ merge_commit = pr.get('mergeCommit') or {}
415
+ oid = merge_commit.get('oid')
416
+ if not oid or oid not in commit_shas:
417
+ continue
418
+ head_ref = pr.get('headRefName') or ''
419
+ issue_num = issue_helper.extract_issue_number_from_branch(head_ref)
420
+ if not issue_num or issue_num in seen:
421
+ continue
422
+ seen.add(issue_num)
423
+ result.append(issue_num)
424
+ return result
425
+
426
+
427
+ def cmd_collect_issue_closes(commit_shas_file: str, merged_prs_file: str) -> int:
428
+ with open(commit_shas_file, encoding='utf-8') as f:
429
+ commit_shas = {line.strip() for line in f if line.strip()}
430
+
431
+ with open(merged_prs_file, encoding='utf-8') as f:
432
+ merged_prs = json.load(f)
433
+
434
+ issue_numbers = filter_release_issue_numbers(commit_shas, merged_prs)
435
+ print(','.join(issue_numbers))
436
+ return 0
437
+
438
+
408
439
  def cmd_classify_bump(commits_file: str) -> int:
409
440
  """커밋 목록 파일을 읽어 semver 승격 폭(major/minor/patch)을 stdout 마지막 줄에 출력.
410
441
 
@@ -811,6 +842,10 @@ def main(argv: list[str] | None = None) -> int:
811
842
  p_ai_summary.add_argument('--pr-title', help='PR 제목 (프롬프트 컨텍스트로 사용, 선택)')
812
843
  p_ai_summary.add_argument('--diff-stat-file', help='git diff --stat 출력 파일 (프롬프트 컨텍스트 확장, 선택)')
813
844
 
845
+ p_collect = sub.add_parser('collect-issue-closes', help='develop에 머지된 PR 중 이번 릴리스에 포함된 이슈 번호 목록 추출')
846
+ p_collect.add_argument('--commit-shas-file', required=True, help='이번 릴리스에 포함된 커밋 SHA 목록 파일 (한 줄당 1개)')
847
+ p_collect.add_argument('--merged-prs-file', required=True, help='gh pr list --json number,headRefName,mergeCommit 출력 JSON 파일')
848
+
814
849
  args = parser.parse_args(argv)
815
850
 
816
851
  if args.command == 'update-from-summary':
@@ -823,6 +858,8 @@ def main(argv: list[str] | None = None) -> int:
823
858
  return cmd_classify_bump(args.commits_file)
824
859
  if args.command == 'ai-summary':
825
860
  return cmd_ai_summary(args.commits_file, args.version, args.output, args.pr_title, args.diff_stat_file)
861
+ if args.command == 'collect-issue-closes':
862
+ return cmd_collect_issue_closes(args.commit_shas_file, args.merged_prs_file)
826
863
  return 2
827
864
 
828
865
 
@@ -14,6 +14,8 @@ without a LICENSE file) — ported to remove the external Action dependency
14
14
 
15
15
  Usage:
16
16
  issue_helper.py run
17
+ issue_helper.py extract-branch-issue <branch_name>
18
+ issue_helper.py link-pr-issues --pr <number> --issue-numbers <csv> [--replace]
17
19
 
18
20
  Reads GITHUB_EVENT_PATH (issues event JSON), GITHUB_TOKEN,
19
21
  GITHUB_REPOSITORY (all auto-provided by the Actions runner except
@@ -59,6 +61,14 @@ def extract_issue_number(issue_url):
59
61
  return parts[-1] if parts and parts[-1] else ""
60
62
 
61
63
 
64
+ _BRANCH_ISSUE_RE = re.compile(r"#(\d+)")
65
+
66
+
67
+ def extract_issue_number_from_branch(branch_name):
68
+ match = _BRANCH_ISSUE_RE.search(branch_name)
69
+ return match.group(1) if match else None
70
+
71
+
62
72
  _TAG_RE = re.compile(r"\[.*?]")
63
73
  _VARIATION_SELECTOR = "️"
64
74
  _ZWJ = "‍"
@@ -121,6 +131,42 @@ def normalize_all(title, issue_url, issue_number, date_yyyymmdd, branch_prefix,
121
131
  return branch_name, commit_message
122
132
 
123
133
 
134
+ # ===================================================================
135
+ # PR 본문 이슈 연결 (Closes #N) — 마커 블록 삽입/치환
136
+ # ===================================================================
137
+
138
+ LINK_MARKER_START = "<!-- auto-issue-link:start -->"
139
+ LINK_MARKER_END = "<!-- auto-issue-link:end -->"
140
+ _LINK_BLOCK_RE = re.compile(re.escape(LINK_MARKER_START) + r".*?" + re.escape(LINK_MARKER_END), re.S)
141
+
142
+
143
+ def build_issue_links_block(issue_numbers):
144
+ lines = "\n".join(f"Closes #{n}" for n in issue_numbers)
145
+ return f"{LINK_MARKER_START}\n{lines}\n{LINK_MARKER_END}"
146
+
147
+
148
+ def upsert_issue_links_in_body(body, issue_numbers, replace_existing):
149
+ if not issue_numbers:
150
+ return body, False
151
+
152
+ # 완전한 START...END 블록이 있을 때만 "마커 있음"으로 취급한다 — START만
153
+ # 있고 END가 없는 손상된 상태를 마커로 오인하면 _LINK_BLOCK_RE.sub()가
154
+ # 아무 것도 치환하지 못한 채 changed=True만 반환해, replace_existing=True
155
+ # 경로(release PR)에서 새 이슈 목록이 영구히 반영되지 않는 채로 조용히
156
+ # "성공"처럼 보이는 버그가 생긴다.
157
+ has_full_block = _LINK_BLOCK_RE.search(body) is not None
158
+ if has_full_block and not replace_existing:
159
+ return body, False
160
+
161
+ block = build_issue_links_block(issue_numbers)
162
+ if has_full_block:
163
+ new_body = _LINK_BLOCK_RE.sub(block, body)
164
+ else:
165
+ separator = "\n\n" if body.strip() else ""
166
+ new_body = f"{body}{separator}{block}"
167
+ return new_body, True
168
+
169
+
124
170
  # ===================================================================
125
171
  # GitHub REST API (urllib.request + GITHUB_TOKEN, 서드파티 의존 없음)
126
172
  # ===================================================================
@@ -222,6 +268,25 @@ def create_branch_if_needed(owner, repo, branch_name, base_branch, create_branch
222
268
  log(f"브랜치 생성됨: {branch_name}")
223
269
 
224
270
 
271
+ def link_pr_issues(owner, repo, pr_number, issue_numbers, token, replace_existing):
272
+ status, pr_data, _ = _api_request("GET", f"{API_BASE}/repos/{owner}/{repo}/pulls/{pr_number}", token)
273
+ if status >= 400:
274
+ raise RuntimeError(f"PR 조회 실패({status}): {pr_number}")
275
+ body = pr_data.get("body") or ""
276
+
277
+ new_body, changed = upsert_issue_links_in_body(body, issue_numbers, replace_existing)
278
+ if not changed:
279
+ log(f"이슈 연결 변경 없음 — 건너뜀 (PR #{pr_number})")
280
+ return
281
+
282
+ status, _, _ = _api_request(
283
+ "PATCH", f"{API_BASE}/repos/{owner}/{repo}/pulls/{pr_number}", token, {"body": new_body},
284
+ )
285
+ if status >= 400:
286
+ raise RuntimeError(f"PR 본문 갱신 실패({status}): {pr_number}")
287
+ log(f"이슈 연결 완료: {', '.join(f'#{n}' for n in issue_numbers)} (PR #{pr_number})")
288
+
289
+
225
290
  # ===================================================================
226
291
  # 이벤트 처리 / CLI
227
292
  # ===================================================================
@@ -312,21 +377,60 @@ def cmd_run():
312
377
  return 0
313
378
 
314
379
 
380
+ def cmd_link_pr_issues(pr_number, issue_numbers_csv, replace_existing):
381
+ repo_full = os.environ.get("GITHUB_REPOSITORY", "")
382
+ if "/" not in repo_full or repo_full.count("/") != 1 or not all(repo_full.split("/")):
383
+ log(f"ERROR: GITHUB_REPOSITORY 형식이 올바르지 않습니다: {repo_full!r}")
384
+ return 1
385
+ owner, repo = repo_full.split("/", 1)
386
+
387
+ token = os.environ.get("GITHUB_TOKEN", "").strip()
388
+ if not token:
389
+ log("ERROR: GITHUB_TOKEN이 없습니다.")
390
+ return 1
391
+
392
+ issue_numbers = [n.strip() for n in issue_numbers_csv.split(",") if n.strip()]
393
+ if not issue_numbers:
394
+ log("이슈 번호가 없음 — 건너뜀")
395
+ return 0
396
+
397
+ link_pr_issues(owner, repo, pr_number, issue_numbers, token, replace_existing)
398
+ return 0
399
+
400
+
315
401
  def build_parser():
316
402
  parser = argparse.ArgumentParser(prog="issue_helper.py")
317
- parser.add_argument("command", choices=["run"])
403
+ sub = parser.add_subparsers(dest="command", required=True)
404
+
405
+ sub.add_parser("run")
406
+
407
+ p_extract = sub.add_parser("extract-branch-issue")
408
+ p_extract.add_argument("branch_name")
409
+
410
+ p_link = sub.add_parser("link-pr-issues")
411
+ p_link.add_argument("--pr", required=True, type=int)
412
+ p_link.add_argument("--issue-numbers", required=True)
413
+ p_link.add_argument("--replace", action="store_true")
414
+
318
415
  return parser
319
416
 
320
417
 
321
418
  def main(argv=None):
322
419
  parser = build_parser()
323
420
  args = parser.parse_args(argv)
324
- if args.command == "run":
325
- try:
421
+ try:
422
+ if args.command == "run":
326
423
  return cmd_run()
327
- except Exception as e:
328
- log(f"실행 실패: {e}")
329
- return 1
424
+ if args.command == "extract-branch-issue":
425
+ result = extract_issue_number_from_branch(args.branch_name)
426
+ if result:
427
+ print(result)
428
+ return 0
429
+ if args.command == "link-pr-issues":
430
+ return cmd_link_pr_issues(args.pr, args.issue_numbers, args.replace)
431
+ except Exception as e:
432
+ log(f"실행 실패: {e}")
433
+ return 1
330
434
  return 2
331
435
 
332
436
 
@@ -425,6 +425,8 @@ def sync_for_type(project_type, new_version, version_code_getter):
425
425
  sync_json_version(Path(path_dir) / "app.json", new_version, ["expo", "version"])
426
426
  elif project_type == "basic":
427
427
  pass
428
+ elif project_type == "go":
429
+ pass
428
430
  else:
429
431
  log(f"WARNING: unknown project type: {project_type} — skipping")
430
432
 
@@ -8,7 +8,7 @@
8
8
  # (then edit major/minor manually, e.g., 1.0.5 -> 1.1.0 or 2.0.0).
9
9
  # ===
10
10
  # Supported project types: spring, flutter, next, react, react-native,
11
- # react-native-expo, node, python, basic
11
+ # react-native-expo, node, python, basic, go
12
12
  # Multi-type projects: list every type in the project_types array
13
13
  # -> the first entry is the primary type (project_types is the single source of truth)
14
14
  # Monorepo paths: project_paths maps each type to its subdir relative to the repo root
@@ -21,6 +21,7 @@
21
21
  # - react-native: Info.plist + build.gradle
22
22
  # - react-native-expo: app.json
23
23
  # - python: pyproject.toml
24
+ # - go: (none — git tag based, go.mod has no version field)
24
25
  # - basic: version.yml only
25
26
  # ===
26
27
 
@@ -89,3 +89,23 @@ jobs:
89
89
  -d @comment_payload.json \
90
90
  "https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" > /dev/null \
91
91
  || echo "comment post failed — continuing (this workflow never blocks the PR)"
92
+
93
+ - name: Link related issue via branch name
94
+ continue-on-error: true
95
+ env:
96
+ GITHUB_TOKEN: ${{ github.token }}
97
+ # 브랜치명을 run: 스크립트에 직접 ${{ }} 보간하지 않고 env로 전달한다 —
98
+ # git ref 이름은 $, 백틱, 세미콜론 등을 포함할 수 있어 직접 보간하면
99
+ # 셸 인젝션 경로가 된다(AUTO-CHANGELOG-CONTROL의 PR_TITLE과 동일한 이유).
100
+ HEAD_REF: ${{ github.event.pull_request.head.ref }}
101
+ run: |
102
+ ISSUE_NUM=$(python3 .github/scripts/issue_helper.py extract-branch-issue "$HEAD_REF")
103
+
104
+ if [ -z "$ISSUE_NUM" ]; then
105
+ echo "브랜치명에서 이슈 번호를 찾지 못함 — 건너뜀: $HEAD_REF"
106
+ exit 0
107
+ fi
108
+
109
+ python3 .github/scripts/issue_helper.py link-pr-issues \
110
+ --pr ${{ github.event.pull_request.number }} \
111
+ --issue-numbers "$ISSUE_NUM"
@@ -108,6 +108,40 @@ jobs:
108
108
  rm -f _full_diff_stat.txt
109
109
  echo "commits to summarize: $(wc -l < commits.txt)"
110
110
 
111
+ - name: Collect issues merged into {{DEVELOP_BRANCH}}
112
+ continue-on-error: true
113
+ env:
114
+ GH_TOKEN: ${{ github.token }}
115
+ GITHUB_TOKEN: ${{ github.token }}
116
+ run: |
117
+ git log --pretty=%H "origin/{{MAIN_BRANCH}}..HEAD" > commit_shas.txt
118
+
119
+ gh pr list --state merged --base {{DEVELOP_BRANCH}} \
120
+ --json number,headRefName,mergeCommit --limit 200 > merged_prs.json
121
+
122
+ MERGED_PR_COUNT=$(python3 -c "import json; print(len(json.load(open('merged_prs.json'))))")
123
+ if [ "$MERGED_PR_COUNT" -ge 200 ]; then
124
+ echo "::warning::gh pr list가 --limit 200에 도달했습니다 — 오래된 병합 PR이 누락됐을 수 있습니다 (조회된 PR: $MERGED_PR_COUNT)"
125
+ fi
126
+
127
+ ISSUE_NUMBERS=$(python3 .github/scripts/changelog_manager.py collect-issue-closes \
128
+ --commit-shas-file commit_shas.txt \
129
+ --merged-prs-file merged_prs.json)
130
+
131
+ rm -f commit_shas.txt merged_prs.json
132
+
133
+ if [ -z "$ISSUE_NUMBERS" ]; then
134
+ echo "이번 릴리스에 연결할 이슈 없음 — 건너뜀"
135
+ exit 0
136
+ fi
137
+
138
+ python3 .github/scripts/issue_helper.py link-pr-issues \
139
+ --pr ${{ github.event.pull_request.number }} \
140
+ --issue-numbers "$ISSUE_NUMBERS" \
141
+ --replace
142
+
143
+ echo "이슈 연결: $ISSUE_NUMBERS"
144
+
111
145
  - name: Confirm release version (bump + sync)
112
146
  id: bump
113
147
  env:
@@ -206,7 +240,7 @@ jobs:
206
240
  VERSION="${{ steps.bump.outputs.new_version }}"
207
241
 
208
242
  # Working files must not land in the repo
209
- rm -f pr_body.md summary.md commits.txt diff_stat.txt comment_body.md comment_payload.json
243
+ rm -f pr_body.md summary.md commits.txt diff_stat.txt comment_body.md comment_payload.json commit_shas.txt merged_prs.json
210
244
 
211
245
  git add -A
212
246
 
@@ -0,0 +1,82 @@
1
+ # project-auto-wizard:managed-workflow
2
+ # ===================================================================
3
+ # Go 빌드 검증 워크플로우 (CI Only)
4
+ # ===================================================================
5
+ #
6
+ # 설명:
7
+ # - {{DEVELOP_BRANCH}} 브랜치에 push/PR 시 go build/vet/test/lint로 검증
8
+ # - Dockerfile이 필요 없어 CLI 도구·라이브러리 등 배포하지 않는 Go 프로젝트도 사용 가능
9
+ # - go.mod의 go 지시자 버전을 자동으로 사용 (버전 하드코딩 없음)
10
+ #
11
+ # ===================================================================
12
+
13
+ name: PROJECT-GO-CI
14
+
15
+ # ===================================================================
16
+ # 트리거 설정
17
+ # ===================================================================
18
+ on:
19
+ push:
20
+ branches:
21
+ - "{{DEVELOP_BRANCH}}" # {{DEVELOP_BRANCH}} 브랜치 push 시 빌드 검증
22
+ pull_request:
23
+ branches:
24
+ - "{{DEVELOP_BRANCH}}" # {{DEVELOP_BRANCH}} 브랜치로의 PR 시 빌드 검증
25
+ workflow_dispatch: # 수동 실행 허용
26
+
27
+ # ===================================================================
28
+ # 환경 변수 설정
29
+ # ===================================================================
30
+ env:
31
+ # 프로젝트 고유 식별자
32
+ PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
33
+
34
+ jobs:
35
+ # ===================================================================
36
+ # 빌드 검증 작업
37
+ # ===================================================================
38
+ build-check:
39
+ name: Go 빌드 검증
40
+ runs-on: ubuntu-latest
41
+
42
+ steps:
43
+ # 1. 소스코드 체크아웃
44
+ - name: 코드 체크아웃
45
+ uses: actions/checkout@v7
46
+
47
+ # 2. Go 툴체인 설정 (go.mod의 go 지시자 버전 자동 사용)
48
+ - name: Go 설정
49
+ uses: actions/setup-go@v7
50
+ with:
51
+ go-version-file: go.mod
52
+ cache: true
53
+
54
+ # 3. 빌드 검증
55
+ - name: go build 검증
56
+ run: go build ./...
57
+
58
+ # 4. 정적 분석
59
+ - name: go vet 검증
60
+ run: go vet ./...
61
+
62
+ # 5. 테스트 실행
63
+ - name: go test 실행
64
+ run: go test ./...
65
+
66
+ # 6. 린트 (v8은 golangci-lint v2 계열을 요구 — .golangci.yml이 v1 형식이면
67
+ # 프로젝트에서 별도 조정 필요. version: latest는 v2 최신 패치를 자동으로 받는다)
68
+ - name: golangci-lint
69
+ uses: golangci/golangci-lint-action@v8
70
+ with:
71
+ version: latest
72
+
73
+ # 7. 빌드 검증 완료 메시지
74
+ - name: 빌드 검증 완료
75
+ run: |
76
+ echo "✅ Go 빌드 검증이 성공적으로 완료되었습니다!"
77
+ echo ""
78
+ echo "📋 빌드 정보:"
79
+ echo " 🎯 프로젝트: ${{ env.PROJECT_NAME }}"
80
+ echo " 🌿 브랜치: ${{ github.ref_name }}"
81
+ echo " 📝 커밋: ${{ github.sha }}"
82
+ echo " ⏰ 검증 시간: $(date '+%Y-%m-%d %H:%M:%S')"