From 90dfcbae50e656dfbdc28290679399863d831c0b Mon Sep 17 00:00:00 2001 From: Mark Rhoades-Brown Date: Fri, 29 May 2026 23:49:14 +0100 Subject: [PATCH 1/5] feat: migrate main PAT to SecretStorage for encrypted storage --- main.ts | 26 +++++++++++++++++++++++++- manifest.json | 2 +- tests/mocks/obsidian.ts | 8 ++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/main.ts b/main.ts index bb7cce4..326df57 100644 --- a/main.ts +++ b/main.ts @@ -593,13 +593,37 @@ export default class GitHubOctokitPlugin extends Plugin { void _ignored; // intentionally unused - just extracting syncState from data void _ignored2; this.settings = Object.assign({}, DEFAULT_SETTINGS, settingsData); + + // Migrate main token from plaintext data.json to SecretStorage + const storedSecret = this.app.secretStorage.getSecret('github-pat'); + if (storedSecret) { + // Token already in SecretStorage — use it + this.settings.auth.token = storedSecret; + } else if (this.settings.auth.token) { + // Legacy: token still in data.json — migrate to SecretStorage + this.app.secretStorage.setSecret('github-pat', this.settings.auth.token); + // Clear from data.json on next save + await this.saveSettings(); + } } async saveSettings() { // Preserve syncState when saving settings const data = (await this.loadData() || {}) as PersistedPluginData; - await this.saveData({ + + // Persist token to SecretStorage, not data.json + if (this.settings.auth.token) { + this.app.secretStorage.setSecret('github-pat', this.settings.auth.token); + } + + // Exclude token from persisted settings + const settingsToSave = { ...this.settings, + auth: { ...this.settings.auth, token: '' }, + }; + + await this.saveData({ + ...settingsToSave, syncState: data.syncState, additionalRepoStates: data.additionalRepoStates, }); diff --git a/manifest.json b/manifest.json index 5f165ac..607ac4f 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "id": "github-octokit", "name": "GitHub Octokit Sync", "version": "0.4.1", - "minAppVersion": "1.7.2", + "minAppVersion": "1.11.4", "description": "Sync your vault with GitHub using the Octokit API.", "author": "M Rhoades-Brown", "authorUrl": "https://github.com/rhoades-brown", diff --git a/tests/mocks/obsidian.ts b/tests/mocks/obsidian.ts index 1d0b3f5..a891fad 100644 --- a/tests/mocks/obsidian.ts +++ b/tests/mocks/obsidian.ts @@ -1,8 +1,16 @@ // Mock Obsidian module for testing +export class SecretStorage { + private secrets: Map = new Map(); + setSecret(id: string, secret: string): void { this.secrets.set(id, secret); } + getSecret(id: string): string | null { return this.secrets.get(id) ?? null; } + listSecrets(): string[] { return Array.from(this.secrets.keys()); } +} + export class App { vault = new Vault(); workspace = new Workspace(); + secretStorage = new SecretStorage(); } export class Vault { From 955c85fa3f6486d01adf00d603ac38de9d2d6263 Mon Sep 17 00:00:00 2001 From: Mark Rhoades-Brown Date: Fri, 29 May 2026 23:50:30 +0100 Subject: [PATCH 2/5] feat: migrate additional repo tokens to SecretStorage --- main.ts | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/main.ts b/main.ts index 326df57..427951e 100644 --- a/main.ts +++ b/main.ts @@ -602,7 +602,24 @@ export default class GitHubOctokitPlugin extends Plugin { } else if (this.settings.auth.token) { // Legacy: token still in data.json — migrate to SecretStorage this.app.secretStorage.setSecret('github-pat', this.settings.auth.token); - // Clear from data.json on next save + } + + // Migrate additional repo tokens to SecretStorage + let needsSave = false; + for (const repo of this.settings.additionalRepos) { + if (repo.useMainToken) continue; + const secretKey = `github-pat-${repo.id}`; + const repoSecret = this.app.secretStorage.getSecret(secretKey); + if (repoSecret) { + repo.token = repoSecret; + } else if (repo.token) { + this.app.secretStorage.setSecret(secretKey, repo.token); + needsSave = true; + } + } + + // Clear legacy tokens from data.json if any were migrated + if (needsSave || (!storedSecret && this.settings.auth.token)) { await this.saveSettings(); } } @@ -611,15 +628,24 @@ export default class GitHubOctokitPlugin extends Plugin { // Preserve syncState when saving settings const data = (await this.loadData() || {}) as PersistedPluginData; - // Persist token to SecretStorage, not data.json + // Persist main token to SecretStorage if (this.settings.auth.token) { this.app.secretStorage.setSecret('github-pat', this.settings.auth.token); } - // Exclude token from persisted settings + // Persist additional repo tokens to SecretStorage + const cleanedRepos = this.settings.additionalRepos.map(repo => { + if (!repo.useMainToken && repo.token) { + this.app.secretStorage.setSecret(`github-pat-${repo.id}`, repo.token); + } + return { ...repo, token: '' }; + }); + + // Exclude all tokens from persisted settings const settingsToSave = { ...this.settings, auth: { ...this.settings.auth, token: '' }, + additionalRepos: cleanedRepos, }; await this.saveData({ From 9130c003bd546a1616f87e2308b1a3ff5ba82a07 Mon Sep 17 00:00:00 2001 From: Mark Rhoades-Brown Date: Fri, 29 May 2026 23:52:19 +0100 Subject: [PATCH 3/5] feat: persist SyncView panel state with loadLocalStorage --- src/views/SyncView.ts | 25 +++++++++++++++++++++++++ tests/mocks/obsidian.ts | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/src/views/SyncView.ts b/src/views/SyncView.ts index ef27770..5b56c6a 100644 --- a/src/views/SyncView.ts +++ b/src/views/SyncView.ts @@ -28,6 +28,29 @@ export class SyncView extends ItemView { constructor(leaf: WorkspaceLeaf, plugin: GitHubOctokitPlugin) { super(leaf); this.plugin = plugin; + this.loadViewState(); + } + + /** Restore persisted UI state from vault-specific localStorage */ + private loadViewState(): void { + const state = this.plugin.app.loadLocalStorage('github-octokit-sync-view') as string | null; + if (state) { + try { + const parsed = JSON.parse(state) as { commitsExpanded?: boolean; logsExpanded?: boolean }; + if (typeof parsed.commitsExpanded === 'boolean') this.commitsExpanded = parsed.commitsExpanded; + if (typeof parsed.logsExpanded === 'boolean') this.logsExpanded = parsed.logsExpanded; + } catch { + // Ignore corrupt state + } + } + } + + /** Persist UI state to vault-specific localStorage */ + private saveViewState(): void { + this.plugin.app.saveLocalStorage('github-octokit-sync-view', JSON.stringify({ + commitsExpanded: this.commitsExpanded, + logsExpanded: this.logsExpanded, + })); } getViewType(): string { @@ -412,6 +435,7 @@ export class SyncView extends ItemView { header.addEventListener('click', () => { this.commitsExpanded = !this.commitsExpanded; + this.saveViewState(); void this.render(); }); @@ -476,6 +500,7 @@ export class SyncView extends ItemView { // Don't toggle if clicking on controls if ((e.target as HTMLElement).closest('.logs-controls')) return; this.logsExpanded = !this.logsExpanded; + this.saveViewState(); void this.render(); }); diff --git a/tests/mocks/obsidian.ts b/tests/mocks/obsidian.ts index a891fad..0374587 100644 --- a/tests/mocks/obsidian.ts +++ b/tests/mocks/obsidian.ts @@ -11,6 +11,11 @@ export class App { vault = new Vault(); workspace = new Workspace(); secretStorage = new SecretStorage(); + private localStorage: Map = new Map(); + loadLocalStorage(key: string): string | null { return this.localStorage.get(key) ?? null; } + saveLocalStorage(key: string, data: unknown | null): void { + if (data === null) { this.localStorage.delete(key); } else { this.localStorage.set(key, String(data)); } + } } export class Vault { From 2cbf1eb8a28bc2876aa4932d96ae2a7eb407680e Mon Sep 17 00:00:00 2001 From: Mark Rhoades-Brown Date: Fri, 29 May 2026 23:53:07 +0100 Subject: [PATCH 4/5] chore: bump minAppVersion to 1.12.7 --- manifest.json | 2 +- versions.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index 607ac4f..3db4d6b 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "id": "github-octokit", "name": "GitHub Octokit Sync", "version": "0.4.1", - "minAppVersion": "1.11.4", + "minAppVersion": "1.12.7", "description": "Sync your vault with GitHub using the Octokit API.", "author": "M Rhoades-Brown", "authorUrl": "https://github.com/rhoades-brown", diff --git a/versions.json b/versions.json index c4636bb..1387841 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,6 @@ { "0.1.0": "0.15.0", "0.3.2": "1.7.2", - "0.4.1": "1.7.2" + "0.4.1": "1.7.2", + "0.5.0": "1.12.7" } From b22390781c58e17e43164f7bb83d43fa795c8cb7 Mon Sep 17 00:00:00 2001 From: Mark Rhoades-Brown Date: Fri, 29 May 2026 23:57:07 +0100 Subject: [PATCH 5/5] ci: upload test artifacts for non-draft PRs --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9486ab6..cdff0aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,17 @@ jobs: - name: Run tests run: npm test + - name: Upload test artifacts + if: github.event_name == 'pull_request' && github.event.pull_request.draft == false + uses: actions/upload-artifact@v4 + with: + name: plugin-build-${{ github.event.pull_request.number }} + path: | + main.js + manifest.json + styles.css + retention-days: 14 + # ------------------------------------------------------------------ # Release — only on push to main/master, after build passes. # Analyses conventional commits since the last tag, bumps the