From ec8a367f79a322deb9e3494890a1779d1fa5217a Mon Sep 17 00:00:00 2001 From: Mara Date: Fri, 27 May 2022 22:21:19 +0200 Subject: [PATCH] feat: use PR + merge + delete branch instead of force push files --- mkdocsPublisher/githubInteraction/branch.ts | 81 ++++ mkdocsPublisher/githubInteraction/delete.ts | 139 ++++++ mkdocsPublisher/githubInteraction/getFiles.ts | 131 ++++++ mkdocsPublisher/githubInteraction/upload.ts | 208 +++++++++ mkdocsPublisher/main.ts | 100 ++-- mkdocsPublisher/utils/publication.ts | 435 ------------------ mkdocsPublisher/utils/utils.ts | 2 +- 7 files changed, 616 insertions(+), 480 deletions(-) create mode 100644 mkdocsPublisher/githubInteraction/branch.ts create mode 100644 mkdocsPublisher/githubInteraction/delete.ts create mode 100644 mkdocsPublisher/githubInteraction/getFiles.ts create mode 100644 mkdocsPublisher/githubInteraction/upload.ts delete mode 100644 mkdocsPublisher/utils/publication.ts diff --git a/mkdocsPublisher/githubInteraction/branch.ts b/mkdocsPublisher/githubInteraction/branch.ts new file mode 100644 index 00000000..37bf99a2 --- /dev/null +++ b/mkdocsPublisher/githubInteraction/branch.ts @@ -0,0 +1,81 @@ +import { Octokit } from "@octokit/core"; +import { MkdocsPublicationSettings } from "../settings/interface"; + +export class GithubBranch { + settings: MkdocsPublicationSettings; + octokit: Octokit; + + constructor(settings: MkdocsPublicationSettings, octokit: Octokit) { + this.settings = settings; + this.octokit = octokit; + } + + async newBranch(branchName: string) { + const allBranch = await this.octokit.request('GET' + ' /repos/{owner}/{repo}/branches', { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + }); + const mainBranch = allBranch.data.find((branch: { name: string; }) => branch.name === 'main' || branch.name === 'master'); + const shaMainBranch = mainBranch.commit.sha; + console.log(mainBranch) + const branch = await this.octokit.request( + "POST" + " /repos/{owner}/{repo}/git/refs", + { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + ref: "refs/heads/" + branchName, + sha: shaMainBranch, + } + ); + return branch.status === 201; + } + + async pullRequest(branchName: string) { + return await this.octokit.request('POST' + + ' /repos/{owner}/{repo}/pulls', { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + title: `PullRequest ${branchName} from Obsidian`, + body: "", + head: branchName, + base: "main", + }); + } + + async deleteBranch(branchName: string) { + const octokit = new Octokit({ + auth: this.settings.GhToken, + }); + const branch = await octokit.request( + "DELETE" + " /repos/{owner}/{repo}/git/refs/heads/" + branchName, + { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + } + ); + return branch.status === 200; + } + + + async mergePullRequest (branchName: string, silent = false, pullRequestNumber: number) { + const octokit = new Octokit({ + auth: this.settings.GhToken, + }); + const branch = await octokit.request( + "PUT" + " /repos/{owner}/{repo}/pulls/{pull_number}/merge", + { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + pull_number: pullRequestNumber, + state: "closed", + } + ); + return branch.status === 200; + } + async updateRepository(branchName: string) { + const pullRequest = await this.pullRequest(branchName); + // @ts-ignore + await this.mergePullRequest(branchName, true, pullRequest.data.number); + await this.deleteBranch(branchName); + } +} diff --git a/mkdocsPublisher/githubInteraction/delete.ts b/mkdocsPublisher/githubInteraction/delete.ts new file mode 100644 index 00000000..b6340cb5 --- /dev/null +++ b/mkdocsPublisher/githubInteraction/delete.ts @@ -0,0 +1,139 @@ +import { Octokit } from "@octokit/core"; +import { Notice } from "obsidian"; +import { MkdocsPublicationSettings } from "../settings/interface"; +import { GetFiles } from "./getFiles"; + +export async function deleteFromGithub(silent = false, settings: MkdocsPublicationSettings, octokit: Octokit, branchName='main', GetFiles: GetFiles) { + const getAllFile = await getAllFileFromRepo(branchName, octokit, settings); + const filesInRepo = await filterGithubFile(getAllFile, + settings + ); + if (!filesInRepo) { + let errorMsg = ""; + if (settings.folderDefaultName.length > 0) { + if (settings.folderDefaultName.length > 0) { + errorMsg = + "You need to configure a" + + " default folder name in the" + + " settings to use this command."; + } else if ( + settings.downloadedFolder === "yamlFrontmatter" && + settings.rootFolder + ) { + errorMsg = + "You need to configure a root folder in the settings to use this command."; + } + if (!silent) { + new Notice("Error : " + errorMsg); + } + } + return false; + } + const allSharedFiles = GetFiles.getAllFileWithPath(); + let deletedSuccess = 0; + let deletedFailed = 0; + for (const file of filesInRepo) { + if (!allSharedFiles.includes(file.file)) { + try { + console.log('trying to delete file : ' + file.file); + const reponse = await octokit.request( + "DELETE" + " /repos/{owner}/{repo}/contents/{path}", + { + owner: settings.githubName, + repo: settings.githubRepo, + path: file.file, + message: "Delete file", + sha: file.sha, + branch: branchName + } + ); + if (reponse.status === 200) { + deletedSuccess++; + } else { + deletedFailed++; + } + } catch (e) { + console.error(e); + } + } + } + let successMsg = 'No files have been deleted'; + let failedMsg = ''; + if (deletedSuccess > 0) { + successMsg = `Successfully deleted ${deletedSuccess} files` + } + if (deletedFailed > 0) { + failedMsg = `Failed to delete ${deletedFailed} files.` + } + if (!silent) { + new Notice(successMsg + failedMsg) + } + return true; +} + +export async function filterGithubFile(fileInRepo: { file: string; sha: string }[], settings: MkdocsPublicationSettings) { + const sharedFilesInRepo = []; + for (const file of fileInRepo) { + if ( + (settings.downloadedFolder === "yamlFrontmatter" && + settings.rootFolder.length === 0) || + settings.folderDefaultName.length === 0 + ) { + return false; + } + if ( + file.file.includes(settings.folderDefaultName) || + (settings.downloadedFolder === "yamlFrontmatter" && + file.file.includes(settings.rootFolder)) || + (settings.defaultImageFolder.length > 0 && + file.file.includes(settings.defaultImageFolder)) + ) { + sharedFilesInRepo.push(file); + } + } + return sharedFilesInRepo; +} + +async function getAllFileFromRepo(ref="main", octokit: Octokit, settings: MkdocsPublicationSettings) { + const filesInRepo = []; + try { + const allBranch = await octokit.request('GET' + + ' /repos/{owner}/{repo}/branches', { + owner: settings.githubName, + repo: settings.githubRepo, + }); + const refBranch = allBranch.data.find((branch: { name: string; }) => branch.name === ref); + console.log(refBranch) + const repoContents = await octokit.request( + "GET" + " /repos/{owner}/{repo}/git/trees/{tree_sha}", + { + owner: settings.githubName, + repo: settings.githubRepo, + tree_sha: ref, + recursive: "true", + } + ); + + if (repoContents.status === 200) { + const files = repoContents.data.tree; + for (const file of files) { + const basename = (name: string) => + /([^/\\.]*)(\..*)?$/.exec(name)[1]; //don't delete file starting with . + if ( + file.type === "blob" && + basename(file.path).length > 0 && + basename(file.path) != 'vault_published' + ) { + filesInRepo.push({ + file: file.path, + sha: file.sha, + }); + } + } + } + } catch (e) { + console.log(e) + } + return filesInRepo; +} + diff --git a/mkdocsPublisher/githubInteraction/getFiles.ts b/mkdocsPublisher/githubInteraction/getFiles.ts new file mode 100644 index 00000000..67284bf7 --- /dev/null +++ b/mkdocsPublisher/githubInteraction/getFiles.ts @@ -0,0 +1,131 @@ +// Credit : https://github.com/oleeskild/obsidian-digital-garden @oleeskild + +import { + MetadataCache, + TFile, + Vault, +} from "obsidian"; +import { MkdocsPublicationSettings } from "../settings/interface"; +import { Octokit } from "@octokit/core"; + +export class GetFiles { + vault: Vault; + metadataCache: MetadataCache; + settings: MkdocsPublicationSettings; + octokit: Octokit; + + constructor( + vault: Vault, + metadataCache: MetadataCache, + settings: MkdocsPublicationSettings, + octokit: Octokit + ) { + this.vault = vault; + this.metadataCache = metadataCache; + this.settings = settings; + this.octokit = octokit; + } + + getSharedFiles() { + const files = this.vault.getMarkdownFiles(); + const shared_File = []; + const sharedkey = this.settings.shareKey; + for (const file of files) { + try { + const frontMatter = this.metadataCache.getCache( + file.path + ).frontmatter; + if (frontMatter && frontMatter[sharedkey] === true) { + shared_File.push(file); + } + } catch { + // ignore + } + } + return shared_File; + } + + getAllFileWithPath() { + const files = this.vault.getFiles(); + const allFileWithPath = []; + const shareKey = this.settings.shareKey; + for (const file of files) { + const fileExtension = file.extension; + if (fileExtension.match(/(png|jpe?g|svg|bmp|gif)$/i)) { + const filepath = + this.settings.defaultImageFolder.length > 0 + ? this.settings.defaultImageFolder + "/" + file.path + : this.settings.folderDefaultName.length > 0 + ? this.settings.folderDefaultName + "/" + file.path + : file.path; + allFileWithPath.push(filepath); + } else if (file.extension == "md") { + const frontMatter = this.metadataCache.getCache( + file.path + ).frontmatter; + let filepath = + this.settings.folderDefaultName.length > 0 + ? this.settings.folderDefaultName + "/" + file.path + : file.path; + if (frontMatter && frontMatter[shareKey] === true) { + if (this.settings.downloadedFolder === "yamlFrontmatter") { + if (frontMatter[this.settings.yamlFolderKey]) { + filepath = + this.settings.rootFolder.length > 0 + ? this.settings.rootFolder + "/" + frontMatter[this.settings.yamlFolderKey] + + "/" + file.name : file.name; + } + } else if ( + this.settings.downloadedFolder === "fixedFolder" + ) { + filepath = + this.settings.folderDefaultName.length > 0 + ? this.settings.folderDefaultName + "/" + file.name : file.name; + } + allFileWithPath.push(filepath); + } + } + } + return allFileWithPath; + } + + getLinkedImage(file: TFile) { + const embed_files = this.metadataCache.getCache(file.path).embeds; + const image_list = []; + if (embed_files != undefined) { + for (const embed_file of embed_files) { + try { + const imageLink = this.metadataCache.getFirstLinkpathDest( + embed_file.link, + file.path + ); + const imgExt = imageLink.extension; + if (imgExt.match(/(png|jpe?g|svg|bmp|gif)$/i)) { + image_list.push(imageLink); + } + } catch (e) { + console.log("Error with this image : " + embed_file); + } + } + return image_list; + } + return []; + } + + checkExcludedFolder(file: TFile) { + const excludedFolder = this.settings.ExcludedFolder.split(",").filter( + (x) => x != "" + ); + if (excludedFolder.length > 0) { + for (let i = 0; i < excludedFolder.length; i++) { + if (file.path.contains(excludedFolder[i].trim())) { + return true; + } + } + } + return false; + } + + + +} diff --git a/mkdocsPublisher/githubInteraction/upload.ts b/mkdocsPublisher/githubInteraction/upload.ts new file mode 100644 index 00000000..663c519e --- /dev/null +++ b/mkdocsPublisher/githubInteraction/upload.ts @@ -0,0 +1,208 @@ +import { + arrayBufferToBase64, + MetadataCache, + Notice, + TFile, + Vault +} from "obsidian"; +import { MkdocsPublicationSettings } from "../settings/interface"; +import { GetFiles } from "./getFiles"; +import { Octokit } from "@octokit/core"; +import { Base64 } from "js-base64"; +import {deleteFromGithub} from "./delete" + +export default class MkdocsPublish { + vault: Vault; + metadataCache: MetadataCache; + settings: MkdocsPublicationSettings; + octokit: Octokit; + + constructor( + vault: Vault, + metadataCache: MetadataCache, + settings: MkdocsPublicationSettings, + octokit: Octokit + ) { + this.vault = vault; + this.metadataCache = metadataCache; + this.settings = settings; + this.octokit = octokit; + } + + async publish(file: TFile, one_file = false, ref = "main") { + const shareFiles = new GetFiles(this.vault, this.metadataCache, this.settings, this.octokit); + const sharedKey = this.settings.shareKey; + const frontmatter = this.metadataCache.getCache(file.path).frontmatter; + if ( + !frontmatter || + !frontmatter[sharedKey] || + shareFiles.checkExcludedFolder(file) || + file.extension !== "md" + ) { + return false; + } + try { + const text = await this.vault.cachedRead(file); + const linkedImage = shareFiles.getLinkedImage(file); + let folderDefault = this.settings.folderDefaultName; + if (folderDefault.length > 0) { + folderDefault = folderDefault + "/"; + } + let path = folderDefault + file.name; + if (this.settings.downloadedFolder === "yamlFrontmatter") { + let folderRoot = this.settings.rootFolder; + if (folderRoot.length > 0) { + folderRoot = folderRoot + "/"; + } + if (frontmatter[this.settings.yamlFolderKey]) { + path = + folderRoot + + frontmatter[this.settings.yamlFolderKey] + + "/" + + file.name; + } + } else if (this.settings.downloadedFolder === "obsidianPath") { + path = folderDefault + file.path; + } + await this.uploadText(file.path, text, path, file.name, ref); + if (linkedImage.length > 0 && this.settings.transferEmbedded) { + for (const image of linkedImage) { + await this.uploadImage(image, ref); + } + } + if (one_file) { + await this.uploadFolder(ref); + await deleteFromGithub(true, this.settings, this.octokit, ref, shareFiles); + } + return true; + } catch (e) { + console.error(e); + return false; + } + } + async uploadFolder(ref = "main") { + const shareFiles = new GetFiles(this.vault, this.metadataCache, this.settings, this.octokit); + const folder = shareFiles.getSharedFiles(); + if (folder.length > 0) { + const publishedFiles = folder.map((file) => file.name); + const publishedFilesText = + JSON.stringify(publishedFiles).toString(); + const vaultPublisherJSON = + this.settings.folderDefaultName.length > 0 + ? `${this.settings.folderDefaultName}/vault_published.json` + : `vault_published.json`; + await this.uploadText( + "vault_published.json", + publishedFilesText, + vaultPublisherJSON, "", ref + ); + } + } + async upload(filePath: string, content: string, path: string, title = "", ref = "main") { + if (!this.settings.githubRepo) { + new Notice( + "Config error : You need to define a github repo in the plugin settings" + ); + throw {}; + } + if (!this.settings.githubName) { + new Notice( + "Config error : You need to define your github username in the plugin settings" + ); + throw {}; + } + const octokit = this.octokit; + const payload = { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + path, + message: `Adding ${title}`, + content: content, + sha: "", + branch: ref, + }; + try { + const response = await octokit.request( + "GET /repos/{owner}/{repo}/contents/{path}", + { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + path, + ref: ref, + } + ); + // @ts-ignore + if (response.status === 200 && response.data.type === "file") { + // @ts-ignore + payload.sha = response.data.sha; + } + } catch { + console.log( + "The 404 error is normal ! It means that the file does not exist yet. Don't worry ❤️." + ); + } + payload.message = `Update note ${title}`; + await octokit.request( + "PUT /repos/{owner}/{repo}/contents/{path}", + payload + ); + } + + async uploadImage(imageFile: TFile, ref = "main") { + const imageBin = await this.vault.readBinary(imageFile); + const image64 = arrayBufferToBase64(imageBin); + let path = this.settings.folderDefaultName + "/" + imageFile.name; + if (this.settings.defaultImageFolder.length > 0) { + path = this.settings.defaultImageFolder + "/" + imageFile.name; + } + await this.upload(imageFile.path, image64, path, "", ref); + } + + async uploadText(filePath: string, text: string, path: string, title = "", ref = "main") { + try { + const contentBase64 = Base64.encode(text).toString(); + await this.upload(filePath, contentBase64, path, title, ref); + } catch (e) { + console.error(e); + } + } + async workflowGestion() { + let finished = false; + if (this.settings.workflowName.length === 0) { + return false; + } else { + const octokit = this.octokit; + await octokit.request( + "POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", + { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + workflow_id: this.settings.workflowName, + ref: "main", + } + ); + while (!finished) { + await sleep(10000); + const workflowGet = await octokit.request( + "GET /repos/{owner}/{repo}/actions/runs", + { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + } + ); + if (workflowGet.data.workflow_runs.length > 0) { + const build = workflowGet.data.workflow_runs.find( + (run) => + run.name === + this.settings.workflowName.replace(".yml", "") + ); + if (build.status === "completed") { + finished = true; + return true; + } + } + } + } + } +} + diff --git a/mkdocsPublisher/main.ts b/mkdocsPublisher/main.ts index 75952fca..bdab7391 100644 --- a/mkdocsPublisher/main.ts +++ b/mkdocsPublisher/main.ts @@ -3,9 +3,14 @@ import { MkdocsSettingsTab, } from "./settings"; import { ShareStatusBar } from "./utils/status_bar"; -import MkdocsPublish from "./utils/publication"; +import MkdocsPublish from "./githubInteraction/upload"; import { disablePublish, noticeMessage } from "./utils/utils"; import {MkdocsPublicationSettings, DEFAULT_SETTINGS} from './settings/interface' +import { deleteFromGithub } from './githubInteraction/delete' +import { GetFiles } from "./githubInteraction/getFiles"; +import {GithubBranch} from "./githubInteraction/branch"; +import { Octokit } from "@octokit/core"; + export default class MkdocsPublication extends Plugin { settings: MkdocsPublicationSettings; @@ -14,6 +19,16 @@ export default class MkdocsPublication extends Plugin { console.log("Mkdocs Publication loaded"); await this.loadSettings(); this.addSettingTab(new MkdocsSettingsTab(this.app, this)); + const octokit = new Octokit({auth: this.settings.GhToken}); + const publish = new MkdocsPublish( + this.app.vault, + this.app.metadataCache, + this.settings, + octokit + ); + const githubBranch = new GithubBranch(this.settings, octokit); + const shareFiles = new GetFiles(this.app.vault, this.app.metadataCache, this.settings, octokit); + this.registerEvent( this.app.workspace.on("file-menu", (menu, file: TFile) => { @@ -31,15 +46,13 @@ export default class MkdocsPublication extends Plugin { .setIcon("share") .onClick(async () => { try { - const publish = new MkdocsPublish( - this.app.vault, - this.app.metadataCache, - this.settings - ); + const branchName = this.app.vault.getName() + "-" + new Date().toLocaleDateString('en-US').replace(/\//g, '-'); + await githubBranch.newBranch(branchName); const publishSuccess = - await publish.publish(file, true); + await publish.publish(file, true, branchName); if (publishSuccess) { await noticeMessage(publish, file, this.settings) + await githubBranch.updateRepository(branchName); } } catch (e) { @@ -69,15 +82,19 @@ export default class MkdocsPublication extends Plugin { .setIcon("share") .onClick(async () => { try { - const publish = new MkdocsPublish( - this.app.vault, - this.app.metadataCache, - this.settings - ); + const branchName = this.app.vault.getName() + "-" + new Date().toLocaleDateString('en-US').replace(/\//g, '-'); + const allBranch = await octokit.request('GET' + ' /repos/{owner}/{repo}/branches', { + owner: this.settings.githubName, + repo: this.settings.githubRepo, + }); + const mainBranch = allBranch.data.find((branch: { name: string; }) => branch.name === 'main' || branch.name === 'master'); + console.log(mainBranch) + await githubBranch.newBranch(branchName); const publishSuccess = - await publish.publish(view.file, true); + await publish.publish(view.file, true, branchName); if (publishSuccess) { - await noticeMessage(publish, view.file, this.settings) + await noticeMessage(publish, view.file, this.settings); + await githubBranch.updateRepository(branchName); } } catch (e) { console.error(e); @@ -103,24 +120,25 @@ export default class MkdocsPublication extends Plugin { ) { if (!checking) { try { - const { vault, workspace, metadataCache } = + const { workspace} = this.app; const currentFile = workspace.getActiveFile(); - const publishFile = new MkdocsPublish( - vault, - metadataCache, - this.settings - ); - const publishSuccess = publishFile.publish( + const branchName = this.app.vault.getName() + "-" + new Date().toLocaleDateString('en-US').replace(/\//g, '-'); + const githubBranch = new GithubBranch(this.settings, octokit); + githubBranch.newBranch(branchName); + const publishSuccess = publish.publish( currentFile, - true + true, + branchName ); if (publishSuccess) { - noticeMessage(publishFile, currentFile, this.settings); + noticeMessage(publish, currentFile, this.settings); + githubBranch.updateRepository(branchName); + } } catch (e) { console.error(e); - new Notice("Error publishing to mkdocs."); + new Notice("Error publishing to github."); } } return true; @@ -137,15 +155,11 @@ export default class MkdocsPublication extends Plugin { if (this.settings.autoCleanUp) { if (!checking) { try { - const { vault, metadataCache } = - this.app; - const publish = new MkdocsPublish( - vault, - metadataCache, - this.settings - ); new Notice(`Starting cleaning ${this.settings.githubRepo} `) - publish.deleteFromGithub(false); + const branchName = this.app.vault.getName() + "-" + new Date().toLocaleDateString('en-US').replace(/\//g, '-'); + githubBranch.newBranch(branchName); + deleteFromGithub(false, this.settings,octokit, branchName, shareFiles); + githubBranch.updateRepository(branchName); } catch (e) { console.error(e); } @@ -162,13 +176,7 @@ export default class MkdocsPublication extends Plugin { callback: async () => { const statusBarItems = this.addStatusBarItem(); try { - const { vault, metadataCache } = this.app; - const publish = new MkdocsPublish( - vault, - metadataCache, - this.settings - ); - const sharedFiles = publish.getSharedFiles(); + const sharedFiles = shareFiles.getSharedFiles(); const statusBar = new ShareStatusBar( statusBarItems, sharedFiles.length @@ -178,13 +186,16 @@ export default class MkdocsPublication extends Plugin { const publishedFiles = sharedFiles.map( (file) => file.name ); - // upload list of published files in Source + // octokit list of published files in Source const publishedFilesText = JSON.stringify(publishedFiles).toString(); + const githubBranch = new GithubBranch(this.settings, octokit); + const branchName = this.app.vault.getName() + "-" + new Date().toLocaleDateString('en-US').replace(/\//g, '-'); + await githubBranch.newBranch(branchName); const vaultPublisherJSON = this.settings.folderDefaultName.length>0? `${this.settings.folderDefaultName}/vault_published.json`:`vault_published.json`; await publish.uploadText( "vault_published.json", publishedFilesText, - vaultPublisherJSON + vaultPublisherJSON,"", branchName ); for ( let files = 0; @@ -194,7 +205,7 @@ export default class MkdocsPublication extends Plugin { try { const file = sharedFiles[files]; statusBar.increment(); - await publish.publish(file); + await publish.publish(file, false, branchName); } catch { errorCount++; new Notice( @@ -205,7 +216,8 @@ export default class MkdocsPublication extends Plugin { statusBar.finish(8000); const noticeValue = `${publishedFiles.length - errorCount} notes` await noticeMessage(publish, noticeValue, this.settings) - await publish.deleteFromGithub(true); + await deleteFromGithub(true, this.settings, octokit, branchName, shareFiles); + await githubBranch.updateRepository(branchName); } } catch (e) { // statusBarItems.remove(); @@ -219,7 +231,7 @@ export default class MkdocsPublication extends Plugin { } onunload() { - console.log("Mkdocs Publication unloaded"); + console.log("Github Publisher unloaded"); } async loadSettings() { diff --git a/mkdocsPublisher/utils/publication.ts b/mkdocsPublisher/utils/publication.ts deleted file mode 100644 index 805aceba..00000000 --- a/mkdocsPublisher/utils/publication.ts +++ /dev/null @@ -1,435 +0,0 @@ -// Credit : https://github.com/oleeskild/obsidian-digital-garden @oleeskild - -import { - MetadataCache, - Notice, - TFile, - Vault, - arrayBufferToBase64, -} from "obsidian"; -import { MkdocsPublicationSettings } from "../settings/interface"; -import { Octokit } from "@octokit/core"; -import { Base64 } from "js-base64"; - -export default class MkdocsPublish { - vault: Vault; - metadataCache: MetadataCache; - settings: MkdocsPublicationSettings; - - constructor( - vault: Vault, - metadataCache: MetadataCache, - settings: MkdocsPublicationSettings - ) { - this.vault = vault; - this.metadataCache = metadataCache; - this.settings = settings; - } - - getSharedFiles() { - const files = this.vault.getMarkdownFiles(); - const shared_File = []; - const sharedkey = this.settings.shareKey; - for (const file of files) { - try { - const frontMatter = this.metadataCache.getCache( - file.path - ).frontmatter; - if (frontMatter && frontMatter[sharedkey] === true) { - shared_File.push(file); - } - } catch { - // ignore - } - } - return shared_File; - } - - getAllFileWithPath() { - const files = this.vault.getFiles(); - const allFileWithPath = []; - const shareKey = this.settings.shareKey; - for (const file of files) { - const fileExtension = file.extension; - if (fileExtension.match(/(png|jpe?g|svg|bmp|gif)$/i)) { - const filepath = - this.settings.defaultImageFolder.length > 0 - ? this.settings.defaultImageFolder + "/" + file.path - : this.settings.folderDefaultName.length > 0 - ? this.settings.folderDefaultName + "/" + file.path - : file.path; - allFileWithPath.push(filepath); - } else if (file.extension == "md") { - const frontMatter = this.metadataCache.getCache( - file.path - ).frontmatter; - let filepath = - this.settings.folderDefaultName.length > 0 - ? this.settings.folderDefaultName + "/" + file.path - : file.path; - if (frontMatter && frontMatter[shareKey] === true) { - if (this.settings.downloadedFolder === "yamlFrontmatter") { - if (frontMatter[this.settings.yamlFolderKey]) { - filepath = - this.settings.rootFolder.length > 0 - ? this.settings.rootFolder + "/" + frontMatter[this.settings.yamlFolderKey] + - "/" + file.name : file.name; - } - } else if ( - this.settings.downloadedFolder === "fixedFolder" - ) { - filepath = - this.settings.folderDefaultName.length > 0 - ? this.settings.folderDefaultName + "/" + file.name : file.name; - } - allFileWithPath.push(filepath); - } - } - } - return allFileWithPath; - } - - getLinkedImage(file: TFile) { - const embed_files = this.metadataCache.getCache(file.path).embeds; - const image_list = []; - if (embed_files != undefined) { - for (const embed_file of embed_files) { - try { - const imageLink = this.metadataCache.getFirstLinkpathDest( - embed_file.link, - file.path - ); - const imgExt = imageLink.extension; - if (imgExt.match(/(png|jpe?g|svg|bmp|gif)$/i)) { - image_list.push(imageLink); - } - } catch (e) { - console.log("Error with this image : " + embed_file); - } - } - return image_list; - } - return []; - } - - checkExcludedFolder(file: TFile) { - const excludedFolder = this.settings.ExcludedFolder.split(",").filter( - (x) => x != "" - ); - if (excludedFolder.length > 0) { - for (let i = 0; i < excludedFolder.length; i++) { - if (file.path.contains(excludedFolder[i].trim())) { - return true; - } - } - } - return false; - } - - async publish(file: TFile, one_file = false) { - const sharedKey = this.settings.shareKey; - const frontmatter = this.metadataCache.getCache(file.path).frontmatter; - if ( - !frontmatter || - !frontmatter[sharedKey] || - this.checkExcludedFolder(file) || - file.extension !== "md" - ) { - return false; - } - try { - const text = await this.vault.cachedRead(file); - const linkedImage = this.getLinkedImage(file); - let folderDefault = this.settings.folderDefaultName; - if (folderDefault.length > 0) { - folderDefault = folderDefault + "/"; - } - let path = folderDefault + file.name; - if (this.settings.downloadedFolder === "yamlFrontmatter") { - let folderRoot = this.settings.rootFolder; - if (folderRoot.length > 0) { - folderRoot = folderRoot + "/"; - } - if (frontmatter[this.settings.yamlFolderKey]) { - path = - folderRoot + - frontmatter[this.settings.yamlFolderKey] + - "/" + - file.name; - } - } else if (this.settings.downloadedFolder === "obsidianPath") { - path = folderDefault + file.path; - } - await this.uploadText(file.path, text, path, file.name); - if (linkedImage.length > 0 && this.settings.transferEmbedded) { - for (const image of linkedImage) { - await this.uploadImage(image); - } - } - if (one_file) { - await this.uploadFolder(); - await this.deleteFromGithub(true); - } - return true; - } catch (e) { - console.error(e); - return false; - } - } - - async uploadFolder() { - const folder = this.getSharedFiles(); - if (folder.length > 0) { - const publishedFiles = folder.map((file) => file.name); - const publishedFilesText = - JSON.stringify(publishedFiles).toString(); - const vaultPublisherJSON = - this.settings.folderDefaultName.length > 0 - ? `${this.settings.folderDefaultName}/vault_published.json` - : `vault_published.json`; - await this.uploadText( - "vault_published.json", - publishedFilesText, - vaultPublisherJSON - ); - } - } - - async getAllFileFromRepo(octokit: Octokit) { - const filesInRepo = []; - try { - const repoContents = await octokit.request( - "GET" + " /repos/{owner}/{repo}/git/trees/{tree_sha}", - { - owner: this.settings.githubName, - repo: this.settings.githubRepo, - tree_sha: "main", - recursive: "true", - } - ); - - if (repoContents.status === 200) { - const files = repoContents.data.tree; - for (const file of files) { - const basename = (name: string) => - /([^/\\.]*)(\..*)?$/.exec(name)[1]; //don't delete file starting with . - if ( - file.type === "blob" && - basename(file.path).length > 0 && - basename(file.path) != 'vault_published' - ) { - filesInRepo.push({ - file: file.path, - sha: file.sha, - }); - } - } - } - } catch (e){ - console.log(e) - } - return filesInRepo; - } - - async filterGithubFile(fileInRepo: { file: string; sha: string }[]) { - const sharedFilesInRepo = []; - for (const file of fileInRepo) { - if ( - (this.settings.downloadedFolder === "yamlFrontmatter" && - this.settings.rootFolder.length === 0) || - this.settings.folderDefaultName.length === 0 - ) { - return false; - } - if ( - file.file.includes(this.settings.folderDefaultName) || - (this.settings.downloadedFolder === "yamlFrontmatter" && - file.file.includes(this.settings.rootFolder)) || - (this.settings.defaultImageFolder.length > 0 && - file.file.includes(this.settings.defaultImageFolder)) - ) { - sharedFilesInRepo.push(file); - } - } - return sharedFilesInRepo; - } - - async deleteFromGithub(silent = false) { - const octokit = new Octokit({ - auth: this.settings.GhToken, - }); - const filesInRepo = await this.filterGithubFile( - await this.getAllFileFromRepo(octokit) - ); - if (!filesInRepo) { - let errorMsg = ""; - if (this.settings.folderDefaultName.length > 0) { - if (this.settings.folderDefaultName.length > 0) { - errorMsg = - "You need to configure a" + - " default folder name in the" + - " settings to use this command."; - } else if ( - this.settings.downloadedFolder === "yamlFrontmatter" && - this.settings.rootFolder - ) { - errorMsg = - "You need to configure a root folder in the settings to use this command."; - } if (!silent) { - new Notice("Error : " + errorMsg); - } - } - return false; - } - const allSharedFiles = this.getAllFileWithPath(); - let deletedSuccess = 0; - let deletedFailed = 0; - for (const file of filesInRepo) { - if (!allSharedFiles.includes(file.file)) { - try { - console.log('trying to delete file : ' + file.file); - const reponse = await octokit.request( - "DELETE" + " /repos/{owner}/{repo}/contents/{path}", - { - owner: this.settings.githubName, - repo: this.settings.githubRepo, - path: file.file, - message: "Delete file", - sha: file.sha, - } - ); - if (reponse.status === 200) { - deletedSuccess++; - } else { - deletedFailed++; - } - } catch (e) { - console.error(e); - } - } - } - let successMsg = 'No files have been deleted'; - let failedMsg = ''; - if (deletedSuccess > 0) { - successMsg = `Successfully deleted ${deletedSuccess} files` - } - if (deletedFailed > 0) { - failedMsg = `Failed to delete ${deletedFailed} files.` - } - if (!silent) { - new Notice(successMsg + failedMsg) - } - return true; - } - - async upload(filePath: string, content: string, path: string, title = "") { - if (!this.settings.githubRepo) { - new Notice( - "Config error : You need to define a github repo in the plugin settings" - ); - throw {}; - } - if (!this.settings.githubName) { - new Notice( - "Config error : You need to define your github username in the plugin settings" - ); - throw {}; - } - const octokit = new Octokit({ - auth: this.settings.GhToken, - }); - - const payload = { - owner: this.settings.githubName, - repo: this.settings.githubRepo, - path, - message: `Adding ${title}`, - content: content, - sha: "", - }; - try { - const response = await octokit.request( - "GET /repos/{owner}/{repo}/contents/{path}", - { - owner: this.settings.githubName, - repo: this.settings.githubRepo, - path, - } - ); - // @ts-ignore - if (response.status === 200 && response.data.type === "file") { - // @ts-ignore - payload.sha = response.data.sha; - } - } catch { - console.log( - "The 404 error is normal ! It means that the file does not exist yet. Don't worry ❤️." - ); - } - payload.message = `Update note ${title}`; - await octokit.request( - "PUT /repos/{owner}/{repo}/contents/{path}", - payload - ); - } - - async uploadImage(imageFile: TFile) { - const imageBin = await this.vault.readBinary(imageFile); - const image64 = arrayBufferToBase64(imageBin); - let path = this.settings.folderDefaultName + "/" + imageFile.name; - if (this.settings.defaultImageFolder.length > 0) { - path = this.settings.defaultImageFolder + "/" + imageFile.name; - } - await this.upload(imageFile.path, image64, path); - } - - async uploadText(filePath: string, text: string, path: string, title = "") { - try { - const contentBase64 = Base64.encode(text).toString(); - await this.upload(filePath, contentBase64, path, title); - } catch (e) { - console.error(e); - } - } - - async workflowGestion() { - let finished = false; - if (this.settings.workflowName.length === 0) { - return false; - } else { - const octokit = new Octokit({ - auth: this.settings.GhToken, - }); - await octokit.request( - "POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", - { - owner: this.settings.githubName, - repo: this.settings.githubRepo, - workflow_id: this.settings.workflowName, - ref: "main", - } - ); - while (!finished) { - await sleep(10000); - const workflowGet = await octokit.request( - "GET /repos/{owner}/{repo}/actions/runs", - { - owner: this.settings.githubName, - repo: this.settings.githubRepo, - } - ); - if (workflowGet.data.workflow_runs.length > 0) { - const build = workflowGet.data.workflow_runs.find( - (run) => - run.name === - this.settings.workflowName.replace(".yml", "") - ); - if (build.status === "completed") { - finished = true; - return true; - } - } - } - } - } -} diff --git a/mkdocsPublisher/utils/utils.ts b/mkdocsPublisher/utils/utils.ts index dbd3124f..b49d0401 100644 --- a/mkdocsPublisher/utils/utils.ts +++ b/mkdocsPublisher/utils/utils.ts @@ -4,7 +4,7 @@ import { Notice } from 'obsidian' import { MkdocsPublicationSettings } from '../settings/interface' -import MkdocsPublish from "./publication"; +import MkdocsPublish from "../githubInteraction/getFiles"; function disablePublish (app: App, settings: MkdocsPublicationSettings, file:TFile) {