diff --git a/src/GitHub/branch.ts b/src/GitHub/branch.ts index 39629591..020cbaf8 100644 --- a/src/GitHub/branch.ts +++ b/src/GitHub/branch.ts @@ -206,8 +206,10 @@ export class GithubBranch extends FilesManagement { * @returns {Promise} True if the update is successful */ async updateRepository( - repoFrontmatter: RepoFrontmatter | RepoFrontmatter[] + repoFrontmatter: RepoFrontmatter | RepoFrontmatter[], + dryRun = false ): Promise { + if (dryRun) return true; repoFrontmatter = Array.isArray(repoFrontmatter) ? repoFrontmatter : [repoFrontmatter]; diff --git a/src/GitHub/delete.ts b/src/GitHub/delete.ts index 66d622e4..15ea44c9 100644 --- a/src/GitHub/delete.ts +++ b/src/GitHub/delete.ts @@ -1,7 +1,7 @@ import { Octokit } from "@octokit/core"; import i18next from "i18next"; import { Base64 } from "js-base64"; -import { Notice, parseYaml } from "obsidian"; +import { MetadataCache, normalizePath, Notice, parseYaml, TAbstractFile, TFile, TFolder, Vault } from "obsidian"; import { Deleted, @@ -65,6 +65,7 @@ async function deleteFromGithubOneRepo( repoProperties: MonoRepoProperties, ): Promise { const repo = repoProperties.frontmatter; + if (repo.dryRun.autoclean) return cleanDryRun(silent, filesManagement, repoProperties); if (!repo.autoclean) return {success: false, deleted: [], undeleted: []}; const getAllFile = await filesManagement.getAllFileFromRepo( branchName, @@ -189,7 +190,7 @@ function excludedFileFromDelete( for (const excludedFile of autoCleanExcluded) { const isRegex = excludedFile.match(FIND_REGEX); const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; - if (regex && regex.test(file)) { + if (regex?.test(file)) { return true; } else if ( file.trim().includes(excludedFile.trim()) && @@ -283,7 +284,7 @@ async function checkIndexFiles( { owner: repoFrontmatter.owner, repo: repoFrontmatter.repo, - path: path, + path, } ); if (fileRequest.status === 200) { @@ -310,3 +311,92 @@ async function checkIndexFiles( } return false; } + +function cleanDryRun( + silent: boolean = false, + filesManagement: FilesManagement, repoProperties: MonoRepoProperties): Deleted { + const {vault, settings} = filesManagement; + const app = filesManagement.plugin.app; + const repo = repoProperties.frontmatter; + const dryRunFolderPath = normalizePath(repo.dryRun.folderName + .replace("{{owner}}", repo.owner) + .replace("{{repo}}", repo.repo) + .replace("{{branch}}", repo.branch)); + const dryRunFolder = vault.getAbstractFileByPath(dryRunFolderPath); + if (!dryRunFolder || dryRunFolder instanceof TFile) return {success: false, deleted: [], undeleted: []}; + const dryRunFiles:TFile[] = []; + Vault.recurseChildren(dryRunFolder as TFolder, (file: TAbstractFile) => { + if (!excludedFileFromDelete(normalizePath(file.path.replace(dryRunFolderPath, "")), settings) && (isAttachment(file.path) || file.path.match("md$")) && file instanceof TFile) dryRunFiles.push(file); + }); + const allSharedFiles = filesManagement.getAllFileWithPath(repoProperties.repo).map((file) => { + return { converted: file.converted, repo: file.repoFrontmatter }; + }); + let deletedSuccess = 0; + const result: Deleted = { + deleted: [], + undeleted: [], + success: false, + }; + const deletedFolder: TAbstractFile[] = []; + for (const file of dryRunFiles) { + const convertedPath = normalizePath(file.path.replace(dryRunFolderPath, "")); + const isInObsidian = allSharedFiles.some( + (f) => f.converted === convertedPath + ); + const isMarkdownForAnotherRepo = file.path.trim().endsWith(".md") ? + !allSharedFiles.some( + (f) => { + let repoFrontmatter = f.repo; + if (Array.isArray(repoFrontmatter)) { + repoFrontmatter = repoFrontmatter.find((r) => JSON.stringify(r.repo) === JSON.stringify(repo.repo)); + } return f.converted === convertedPath && repoFrontmatter; + }) + : false; + const isNeedToBeDeleted = isInObsidian ? isMarkdownForAnotherRepo : true; + if (isNeedToBeDeleted) { + const indexFile = (convertedPath.contains(settings.upload.folderNote.rename)) ? indexFileDryRun(file as TFile, app.metadataCache) : false; + if (!indexFile) { + notif({settings}, `[DRYRUN] trying to delete file : ${file.path} from ${dryRunFolderPath}`); + vault.trash(file, false); + deletedSuccess++; + deletedFolder.push(file); + } + } + } + + //recursive delete empty folder in dryRunFolder + //empty folder are folder with children.length === 0 + const dryRunFolders:TFolder[] = []; + Vault.recurseChildren(vault.getAbstractFileByPath(dryRunFolderPath) as TFolder, (file: TAbstractFile) => { + if (file instanceof TFolder) { + dryRunFolders.push(file); + } + }); + for (const folder of dryRunFolders.reverse()) { + const children = folder.children.filter((child) => !deletedFolder.includes(child)); + if (children.length === 0) { + deletedFolder.push(folder); + vault.trash(folder, false); + deletedSuccess++; + } + } + + const successMsg = deletedSuccess > 0 ? (i18next.t("deletion.success", {nb: deletedSuccess.toString()})) : i18next.t("deletion.noFile"); + if (!silent) + new Notice(successMsg); + result.success = deletedSuccess === 0; + return result; +} + +function indexFileDryRun(file: TFile, metadataCache: MetadataCache):boolean { + const frontmatter = metadataCache.getFileCache(file)?.frontmatter; + if (frontmatter) { + const index = frontmatter.index; + const deleteFile = frontmatter.delete; + const share = frontmatter.share; + if (index === true || deleteFile === false || share === false) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/GitHub/files.ts b/src/GitHub/files.ts index c2feff05..6de7fddb 100644 --- a/src/GitHub/files.ts +++ b/src/GitHub/files.ts @@ -89,7 +89,7 @@ export class FilesManagement extends Publisher { */ getAllFileWithPath(repo: Repository | null): ConvertedLink[] { - const files = this.vault.getFiles(); + const files = this.vault.getFiles().filter((x) => !x.path.startsWith(this.settings.github.dryRun.folderName)); const allFileWithPath: ConvertedLink[] = []; for (const file of files) { if (isAttachment(file.name)) { diff --git a/src/GitHub/upload.ts b/src/GitHub/upload.ts index 7911c8ee..21d764e7 100644 --- a/src/GitHub/upload.ts +++ b/src/GitHub/upload.ts @@ -4,8 +4,10 @@ import { Base64 } from "js-base64"; import { arrayBufferToBase64, MetadataCache, + normalizePath, Notice, TFile, + TFolder, Vault, } from "obsidian"; @@ -330,7 +332,7 @@ export default class Publisher { const embeddedUploaded = embeded.uploaded; embeddedUploaded.push(uploaded); - if (autoclean && repo.autoclean) { + if (autoclean || repo.dryRun.autoclean) { deleted = await deleteFromGithub( true, this.branchName, @@ -433,6 +435,7 @@ export default class Publisher { properties: MonoProperties, ) { const imageBin = await this.vault.readBinary(imageFile); + const repoFrontmatter = properties.frontmatter.repo; let image64 = arrayBufferToBase64(imageBin); if (imageFile.name.includes("excalidraw")) { const svg = await convertToHTMLSVG(imageFile, this.vault); @@ -446,6 +449,33 @@ export default class Publisher { this.settings, properties.frontmatter.general ); + if (this.settings.github.dryRun.enable) { + const folderName = this.settings.github.dryRun.folderName + .replace("{{repo}}", repoFrontmatter.repo) + .replace("{{branch}}", repoFrontmatter.branch) + .replace("{{owner}}", repoFrontmatter.owner); + const dryRunPath = normalizePath(`${folderName}/${path}`); + const isAlreadyExist = this.vault.getAbstractFileByPath(dryRunPath); + if (isAlreadyExist && isAlreadyExist instanceof TFile) { + const needToByUpdated = isAlreadyExist.stat.mtime > imageFile.stat.mtime; + if (needToByUpdated) { + this.vault.modifyBinary(isAlreadyExist, imageBin); + } + return { + isUpdated: needToByUpdated, + file: imageFile.name + }; + } + const folder = dryRunPath.split("/").slice(0, -1).join("/"); + const folderExists = this.vault.getAbstractFileByPath(folder); + if (!folderExists || !(folderExists instanceof TFolder)) + await this.vault.createFolder(folder); + await this.vault.createBinary(path, imageBin); + return { + isUpdated: true, + file: imageFile.name + }; + } return await this.upload(image64, path, "", properties.frontmatter.repo); } @@ -463,8 +493,35 @@ export default class Publisher { text: string, path: string, title: string = "", - repoFrontmatter: RepoFrontmatter + repoFrontmatter: RepoFrontmatter, ): Promise { + if (this.settings.github.dryRun.enable) { + //create a new file in the vault + const folderName = this.settings.github.dryRun.folderName + .replace("{{repo}}", repoFrontmatter.repo) + .replace("{{branch}}", repoFrontmatter.branch) + .replace("{{owner}}", repoFrontmatter.owner); + + const newPath = normalizePath(`${folderName}/${path}`); + const isAlreadyExist = this.vault.getAbstractFileByPath(newPath); + if (isAlreadyExist && isAlreadyExist instanceof TFile) { + //modify + await this.vault.modify(isAlreadyExist, text); + return { + isUpdated: true, + file: title + }; + } //create + const folder = newPath.split("/").slice(0, -1).join("/"); + const folderExists = this.vault.getAbstractFileByPath(folder); + if (!folderExists || !(folderExists instanceof TFolder)) + await this.vault.createFolder(folder); + await this.vault.create(newPath, text); + return { + isUpdated: false, + file: title + }; + } try { const contentBase64 = Base64.encode(text).toString(); return await this.upload( @@ -491,6 +548,7 @@ export default class Publisher { repoFrontmatter: RepoFrontmatter | RepoFrontmatter[] ): Promise { if (metadataExtractor) { + if (this.settings.github.dryRun.enable) return; for (const file of Object.values(metadataExtractor)) { if (file) { const contents = await this.vault.adapter.read(file); @@ -521,6 +579,7 @@ export default class Publisher { */ async workflowGestion(repoFrontmatter: RepoFrontmatter): Promise { + if (this.settings.github.dryRun.enable) return false; let finished = false; if (repoFrontmatter.workflowName.length === 0) { return false; @@ -579,6 +638,14 @@ export default class Publisher { properties.frontmatter.general ); const repoFrontmatter = properties.frontmatter; + if (this.settings.github.dryRun.enable) { + newLinkedFiles.push(file); + continue; + } + if (!this.settings.github.dryRun.enable) { + newLinkedFiles.push(file); + continue; + } try { if (forcePushAttachment(file, this.settings)) { newLinkedFiles.push(file); @@ -616,9 +683,8 @@ export default class Publisher { } catch (e) { newLinkedFiles.push(file); } - } else { - newLinkedFiles.push(file); } + } return newLinkedFiles; } diff --git a/src/commands/file_menu.ts b/src/commands/file_menu.ts index a9aafe6e..7774fc77 100644 --- a/src/commands/file_menu.ts +++ b/src/commands/file_menu.ts @@ -3,7 +3,7 @@ import { Menu, MenuItem, Platform, TFile, TFolder} from "obsidian"; import GithubPublisher from "../main"; import {MonoRepoProperties, Repository} from "../settings/interface"; -import {defaultRepo, getRepoSharedKey, isShared, multipleSharedKey} from "../utils/data_validation_test"; +import {defaultRepo, getRepoSharedKey, isExcludedPath, isInDryRunFolder, isShared, multipleSharedKey} from "../utils/data_validation_test"; import { getRepoFrontmatter } from "../utils/parse_frontmatter"; import {shareAllMarkedNotes, shareOneNote} from "."; import {ChooseRepoToRun} from "./suggest_other_repo_commands_modal"; @@ -46,21 +46,24 @@ export async function shareFolderRepo(plugin: GithubPublisher, folder: TFolder, export function addSubMenuCommandsFolder(plugin: GithubPublisher, item: MenuItem, folder: TFolder, branchName: string, originalMenu: Menu): Menu { //@ts-ignore const subMenu = Platform.isDesktop ? item.setSubmenu() as Menu : originalMenu; - subMenu.addItem((subItem) => { - subItem - .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { - smartKey: i18next.t("common.default").toUpperCase(), - doc: folder.name - })) - .setIcon("folder-up") - .onClick(async () => { - const repo = getRepoSharedKey(plugin.settings, undefined); - await shareFolderRepo(plugin, folder, branchName, repo); - }); - }); + if (!isExcludedPath(plugin.settings,folder, defaultRepo(plugin.settings))) { + subMenu.addItem((subItem) => { + subItem + .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { + smartKey: i18next.t("common.default").toUpperCase(), + doc: folder.name + })) + .setIcon("folder-up") + .onClick(async () => { + const repo = getRepoSharedKey(plugin.settings, undefined); + await shareFolderRepo(plugin, folder, branchName, repo); + }); + }); + } const activatedRepoCommands = plugin.settings.github.otherRepo.filter((repo) => repo.createShortcuts); if (activatedRepoCommands.length > 0) { activatedRepoCommands.forEach((otherRepo) => { + if (isInDryRunFolder(plugin.settings, otherRepo, folder)) return; subMenu.addItem((item) => { item.setTitle( i18next.t("commands.shareViewFiles.multiple.on", { diff --git a/src/commands/index.ts b/src/commands/index.ts index 6095139d..fb1ada6c 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -134,19 +134,23 @@ export async function purgeNotesRemote( monoRepo: MonoRepoProperties, ): Promise { try { + const noticeFragment = document.createDocumentFragment(); + noticeFragment.createSpan({ cls: ["obsidian-publisher", "notification"] }).innerHTML = i18next.t("informations.startingClean", { repo: monoRepo.frontmatter }); new Notice( - i18next.t("informations.startingClean", {repo: monoRepo.frontmatter}) + noticeFragment ); - const isValid = checkRepositoryValidityWithRepoFrontmatter(PublisherManager, monoRepo.frontmatter); + const isValid = await checkRepositoryValidityWithRepoFrontmatter(PublisherManager, monoRepo.frontmatter); if (!isValid) return false; - await PublisherManager.newBranch(monoRepo.frontmatter); + if (!PublisherManager.settings.github.dryRun.enable) + await PublisherManager.newBranch(monoRepo.frontmatter); const deleted = await deleteFromGithub( false, branchName, PublisherManager, monoRepo ); - await PublisherManager.updateRepository(monoRepo.frontmatter); + if (!PublisherManager.settings.github.dryRun.enable) + await PublisherManager.updateRepository(monoRepo.frontmatter); if (PublisherManager.settings.plugin.displayModalRepoEditing) new ListChangedFiles(PublisherManager.plugin.app, deleted).open(); } catch (e) { notif({settings: PublisherManager.settings, e: true}, e); @@ -181,7 +185,8 @@ export async function shareOneNote( repo: repository }; if (!isValid) return false; - await PublisherManager.newBranch(repoFrontmatter); + if (!settings.github.dryRun.enable) + await PublisherManager.newBranch(repoFrontmatter); const publishSuccess = await PublisherManager.publish( file, true, @@ -206,7 +211,7 @@ export async function shareOneNote( } } const update = await PublisherManager.updateRepository( - repoFrontmatter + repoFrontmatter, settings.github.dryRun.enable ); if (update) { await publisherNotification( @@ -275,7 +280,7 @@ export async function shareNewNote( ); const statusBarElement = plugin.addStatusBarItem(); - const isValid = checkRepositoryValidityWithRepoFrontmatter(PublisherManager, monoRepo.frontmatter, newlySharedNotes.length); + const isValid = await checkRepositoryValidityWithRepoFrontmatter(PublisherManager, monoRepo.frontmatter, newlySharedNotes.length); if (!isValid) return false; await PublisherManager.newBranch(monoRepo.frontmatter); await shareAllMarkedNotes( @@ -325,7 +330,7 @@ export async function shareAllEditedNotes( ); const statusBarElement = plugin.addStatusBarItem(); - const isValid = checkRepositoryValidityWithRepoFrontmatter(PublisherManager, monoRepo.frontmatter, newlySharedNotes.length); + const isValid = await checkRepositoryValidityWithRepoFrontmatter(PublisherManager, monoRepo.frontmatter, newlySharedNotes.length); if (!isValid) return false; await PublisherManager.newBranch(monoRepo.frontmatter); await shareAllMarkedNotes( @@ -371,7 +376,7 @@ export async function shareOnlyEdited( (i18next.t("informations.foundNoteToSend", {nbNotes: newlySharedNotes.length})) ); const statusBarElement = PublisherManager.plugin.addStatusBarItem(); - const isValid = checkRepositoryValidityWithRepoFrontmatter(PublisherManager, repoFrontmatter, newlySharedNotes.length); + const isValid = await checkRepositoryValidityWithRepoFrontmatter(PublisherManager, repoFrontmatter, newlySharedNotes.length); if (!isValid) return false; await PublisherManager.newBranch(repoFrontmatter); await shareAllMarkedNotes( diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index becadf52..9290f65d 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -294,6 +294,16 @@ "desc": "If you use a different branch than \"main\"", "title": "Main branch" }, + "dryRun": { + "enable": { + "desc": "Disable GitHub push and all other action and only perform a dry-run to see what would be pushed or deleted in the repository.", + "title": "Test mode" + }, + "folder": { + "desc": "Use {{owner}}, {{repo}} and {{branch}} to dynamically name the folder.", + "title": "Folder where the repository will be simulated" + } + }, "ghToken": { "button": { "configDir": ": The configuration folder of Obsidian", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 9035dcd1..a06932d4 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -293,6 +293,16 @@ "desc": "Dans le cas où vous souhaitez utiliser une branche différente de \"main\".", "title": "Branche principale" }, + "dryRun": { + "enable": { + "desc": "Désactiver le push GitHub et toutes les autres actions et effectuer seulement une simulation pour voir ce qui serait transféré ou supprimé dans le dépôt.", + "title": "Mode de test" + }, + "folder": { + "desc": "Use {{owner}}, {{repo}} and {{branch}} to dynamically name the folder.", + "title": "Dossier où le dépôt sera simulé" + } + }, "ghToken": { "button": { "configDir": " : Le dossier de configuration d'Obsidian", diff --git a/src/settings.ts b/src/settings.ts index 70b447f3..f6a8cf4b 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -294,6 +294,32 @@ export class GithubPublisherSettingsTab extends PluginSettingTab { await this.plugin.saveSettings(); }) ); + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.dryRun.enable.title")) + .setDesc(i18next.t("settings.github.dryRun.enable.desc")) + .addToggle((toggle) => + toggle + .setValue(githubSettings.dryRun.enable) + .onChange(async (value) => { + githubSettings.dryRun.enable = value; + await this.plugin.saveSettings(); + this.renderSettingsPage(EnumbSettingsTabId.github); + }) + ); + if (githubSettings.dryRun.enable) { + new Setting(this.settingsPage) + .setName(i18next.t("settings.github.dryRun.folder.title")) + .setDesc(i18next.t("settings.github.dryRun.folder.desc")) + .addText((text) => + text + .setPlaceholder("github-publisher") + .setValue(githubSettings.dryRun.folderName) + .onChange(async (value) => { + githubSettings.dryRun.folderName = value.trim(); + await this.plugin.saveSettings(); + }) + ); + } new Setting(this.settingsPage) .setClass("no-display") diff --git a/src/settings/interface.ts b/src/settings/interface.ts index fcfc74f4..adb034aa 100644 --- a/src/settings/interface.ts +++ b/src/settings/interface.ts @@ -60,6 +60,10 @@ export interface GitHubPublisherSettings { branch: string; tokenPath: string; automaticallyMergePR: boolean; + dryRun: { + enable: boolean; + folderName: string; + } api: { tiersForApi: GithubTiersVersion; hostname: string; @@ -216,6 +220,10 @@ export const DEFAULT_SETTINGS: Partial = { repo: "", branch: "main", automaticallyMergePR: true, + dryRun: { + enable: false, + folderName: "github-publisher", + }, tokenPath: TOKEN_PATH, api: { tiersForApi: GithubTiersVersion.free, @@ -382,6 +390,11 @@ export interface RepoFrontmatter { automaticallyMergePR: boolean; verifiedRepo?: boolean; path?: Path; + dryRun: { + enable: boolean; + folderName: string; + autoclean: boolean; + } } export interface ListEditedFiles { diff --git a/src/utils/data_validation_test.ts b/src/utils/data_validation_test.ts index afaa3561..f8c2ebe0 100644 --- a/src/utils/data_validation_test.ts +++ b/src/utils/data_validation_test.ts @@ -1,6 +1,6 @@ import { Octokit } from "@octokit/core"; import i18next from "i18next"; -import {FrontMatterCache, Notice, TFile} from "obsidian"; +import {FrontMatterCache, normalizePath,Notice, TFile, TFolder} from "obsidian"; import GithubPublisher from "src/main"; import {GithubBranch} from "../GitHub/branch"; @@ -31,10 +31,10 @@ export function isInternalShared( if (properties.repository?.shareAll?.enable) { const excludedFileName = properties.repository.shareAll.excludedFileName; - return !file.basename.startsWith(excludedFileName); + return !file.basename.startsWith(excludedFileName) && !isInDryRunFolder(properties.settings, properties.repository, file); } if (!frontmatter) return false; - if (isExcludedPath(properties.settings, file)) return false; + if (isExcludedPath(properties.settings, file, properties.repository)) return false; const shareKey = properties.repository?.shareKey || properties.settings.plugin.shareKey; logs({settings: properties.settings}, "shareKey", shareKey, "frontmatter", frontmatter[shareKey]); if (frontmatter[shareKey] == null || frontmatter[shareKey] === undefined || ["false", "0", "no"].includes(frontmatter[shareKey].toString().toLowerCase())) return false; @@ -80,7 +80,7 @@ export function isShared( const otherRepoWithShareAll = settings.github.otherRepo.filter((repo) => repo.shareAll?.enable); if (!settings.plugin.shareAll?.enable && otherRepoWithShareAll.length === 0) { const shareKey = otherRepo ? otherRepo.shareKey : settings.plugin.shareKey; - if ( meta == null || !meta[shareKey] || meta[shareKey] == null || isExcludedPath(settings, file) || meta[shareKey] === undefined || ["false", "0", "no"].includes(meta[shareKey].toString().toLowerCase())) { + if ( meta == null || !meta[shareKey] || meta[shareKey] == null || isExcludedPath(settings, file, otherRepo) || meta[shareKey] === undefined || ["false", "0", "no"].includes(meta[shareKey].toString().toLowerCase())) { return false; } const shareKeyInFrontmatter:string = meta[shareKey].toString().toLowerCase(); @@ -88,8 +88,8 @@ export function isShared( } else if (settings.plugin.shareAll?.enable || otherRepoWithShareAll.length > 0) { const allExcludedFileName = otherRepoWithShareAll.map((repo) => repo.shareAll!.excludedFileName); allExcludedFileName.push(settings.plugin.shareAll!.excludedFileName); - if (allExcludedFileName.some(prefix => !file.basename.startsWith(prefix)) && !isExcludedPath(settings, file)) { - return true; + if (allExcludedFileName.some(prefix => !file.basename.startsWith(prefix))) { + return !isExcludedPath(settings, file, otherRepo); } } return false; @@ -100,16 +100,22 @@ export function isShared( * @param file {TFile} * @returns boolean */ -function isExcludedPath(settings: GitHubPublisherSettings, file: TFile):boolean { +export function isExcludedPath(settings: GitHubPublisherSettings, file: TFile | TFolder, repository: Repository | null):boolean { const excludedFolder = settings.plugin.excludedFolder; + if (settings.plugin.shareAll?.enable || repository?.shareAll?.enable) { + const excludedFromShare = repository?.shareAll?.excludedFileName ?? settings.plugin.shareAll?.excludedFileName; + if (excludedFromShare && file.name.startsWith(excludedFromShare)) { + return true; + } + } for (const folder of excludedFolder) { const isRegex = folder.match(FIND_REGEX); const regex = isRegex ? new RegExp(isRegex[1], isRegex[2]) : null; - if ((regex && regex.test(file.path)) || file.path.contains(folder.trim())) { + if ((regex?.test(file.path)) || file.path.contains(folder.trim())) { return true; } } - return false; + return isInDryRunFolder(settings, repository, file); } @@ -318,6 +324,7 @@ export async function checkRepositoryValidityWithRepoFrontmatter( numberOfFile: number=1 ): Promise { const settings = PublisherManager.settings; + if (settings.github.dryRun.enable) return true; try { /** * verify for each repoFrontmatter if verifiedRepo is true @@ -422,4 +429,18 @@ export function isFolderNote(properties: MultiProperties) { return filename === model; } return false; +} + +export function isInDryRunFolder(settings: GitHubPublisherSettings, repo: Repository | null, file: TFile | TFolder) { + if (!settings.github.dryRun.enable) return false; + const variables = { + owner: repo?.user ?? settings.github.user, + repo: repo?.repo ?? settings.github.repo, + branch: repo?.branch ?? settings.github.branch, + }; + const folder = settings.github.dryRun.folderName + .replace("{{owner}}", variables.owner) + .replace("{{repo}}", variables.repo) + .replace("{{branch}}", variables.branch); + return file.path.startsWith(normalizePath(folder)); } \ No newline at end of file diff --git a/src/utils/parse_frontmatter.ts b/src/utils/parse_frontmatter.ts index 519587be..116c096f 100644 --- a/src/utils/parse_frontmatter.ts +++ b/src/utils/parse_frontmatter.ts @@ -144,11 +144,15 @@ export function getRepoFrontmatter( branch: github.branch, repo: github.repo, owner: github.user, - autoclean: settings.upload.autoclean.enable, + autoclean: !settings.github.dryRun.enable && settings.upload.autoclean.enable, workflowName: github.workflow.name, commitMsg: github.workflow.commitMessage, automaticallyMergePR: github.automaticallyMergePR, verifiedRepo: github.verifiedRepo ?? false, + dryRun: { + ...settings.github.dryRun, + autoclean: settings.upload.autoclean.enable && settings.github.dryRun.enable + } }; if (settings.upload.behavior === FolderSettings.fixed) { repoFrontmatter.autoclean = false; @@ -220,15 +224,7 @@ function parseMultipleRepo( ) { for (const repo of frontmatter.multipleRepo) { if (typeof repo === "object") { - const repository: RepoFrontmatter = { - branch: repoFrontmatter.branch, - repo: repoFrontmatter.repo, - owner: repoFrontmatter.owner, - autoclean: false, - automaticallyMergePR: repoFrontmatter.automaticallyMergePR, - workflowName: repoFrontmatter.workflowName, - commitMsg: repoFrontmatter.commitMsg - }; + const repository: RepoFrontmatter = structuredClone(repoFrontmatter); if (repo.branch !== undefined) { repository.branch = repo.branch; } @@ -245,15 +241,7 @@ function parseMultipleRepo( } else { //is string const repoString = repo.split("/"); - const repository: RepoFrontmatter = { - branch: repoFrontmatter.branch, - repo: repoFrontmatter.repo, - owner: repoFrontmatter.owner, - autoclean: false, - automaticallyMergePR: repoFrontmatter.automaticallyMergePR, - workflowName: repoFrontmatter.workflowName, - commitMsg: repoFrontmatter.commitMsg - }; + const repository: RepoFrontmatter = structuredClone(repoFrontmatter); multipleRepo.push( repositoryStringSlice(repoString, repository) ); @@ -289,9 +277,9 @@ function multipleShortKeyRepo(frontmatter: FrontMatterCache, allRepo: Repository if (smartKey === "default") { multipleRepo.push(repoFrontmatter); } else { - const shortRepo = allRepo.filter((repo) => { + const shortRepo = allRepo.find((repo) => { return repo.smartKey.toLowerCase() === smartKey; - })[0]; + }); if (shortRepo) { let repo = { branch: shortRepo.branch, @@ -300,7 +288,8 @@ function multipleShortKeyRepo(frontmatter: FrontMatterCache, allRepo: Repository autoclean: repoFrontmatter.autoclean, automaticallyMergePR: shortRepo.automaticallyMergePR, workflowName: shortRepo.workflow.name, - commitMsg: shortRepo.workflow.commitMessage + commitMsg: shortRepo.workflow.commitMessage, + dryRun: repoFrontmatter.dryRun } as RepoFrontmatter; const parsedPath = parsePath(setting, shortRepo, repo); repo = Array.isArray(parsedPath) ? parsedPath[0] : parsedPath; @@ -329,16 +318,8 @@ function multipleShortKeyRepo(frontmatter: FrontMatterCache, allRepo: Repository */ function repositoryStringSlice(repo: string, repoFrontmatter: RepoFrontmatter): RepoFrontmatter { - const newRepo: RepoFrontmatter = { - branch: repoFrontmatter.branch, - repo: repoFrontmatter.repo, - owner: repoFrontmatter.owner, - autoclean: false, - automaticallyMergePR: repoFrontmatter.automaticallyMergePR, - workflowName: repoFrontmatter.workflowName, - commitMsg: repoFrontmatter.commitMsg - }; - if (repo.length >= 4) { + const newRepo: RepoFrontmatter = structuredClone(repoFrontmatter); + if (repo.length === 4) { newRepo.branch = repo[2]; newRepo.repo = repo[1]; newRepo.owner = repo[0];