Commit f4912f9c authored by platyhouse's avatar platyhouse

# CLI 스크립트 표준 가이드라인 추가 및 ptyMysqlBackup 개선

## CLAUDE.md 문서 업데이트

### CLI 스크립트 작성 표준 가이드라인 추가
- CLAUDE.md: CLI 스크립트 작성 시 필수 규칙 섹션 신규 추가
  - 필수 라이브러리 참조 목록 (ptyCliOptionParser, ptyCliLog, ptyCliColor 등)
  - 공통 필수 옵션 정의 (--help, --verbose, --edit, --dry-run)
  - 서비스별 필수 옵션 정의 (--mysql, --elastic, --ai)
  - 스크립트 기본 구조 템플릿 코드 추가
  - 네이밍 규칙 (pty + 서비스명 + 동작, 확장자 없음)
  - Exit 코드 규칙 (0: 정상, 1: 에러)
  - --dry-run 및 --edit 구현 패턴
  - x_ 접두사 무시 패턴
  - 주의사항 (glob 패턴 따옴표, 디렉토리 생성, 타임스탬프 로그)

### ptyMysqlBackup 문서 개선
- CLAUDE.md: ptyMysqlBackup 예시에 따옴표 표기 추가 (glob 패턴 셸 확장 방지)
- CLAUDE.md: --ignore-x, --dry-run, --edit 옵션 설명 추가
- CLAUDE.md: x_ 접두사 테이블도 무시 대상임을 명확히 기술

## ptyMysqlBackup 스크립트 기능 개선

### 새로운 옵션 추가
- ptyMysqlBackup: --ignore-x 옵션 추가 (x_ 접두사 DB/테이블 무시, 기본값: true)
- ptyMysqlBackup: --dry-run 옵션 추가 (실제 백업 없이 대상 목록만 출력)
- ptyMysqlBackup: --edit 옵션 추가 (스크립트를 에디터로 열기)

