Metadata-Version: 2.4
Name: git-catcher
Version: 0.3.0
Summary: Smee.io 기반 GitHub push webhook 자동 배포 CLI 도구
License: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: InquirerPy>=0.3.4
Requires-Dist: rich>=13.0.0
Provides-Extra: dev
Requires-Dist: hypothesis>=6.100.0; extra == "dev"
Requires-Dist: pytest>=8.0.0; extra == "dev"

# Git Catcher

Smee.io SSE proxy를 활용하여 GitHub push webhook을 실시간 수신하고, 허용된 브랜치에 대해 자동으로 `git pull` 및 사용자 정의 배포 명령을 실행하는 Python CLI 도구입니다.

방화벽/NAT 환경에서도 동작하며, 연결 끊김 시 exponential backoff 기반 자동 재연결을 지원합니다.

## 설치

```bash
pip install -r git_catcher/requirements.txt
```

### 의존성

| 패키지 | 용도 |
|--------|------|
| `httpx` | SSE 스트림 HTTP 클라이언트 |
| `rich` | 컬러 콘솔 로깅 |
| `python-dotenv` | `.env` 파일 환경변수 로드 |
| `InquirerPy` | Interactive CLI 질문 (init 명령어) |
| `hypothesis` | Property-based testing (개발용) |
| `pytest` | 테스트 프레임워크 (개발용) |

## 빠른 시작

### 1. Interactive 초기 설정 (권장)

```bash
python -m git_catcher.main init
```

또는 간편 실행:

```bash
python run.py init
```

대화형 질문에 답하면 `.env` 파일과 `docker-compose.yml`이 자동 생성됩니다.

- Smee.io URL 자동 생성 (브라우저 열기)
- Webhook Secret 랜덤 생성
- 저장소 경로, 허용 브랜치 선택
- Slack 알림 설정

### 2. 수동 설정

`.env.example` 파일을 복사하여 `.env` 파일을 생성하고 값을 수정합니다.

```bash
cp git_catcher/.env.example git_catcher/.env
```

