feat: add update script and operations docs/templates

This commit is contained in:
2026-04-14 22:02:39 +09:00
parent 974c3d0dbd
commit edf7e61a44
8 changed files with 359 additions and 55 deletions
+10 -2
View File
@@ -38,13 +38,21 @@ bash .github/tools/calc_priority_scores.sh .github/templates/planning-backlog-sc
- .github/runbooks/services/web.rollback-runbook.md - .github/runbooks/services/web.rollback-runbook.md
3. 배포 전 각 서비스 인스턴스의 Trigger/Procedure/Validation 최신화 3. 배포 전 각 서비스 인스턴스의 Trigger/Procedure/Validation 최신화
## 4. Verification Exception Quick Start ## 4. Operations Quick Start
1. 기본 템플릿: .github/templates/operations-note.template.md
2. 생성 프롬프트: /operation-draft rel-001
3. 검토 프롬프트: /operation-review rel-001
4. 승인 프롬프트: /operation-confirm rel-001
5. 산출 경로: docs/sdlc/operations/ops-<3자리문서번호>_YYYY-MM-DD_<topic-slug>.operations.md
## 5. Verification Exception Quick Start
1. Major 결함 예외는 승인자 정책을 따름 1. Major 결함 예외는 승인자 정책을 따름
2. Valid Until 설정은 만료 전 재검증 필수 2. Valid Until 설정은 만료 전 재검증 필수
3. 만료/해소 상태를 검증 리포트에 반영 3. 만료/해소 상태를 검증 리포트에 반영
## 5. Minimum Go-live Checklist ## 6. Minimum Go-live Checklist
1. 우선순위 점수표 완료 1. 우선순위 점수표 완료
2. 테스트 실행 근거 첨부 2. 테스트 실행 근거 첨부
+2 -2
View File
@@ -33,8 +33,8 @@ tools: [read, edit, search, todo]
7. 승인 가능 시 아래 순서로 문서를 수정한다: 7. 승인 가능 시 아래 순서로 문서를 수정한다:
a. `# 0. 문서 상태`의 상태를 `confirmed`로 변경한다. a. `# 0. 문서 상태`의 상태를 `confirmed`로 변경한다.
b. `# 0. 문서 상태`의 승인 시각을 현재 날짜(YYYY-MM-DD)로 기재한다. `{{CONFIRM:` 형식이 남아 있으면 현재 날짜로 대체한다. b. `# 0. 문서 상태`의 승인 시각을 현재 날짜(YYYY-MM-DD)로 기재한다. `{{CONFIRM:` 형식이 남아 있으면 현재 날짜로 대체한다.
c. `# 7. 파일 처리 결과` 항목에 Operations 완료 시각과 마감 상태를 추가한다. c. `# 6. 파일 처리 결과` 항목에 Operations 완료 시각과 마감 상태를 추가한다.
d. `# 8. 사용자 결정 필요 항목 요약`의 내용을 `현재 미해결 플레이스홀더 없음`으로 대체한다. 기존 `##` 하위 항목은 모두 제거한다. d. `# 7. 사용자 결정 필요 항목 요약`의 내용을 `현재 미해결 플레이스홀더 없음`으로 대체한다. 기존 `##` 하위 항목은 모두 제거한다.
8. 환류 항목 처리 규칙: 8. 환류 항목 처리 규칙:
- `# 5. 다음 반복 환류 항목`에 명시된 각 개선 항목에 대해 다음 Discovery/Planning 백로그 연결을 확인한다. - `# 5. 다음 반복 환류 항목`에 명시된 각 개선 항목에 대해 다음 Discovery/Planning 백로그 연결을 확인한다.
- 자동으로 연결하지 않으며, 사용자가 수동 등록한 것만 인계 완료로 표시한다. - 자동으로 연결하지 않으며, 사용자가 수동 등록한 것만 인계 완료로 표시한다.
+4 -3
View File
@@ -24,6 +24,7 @@ tools: [read, edit, search, agent, todo]
- 파일명: ops-<3자리문서번호>_YYYY-MM-DD_<topic-slug>.operations.md - 파일명: ops-<3자리문서번호>_YYYY-MM-DD_<topic-slug>.operations.md
- 문서번호: 기존 ops 번호 최대값 + 1, 없으면 001부터 시작 - 문서번호: 기존 ops 번호 최대값 + 1, 없으면 001부터 시작
- 날짜: 생성일 기준, 이후 수정 시 파일명 날짜 유지 - 날짜: 생성일 기준, 이후 수정 시 파일명 날짜 유지
- 본문 템플릿: .github/templates/operations-note.template.md
- append 금지, 생성/갱신 시 overwrite - append 금지, 생성/갱신 시 overwrite
6. Operations 작성 원칙: 6. Operations 작성 원칙:
- Release의 배포 체크리스트, 롤백 계획, 관찰 계획을 근거로 운영 추적 문맥을 작성한다. - Release의 배포 체크리스트, 롤백 계획, 관찰 계획을 근거로 운영 추적 문맥을 작성한다.
@@ -42,7 +43,7 @@ tools: [read, edit, search, agent, todo]
- 상태: 초안 생성 시 `draft` - 상태: 초안 생성 시 `draft`
- 승인자: Release 문서에서 인계받음 - 승인자: Release 문서에서 인계받음
- 승인 시각: `{{CONFIRM: 배포 완료 후 Operations 확인}}` - 승인 시각: `{{CONFIRM: 배포 완료 후 Operations 확인}}`
9. # 6 사용자 결정 필요 항목 요약 규칙: 9. # 7 사용자 결정 필요 항목 요약 규칙:
- 본문의 {{DECIDE}}/{{CONFIRM}}/{{DATA}}를 유형별로 집계한다. - 본문의 {{DECIDE}}/{{CONFIRM}}/{{DATA}}를 유형별로 집계한다.
- 미해결 플레이스홀더가 없으면 `현재 미해결 플레이스홀더 없음`으로 작성한다. - 미해결 플레이스홀더가 없으면 `현재 미해결 플레이스홀더 없음`으로 작성한다.
@@ -57,7 +58,7 @@ tools: [read, edit, search, agent, todo]
8. Postmortem 섹션 생성 (8개 항목 모두 {{DATA:}} 또는 등급별 예시 포함) 8. Postmortem 섹션 생성 (8개 항목 모두 {{DATA:}} 또는 등급별 예시 포함)
9. 환류 항목 섹션 생성 (발견된 개선 사항 추출 또는 {{DATA:}}) 9. 환류 항목 섹션 생성 (발견된 개선 사항 추출 또는 {{DATA:}})
10. Operations 문서 저장 10. Operations 문서 저장
11. 본문 플레이스홀더 집계 후 # 6 작성 11. 본문 플레이스홀더 집계 후 # 7 작성
12. 최종 응답에 처리 요약 제공 12. 최종 응답에 처리 요약 제공
Operations 문서 출력 섹션 순서(고정): Operations 문서 출력 섹션 순서(고정):
@@ -75,7 +76,7 @@ Operations 문서 출력 섹션 순서(고정):
2. 입력 Release 문서 경로 2. 입력 Release 문서 경로
3. Operations 문서 생성/갱신 결과 3. Operations 문서 생성/갱신 결과
4. 배포 실행 기록 초안 요약 4. 배포 실행 기록 초안 요약
5. 모니터링 관찰 항목 복사 상여 5. 모니터링 관찰 항목 복사 상세
6. Postmortem 템플릿 완성도 6. Postmortem 템플릿 완성도
7. 업데이트된 파일 경로 목록 7. 업데이트된 파일 경로 목록
8. 다음 액션 (배포 실행자가 DATA 항목 채우기) 8. 다음 액션 (배포 실행자가 DATA 항목 채우기)
+1 -1
View File
@@ -30,7 +30,7 @@ tools: [read, edit, search, todo]
- 본문에 `{{DATA:` 플레이스홀더가 남아 있으면 미완성으로 표시한다. - 본문에 `{{DATA:` 플레이스홀더가 남아 있으면 미완성으로 표시한다.
6. 정규화 규칙: 6. 정규화 규칙:
- {{DATA:}} 플레이스홀더는 유지하면서, 사용자가 실제 데이터를 작성한 항목은 플레이스홀더 제거 - {{DATA:}} 플레이스홀더는 유지하면서, 사용자가 실제 데이터를 작성한 항목은 플레이스홀더 제거
- 모든 섹션이 완성되면 # 7 플레이스홀더를 정리한다 - 모든 섹션이 완성되면 # 7 사용자 결정 필요 항목 요약 정리를 안내한다
- 배포 실행 결과나 Postmortem이 일부 만료되지 않았다면, 진행 중(미완료) 상태 명시 후 진행 - 배포 실행 결과나 Postmortem이 일부 만료되지 않았다면, 진행 중(미완료) 상태 명시 후 진행
7. 응답 규칙: 7. 응답 규칙:
- 검토 결과를 미충족 항목 목록으로 보고한다 (별도 수정 없음) - 검토 결과를 미충족 항목 목록으로 보고한다 (별도 수정 없음)
@@ -0,0 +1,80 @@
# 0. 문서 상태
- 상태: draft
- 승인자: {{DECIDE: 이 Operations의 최종 승인자는 누구인가?}}
- 승인 시각: {{CONFIRM: 배포 완료 후 Operations 확인}}
# 1. 배포 실행 기록
- 입력 Release: {{DATA: docs/sdlc/release/rel-XXX_YYYY-MM-DD_topic.release.md}}
- 배포 방식: {{DATA: 점진 배포/일괄 배포}}
- 배포 환경: {{DATA: production/staging 등}}
- 배포 창(UTC): {{DATA: 시작/종료 시각}}
## 사전 검증
- 체크리스트 점검 결과: {{DATA: 완료/미완료 항목}}
- 롤백 준비 상태: {{DATA: 준비됨/미준비 + 근거}}
## 실행 결과
- 실행 명령/절차 요약: {{DATA: 실행 로그 요약}}
- 결과: {{DATA: 성공/부분성공/실패}}
- 영향 범위: {{DATA: 사용자/시스템 영향}}
## 배포 직후 검증 결과
- 핵심 시나리오 검증: {{DATA: 통과/실패 + 근거}}
- 예외/알림 발생 여부: {{DATA: 있음/없음 + 내용}}
# 2. 모니터링 관찰 결과
| 모니터링 항목 | 측정 지표 | 정상 범위 | 이상 감지 기준 | 관찰 결과 | 비고 |
|---|---|---|---|---|---|
| {{DATA: 항목명}} | {{DATA: 지표}} | {{DATA: 정상 범위}} | {{DATA: 임계치}} | {{DATA: 결과}} | |
## 관찰 기간
- 집중 관찰 기간: {{DATA: 시작~종료}}
- 일반 관찰 기간: {{DATA: 시작~종료}}
# 3. 인시던트 대응 기록
- 인시던트 발생 여부: {{DATA: 발생/해당없음}}
## 발생한 경우 기록
- 영향도 분류: {{DATA: 사용자/기능/데이터 영향}}
- 타임라인:
- 탐지 시각(UTC): {{DATA: 시각}}
- 대응 시작(UTC): {{DATA: 시각}}
- 완화 시각(UTC): {{DATA: 시각}}
- 복구 완료(UTC): {{DATA: 시각}}
- 커뮤니케이션:
- 채널: {{DATA: 채널}}
- 공지 시각(UTC): {{DATA: 시각}}
- 업데이트 주기: {{DATA: 주기}}
- 우회책 실행 여부: {{DATA: 실행/미실행 + 내용}}
- 복구 절차 요약: {{DATA: 복구 단계}}
# 4. Postmortem
- What Happened: {{DATA: 사건 개요와 사용자 관찰 증상}}
- Impact: {{DATA: 서비스/사용자/비즈니스 영향}}
- Root Cause: {{DATA: 기술적 원인 + 시스템적 원인}}
- Detection: {{DATA: 탐지 경로/지연 요인/알림 품질}}
- Response: {{DATA: 대응 의사결정 및 실행 평가}}
- Recovery: {{DATA: 복구 절차와 검증 결과}}
- Preventive Actions: {{DATA: 재발 방지 액션(담당자/기한 포함)}}
- Follow-up Tracking: {{DATA: Discovery/Planning 백로그 연결 상태}}
# 5. 다음 반복 환류 항목
| 환류 ID | 개선 항목 | 연결 대상(Discovery/Planning) | 담당자 | 목표일 | 상태 |
|---|---|---|---|---|---|
| FB-001 | {{DATA: 개선 항목}} | {{DATA: dcy/pln 문서 또는 티켓}} | {{DATA: 담당자}} | {{DATA: YYYY-MM-DD}} | {{DATA: 등록됨/대기}} |
# 6. 파일 처리 결과
- 생성/갱신 경로: {{DATA: docs/sdlc/operations/ops-XXX_YYYY-MM-DD_topic.operations.md}}
- 입력 Release 경로: {{DATA: docs/sdlc/release/rel-XXX_YYYY-MM-DD_topic.release.md}}
- 처리 결과: {{DATA: 생성/갱신/검토}}
- 비고: {{DATA: 특이사항}}
# 7. 사용자 결정 필요 항목 요약
## {{DECIDE}}
- (없으면 이 섹션 제거)
## {{CONFIRM}}
- (없으면 이 섹션 제거)
## {{DATA}}
- (없으면 이 섹션 제거)
+97
View File
@@ -0,0 +1,97 @@
# 사용방법
## 최초 등록
```bash
# 리모트 등록
git remote add stage-pilot https://github.com/genichin/stage-pilot.git
# subtree 추가
git fetch stage-pilot main
git subtree add --prefix=.vendor/stage-pilot stage-pilot main --squash
# 설치
bash .vendor/stage-pilot/bootstrap/install.sh .
```
## 업데이트
```bash
# 원격이 등록된 경우(stage-pilot remote 우선 사용)
bash .vendor/stage-pilot/bootstrap/update.sh .
# 원격 없이 URL로 직접 업데이트
bash .vendor/stage-pilot/bootstrap/update.sh --repo-url https://github.com/genichin/stage-pilot.git .
# 설치 재적용 없이 subtree만 갱신
bash .vendor/stage-pilot/bootstrap/update.sh --skip-install .
```
## SDLC 프롬프트 사용법
`.github/prompts/`에 단계별 프롬프트가 정의되어 있으며, Copilot Chat에서 `/프롬프트이름 인자` 형태로 실행한다.
### 기본 실행 순서
1. Discovery
2. Planning
3. Design
4. Implementation
5. Verification
6. Release
7. Operations
### 단계별 명령 예시
```text
# Discovery
/discovery-draft 결제 실패 알림 정책을 도입한다
/discovery-review dcy-001
/discovery-confirm dcy-001
# Planning
/planning-draft dcy-001
/planning-review dcy-001
/planning-confirm pln-001
# Design
/design-draft pln-001
/design-review dsn-001
/design-confirm dsn-001
# Implementation
/implementation-draft dsn-001
/implementation imp-001 I-001, I-002
/implementation-confirm imp-001
# Verification
/verification-draft imp-001
/verification-review ver-001
/verification-confirm ver-001
# Release
/release-draft ver-001
/release-review ver-001
/release-confirm ver-001
# Operations
/operation-draft rel-001
/operation-review rel-001
/operation-confirm rel-001
```
### 문서 ID 규칙
1. Discovery: `dcy-001`
2. Planning: `pln-001`
3. Design: `dsn-001`
4. Implementation: `imp-001`
5. Verification: `ver-001`
6. Release: `rel-001`
### 참고 파일
1. 시작 가이드: `.github/START-HERE.md`
2. 단계 규칙: `.github/instructions/`
3. 프롬프트 정의: `.github/prompts/`
4. 템플릿: `.github/templates/`
5. Operations 템플릿: `.github/templates/operations-note.template.md`
+21 -47
View File
@@ -11,10 +11,8 @@ fail() {
exit 1 exit 1
} }
require_linux() { current_os() {
if [ "$(uname -s)" != "Linux" ]; then uname -s
fail "This bootstrap script supports Linux only."
fi
} }
HOST_ROOT="${1:-$(pwd)}" HOST_ROOT="${1:-$(pwd)}"
@@ -51,55 +49,31 @@ report_github_structure() {
fi fi
} }
has_python_311_or_higher() {
command -v python3 >/dev/null 2>&1 \
&& python3 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 1)'
}
install_python_311() {
if has_python_311_or_higher; then
return 0
fi
if command -v apt-get >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1; then
log "Installing Python 3.11 via apt-get (minimum required: 3.11)."
sudo apt-get update
sudo apt-get install -y python3.11
else
fail "Python 3.11+ is required. Install it manually or provide apt-get + sudo."
fi
has_python_311_or_higher || fail "Python 3.11+ installation check failed."
}
has_node_20() {
command -v node >/dev/null 2>&1 \
&& node -e 'const [major] = process.versions.node.split(".").map(Number); process.exit(major >= 20 ? 0 : 1);'
}
install_node_20() {
if has_node_20; then
return 0
fi
if command -v apt-get >/dev/null 2>&1 && command -v sudo >/dev/null 2>&1 && command -v curl >/dev/null 2>&1; then
log "Installing Node.js 20 LTS via NodeSource (minimum required: v20)."
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
else
fail "Node.js v20+ is required. Install it manually or provide apt-get + sudo + curl."
fi
has_node_20 || fail "Node.js v20+ installation check failed."
}
main() { main() {
require_linux os_name="$(current_os)"
[ -d "${HOST_ROOT}" ] || fail "Host root not found: ${HOST_ROOT}" [ -d "${HOST_ROOT}" ] || fail "Host root not found: ${HOST_ROOT}"
log "Host root: ${HOST_ROOT}" log "Host root: ${HOST_ROOT}"
ensure_host_structure ensure_host_structure
install_python_311
install_node_20 # Intentionally do not install dependencies in bootstrap.
# This script only prepares the host directory layout.
log "${os_name} detected. Skipping Python/Node installation by design."
report_github_structure report_github_structure
log "python3: $(python3 --version 2>&1)"
log "node: $(node --version 2>&1)" if command -v python3 >/dev/null 2>&1; then
log "python3: $(python3 --version 2>&1)"
else
log "python3: not found"
fi
if command -v node >/dev/null 2>&1; then
log "node: $(node --version 2>&1)"
else
log "node: not found"
fi
log "Bootstrap completed successfully." log "Bootstrap completed successfully."
} }
+144
View File
@@ -0,0 +1,144 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: update.sh [options] [HOST_ROOT]
Options:
--dry-run Show planned commands without executing them.
--remote <name> Git remote name to use when available (default: stage-pilot)
--repo-url <url> Fallback repository URL when remote is missing.
(default: https://github.com/genichin/stage-pilot.git)
--branch <name> Upstream branch to sync (default: main)
--prefix <path> Subtree prefix path (default: .vendor/stage-pilot)
--policy <name> install.sh conflict policy: replace|preserve|fail
(default: preserve)
--skip-install Only update subtree. Do not run install.sh.
-h, --help Show this help message.
Arguments:
HOST_ROOT Host repository root (default: current directory)
EOF
}
log() {
printf '[update] %s\n' "$*"
}
fail() {
printf '[update] ERROR: %s\n' "$*" >&2
exit 1
}
run_cmd() {
if [ "${DRY_RUN}" -eq 1 ]; then
log "[dry-run] $*"
else
"$@"
fi
}
DRY_RUN=0
REMOTE_NAME="stage-pilot"
REPO_URL="https://github.com/genichin/stage-pilot.git"
BRANCH="main"
PREFIX=".vendor/stage-pilot"
POLICY="preserve"
SKIP_INSTALL=0
ARGS=()
while [ "$#" -gt 0 ]; do
case "$1" in
--dry-run)
DRY_RUN=1
;;
--remote)
[ "$#" -ge 2 ] || fail "--remote requires a value"
REMOTE_NAME="$2"
shift
;;
--repo-url)
[ "$#" -ge 2 ] || fail "--repo-url requires a value"
REPO_URL="$2"
shift
;;
--branch)
[ "$#" -ge 2 ] || fail "--branch requires a value"
BRANCH="$2"
shift
;;
--prefix)
[ "$#" -ge 2 ] || fail "--prefix requires a value"
PREFIX="$2"
shift
;;
--policy)
[ "$#" -ge 2 ] || fail "--policy requires a value"
POLICY="$2"
shift
;;
--skip-install)
SKIP_INSTALL=1
;;
-h|--help)
usage
exit 0
;;
--*)
fail "Unknown option: $1"
;;
*)
ARGS+=("$1")
;;
esac
shift
done
HOST_ROOT="${ARGS[0]:-$(pwd)}"
case "${POLICY}" in
replace|preserve|fail)
;;
*)
fail "Invalid --policy '${POLICY}'. Allowed: replace|preserve|fail"
;;
esac
[ -d "${HOST_ROOT}" ] || fail "Host root not found: ${HOST_ROOT}"
if ! git -C "${HOST_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
fail "Host root is not a git repository: ${HOST_ROOT}"
fi
if git -C "${HOST_ROOT}" remote get-url "${REMOTE_NAME}" >/dev/null 2>&1; then
SOURCE_REF="${REMOTE_NAME}"
log "Using git remote '${REMOTE_NAME}'"
else
SOURCE_REF="${REPO_URL}"
log "Remote '${REMOTE_NAME}' not found; using URL '${REPO_URL}'"
fi
if [ -d "${HOST_ROOT}/${PREFIX}" ]; then
run_cmd git -C "${HOST_ROOT}" subtree pull --prefix="${PREFIX}" "${SOURCE_REF}" "${BRANCH}" --squash
else
log "Subtree prefix not found; adding subtree at ${PREFIX}"
run_cmd git -C "${HOST_ROOT}" subtree add --prefix="${PREFIX}" "${SOURCE_REF}" "${BRANCH}" --squash
fi
if [ "${SKIP_INSTALL}" -eq 1 ]; then
log "Skip install requested. Update completed."
exit 0
fi
INSTALL_SCRIPT="${HOST_ROOT}/${PREFIX}/bootstrap/install.sh"
[ -f "${INSTALL_SCRIPT}" ] || fail "Install script not found after update: ${INSTALL_SCRIPT}"
if [ "${DRY_RUN}" -eq 1 ]; then
run_cmd /bin/bash "${INSTALL_SCRIPT}" --dry-run "${HOST_ROOT}" "${POLICY}"
else
/bin/bash "${INSTALL_SCRIPT}" "${HOST_ROOT}" "${POLICY}"
fi
log "Update completed successfully."