Repository navigation
Expand file tree
/
Copy pathCargo.toml
More file actions
168 lines (161 loc) · 11 KB
/
Copy pathCargo.toml
File metadata and controls
168 lines (161 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
[package]
name = "x-backup"
version = "0.4.0"
edition = "2021"
# rust-version: mongodb v3.7 요구사항(1.88+)을 고려해 MSRV를 1.88로 고정한다.
rust-version = "1.88"
description = "MongoDB·PostgreSQL 백업·복구 CLI — 풀/증분(oplog·logical decoding)/PITR, 로컬/원격(S3 호환), 암호화, 외부 의존 없는 단일 정적 바이너리"
license = "MIT"
repository = "https://github.com/x-mesh/x-backup"
homepage = "https://github.com/x-mesh/x-backup"
readme = "README.md"
keywords = ["backup", "mongodb", "postgresql", "mysql", "pitr"]
categories = ["command-line-utilities", "database"]
[features]
# 통합 테스트 격리(환경에 mongodump/mongorestore + replica set 필요).
# 기본 빌드/CI 단위 테스트에는 포함하지 않는다 — `--features integration-tests`로만 활성화.
integration-tests = []
# S3 통합 테스트 격리(환경에 docker + MinIO 필요). 기본 빌드/단위 테스트에는
# 포함하지 않으며 `--features s3-integration`으로만 활성화한다(integration-tests 패턴 동일).
s3-integration = []
# PostgreSQL 통합 테스트 격리(환경에 PG wal_level=logical 필요 — make postgres-up).
# 기본 빌드/단위 테스트에는 포함하지 않으며 `--features pg-integration`으로만 활성화한다.
# 검증: H3(unchanged-TOAST 보존)·C2(슬롯 gap 감지)·풀→복구 라운드트립(H1 스냅샷 경로).
pg-integration = []
# MySQL 통합 테스트 격리(환경에 MySQL log_bin=ON·binlog_format=ROW·gtid_mode=ON 필요 — make mysql-up).
# 기본 빌드/단위 테스트에는 포함하지 않으며 `--features mysql-integration`으로만 활성화한다.
# 검증: 풀→복구 라운드트립·binlog ROW 디코드·PITR(GTID 컷).
mysql-integration = []
[[bin]]
name = "x-backup"
path = "src/main.rs"
[lib]
name = "x_backup"
path = "src/lib.rs"
# 의존성은 리서치 stack 최종 표 기준의 "골격" 범위만 선언한다.
# 무거운 도메인 의존성(mongodb, object_store, age, async-compression 등)은
# 각 기능 태스크가 필요 시 추가한다 — 스캐폴드 단계에서는 의도적으로 제외.
[dependencies]
# 비동기 런타임 (PRD §7 async 파이프라인 토대)
tokio = { version = "1.47", features = ["full"] }
# CLI 파서 — derive + env(시크릿 env 참조/오버라이드 보조)
clap = { version = "4", features = ["derive", "env"] }
# 직렬화/역직렬화 + config.toml 파싱 + JSON 출력(--json)
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.8"
# 에러 모델 (PRD §9 exit code 매핑의 토대)
thiserror = "2"
anyhow = "1"
# 구조화 로그 (PRD §11 관측성, --json 로그)
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
# 체크섬 (PRD §FR-7 sha256, 스캐폴드 단계에서는 미사용이나 stack 기준에 포함)
sha2 = "0.10"
# 웹 콘솔 세션 ID용 CSPRNG. OS 엔트로피만 쓰는 얇은 크레이트라 PRNG 상태·시딩을 우리가
# 들고 다니지 않는다. 이 버전은 이미 빌드 트리에 전이 의존성으로 있어 컴파일이 늘지 않는다.
getrandom = "0.3"
# 추상 스토리지 백엔드 (PRD §10 Storage trait). LocalFileSystem은 기본 feature(fs)로 포함되며,
# aws feature는 S3 호환 백엔드(t7)가 AmazonS3Builder(with_endpoint+path-style)로 멀티파트
# 업로드/abort를 구현하는 데 필요하다. reqwest→rustls→ring 체인으로 musl 정적 빌드와 호환된다.
object_store = { version = "0.13", features = ["aws"] }
# async fn in trait(dyn) — AFIT는 dyn 미지원이므로 async_trait 매크로를 채택(리서치 architecture §2).
async-trait = "0.1"
# AsyncRead ↔ object_store 스트림 경계 어댑터(ReaderStream / StreamReader).
# ReaderStream이 yield하는 bytes::Bytes는 object_store WriteMultipart::put로 그대로
# 흘러가므로, 이 계층에서 bytes 크레이트를 직접 참조하지 않는다(t4/t7이 필요 시 추가).
# compat: age async API가 futures-io AsyncRead/AsyncWrite를 쓰므로 tokio↔futures
# 경계 변환에 tokio-util compat 어댑터가 필요하다(t6 crypto/age).
tokio-util = { version = "0.7", features = ["io", "compat"] }
# Stream 콤비네이터(list 결과 수집, GetResult 스트림 매핑).
futures = "0.3"
# MongoDB 드라이버 — dump 전후 oplog 최신 ts·서버 버전·토폴로지(setName) 조회용(t4 범위).
# default-features=false + rustls-tls로 musl 정적 빌드 호환(리서치 §4/§7). dump 자체는
# mongodump 서브프로세스가 수행하므로 드라이버는 메타데이터 질의에만 쓴다.
mongodb = { version = "3.7", default-features = false, features = ["rustls-tls", "dns-resolver", "bson-2", "compat-3-3-0"] }
# BSON Timestamp{time,increment} — oplog ts를 manifest OplogTimestamp{t,i}로 보존.
bson = "2"
# 백업 ID — UUID v7(시간 정렬 가능, 사전순 = 생성순). manifest.id / 저장 레이아웃 디렉터리.
uuid = { version = "1", features = ["v7"] }
# manifest created_at(UTC RFC3339).
chrono = { version = "0.4", default-features = false, features = ["clock", "serde"] }
# 사이드카 체크섬·manifest checksum_sha256의 hex 인코딩.
hex = "0.4"
# --- update 서브커맨드(자기 갱신) ---
# reqwest는 object_store 경유로 이미 트리에 있는 0.12 계열을 직접 의존으로 노출만 한다
# (rustls 체인 — musl 정적 빌드 호환). flate2/tar는 릴리스 tarball 추출용(pure Rust).
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
flate2 = "1"
tar = "0.4"
# 자식 프로세스에 URI를 argv로 넘기지 않기 위한 0600 임시 config 파일(런타임 사용).
tempfile = "3"
# 빌드 호환 핀: time 0.3.44+는 bson 2.15→mongodb 3.7.0의 blanket impl(Checked<T>)과
# 충돌(E0119)한다. 직접 사용하지 않지만 transitive 해석을 0.3.43에 고정해 빌드를 보호한다.
# (mongodb가 time 호환을 갱신하면 제거 가능.)
time = "=0.3.43"
# 압축(t6) — async-compression(tokio bufread zstd 어댑터). PipelineStage가
# AsyncRead→AsyncRead 어댑터로 직접 합성한다(리서치 stack §3, compress→encrypt 고정).
async-compression = { version = "0.4", features = ["tokio", "zstd"] }
# 암호화 기본(t6) — age v0.11(X25519 공개키 격리, §8.1). async feature로
# Encryptor::wrap_async_output / Decryptor::new_async를 tokio 파이프라인에 브리지한다.
age = { version = "0.11", features = ["async"] }
# 암호화 대안(t6) — AES-256-GCM + aead STREAM(BE32) 청크 프레이밍(§8.2, pitfall 4-2).
# config algorithm="aes-256-gcm" 선택 시 활성. 키는 env(32바이트 hex) 참조.
aes-gcm = "0.10"
aead = { version = "0.5", features = ["stream"] }
# 진행 표시(t13, FR-9/R16) — indicatif progress bar/spinner를 **stderr**에 그린다
# (stdout은 결과·--json 전용, pitfall 9-1). default-features로 충분(unicode-width 등).
# init 마법사의 단순 라인 Q&A는 별도 대화형 크레이트 대신 std stdin을 직접 읽는다 —
# 입력 시퀀스 주입(테스트)이 trait 추상화로 더 단순하고 의존성을 줄이기 위함이다.
indicatif = "0.18"
# 동시 실행 잠금(t12, FR-12) — stale lock 판정의 PID 생존 확인(kill(pid,0)).
# std에는 시그널 전송 API가 없어 libc::kill을 직접 호출한다. 이미 transitive로
# 잠겨 있던 0.2 라인을 직접 의존으로 끌어올린다(추가 빌드 비용 없음).
libc = "0.2"
# PostgreSQL 드라이버(2차) — 외부 pg_dump/pg_restore 없이 COPY 프로토콜로 테이블 데이터를
# 스트리밍 백업/복구하고, status(버전·크기·테이블 수)를 SQL로 조회한다. 로컬 1차는 NoTls.
tokio-postgres = "0.7"
# COPY FROM STDIN sink에 넘길 Bytes 타입(드라이버와 동일 계열).
bytes = "1"
# PG 연결 TLS(리뷰 #3) — sslmode=prefer(기본)면 TLS 시도 후 미지원 서버엔 평문 폴백,
# require/verify면 TLS 강제. rustls 0.23 + ring(트리와 동일 provider) + webpki 신뢰 루트.
tokio-postgres-rustls = "0.13"
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12", "logging"] }
webpki-roots = "1"
# MySQL/MariaDB 드라이버(3차) — 외부 mysqldump/mysql/mysqlbinlog 없이 SHOW CREATE + SELECT 스트림으로
# 백업/복구하고, 내장 binlog stream(Conn::get_binlog_stream)으로 ROW 이벤트 증분/PITR을 수행한다.
# default-features=false로 native-tls·C-zlib를 배제하고 default-rustls-ring(rustls + ring +
# webpki-roots + 순수 Rust zlib)로 musl 정적 빌드와 호환한다. binlog feature가 mysql_common의
# binlog 파서(TableMapEvent/WriteRows/UpdateRows/DeleteRows 디코드)를 켠다.
mysql_async = { version = "0.37", default-features = false, features = ["default-rustls-ring", "binlog"] }
bubbletea-rs = { version = "=0.0.9", default-features = false, features = ["tokio-runtime"] }
bubbletea-widgets = { version = "=0.1.12", default-features = false }
lipgloss-extras = { version = "=0.1.1", features = ["tables"] }
crossterm = "=0.29.0"
# --- 웹 운영 콘솔(serve) ---
# HTTP 서버 — hyper·hyper-util·http·tower가 이미 트리에 있어(reqwest/object_store 경유)
# 추가 무게가 작다. default-features를 끄고 실제로 쓰는 것만 켠다:
# http1 — HTTP/1.1. TLS·HTTP/2 종단은 앞단 리버스 프록시 책임이므로 h2는 켜지 않는다.
# tokio — axum::serve + TcpListener 통합.
# json/form/query/macros는 그 기능을 실제로 쓰는 태스크가 켠다(쓰지 않는 코드 생성 회피).
axum = { version = "0.8", default-features = false, features = ["http1", "tokio", "query"] }
# 서버 사이드 HTML — `html!` 매크로가 컴파일 타임에 Rust 코드로 펼쳐지므로 런타임 템플릿
# 파일이 없다(단일 정적 바이너리 원칙 유지). 보간값을 기본 HTML 이스케이프해 XSS 표면을
# 좁힌다. axum feature가 Markup→Response(IntoResponse) 변환을 제공한다.
maud = { version = "0.27", features = ["axum"] }
[dev-dependencies]
# Storage trait mock 자동 생성(단위 테스트에서 파이프라인 로직 주입).
mockall = "0.14"
# 실제 바이너리 E2E — 빌드된 x-backup을 스폰해 exit code/출력을 검증한다(SC6, C3).
# Docker·DB 없이 동작하는 시나리오(설정 오류=2, PITR+--only=2, 점검 실패=3, 잠금 충돌=5)만 다룬다.
assert_cmd = "2"
predicates = "3"
# axum 라우터를 실제 포트 없이 직접 호출한다(`ServiceExt::oneshot`) — 웹 라우팅 테스트가
# 포트 점유·방화벽·타이밍에 흔들리지 않게 한다. tower는 이미 트리에 있고 util feature만 켠다.
tower = { version = "0.5", features = ["util"] }
# oneshot 응답 본문(http_body::Body)을 바이트로 모으는 테스트 유틸.
http-body-util = "0.1"
# 가상 시계(`#[tokio::test(start_paused = true)]`). tokio의 "full"에는 들어 있지 않다 —
# 런타임 기능이 아니라 테스트 전용이라 별도 feature다. 라이브 모니터의 grace 종료
# (`web::routes::monitor`)처럼 "몇 초 뒤에 일어나는 일"을 실제로 기다리지 않고 고정한다.
tokio = { version = "1.47", features = ["test-util"] }