Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dryRun settings #277

Merged
merged 15 commits into from
Jan 21, 2024
Merged
4 changes: 3 additions & 1 deletion src/GitHub/branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ export class GithubBranch extends FilesManagement {
* @returns {Promise<boolean>} True if the update is successful
*/
async updateRepository(
repoFrontmatter: RepoFrontmatter | RepoFrontmatter[]
repoFrontmatter: RepoFrontmatter | RepoFrontmatter[],
dryRun = false
): Promise<boolean> {
if (dryRun) return true;
repoFrontmatter = Array.isArray(repoFrontmatter)
? repoFrontmatter
: [repoFrontmatter];
Expand Down
96 changes: 93 additions & 3 deletions src/GitHub/delete.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -65,6 +65,7 @@ async function deleteFromGithubOneRepo(
repoProperties: MonoRepoProperties,
): Promise<Deleted> {
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,
Expand Down Expand Up @@ -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()) &&
Expand Down Expand Up @@ -283,7 +284,7 @@ async function checkIndexFiles(
{
owner: repoFrontmatter.owner,
repo: repoFrontmatter.repo,
path: path,
path,
}
);
if (fileRequest.status === 200) {
Expand All @@ -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;
}
2 changes: 1 addition & 1 deletion src/GitHub/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
127 changes: 96 additions & 31 deletions src/GitHub/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { Base64 } from "js-base64";
import {
arrayBufferToBase64,
MetadataCache,
normalizePath,
Notice,
TFile,
TFolder,
Vault,
} from "obsidian";

Expand Down Expand Up @@ -330,7 +332,7 @@ export default class Publisher {

const embeddedUploaded = embeded.uploaded;
embeddedUploaded.push(uploaded);
if (autoclean && repo.autoclean) {
if ((autoclean && repo.autoclean) || repo.dryRun.autoclean) {
Mara-Li marked this conversation as resolved.
Show resolved Hide resolved
deleted = await deleteFromGithub(
true,
this.branchName,
Expand Down Expand Up @@ -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);
Expand All @@ -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);

}
Expand All @@ -463,8 +493,35 @@ export default class Publisher {
text: string,
path: string,
title: string = "",
repoFrontmatter: RepoFrontmatter
repoFrontmatter: RepoFrontmatter,
): Promise<UploadedFiles | undefined> {
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(
Expand All @@ -491,6 +548,7 @@ export default class Publisher {
repoFrontmatter: RepoFrontmatter | RepoFrontmatter[]
): Promise<void> {
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);
Expand Down Expand Up @@ -521,6 +579,7 @@ export default class Publisher {
*/

async workflowGestion(repoFrontmatter: RepoFrontmatter): Promise<boolean> {
if (this.settings.github.dryRun.enable) return false;
let finished = false;
if (repoFrontmatter.workflowName.length === 0) {
return false;
Expand Down Expand Up @@ -579,42 +638,48 @@ export default class Publisher {
properties.frontmatter.general
);
const repoFrontmatter = properties.frontmatter;
try {
if (forcePushAttachment(file, this.settings)) {
newLinkedFiles.push(file);
continue;
}
//first search if the file exists
const response = await this.octokit.request(
"GET /repos/{owner}/{repo}/contents/{path}",
{
owner: repoFrontmatter.repo.owner,
repo: repoFrontmatter.repo.repo,
path: imagePath,
ref: this.branchName,
});
if (response.status === 200) {
const reply = await this.octokit.request(
"GET /repos/{owner}/{repo}/commits",
if (this.settings.github.dryRun.enable) {
newLinkedFiles.push(file);
continue;
}
if (!this.settings.github.dryRun.enable) {
try {
if (forcePushAttachment(file, this.settings)) {
newLinkedFiles.push(file);
continue;
}
//first search if the file exists
const response = await this.octokit.request(
"GET /repos/{owner}/{repo}/contents/{path}",
{
owner: repoFrontmatter.repo.owner,
repo: repoFrontmatter.repo.repo,
path: imagePath,
sha: this.branchName,
ref: this.branchName,
});
if (reply.status === 200) {
const data = reply.data;
const lastEditedInRepo = data[0]?.commit?.committer?.date;
const lastEditedDate = lastEditedInRepo ? new Date(lastEditedInRepo) : undefined;
const lastEditedAttachment = new Date(file.stat.mtime);
//if the file in the vault is newer than the file in the repo, push it
if (lastEditedDate && lastEditedAttachment > lastEditedDate || !lastEditedDate) {
newLinkedFiles.push(file);
} else logs({ settings: this.settings }, i18next.t("error.alreadyExists", { file: file.name }));
if (response.status === 200) {
const reply = await this.octokit.request(
"GET /repos/{owner}/{repo}/commits",
{
owner: repoFrontmatter.repo.owner,
repo: repoFrontmatter.repo.repo,
path: imagePath,
sha: this.branchName,
});
if (reply.status === 200) {
const data = reply.data;
const lastEditedInRepo = data[0]?.commit?.committer?.date;
const lastEditedDate = lastEditedInRepo ? new Date(lastEditedInRepo) : undefined;
const lastEditedAttachment = new Date(file.stat.mtime);
//if the file in the vault is newer than the file in the repo, push it
if (lastEditedDate && lastEditedAttachment > lastEditedDate || !lastEditedDate) {
newLinkedFiles.push(file);
} else logs({ settings: this.settings }, i18next.t("error.alreadyExists", { file: file.name }));
}
}
} catch (e) {
newLinkedFiles.push(file);
Mara-Li marked this conversation as resolved.
Show resolved Hide resolved
}
} catch (e) {
newLinkedFiles.push(file);
}
} else {
newLinkedFiles.push(file);
Expand Down
29 changes: 16 additions & 13 deletions src/commands/file_menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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", {
Expand Down
Loading