From 75f1ea833f9b2f54064541a63d009ccd63fd675e Mon Sep 17 00:00:00 2001 From: learncold Date: Fri, 20 Mar 2026 21:57:46 +0900 Subject: [PATCH] repo: add PR policy and templates --- .github/CODEOWNERS | 2 + .github/PULL_REQUEST_TEMPLATE.md | 34 ++++++++++++++ .github/workflows/pr-policy.yml | 77 ++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 73 ++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/pr-policy.yml create mode 100644 CONTRIBUTING.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..79c6f12 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Expand this file when more maintainers join the project. +* @learncold diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..dc812d3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,34 @@ +## Summary + +- + +## Related Issue + +- Closes # + +## Area + +- [ ] Engine +- [ ] Domain +- [ ] Application +- [ ] Docs +- [ ] Build +- [ ] Analysis +- [ ] Chore + +## Architecture Check + +- [ ] I kept the dependency direction `application -> domain -> engine`. +- [ ] I did not add Qt UI code to `src/domain`. +- [ ] I did not add `domain` or `application` dependencies to `src/engine`. +- [ ] I used `src/` as the include root. + +## Verification + +- [ ] `cmake --preset windows-debug` +- [ ] `cmake --build --preset build-debug` +- [ ] Not run (reason below) + +## Risks / Follow-up + +- diff --git a/.github/workflows/pr-policy.yml b/.github/workflows/pr-policy.yml new file mode 100644 index 0000000..3caee93 --- /dev/null +++ b/.github/workflows/pr-policy.yml @@ -0,0 +1,77 @@ +name: PR Policy + +on: + pull_request: + types: + - opened + - edited + - synchronize + - reopened + - ready_for_review + +permissions: + contents: read + pull-requests: read + +jobs: + validate-pr: + name: Validate PR + runs-on: ubuntu-latest + steps: + - name: Check title and body + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const title = pr.title || ""; + const body = pr.body || ""; + const errors = []; + + const titlePattern = /^\[(Engine|Domain|Application|Docs|Build|Analysis|Chore)\]\s.+$/; + if (!titlePattern.test(title)) { + errors.push("PR title must match `[Area] short summary`."); + } + + const requiredHeadings = [ + "## Summary", + "## Related Issue", + "## Area", + "## Architecture Check", + "## Verification", + ]; + + for (const heading of requiredHeadings) { + if (!body.includes(heading)) { + errors.push(`Missing section: ${heading}`); + } + } + + const section = (heading) => { + const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = new RegExp(`${escaped}\\s*([\\s\\S]*?)(?=\\n## |$)`, "i"); + const match = body.match(pattern); + return match ? match[1] : ""; + }; + + const relatedIssueSection = section("## Related Issue"); + const areaSection = section("## Area"); + const verificationSection = section("## Verification"); + + 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`."); + } + + if (!/- \[[xX]\] /.test(areaSection)) { + errors.push("Select at least one checkbox in `## Area`."); + } + + if (!/- \[[xX]\] /.test(verificationSection)) { + errors.push("Select at least one checkbox in `## Verification`."); + } + + if (errors.length > 0) { + core.setFailed(errors.join("\n")); + } else { + core.info("PR policy checks passed."); + } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0c9e7a0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,73 @@ +# SafeCrowd PR Rules + +이 저장소는 `application -> domain -> engine` 계층을 유지하는 것을 가장 중요한 규칙으로 둡니다. +PR은 작은 단위로 나누고, 어떤 issue를 해결하는지 분명하게 남겨 주세요. + +## 기본 원칙 + +- 기본 작업 흐름은 `issue -> branch -> PR -> merge`입니다. +- 가능하면 하나의 PR은 하나의 issue만 해결합니다. +- 서로 다른 계층을 동시에 크게 건드리는 PR은 피합니다. +- 구조, 빌드, 의존성 규칙이 바뀌면 관련 문서를 함께 업데이트합니다. + +## PR 제목 규칙 + +PR 제목은 아래 형식을 따릅니다. + +```text +[Area] short summary +``` + +허용되는 `Area` 값: + +- `Engine` +- `Domain` +- `Application` +- `Docs` +- `Build` +- `Analysis` +- `Chore` + +예시: + +```text +[Engine] implement generation-safe entity registry +[Docs] clarify build presets and architecture notes +``` + +## PR 본문 규칙 + +모든 PR은 아래 내용을 포함해야 합니다. + +- 변경 요약 +- 연결된 issue +- 변경이 속한 영역 +- 아키텍처 규칙 점검 결과 +- 빌드/검증 결과 또는 미실행 사유 +- 남은 리스크나 후속 작업 + +## 아키텍처 체크 + +PR 작성 시 아래 항목을 항상 점검합니다. + +- `engine`에 `domain` 또는 `application` 의존성을 추가하지 않았는가 +- `domain`에 Qt UI 코드를 추가하지 않았는가 +- `application`이 UI와 도메인 조립 책임을 벗어나지 않았는가 +- include 경로가 `src/` 루트 기준(`application/...`, `domain/...`, `engine/...`)을 따르는가 + +## 검증 규칙 + +가능하면 아래 명령으로 검증합니다. + +```powershell +cmake --preset windows-debug +cmake --build --preset build-debug +``` + +실행하지 못했다면 PR 본문에 이유를 남깁니다. + +## 머지 규칙 + +- `main`에는 직접 push하지 않고 PR로 반영합니다. +- 머지는 squash merge를 기본으로 사용합니다. +- PR 체크가 실패한 상태에서는 머지하지 않습니다.