Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- name: Ensure git-lfs is installed
run: git lfs version || (sudo apt-get update && sudo apt-get install -y git-lfs)
- run: npm run lint
- run: npm run typecheck
- name: Wait for the S3 server
Expand Down
3 changes: 3 additions & 0 deletions LEARNINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ Claude Code bulut oturumlarında `~/.claude/CLAUDE.md` bulunmaz. Yalnızca repod

## 2026-10-14 — AWS SDK v3 varsayılan olarak CRC32 sağlama toplamı ekler
Üçüncü parti S3 servisleri bu başlıkları reddedebildiği için istemci `requestChecksumCalculation` ve `responseChecksumValidation` için `WHEN_REQUIRED` ile oluşturulur (K-035); `DeleteObjects` gibi zorunlu kılan işlemler yine sağlama toplamı gönderir.

## 2026-10-14 — LFS testleri için sunucu gerekmez: standalone file transfer
`git-lfs` yerleşik `lfs-standalone-file` aktarımıyla `lfs.url=file://<dizin>` bir LFS sunucusu gibi kullanılabilir (`lfs.standalonetransferagent=lfs-standalone-file`). Dizin var olan bir git dizini (ör. `git init --bare`) olmalıdır; aksi halde "chdir" veya "not a git repository" hatası gelir. Nesneler `<dizin>/lfs/objects` altına yazılır. Bare mirror'da `git lfs fetch --all` LFS'siz repoda da hata vermez ve boş bir `lfs/` bırakır; "LFS var mı" kontrolü dizinin değil dosyanın varlığına bakmalıdır.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A Node.js/TypeScript CLI (`github-backup`) that backs up every repository you can reach through the GitHub CLI (`gh`): your personal repositories and those of all organizations you belong to, with wikis and Git LFS objects. Backups go to a local directory and/or any S3-compatible bucket (AWS S3, Cloudflare R2, Backblaze B2, MinIO) as git bundle snapshots, and a new snapshot is created only when refs change.

Status: under development. `list` and `backup` to local and S3 targets work; Git LFS is not implemented yet. See the plan (Turkish) in [plans/README.md](plans/README.md).
Status: under development. `list` and `backup` to local and S3 targets work, including wikis and Git LFS objects (`<stamp>.lfs.tar` next to each bundle; needs `git-lfs`, or pass `--no-lfs`). See the plan (Turkish) in [plans/README.md](plans/README.md).

## Requirements

Expand Down
2 changes: 1 addition & 1 deletion developer-todos.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Sıradaki fazı engelleyen işler.

### Faz 4 — Git LFS

- [ ] 💻 LFS kullanan bir repo varsa yedekleyip README'deki adımlarla geri yükle.
- [ ] 💻 LFS kullanan gerçek bir GitHub reposunu yedekle (`node dist/cli.js backup --local ~/git-backup-test --include '<owner>/<repo>'`), `<stamp>.lfs.tar` oluştuğunu gör ve [faz-04-lfs.md](plans/faz-04-lfs.md) **Devam notları**'ndaki adımlarla bir LFS dosyasını geri yükleyip sha256'sını orijinalle karşılaştır: gerçek `gh` oturumu ve gerçek LFS sunucusu gerekir, bulutta yok.

### Faz 5 — Dokümantasyon ve son kontrol

Expand Down
68 changes: 68 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@aws-sdk/client-s3": "^3.1152.0",
"@aws-sdk/lib-storage": "^3.1152.0",
"commander": "^15.0.0",
"tar": "^7.5.22",
"zod": "^4.6.7"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion plans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Fazlar tablodaki sırayla uygulanır; sıranın tek kaynağı bu tablodur. Her f
| 1 | İskelet, config ve `list` | Tamamlandı | [faz-01-iskelet-ve-list.md](faz-01-iskelet-ve-list.md) |
| 2 | Mirror, snapshot ve `local` hedefi | Tamamlandı | [faz-02-mirror-ve-local.md](faz-02-mirror-ve-local.md) |
| 3 | `s3` hedefi | Tamamlandı | [faz-03-s3.md](faz-03-s3.md) |
| 4 | Git LFS | Bekliyor | [faz-04-lfs.md](faz-04-lfs.md) |
| 4 | Git LFS | Tamamlandı | [faz-04-lfs.md](faz-04-lfs.md) |
| 5 | Dokümantasyon ve son kontrol | Bekliyor | [faz-05-dokuman-ve-son-kontrol.md](faz-05-dokuman-ve-son-kontrol.md) |

## Çalışma kuralları
Expand Down
36 changes: 25 additions & 11 deletions plans/faz-04-lfs.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Faz 4 — Git LFS

**Durum:** Bekliyor
**Durum:** Tamamlandı

## Amaç

Expand All @@ -13,7 +13,7 @@ LFS kullanan repoların LFS nesneleri de yedeklenir: her yeni snapshot'ın yanı
## Kapsam

- Ön koşul: LFS açıksa (`lfs: true`, varsayılan) `git lfs version` başarısızsa çıkış 2 ve mesaj: git-lfs kurulum linki (https://git-lfs.com) + "or rerun with --no-lfs to skip LFS objects".
- Mirror sonrası `git lfs fetch --all origin` (kaynağın `gitEnv()`'i ile).
- Yeni snapshot gerektiğinde (bundle sonrası) `git lfs fetch --all origin` (kaynağın `gitEnv()`'i ile); wiki birimlerinde LFS yoktur.
- Snapshot: mirror'daki `lfs/objects` dizini boş değilse `tar` paketiyle `lfs/objects/...` yolunu koruyan `<stamp>.lfs.tar`; manifest'e `lfsTarBytes`.
- Ref'ler değişmediyse LFS ayrıca kontrol edilmez (LFS nesneleri ref'lere bağlıdır).
- `--no-lfs` / `lfs: false` / `GHBACKUP_LFS=false`: LFS adımları atlanır, ön koşul kontrol edilmez.
Expand All @@ -25,17 +25,17 @@ LFS kullanan repoların LFS nesneleri de yedeklenir: her yeni snapshot'ın yanı

## Görevler

- [ ] Doğrulama: `git lfs fetch --all` komutunun bare repoda çalıştığını ve nesneleri `<gitdir>/lfs/objects` altına yazdığını resmi kaynaktan veya deneyle doğrula; farklıysa mimari.md ve bu fazı güncelle.
- [ ] Doğrulama: yerel test için LFS sunucusu gerektirmeyen bir senaryo bul (ör. `lfs.url` ile `file://` LFS deposu, git-lfs ≥ 3 standalone file transfer); yoksa entegrasyon testini atlanabilir yap ve elle doğrulamaya bırak.
- [ ] LFS ön koşulu ve `--no-lfs` + birim testleri.
- [ ] Mirror'a `git lfs fetch --all`.
- [ ] `lfs.tar` üretimi, iki hedefe yükleme, saklamada birlikte silme.
- [ ] Entegrasyon testi (mümkünse): LFS dosyalı repo → snapshot → geri yükleme → dosya içeriği doğru.
- [ ] CI'da `git-lfs` kurulu olduğundan emin ol (ubuntu runner'da yoksa `apt-get install git-lfs`).
- [x] Doğrulama: `git lfs fetch --all` komutunun bare repoda çalıştığını ve nesneleri `<gitdir>/lfs/objects` altına yazdığını resmi kaynaktan veya deneyle doğrula; farklıysa mimari.md ve bu fazı güncelle.
- [x] Doğrulama: yerel test için LFS sunucusu gerektirmeyen bir senaryo bul (ör. `lfs.url` ile `file://` LFS deposu, git-lfs ≥ 3 standalone file transfer); yoksa entegrasyon testini atlanabilir yap ve elle doğrulamaya bırak.
- [x] LFS ön koşulu ve `--no-lfs` + birim testleri.
- [x] Mirror'a `git lfs fetch --all`.
- [x] `lfs.tar` üretimi, iki hedefe yükleme, saklamada birlikte silme.
- [x] Entegrasyon testi (mümkünse): LFS dosyalı repo → snapshot → geri yükleme → dosya içeriği doğru.
- [x] CI'da `git-lfs` kurulu olduğundan emin ol (ubuntu runner'da yoksa `apt-get install git-lfs`).

## 👤 Kullanıcı aksiyonları

- [ ] 💻 LFS kullanan bir repo varsa kendi makinende yedekleyip README'deki geri yükleme adımlarıyla bir LFS dosyasını açarak doğrula.
- [ ] 💻 LFS kullanan gerçek bir GitHub reposunu kendi makinende yedekle (`github-backup backup --local ~/git-backup-test --include '<owner>/<repo>'`) ve aşağıdaki geri yükleme adımlarıyla bir LFS dosyasını aç, sha256'sını orijinalle karşılaştır: gerçek `gh` oturumu ve gerçek bir LFS sunucusu gerekir, bulutta yok.

## Kabul kriterleri

Expand All @@ -49,4 +49,18 @@ LFS kullanan repoların LFS nesneleri de yedeklenir: her yeni snapshot'ın yanı

## Devam notları

_Henüz başlanmadı._ Faz 2'den devralınanlar için [faz-02-mirror-ve-local.md](faz-02-mirror-ve-local.md) **Devam notları**'na bak: LFS adımının `core/run.ts` içinde takılacağı yer, `lfsTar`/`lfsTarBytes` için hazır alanlar ve ön koşulun `commands/backup.ts`'teki yeri.
Faz tamamlandı; kalan tek iş yukarıdaki 💻 maddesi (gerçek GitHub LFS reposuyla elle doğrulama).

Yapılan: `src/core/lfs.ts` (`fetchLfs`, `hasLfsObjects`, `createLfsTar`), `core/run.ts` (`BackupInput.lfs`, LFS yalnızca yeni snapshot gerektiğinde ve `kind === "repo"` için), `commands/backup.ts` (`checkLfs`, `git lfs version` başarısızsa çıkış 2), `test/integration/lfs.test.ts` (sunucusuz düzen: `lfs.url=file://<bare repo>` + `lfs.standalonetransferagent=lfs-standalone-file`), CI'da `git-lfs` kontrolü.

Doğrulananlar: `git lfs fetch --all origin` bare mirror'da çalışır ve nesneleri `<mirror>/lfs/objects` altına yazar; LFS'siz repoda çıkış 0 verir ve `lfs/` boş kalır (bu yüzden `hasLfsObjects` dizinin değil dosyanın varlığına bakar).

Faz 5 README'si için geri yükleme adımları (entegrasyon testindekiyle aynı):

```bash
git clone <stamp>.bundle restored
tar -xf <stamp>.lfs.tar -C restored/.git # lfs/objects/... klonun .git/lfs/objects altına açılır
cd restored && git lfs install --local && git lfs checkout
```

`.lfs.tar` yalnızca repoda LFS nesnesi varsa vardır; wiki'lerde hiç yoktur. Test düzenini kuran yardımcılar `test/helpers/git.ts` içinde: `enableLfs`, `hasGitLfs`.
2 changes: 1 addition & 1 deletion plans/faz-05-dokuman-ve-son-kontrol.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Kullanıcı README'yi okuyarak aracı kurup, yapılandırıp, cron ile zamanlay

## Ön koşullar

- Faz 4 tamamlandı.
- Faz 4 tamamlandı. Geri yükleme (LFS dahil) adımları [faz-04-lfs.md](faz-04-lfs.md) **Devam notları**'nda.

## Kapsam

Expand Down
1 change: 1 addition & 0 deletions plans/kararlar.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ Projenin güncel kararları. Bir karar değişirse satırı güncellenir ve tari
| K-034 | 2026-10-14 | Wiki ayrı birimdir (`<repo>.wiki` anahtarı, kendi mirror'ı ve snapshot'ları). Wiki'de "repository not found" `skipped: no wiki`; ref'siz mirror `skipped: empty`. | Wiki açık ama sayfasız repolar hata üretmesin; wiki yedeği ana repodan bağımsız saklanır. |
| K-035 | 2026-10-14 | S3 istemcisi `requestChecksumCalculation` ve `responseChecksumValidation` için `WHEN_REQUIRED` kullanır. | AWS SDK JS v3'ün varsayılan CRC32 sağlama toplamı R2, B2 gibi üçüncü parti servislerde imza/başlık hatası verebilir; yalnızca API'nin zorunlu kıldığı yerde (ör. `DeleteObjects`) eklenir. |
| K-036 | 2026-10-14 | `Target` arayüzünde isteğe bağlı `checkAccess()` vardır; `backup` her hedef için kilitten önce çağırır ve hata çıkış 2 verir (`s3`: `HeadBucket`). Yarım multipart temizliği en iyi çaba esaslıdır. | Yanlış kimlik/bucket ilk repoda değil başta anlaşılsın; multipart listelemeyi desteklemeyen servisler saklama adımını bozmasın. |
| K-037 | 2026-10-14 | LFS yalnızca yeni snapshot gerektiğinde (ref özeti hedeflerden birinde değiştiğinde) ve yalnızca repo birimleri için işlenir: bundle sonrası `git lfs fetch --all origin`, `lfs/objects` altında dosya varsa `snapshot.lfs.tar` (`tar` paketiyle) ve manifest'e `lfsTarBytes`. Wiki'lerde LFS yoktur. | LFS nesneleri ref'lere bağlıdır; ref'ler değişmediyse ağa gitmek gereksizdir. Snapshot yalnızca tüm adımlar başarılıysa yazıldığı için eksik LFS'li "unchanged" oluşmaz. |
Loading
Loading