| 변수 | 기본값 | 설명 |
|------|--------|------|
| `SMEE_URL` | `https://smee.io/YOUR_CHANNEL_ID` | Smee.io 채널 URL ([smee.io/new](https://smee.io/new)에서 생성) |
| `WEBHOOK_SECRET` | `""` | GitHub Webhook Secret (미설정 시 서명 검증 스킵) |
| `REPO_PATH` | `/path/to/your/repo` | 배포 대상 Git 저장소 로컬 경로 |
| `ALLOWED_BRANCHES` | `main,master` | 배포 허용 브랜치 (쉼표 구분) |
| `POST_PULL_COMMAND` | `""` | `git pull` 후 실행할 shell 명령어 |
| `RECONNECT_DELAY` | `1` | 초기 재연결 대기 시간 (초) |
| `MAX_RECONNECT_DELAY` | `300` | 최대 재연결 대기 시간 (초) |
| `ALLOWED_REPOS` | `""` | 허용할 저장소 목록 (`owner/repo` 형식, 쉼표 구분). 빈 문자열이면 `REPO_PATH`의 git remote origin에서 자동 감지 |
| `SLACK_WEBHOOK_URL` | `""` | Slack Incoming Webhook URL. 빈 문자열이면 알림 스킵 |
| `NOTIFY_ON_START` | `true` | 시작 시 Slack 알림 전송 여부 (`true`/`false`) |
| `POLL_INTERVAL` | `0` | Git 상태 폴링 간격 (초, 0이면 비활성) |
| `LOG_LEVEL` | `INFO` | 로그 레벨 (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |
| `LOG_FILE` | `""` | 로그 파일 경로 (빈 문자열이면 파일 로그 비활성) |
| `SHOW_ALL_EVENTS` | `false` | 모든 push 이벤트 로그 출력 (`true`/`false`) |

### 2. Smee.io 채널 생성

1. [https://smee.io/new](https://smee.io/new)에 접속합니다.
2. 자동으로 고유한 채널 URL이 생성됩니다. (예: `https://smee.io/AbCdEfGhIjKlMn`)
3. 이 URL을 복사해 둡니다. GitHub Webhook의 Payload URL과 `.env` 파일의 `SMEE_URL`에 동일한 값을 사용합니다.

> **참고:** `git-catcher init` 명령을 사용하면 브라우저에서 자동으로 Smee.io를 열어줍니다.

### 3. GitHub Repository Webhook 추가

1. GitHub 리포지토리 페이지에서 **Settings** → **Webhooks** → **Add webhook**을 클릭합니다.
2. 아래와 같이 설정합니다:

| 항목 | 값 |
|------|-----|
| Payload URL | Smee.io 채널 URL (예: `https://smee.io/AbCdEfGhIjKlMn`) |
| Content type | `application/json` |
| Secret | `.env`의 `WEBHOOK_SECRET`과 동일한 값 |
| Which events? | "Just the push event" 선택 |
| Active | 체크 |

3. **Add webhook** 버튼을 클릭하여 저장합니다.

> **참고:** Secret을 비워두면 Git Catcher도 서명 검증을 스킵합니다. 보안을 위해 Secret 설정을 권장합니다.

### 3-1. GitHub App으로 전체 저장소 Webhook 수신 (선택)

> 단일 저장소만 필요하면 위의 [3. GitHub Repository Webhook 추가](#3-github-repository-webhook-추가)를 사용하세요.
> 개인 계정의 **모든 저장소**(향후 생성 포함)에서 push webhook을 수신하려면 GitHub App을 생성합니다.

#### 1) GitHub App 생성

[GitHub Settings → Developer settings → GitHub Apps → New GitHub App](https://github.com/settings/apps/new)에서 앱을 생성합니다.

| 항목 | 값 | 설명 |
|------|-----|------|
| App name | 임의의 이름 | 예: `my-git-catcher` |
| Homepage URL | `https://github.com/본인계정` | 앱 식별용 필수 항목 |
| Webhook URL | Smee.io 채널 URL | 실제 webhook 데이터를 수신할 목적지 |
| Webhook Secret | `.env`의 `WEBHOOK_SECRET`과 동일한 값 | 서버-GitHub 간 페이로드 위변조 방지용 공유 키 |

#### 2) 권한 및 이벤트 구독

생성 화면 하단에서 다음을 설정합니다:

- **Repository Permissions** → **Contents**: `Read-only` 이상으로 설정 (코드 변경 감지에 필요)
- **Subscribe to events** → **Push** 체크 (코드 push 시에만 webhook 발생)

#### 3) 계정에 앱 설치

이 단계가 가장 중요합니다. 앱을 생성만 하면 webhook이 동작하지 않으며, 반드시 계정에 **설치**해야 합니다.

1. 생성된 앱 페이지에서 **Install App** 메뉴를 클릭합니다.
2. 본인 계정을 선택하고 **All repositories**를 선택합니다.
3. **Install** 버튼을 클릭합니다.

> **All repositories**를 선택하면 현재 존재하는 저장소뿐 아니라, 향후 생성되는 모든 저장소에도 자동으로 webhook이 적용됩니다.

#### 4) 보안 및 검증

| 항목 | 설명 |
|------|------|
| Private Key (`.pem`) | 앱 설정 페이지 하단에서 생성. 서버에서 GitHub API를 호출할 때 인증에 사용 |
| Recent Deliveries | 앱 설정 → **Advanced** 탭에서 전송된 webhook 페이로드 확인 및 **Redeliver**로 재전송 테스트 가능 |

### 4. .env 예시

`SMEE_URL`은 Smee.io에서 생성한 채널 URL, `WEBHOOK_SECRET`은 GitHub Webhook에서 입력한 Secret과 반드시 동일해야 합니다.

```dotenv
SMEE_URL=https://smee.io/AbCdEfGhIjKlMn
WEBHOOK_SECRET=your-webhook-secret-here
REPO_PATH=/path/to/your/repo
ALLOWED_BRANCHES=main,master
POST_PULL_COMMAND=
ALLOWED_REPOS=
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T.../B.../xxx
NOTIFY_ON_START=true
POLL_INTERVAL=0
LOG_LEVEL=INFO
LOG_FILE=
SHOW_ALL_EVENTS=false
```

각 변수의 상세 설명은 [환경변수 테이블](#2-수동-설정)을 참고하세요.

## 실행

### 기본 실행

```bash
# 방법 1: 모듈로 실행
python -m git_catcher.main

# 방법 2: 간편 실행 (권장)
python run.py
```

실행하면 Smee.io SSE 스트림에 연결되어 GitHub push 이벤트를 대기합니다. 허용된 브랜치에 push가 감지되면 자동으로 `git fetch` → `git checkout` → `git pull` → post-pull 명령을 순차 실행합니다.

### 시작 알림

`NOTIFY_ON_START=true`로 설정하면 git-catcher 시작 시 Slack에 알림을 전송합니다. 알림에는 다음 정보가 포함됩니다:

- 저장소명
- 허용 브랜치 목록
- 호스트명
- 외부 IP 주소
- 시작 시각 (UTC)

### CLI 명령어

| 명령어 | 설명 |
|--------|------|
| `git-catcher init` | Interactive 초기 설정 (`.env` 및 `docker-compose.yml` 생성) |
| `git-catcher run` | git-catcher 실행 (기본 동작) |

### CLI 옵션 (run 명령어)

| 옵션 | 설명 |
|------|------|
| `-v` | `git_catcher` 모듈만 DEBUG 로그 출력 |
| `-vv` | 전체 모듈 DEBUG 로그 출력 |
| `-vvvv` | raw webhook 페이로드까지 출력 |
| `--show-all-events` | 모든 push 이벤트 로그 출력 (배포는 여전히 필터링 적용) |
| `--log-file PATH` | 로그 파일 경로 (설정 시 파일에도 DEBUG 로그 기록) |
| `--log-level LEVEL` | 로그 레벨 (`DEBUG`, `INFO`, `WARNING`, `ERROR`) |

```bash
# 기본 실행
python run.py

# git_catcher 모듈 DEBUG + 로그 파일 기록
python run.py -v --log-file deploy.log

# 모든 push 이벤트 확인 + 전체 DEBUG
python run.py -vv --show-all-events

# Interactive 초기 설정
python run.py init
```

### 주요 기능

- **Interactive 초기 설정**: `git-catcher init` 명령으로 대화형 질문에 답하면 `.env` 및 `docker-compose.yml` 자동 생성
- **간편 실행**: `python run.py`로 빠르게 실행
- **시작 알림**: `NOTIFY_ON_START=true` 설정 시 Slack에 시작 알림 전송 (저장소, 브랜치, 호스트, IP 정보 포함)
- **저장소 자동 감지**: `ALLOWED_REPOS` 미설정 시 `REPO_PATH`의 git remote origin URL에서 `owner/repo`를 자동 추출
- **Slack 배포 알림**: `SLACK_WEBHOOK_URL` 설정 시 배포 성공/실패를 Slack 채널에 알림
- **SSE 재연결 복구**: `Last-Event-ID` 기반으로 연결 끊김 시 마지막 수신 이벤트 이후부터 재수신
- **Rich 콘솔 로깅**: 컬러 + 타임스탬프(시:분:초.ms) 포맷의 가독성 높은 콘솔 출력

## 동작 확인

1. Git Catcher를 [실행](#실행)합니다:

   ```bash
   python run.py
   ```

2. `SSE 스트림 연결 성공` 로그가 출력되면 연결 성공입니다.
3. `NOTIFY_ON_START=true`로 설정했다면 Slack에 시작 알림이 전송됩니다.
4. 허용된 브랜치에 커밋을 push합니다:

   ```bash
   git add .
   git commit -m "test webhook"
   git push origin main
   ```

5. Git Catcher 터미널에서 다음 로그가 순서대로 출력되는지 확인합니다:
   - Push 감지: `[main] test webhook`
   - `git fetch` → `git checkout` → `git pull` 완료
   - `POST_PULL_COMMAND` 실행 (설정 시)
   - Slack 알림 전송 (설정 시)

설정에 문제가 있으면 GitHub Webhook 페이지의 **Recent Deliveries** 탭에서 전송 상태를 확인할 수 있습니다.

## 테스트

```bash
pytest tests/ -v
```
