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 diff --git a/main.ts b/main.ts index bb7cce4..427951e 100644 --- a/main.ts +++ b/main.ts @@ -593,13 +593,63 @@ 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); + } + + // 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(); + } } async saveSettings() { // Preserve syncState when saving settings const data = (await this.loadData() || {}) as PersistedPluginData; - await this.saveData({ + + // Persist main token to SecretStorage + if (this.settings.auth.token) { + this.app.secretStorage.setSecret('github-pat', this.settings.auth.token); + } + + // 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({ + ...settingsToSave, syncState: data.syncState, additionalRepoStates: data.additionalRepoStates, }); diff --git a/manifest.json b/manifest.json index 5f165ac..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.7.2", + "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/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 1d0b3f5..0374587 100644 --- a/tests/mocks/obsidian.ts +++ b/tests/mocks/obsidian.ts @@ -1,8 +1,21 @@ // 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(); + 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 { 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" }