diff --git a/.github/ISSUE_TEMPLATE/lightweight-task.yml b/.github/ISSUE_TEMPLATE/lightweight-task.yml index 0ccc766..0f1d6a2 100644 --- a/.github/ISSUE_TEMPLATE/lightweight-task.yml +++ b/.github/ISSUE_TEMPLATE/lightweight-task.yml @@ -1,13 +1,13 @@ name: Lightweight Task description: Create a small Docs, Chore, or Analysis task without implementation-heavy required fields. -title: "T- " +title: "Task- " body: - type: markdown attributes: value: | Use this form for a small documentation, maintenance, or analysis task. - GitHub assigns the numeric issue number automatically. Enter only the short title after `T- `. + GitHub assigns the numeric issue number automatically. Enter only the short title after `Task- `. Keep the issue focused enough that it can usually map to one PR. - type: textarea id: summary diff --git a/.github/ISSUE_TEMPLATE/task.yml b/.github/ISSUE_TEMPLATE/task.yml index 00f058a..b3aaf63 100644 --- a/.github/ISSUE_TEMPLATE/task.yml +++ b/.github/ISSUE_TEMPLATE/task.yml @@ -1,13 +1,13 @@ name: Implementation Task description: Create a scoped code or build task for Engine, Domain, Application, or Build work. -title: "T- " +title: "Task- " body: - type: markdown attributes: value: | Use this form for a single implementation or build task. - GitHub assigns the numeric issue number automatically. Enter only the short title after `T- `. + GitHub assigns the numeric issue number automatically. Enter only the short title after `Task- `. Prefer one task per PR, and keep the task scoped tightly enough that it can be reviewed and merged independently. - type: textarea id: summary diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 95ee47c..cb1639c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,6 +5,7 @@ ## Related Issue - Closes # +- None (docs/policy-only PR) ## Area diff --git a/.github/workflows/pr-policy.yml b/.github/workflows/pr-policy.yml index 3caee93..a4058fe 100644 --- a/.github/workflows/pr-policy.yml +++ b/.github/workflows/pr-policy.yml @@ -56,13 +56,44 @@ jobs: const relatedIssueSection = section("## Related Issue"); const areaSection = section("## Area"); const verificationSection = section("## Verification"); + const selectedAreas = [...areaSection.matchAll(/- \[[xX]\] (.+)/g)].map((match) => match[1].trim()); + const noIssuePattern = /\b(?:none|n\/a|no separate issue)\b/i; + + const changedFiles = await github.paginate(github.rest.pulls.listFiles, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number, + per_page: 100, + }); + + const docsPolicyOnlyPatterns = [ + /^docs\//, + /^uml\//, + /^\.github\/ISSUE_TEMPLATE\//, + /^\.github\/PULL_REQUEST_TEMPLATE\.md$/, + /^\.github\/workflows\/pr-policy\.yml$/, + /^CONTRIBUTING\.md$/, + /^README\.md$/, + ]; + + const isDocsPolicyOnlyPr = + changedFiles.length > 0 && + changedFiles.every((file) => + docsPolicyOnlyPatterns.some((pattern) => pattern.test(file.filename)) + ); + const isDocsAreaOnly = selectedAreas.length === 1 && selectedAreas[0] === "Docs"; const issuePattern = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|refs?)\s+#\d+\b/i; - if (!issuePattern.test(relatedIssueSection)) { - errors.push("`## Related Issue` must include an issue reference such as `Closes #12`."); + const hasIssueReference = issuePattern.test(relatedIssueSection); + const usesDocsOnlyException = noIssuePattern.test(relatedIssueSection); + + if (!hasIssueReference) { + if (!(usesDocsOnlyException && isDocsAreaOnly && isDocsPolicyOnlyPr)) { + errors.push("`## Related Issue` must include an issue reference such as `Closes #12`, unless this is a Docs-only docs/policy PR and the section says `None (docs/policy-only PR)`."); + } } - if (!/- \[[xX]\] /.test(areaSection)) { + if (selectedAreas.length === 0) { errors.push("Select at least one checkbox in `## Area`."); } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 401730c..ada8d56 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,12 @@ # SafeCrowd Contribution Rules 이 저장소는 `application -> domain -> engine` 계층을 유지하는 것을 가장 중요한 규칙으로 둡니다. -PR은 작은 단위로 나누고, 어떤 issue를 해결하는지 분명하게 남겨 주세요. +PR은 작은 단위로 나누고, 기본적으로 어떤 issue를 해결하는지 분명하게 남겨 주세요. ## 기본 원칙 - 기본 작업 흐름은 `issue -> branch -> PR -> merge`입니다. +- 다만 `docs/`, `uml/`, `CONTRIBUTING.md`, PR/issue template, PR 정책 워크플로처럼 문서 또는 기여 정책만 다루는 변경은 별도 issue 없이 `branch -> PR -> merge`로 진행할 수 있습니다. - 가능하면 하나의 PR은 하나의 issue만 해결합니다. - 서로 다른 계층을 동시에 크게 건드리는 PR은 피합니다. - 구조, 빌드, 의존성 규칙이 바뀌면 관련 문서를 함께 업데이트합니다. @@ -16,7 +17,7 @@ PR은 작은 단위로 나누고, 어떤 issue를 해결하는지 분명하게 - `Epic`은 여러 task를 묶는 상위 계획에 사용합니다. - `Implementation Task`는 `Engine`, `Domain`, `Application`, `Build` 같은 코드/빌드 작업에 사용합니다. - `Lightweight Task`는 `Docs`, `Chore`, `Analysis` 같은 작은 작업에 사용합니다. -- 제목은 저장소 관례에 맞춰 `EPIC- short title` 또는 `T- short title` 형식을 유지합니다. +- 제목은 저장소 관례에 맞춰 `EPIC- short title` 또는 `Task- short title` 형식을 유지합니다. - GitHub issue 번호(`#12` 같은 값)는 생성 후 자동으로 붙습니다. 제목에는 숫자를 직접 적지 않습니다. - 가능하면 task는 parent epic을 함께 적고, Sprint와 Area를 바로 정합니다. - 구조나 의존성에 영향을 주는 issue는 본문에 계층 영향과 검증 계획을 남깁니다. @@ -51,7 +52,7 @@ PR 제목은 아래 형식을 따릅니다. 모든 PR은 아래 내용을 포함해야 합니다. - 변경 요약 -- 연결된 issue +- 연결된 issue 또는 docs/policy-only PR 예외 사유 - 변경이 속한 영역 - 아키텍처 규칙 점검 결과 - 빌드/테스트 검증 결과 또는 미실행 사유 diff --git a/docs/GitHub Project.md b/docs/GitHub Project.md index ac34db4..fa5e967 100644 --- a/docs/GitHub Project.md +++ b/docs/GitHub Project.md @@ -8,6 +8,7 @@ - Epic과 Task를 모두 issue로 관리한다. - Epic-Task 관계는 GitHub native `Parent issue` / `sub-issue`를 사용한다. - 선행 작업 관계는 GitHub native issue dependency `blocked by`를 사용한다. + - Task 제목은 `Task- short title` 형식을 사용하고, 순서는 제목 번호가 아니라 GitHub issue 번호, Sprint, Parent issue, dependency로 관리한다. ## 현재 필드 - `Title` @@ -46,17 +47,18 @@ - `Sprint 1` - Epic: `#1 EPIC-1 Engine Foundation` - Epic: `#2 EPIC-2 Sprint 1 Demo Vertical Slice` - - Task: `#6 ~ #20` + - Task: `#6 ~ #20` (`Task- ...` 형식) - `Sprint 2` - Epic: `#3 EPIC-3 Product Completion for Sprint 2` - Epic: `#4 EPIC-4 Compare and Presentation Readiness` - - Task: `#21 ~ #30` + - Task: `#21 ~ #30` (`Task- ...` 형식) - `Sprint 3` - Epic: `#5 EPIC-5 Finish and Optional Extensions` - - Task: `#31 ~ #35` + - Task: `#31 ~ #35` (`Task- ...` 형식) ## 메모 - `Docs`, `Chore`, `Analysis`는 `Lightweight Task` form으로 가볍게 등록한다. - `Engine`, `Domain`, `Application`, `Build`는 `Implementation Task` form으로 범위와 검증 계획까지 남긴다. - 세부 작업명, 부모-자식 관계, dependency는 GitHub Project와 issue 자체를 기준으로 관리한다. -- 2026-03-20 기준 현재 `In Progress` 상태인 작업은 `#6 T-001`이다. +- 문서 또는 기여 정책만 다루는 PR은 별도 issue 없이 진행할 수 있다. +- Task의 순서는 제목 접두사 뒤 숫자로 관리하지 않는다. 중간 작업이 생기면 새 issue를 추가하고 `Sprint`, `Parent issue`, `blocked by`로 위치를 표현한다.