feat(sync): repo profile writer + Sync command#8
Conversation
First real sync path. Fetches per-repo metadata + README and writes a
vault-native markdown file for each allowlisted entry.
Architecture
- VaultWriter interface (src/vault/writer.ts) abstracts vault I/O the
same way HttpFn abstracts HTTP. ObsidianVaultWriter is the default;
InMemoryVaultWriter backs unit tests.
- syncRepoProfile(owner, repo, options) in src/sync/repo-profile-writer.ts
validates name -> checks allowlist -> composes path via
joinInsideRoot -> fetches metadata -> optional README -> ensures
folders -> writes body -> sets frontmatter atomically via
updateFrontmatter.
- New "GitHub Data: Sync all repo profiles" command iterates the
allowlist, calls syncRepoProfile per entry, persists lastSyncedAt,
and reports ok/failed totals via Notice.
Security layering (all enforced before any network call)
- H3: allowlist check fails closed even if owner/repo pass validation.
- C1: joinInsideRoot + sanitizePathSegment -- malformed inputs cannot
escape 02_AREAS/GitHub/Repos. Containment check asserts final path
stays inside the root.
- C3: frontmatter set via updateFrontmatter (-> processFrontMatter on
the Obsidian side), never raw string concat into YAML.
- L3: README body fenced as a code block in v0.1. Defeats inline-HTML
/ Templater / Dataview execution against the synced content while
the full middle-ground body sanitizer is being built.
Data egress
- Plugin runtime now has three possible outbound calls, all user-
initiated: GET /user (Test connection), GET /repos/{o}/{r}, and
GET /repos/{o}/{r}/readme. docs/data-egress.md updated. Still no
background polls, scheduled syncs, or auto-fetches.
Tests (129 passing across 7 suites; +8 from this slice)
- src/sync/repo-profile-writer.test.ts covers: full-happy-path with
README, missing README (404) tolerated, allowlist reject, malformed-
owner reject before any API call, 403 mapping to structured error,
re-sync overwrite, case-insensitive allowlist match, folder ensure.
- src/main.test.ts updated: both ping + sync commands registered.
Bundle size 121k (up from 113k -- sync writer + path utils + allowlist).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR introduces a new repo profile synchronization feature with vault-writing abstractions. It adds VaultWriter interface with ObsidianVaultWriter and InMemoryVaultWriter implementations, a syncRepoProfile function that validates and syncs individual repos to vault, and a syncAllRepoProfiles command that batches syncs across allowlisted repos with progress/result reporting. Changes
Sequence DiagramsequenceDiagram
actor User
participant Plugin as GithubDataPlugin
participant RepoSync as syncRepoProfile
participant GitHub as GitHub API
participant VaultWriter as VaultWriter
participant Settings as Settings
User->>Plugin: Trigger "Sync all repo profiles" command
Plugin->>Settings: Get repoAllowlist & token
Plugin->>Plugin: Validate token & allowlist
loop For each allowlisted repo
Plugin->>RepoSync: syncRepoProfile(owner, repo)
RepoSync->>RepoSync: Validate allowlist membership
RepoSync->>RepoSync: Validate owner/repo names
RepoSync->>GitHub: GET /repos/{owner}/{repo}
GitHub-->>RepoSync: Repo metadata
RepoSync->>GitHub: GET /repos/{owner}/{repo}/readme
alt README exists
GitHub-->>RepoSync: README content
else README missing
GitHub-->>RepoSync: 404
RepoSync->>RepoSync: Continue with empty README
end
RepoSync->>VaultWriter: ensureFolder(path)
VaultWriter-->>RepoSync: Folder created
RepoSync->>VaultWriter: writeFile(path, markdown)
VaultWriter-->>RepoSync: File written
RepoSync->>VaultWriter: updateFrontmatter(path, mutate)
VaultWriter-->>RepoSync: Frontmatter updated
RepoSync-->>Plugin: SyncResult { ok, path, syncedAt }
Plugin->>Settings: Update lastSyncedAt for repo
end
Plugin->>Settings: Persist updated settings
Plugin->>User: Show aggregated success/failure Notice
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Summary
First real sync path. Six commits in, we can now actually pull data out of GitHub into the vault.
src/vault/writer.ts:VaultWriterinterface +ObsidianVaultWriter(defensive folder creation, atomic frontmatter viaprocessFrontMatter) +InMemoryVaultWriterfor tests.src/sync/repo-profile-writer.ts:syncRepoProfile(owner, repo, options)— fetches repo metadata, tries README (tolerates 404), composes a contained vault path, writes body + frontmatter atomically.GitHub Data: Sync all repo profilesiterates the allowlist, writes a profile file per entry, persistslastSyncedAt, reports ok/failed totals.joinInsideRoot— malformed owner/repo can't escape02_AREAS/GitHub/Repos/(Security Invariant C1).processFrontMatter— never raw string concat into YAML (Security Invariant C3).Output shape (per repo)
File at
02_AREAS/GitHub/Repos/{owner}__{repo}/00_{repo}.md:type,repo,owner,name,description,language,topics,visibility,stars,forks,open_issues_plus_prs,default_branch,license,homepage,html_url,created,pushed,last_synced,schema_version,tagsData egress update
Plugin runtime now has three possible outbound calls, all user-initiated:
GET /user— Test Connection buttonGET /repos/{o}/{r}— Sync commandGET /repos/{o}/{r}/readme— Sync commandNo background polls, no scheduled syncs, no auto-fetches.
docs/data-egress.mdupdated.Tests
129 passing across 7 suites (+8 from this slice). New: full happy path with README, missing README (404) tolerated, allowlist reject, malformed owner rejected before any API call, 403 mapping, re-sync overwrite, case-insensitive allowlist match, folder ensure ordering.
Out of scope
🤖 Generated with Claude Code