project-auto-wizard 0.5.1 → 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 +12 -17
- package/package.json +1 -1
- package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
- package/payload/scripts/changelog_manager.py +37 -0
- package/payload/scripts/issue_helper.py +110 -6
- package/payload/scripts/version_manager.py +2 -0
- package/payload/version.yml.template +2 -1
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +20 -0
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +35 -1
- package/payload/workflows/go/PROJECT-GO-CI.yaml +82 -0
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +2193 -0
- package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +384 -0
- package/src/cli/help.js +1 -1
- package/src/commands/doctor.js +8 -11
- package/src/context.js +1 -1
- package/src/core/detect.js +2 -1
- package/src/core/paths-resolve.js +2 -1
- package/src/ui/prompts.js +1 -1
- package/src/ui/summary.js +3 -2
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npx project-auto-wizard
|
|
|
17
17
|
[](package.json)
|
|
18
18
|
|
|
19
19
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
20
|
-
## 최신 버전 : v0.5.
|
|
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 마법사** | 마커 파일로 프로젝트 타입 자동 감지 — **
|
|
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`
|
|
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
|
-
|
|
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** (
|
|
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
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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.
|
|
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
|
-
|
|
325
|
-
|
|
421
|
+
try:
|
|
422
|
+
if args.command == "run":
|
|
326
423
|
return cmd_run()
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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')"
|