### 기능 개선
- ptyMysqlBackup: x_ 접두사 테이블도 무시 대상에 포함
- ptyMysqlBackup: dry-run 모드에서 디렉토리 생성 방지
- ptyMysqlBackup: 도움말에 glob 패턴 따옴표 필수 안내 추가
parent 61bfc6e6
......@@ -10,6 +10,209 @@ pty_centos.git은 Elasticsearch, MySQL 등 다양한 서비스를 다루는 CLI
모든 PHP 코드는 `platyFramework` 네임스페이스를 사용합니다.
---
## CLI 스크립트 작성 표준 가이드라인
새로운 CLI 스크립트 작성 시 반드시 따라야 하는 규칙입니다.
### 필수 라이브러리 참조
모든 스크립트는 `ptyLibrary_PHP` 폴더의 공통 라이브러리를 사용합니다:
```php
require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliOptionParser.php'; // 필수
require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliLog.php'; // 로깅 시
require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliColor.php'; // 색상 출력 시
require_once __DIR__ . '/ptyLibrary_PHP/mysql/ptyMysqlConfig.php'; // MySQL 사용 시
require_once __DIR__ . '/ptyLibrary_PHP/elastic/ptyElasticConfig.php'; // Elastic 사용 시
require_once __DIR__ . '/ptyLibrary_PHP/ai/ptyAIConfig.php'; // AI API 사용 시
```
### 필수 옵션 (모든 스크립트에 포함)
| 옵션 | 설명 | 구현 필수 |
|------|------|:--------:|
| `--help` | 도움말 출력 | O |
| `--verbose` | 상세 로그 출력 | O |
| `--edit` | 스크립트를 에디터로 열기 | O |
| `--dry-run` | 실제 실행 없이 대상/결과 미리보기 | O (데이터 변경 스크립트) |
### 서비스별 필수 옵션
| 서비스 | 옵션 | 설명 |
|--------|------|------|
| MySQL | `--mysql=섹션명` | INI 파일 섹션 (기본값: default) |
| Elasticsearch | `--elastic=섹션명` | INI 파일 섹션 (기본값: default) |
| AI API | `--ai=섹션명` | INI 파일 섹션 (기본값: default) |
### 스크립트 기본 구조
```php
#!/usr/bin/env php
<?php
/**
* 스크립트 설명
*
* 설정 파일: ~/.pty[Service]Config.ini
*
* Usage: ./ptyXXX <args> [options]
*/
namespace platyFramework;
require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliOptionParser.php';
// ... 필요한 라이브러리 require ...
// ============================================================
// 인자 파싱
// ============================================================
$parsed = ptyCliOptionParser::parse($argv);
$positionalArgs = $parsed['positional'];
$options = $parsed['options'];
// 공통 옵션
$verbose = isset($options['verbose']);
$dryRun = isset($options['dry-run']);
// --edit 옵션: 스크립트 편집 (도움말보다 먼저 처리)
if (isset($options['edit'])) {
$editor = getenv('EDITOR') ?: 'vi';
passthru("$editor " . escapeshellarg(__FILE__));
exit(0);
}
// ============================================================
// 도움말
// ============================================================
if (count($positionalArgs) < 1 || isset($options['help'])) {
echo "사용법: {$argv[0]} <필수인자> [옵션]\n";
echo "\n";
echo "인자:\n";
echo " <필수인자> 설명\n";
echo "\n";
echo "옵션:\n";
echo " --dry-run 실제 실행 없이 대상 목록만 출력\n";
echo " --verbose 상세 로그 출력\n";
echo " --edit 이 스크립트를 에디터로 열기\n";
echo " --help 도움말 출력\n";
echo "\n";
// 설정 파일 예시 출력 (해당 시)
// echo ServiceConfig::getConfigExample() . "\n";
exit(isset($options['help']) ? 0 : 1);
}
// ============================================================
// 메인 로직
// ============================================================
try {
if ($dryRun) {
echo "[DRY-RUN] 실제 실행 없이 미리보기 모드\n";
}
// ... 비즈니스 로직 ...
if ($dryRun) {
echo "[DRY-RUN] 완료\n";
}
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
exit(1);
}
```
### 네이밍 규칙
| 항목 | 규칙 | 예시 |
|------|------|------|
| 스크립트 파일명 | `pty` + 서비스명 + 동작 (CamelCase) | `ptyMysqlBackup`, `ptyElasticGetIndex` |
| 확장자 | 없음 (shebang으로 실행) | `ptyMysqlBackup` (not `.php`) |
| 설정 파일 | `~/.pty[Service]Config.ini` | `~/.ptyMysqlConfig.ini` |
### Exit 코드
| 코드 | 의미 |
|------|------|
| `0` | 정상 종료 (`--help` 포함) |
| `1` | 에러 또는 필수 인자 누락 |
### --dry-run 구현 패턴
```php
$dryRun = isset($options['dry-run']);
// 시작 시 모드 표시
if ($dryRun) {
logMessage("Mode: DRY-RUN (실제 실행 없음)", true);
}
// 작업 시 분기
if ($dryRun) {
logMessage("WOULD DO: $action", true);
} else {
// 실제 작업 수행
doAction($action);
}
// 완료 시 메시지 분기
if ($dryRun) {
logMessage("DRY-RUN 완료: $count 대상", true);
} else {
logMessage("완료: $count 처리됨", true);
}
```
### --edit 구현 패턴
```php
// 도움말 처리보다 먼저 위치 (인자 없이도 동작해야 함)
if (isset($options['edit'])) {
$editor = getenv('EDITOR') ?: 'vi';
passthru("$editor " . escapeshellarg(__FILE__));
exit(0);
}
```
### x_ 접두사 무시 패턴 (선택)
데이터베이스/테이블/인덱스 등에서 `x_` 접두사 항목을 무시하는 옵션:
```php
$ignoreX = ($options['ignore-x'] ?? 'true') !== 'false'; // 기본값: true
// 사용
if ($ignoreX && strpos($name, 'x_') === 0) {
logMessage("SKIP: $name (x_ prefix)", true);
continue;
}
```
### 주의사항
1. **glob 패턴 사용 시**: 사용자에게 따옴표 사용 안내
```php
echo " {$argv[0]} '*' '*' # 전체 (따옴표 필수!)\n";
```
2. **디렉토리 생성**: dry-run 시에는 생성하지 않음
```php
if (!$dryRun && !is_dir($outputDir)) {
mkdir($outputDir, 0755, true);
}
```
3. **타임스탬프 로그**: 일관된 형식 사용
```php
function logMessage($message, $show = true) {
if (!$show) return;
$timestamp = date('Y-m-d H:i:s');
echo "[$timestamp] $message\n";
}
```
---
## ptyElastic* 스크립트 작성 규칙
새로운 Elasticsearch CLI 스크립트 생성 시 다음 패턴을 따릅니다:
......@@ -450,9 +653,11 @@ mysqldump를 사용하여 테이블을 백업합니다.
```bash
./ptyMysqlBackup <database> <table> [옵션]
./ptyMysqlBackup mydb users # mydb.users.sql 생성
./ptyMysqlBackup mydb * # mydb의 모든 테이블 백업
./ptyMysqlBackup * * # 전체 백업
./ptyMysqlBackup mydb '*' # mydb의 모든 테이블 백업
./ptyMysqlBackup '*' '*' # 전체 백업 (따옴표 필수!)
./ptyMysqlBackup mydb users --output=/backup # 지정 경로에 저장
./ptyMysqlBackup '*' '*' --dry-run # 백업 대상만 미리보기
./ptyMysqlBackup '*' '*' --ignore-x=false # x_ 접두사 포함 전체 백업
```
**옵션:**
......@@ -461,12 +666,15 @@ mysqldump를 사용하여 테이블을 백업합니다.
|------|------|--------|
| `--mysql=섹션명` | INI 파일 섹션 | `default` |
| `--output=경로` | 출력 디렉토리 | `.` |
| `--ignore-x=true\|false` | x_ 접두사 DB/테이블 무시 | `true` |
| `--dry-run` | 실제 백업 없이 대상 목록만 출력 | - |
| `--verbose` | 상세 로그 출력 | - |
| `--edit` | 스크립트를 에디터로 열기 | - |
**백업 파일:**
- 파일명 형식: `{database}.{table}.sql`
- 파일 헤더에 백업 시간, 실행 명령어 포함
- `x_` 접두사 DB는 자동 스킵
- `x_` 접두사 DB/테이블은 기본 스킵 (`--ignore-x=false`로 포함 가능)
**mysqldump 옵션:**
```
......
......@@ -22,6 +22,16 @@ $options = $parsed['options'];
$mysqlSection = $options['mysql'] ?? 'default';
$verbose = isset($options['verbose']);
$outputDir = $options['output'] ?? '.';
$ignoreX = ($options['ignore-x'] ?? 'true') !== 'false'; // 기본값: true
$dryRun = isset($options['dry-run']);
// --edit 옵션: 스크립트 편집
if (isset($options['edit'])) {
$editor = getenv('EDITOR') ?: 'vi';
$scriptPath = __FILE__;
passthru("$editor " . escapeshellarg($scriptPath));
exit(0);
}
// 도움말 또는 필수 인자 확인
if (count($positionalArgs) < 2 || isset($options['help'])) {
......@@ -34,15 +44,19 @@ if (count($positionalArgs) < 2 || isset($options['help'])) {
echo "옵션:\n";
echo " --mysql=섹션명 INI 파일 섹션 (기본값: default)\n";
echo " --output=경로 출력 디렉토리 (기본값: 현재 디렉토리)\n";
echo " --ignore-x=true|false x_ 접두사 DB/테이블 무시 (기본값: true)\n";
echo " --dry-run 실제 백업 없이 대상 목록만 출력\n";
echo " --verbose 상세 로그 출력\n";
echo " --edit 이 스크립트를 에디터로 열기\n";
echo " --help 도움말 출력\n";
echo "\n";
echo "예시:\n";
echo " {$argv[0]} mydb users # mydb.users.sql 생성\n";
echo " {$argv[0]} mydb * # mydb의 모든 테이블 백업\n";
echo " {$argv[0]} * * # 전체 백업\n";
echo " {$argv[0]} mydb '*' # mydb의 모든 테이블 백업\n";
echo " {$argv[0]} '*' '*' # 전체 백업 (따옴표 필수!)\n";
echo " {$argv[0]} mydb users --output=/backup # /backup/mydb.users.sql 생성\n";
echo " {$argv[0]} mydb users --mysql=production # production 섹션 사용\n";
echo " {$argv[0]} '*' '*' --ignore-x=false # x_ 접두사 포함 전체 백업\n";
echo "\n";
echo "기본 mysqldump 옵션:\n";
echo " --default-character-set=utf8mb4\n";
......@@ -162,14 +176,17 @@ try {
$connection = $conn['connection'];
$config = $conn['config'];
logMessage("MySQL 백업 시작", true);
logMessage($dryRun ? "MySQL 백업 (DRY-RUN)" : "MySQL 백업 시작", true);
logMessage("Host: {$config['host']}", true);
logMessage("User: {$config['username']}", true);
logMessage("Output: $outputDir", true);
if ($dryRun) {
logMessage("Mode: DRY-RUN (실제 백업 없음)", true);
}
echo str_repeat("=", 60) . "\n";
// 출력 디렉토리 생성
if (!is_dir($outputDir)) {
// 출력 디렉토리 생성 (dry-run 제외)
if (!$dryRun && !is_dir($outputDir)) {
if (!mkdir($outputDir, 0755, true)) {
throw new \Exception("출력 디렉토리 생성 실패: $outputDir");
}
......@@ -196,7 +213,7 @@ try {
foreach ($databases as $dbName) {
// x_ 접두사 DB 제외
if (strpos($dbName, 'x_') === 0) {
if ($ignoreX && strpos($dbName, 'x_') === 0) {
logMessage("SKIP: $dbName (x_ prefix)", true);
continue;
}
......@@ -222,9 +239,19 @@ try {
}
foreach ($tables as $tableName) {
// x_ 접두사 테이블 제외
if ($ignoreX && strpos($tableName, 'x_') === 0) {
logMessage("SKIP: $dbName.$tableName (x_ prefix)", true);
continue;
}
$totalBackups++;
if (backupTable($config['host'], $config['username'], $config['password'], $dbName, $tableName, $outputDir, $verbose, $originalCommand)) {
if ($dryRun) {
logMessage("WOULD BACKUP: $dbName.$tableName -> $outputDir/$dbName.$tableName.sql", true);
$successBackups++;
} else {
if (backupTable($config['host'], $config['username'], $config['password'], $dbName, $tableName, $outputDir, $verbose, $originalCommand)) {
$successBackups++;
}
}
}
}
......@@ -232,7 +259,11 @@ try {
mysqli_close($connection);
echo "\n" . str_repeat("=", 60) . "\n";
logMessage("백업 완료: $successBackups / $totalBackups", true);
if ($dryRun) {
logMessage("DRY-RUN 완료: $successBackups / $totalBackups 대상", true);
} else {
logMessage("백업 완료: $successBackups / $totalBackups", true);
}
if ($successBackups < $totalBackups) {
exit(1);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment