Skip to content

Latest commit

ย 

History

History
91 lines (77 loc) ยท 7.69 KB

File metadata and controls

91 lines (77 loc) ยท 7.69 KB

์•„ํ‚คํ…์ฒ˜

RepositoryMonitor๋Š” ๋‹จ์ผ Tauri 2 ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ Rust ๋ฐฑ์—”๋“œ(์ €์žฅ์†Œ ๋ฐœ๊ฒฌยท์ƒํƒœ ์ฝ๊ธฐยท์ง‘๊ณ„)์™€ Svelte 5 ํ”„๋ก ํŠธ์—”๋“œ(๋ Œ๋”)๊ฐ€ IPC๋กœ ์—ฐ๊ฒฐ๋œ ๊ตฌ์กฐ๋‹ค. ๋ฐ์ดํ„ฐ๋Š” ๋ฐฑ์—”๋“œ โ†’ ํ”„๋ก ํŠธ ๋‹จ๋ฐฉํ–ฅ push(repos_updated ์ด๋ฒคํŠธ), ์ œ์–ด๋Š” ํ”„๋ก ํŠธ โ†’ ๋ฐฑ์—”๋“œ command invoke๋กœ๋งŒ ํ๋ฅธ๋‹ค.

์„ค๊ณ„ ์›์น™

  • ๋กœ์ปฌ ์ „์šฉ / ๋„คํŠธ์›Œํฌ 0 โ€” git status ยท svn status ๋“ฑ read-only ๋กœ์ปฌ ๋ช…๋ น๋งŒ. fetch/pull/push ์—†์Œ.
  • VCS ์ถ”์ƒํ™” โ€” VcsKind { Git, Svn }๋กœ git/svn์„ ํ†ต์ผ๋œ RepoStatus๋กœ ํ‘œํ˜„. reader๋งŒ ๋ถ„๊ธฐ.
  • ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„ โ€” ํ”„๋ก ํŠธ์—” git ๋กœ์ง์ด ์—†๋‹ค. ๋ฐฑ์—”๋“œ๊ฐ€ ์ง„์‹ค์˜ ์›์ฒœ(์Šค๋ƒ…์ƒท)์„ push.
  • ์ˆœ์ˆ˜ ๋กœ์ง ๋ถ„๋ฆฌ โ€” ํ‘œ์‹œ ๊ทœ์น™(clean ์ˆ ์–ดยท์ •๋ ฌยทํ•„ํ„ฐยท๊ทธ๋ฃน)๊ณผ ํŒŒ์‹ฑ(porcelain v2ยทsvn status)์„ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ.

๋””๋ ‰ํ† ๋ฆฌ

RepositoryMonitor/
โ”œโ”€โ”€ src/                       # Svelte 5 ํ”„๋ก ํŠธ
โ”‚   โ”œโ”€โ”€ App.svelte             # ์ง„์ž…์ : store ์ดˆ๊ธฐํ™”, EmptyState/Grid ๋ถ„๊ธฐ, ์ปจํ…์ŠคํŠธ ๋ฉ”๋‰ด
โ”‚   โ”œโ”€โ”€ lib/
โ”‚   โ”‚   โ”œโ”€โ”€ types.ts           # Rust ํƒ€์ž… ๋ฏธ๋Ÿฌ(snake_case)
โ”‚   โ”‚   โ”œโ”€โ”€ tauri.ts           # invoke/listen ๋ž˜ํผ (๋ฐฑ์—”๋“œ ์˜์กด ๋‹จ์ผ ์ง€์ )
โ”‚   โ”‚   โ”œโ”€โ”€ logic.ts           # ์ˆœ์ˆ˜ ํ‘œ์‹œ ๋กœ์ง (clean/rank/filter/format/group)
โ”‚   โ”‚   โ”œโ”€โ”€ store.svelte.ts    # reactive store (repos_updated ๊ตฌ๋…, seq ํ๊ธฐ)
โ”‚   โ”‚   โ””โ”€โ”€ theme.svelte.ts    # ๋‹คํฌ/๋ผ์ดํŠธ (system/light/dark, localStorage)
โ”‚   โ”œโ”€โ”€ components/            # RepoCard ยท Grid ยท Header ยท Settings ยท EmptyState
โ”‚   โ””โ”€โ”€ app.css                # CSS ๋ณ€์ˆ˜ ํŒ”๋ ˆํŠธ (๋ผ์ดํŠธ/๋‹คํฌ)
โ”œโ”€โ”€ src-tauri/                 # Rust ๋ฐฑ์—”๋“œ + Tauri ์…ธ
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ main.rs            # repositorymonitor::run() ํ˜ธ์ถœ
โ”‚   โ”‚   โ”œโ”€โ”€ lib.rs             # ๋ชจ๋“ˆ ์„ ์–ธ + run() (Builder ๋ฐฐ์„ )
โ”‚   โ”‚   โ”œโ”€โ”€ model.rs           # VcsKind ยท RepoRef ยท RepoStatus ยท RepoState ยท ActionKind ยท RepoSnapshot
โ”‚   โ”‚   โ”œโ”€โ”€ config.rs          # Config ยท TerminalApp ยท ๋กœ๋“œ/์ €์žฅ(๋ฒ„์ €๋‹ยท๋ฐฑ์—…)
โ”‚   โ”‚   โ”œโ”€โ”€ discovery.rs       # ๋ฃจํŠธ ์Šค์บ” ยท ์ œ์™ธ ๊ธ€๋กญ ยท ์นดํ…Œ๊ณ ๋ฆฌ ยท .git/.svn ํŒ์ •
โ”‚   โ”‚   โ”œโ”€โ”€ git_reader.rs      # porcelain v2 ํŒŒ์„œ + stash/state/worktree/fetch
โ”‚   โ”‚   โ”œโ”€โ”€ svn_reader.rs      # svn status/info ํŒŒ์„œ (๋กœ์ปฌ ์ „์šฉ)
โ”‚   โ”‚   โ”œโ”€โ”€ batch.rs           # ๋น„๋™๊ธฐ ์ƒํƒœ ๋ฐฐ์น˜ (semaphore + timeout, vcs ๋””์ŠคํŒจ์น˜)
โ”‚   โ”‚   โ”œโ”€โ”€ scheduler.rs       # ํด๋ง ํŒ๋‹จ (should_run_poll)
โ”‚   โ”‚   โ”œโ”€โ”€ emit_gate.rs       # ์Šค๋ƒ…์ƒท ๋ณ€๊ฒฝ ๊ฐ์ง€ (should_emit)
โ”‚   โ”‚   โ”œโ”€โ”€ snapshot.rs        # ์‹คํŒจ repo ์ง์ „๊ฐ’ ๋จธ์ง€
โ”‚   โ”‚   โ”œโ”€โ”€ app_state.rs       # AppState (.manage)
โ”‚   โ”‚   โ””โ”€โ”€ commands.rs        # IPC ์ปค๋งจ๋“œ 5์ข… + do_scan/do_refresh
โ”‚   โ”œโ”€โ”€ tests/                 # ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ (discovery/git_reader/batch/svn)
โ”‚   โ”œโ”€โ”€ capabilities/          # Tauri ๊ถŒํ•œ (window/event/dialog)
โ”‚   โ””โ”€โ”€ tauri.conf.json        # ๋‹จ์ผ main ์œˆ๋„์šฐ, identifier com.dgitx.repositorymonitor
โ””โ”€โ”€ docs/                      # ๋ณธ ๋ฌธ์„œ๋“ค

๋ฐฑ์—”๋“œ ๋ชจ๋“ˆ ์ฑ…์ž„ (Rust)

๋ชจ๋“ˆ ์ฑ…์ž„
config Config(rootsยทmanual_pathsยทexclude_globsยทpoll_interval_secsยทscan_depthยทstale_fetch_daysยทterminal_app) ๋กœ๋“œ/์ €์žฅ. ๊ฒฝ๋กœ๋Š” dirs_next::config_dir()/RepositoryMonitor/config.json. forward-compat ์—ญ์ง๋ ฌํ™”(#[serde(default)]), ์†์ƒ ์‹œ .bak ๋ฐฑ์—… ํ›„ ๊ธฐ๋ณธ๊ฐ’ ์žฌ์ƒ์„ฑ
discovery ๋ฃจํŠธ๋ฅผ walk(๊นŠ์ด ์ œํ•œยทnode_modules/target/Pods/.build/.git/.svn pruneยท์‹ฌ๋งํฌ ๋ฏธ์ถ”์ )ํ•˜๋ฉฐ .git(๋””๋ ‰ํ† ๋ฆฌ)=git, .svn=svn ์œผ๋กœ RepoRef{path,name,category,vcs} ์‚ฐ์ถœ. ์ œ์™ธ ๊ธ€๋กญ์€ repo ์ ˆ๋Œ€๊ฒฝ๋กœ์— globset(globstarยท๋Œ€์†Œ๋ฌธ์ž๋ฌด์‹œ) ๋งค์นญ. ์นดํ…Œ๊ณ ๋ฆฌ=๋ฃจํŠธ ๊ธฐ์ค€ ์ƒ๋Œ€๊ฒฝ๋กœ ์ฒซ ์„ธ๊ทธ๋จผํŠธ
git_reader git status --porcelain=v2 --branch ๋‹จ์ผ ํŒŒ์‹ฑ(๋ธŒ๋žœ์น˜ยทupstreamยทahead/behindยทXY ์ฝ”๋“œ). ๋ณด์กฐ: stash(stash list ์ค„ ์ˆ˜), state ๋งˆ์ปค(rev-parse --git-path), worktree(worktree list), last_fetch(FETCH_HEAD mtime)
svn_reader svn status(๋กœ์ปฌ) ํŒŒ์‹ฑ(M/A/D/R/!/~โ†’modified, ?โ†’untracked, Cโ†’conflict) + svn info --show-item relative-url๋กœ ๋ธŒ๋žœ์น˜ ๋„์ถœ. ahead/behind/staged/stash ์—†์Œ
batch RepoRef[]๋ฅผ tokio ๋ธ”๋กœํ‚น ํƒœ์Šคํฌ๋กœ ๋ณ‘๋ ฌ ์‹คํ–‰(Semaphore ๋™์‹œ ์ƒํ•œ 8, repo๋‹น 5์ดˆ timeout). repo.vcs๋กœ git/svn reader ๋””์ŠคํŒจ์น˜. ํƒ€์ž„์•„์›ƒ/์‹คํŒจ๋Š” error RepoStatus
scheduler should_run_poll(polling_active, in_flight) โ€” ์ฐฝ ํฌ์ปค์Šค ์ค‘์ด๊ณ  ์ง„ํ–‰ ๋ฐฐ์น˜๊ฐ€ ์—†์„ ๋•Œ๋งŒ ํด๋ง. ์‹ค์ œ ๋ฃจํ”„๋Š” lib.rs setup()์ด WindowEvent::Focused๋กœ polling_active๋ฅผ ํ† ๊ธ€ํ•˜๋ฉฐ ๊ตฌ๋™
emit_gate should_emit(prev, next) โ€” last_checked๋ฅผ ์ œ์™ธํ•œ ์˜๋ฏธ ๋น„๊ต๋กœ ๋ฌด์˜๋ฏธ emit ์ฐจ๋‹จ
snapshot merge_failed_with_previous โ€” ์ผ์‹œ ์‹คํŒจ repo๋Š” ์ง์ „ ์Šค๋ƒ…์ƒท ์ˆ˜์น˜๋ฅผ ์œ ์ง€ํ•˜๊ณ  error/last_checked๋งŒ ๊ฐฑ์‹ 
app_state AppState{ config, repos, last_snapshot, polling_active, in_flight, seq } โ€” .manage(Arc<AppState>)๋กœ ๊ณต์œ 
commands IPC 5์ข… + do_scan/do_refresh(ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•˜๋„๋ก emit์„ ์ฝœ๋ฐฑ์œผ๋กœ ๋ถ„๋ฆฌํ•œ do_refresh_inner)

๋™์‹œ์„ฑยท๊ฐฑ์‹  ๊ทœ์•ฝ

  • ๋‹จ์ผ in-flight โ€” do_refresh๋Š” in_flight๋ฅผ swap(true)๋กœ ํš๋“(์ด๋ฏธ ์ง„ํ–‰ ์ค‘์ด๋ฉด coalesce). RAII ๊ฐ€๋“œ(Drop)๋กœ ์–ด๋–ค ๊ฒฝ๋กœ๋กœ ๋น ์ ธ๋„ false ๋ณต์› โ†’ "in_flight ์˜๊ตฌ true" ๋ฐ๋“œ๋ฝ ๋ฐฉ์ง€.
  • ํฌ์ปค์Šค ๊ฒŒ์ดํŒ… โ€” WindowEvent::Focused(true) โ†’ ํด๋ง ON + ์ฆ‰์‹œ 1ํšŒ, Focused(false) โ†’ OFF. ์ฃผ๊ธฐ๋Š” poll_interval_secs(clamp 10โ€“300).
  • seq โ€” emit๋งˆ๋‹ค seq ์ฆ๊ฐ€. ํ”„๋ก ํŠธ๋Š” seq < lastSeq์ธ ์˜ค๋ž˜๋œ ์Šค๋ƒ…์ƒท์„ ํ๊ธฐ.

ํ”„๋ก ํŠธ ๋ชจ๋“ˆ (Svelte 5)

ํŒŒ์ผ ์ฑ…์ž„
lib/types.ts Rust serde ์ง๋ ฌํ™”์™€ 1:1 ๋ฏธ๋Ÿฌ(snake_case ํ•„๋“œ, enum snake_case). ์ด๋ฒคํŠธ payload๋Š” camelCase ์ž๋™๋ณ€ํ™˜์ด ์—†์œผ๋ฏ€๋กœ snake_case ์œ ์ง€
lib/tauri.ts invoke/listen ๋ž˜ํผ โ€” ๋ฐฑ์—”๋“œ ์˜์กด ๋‹จ์ผ ์ง€์ . ์ปค๋งจ๋“œ ์ธ์ž๋Š” Tauri๊ฐ€ camelCase ๋ณ€ํ™˜(repo_pathโ†’repoPath)
lib/logic.ts isClean ๋‹จ์ผ ์ˆ ์–ด, rank+compareRepos(์ •๋ ฌ), filterProblems, formatFetched/isStale, groupByCategory, summarize โ€” ์ „๋ถ€ ์ˆœ์ˆ˜, vitest ์ปค๋ฒ„
lib/store.svelte.ts $state repos/config/lastSeq, init(๊ตฌ๋…ยทconfig ๋กœ๋“œ)/dispose, refresh/rescan/saveConfig/excludeRepo
lib/theme.svelte.ts system/light/dark ์„ ํƒ์„ localStorage ์˜์† + <html data-theme> ์ ์šฉ
components/RepoCard ยง7 ์‹ ํ˜ธ ๋ Œ๋” + ํ˜ธ๋ฒ„ ์•ก์…˜ + ์šฐํด๋ฆญ(onContext) + clean/dirty/conflict ๊ฐ•์กฐ
components/Grid filterProblems โ†’ ๊ฒ€์ƒ‰ โ†’ groupByCategory(๋‚ด๋ถ€ compareRepos ์ •๋ ฌ)
components/Header ๊ฒ€์ƒ‰ยท๋ฌธ์ œ๋งŒยทํ…Œ๋งˆํ† ๊ธ€ยท์ƒˆ๋กœ๊ณ ์นจยท์„ค์ •ยท์š”์•ฝ
components/Settings ๋ฃจํŠธ(dialog ํด๋”์„ ํƒ)ยท์ œ์™ธ๊ธ€๋กญยท์ฃผ๊ธฐยท๊นŠ์ดยทstaleยทํ„ฐ๋ฏธ๋„ ์•ฑ
components/EmptyState ๋ฃจํŠธ 0๊ฐœ first-run CTA

๋งˆ์ผ์Šคํ†ค ์ด๋ ฅ

  • M1 ๋ฐฑ์—”๋“œ ์ฝ”์–ด(config/discovery/git_reader)
  • M2 Tauri ํ†ตํ•ฉ(AppStateยทIPCยท์ด๋ฒคํŠธยท์Šค์ผ€์ค„๋Ÿฌยท๋ฐฐ์น˜ยท์•ก์…˜)
  • M3 Svelte 5 ํ”„๋ก ํŠธ(๊ทธ๋ฆฌ๋“œ UI)
  • M4 SVN ์ถ”์ (VcsKind ์ถ”์ƒํ™”ยทsvn_reader)
  • ์ดํ›„ UI ๋ฐ˜๋ณต: ๋‹คํฌ/๋ผ์ดํŠธ, ๋น„ํด๋ฆฐ ๊ฐ•์กฐ, ์šฐํด๋ฆญ ์ปจํ…์ŠคํŠธ ๋ฉ”๋‰ด, ๋ฆฌ์ŠคํŠธ ๊ฑฐํ„ฐ

๋ณธ ํ”„๋กœ์ ํŠธ๋Š” M1~M4 ๊ธฐ๊ฐ„ ๋™์•ˆ GitMonitor๋ผ๋Š” ์ด๋ฆ„์ด์—ˆ๊ณ , SVN ์ถ”๊ฐ€ ์ดํ›„ RepositoryMonitor๋กœ ๊ฐœ๋ช…๋˜์—ˆ๋‹ค. docs/superpowers/์˜ ์„ค๊ณ„์„œยท๊ณ„ํš์„œ๋Š” ๊ทธ ์‹œ์ ์˜ ๊ธฐ๋ก์ด๋ผ ์˜› ์ด๋ฆ„์„ ๋ณด์กดํ•œ๋‹ค.