├── src ├── utils │ ├── constants.ts │ ├── notifier.ts │ └── logger.ts ├── types.ts ├── services │ ├── gitignoreService.ts │ └── gitService.ts ├── views │ ├── modals │ │ └── unmergedFilesView.ts │ └── settingsTab.ts ├── controllers │ └── syncController.ts └── main.ts ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── release.yml ├── .eslintignore ├── styles.css ├── .editorconfig ├── versions.json ├── ROADMAP.md ├── manifest.json ├── .release-it.json ├── .eslintrc ├── tsconfig.json ├── flake.nix ├── scripts ├── update-project-version.mjs └── esbuild.config.mjs ├── LICENSE ├── package.json ├── flake.lock ├── FAQ.md ├── README.md ├── .gitignore ├── CONTRIBUTING.md └── CHANGELOG.md /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const PLUGIN_NAME = "YAOS"; 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # node modules 2 | node_mdules 3 | 4 | # outputted build file 5 | main.js 6 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .yaos-title { 2 | margin-top: 0; 3 | } 4 | 5 | .yaos-conflicting-files { 6 | margin-top: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type BooleanKeys = { 2 | [K in keyof T]: T[K] extends boolean ? K : never; 3 | }[keyof T]; 4 | -------------------------------------------------------------------------------- /src/utils/notifier.ts: -------------------------------------------------------------------------------- 1 | import { Notice } from "obsidian"; 2 | 3 | export const notifyUserAboutFailure = (message: string) => 4 | new Notice(`FATAL: ${message}`); 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | tab_width = 2 10 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.4.0": "0.15.0", 3 | "0.4.2": "0.15.0", 4 | "0.5.0": "0.15.0", 5 | "0.6.0": "0.15.0", 6 | "0.6.1": "0.15.0", 7 | "0.6.2": "0.15.0", 8 | "0.6.3": "0.15.0", 9 | "0.7.0": "0.15.0" 10 | } -------------------------------------------------------------------------------- /src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import { PLUGIN_NAME } from "@/utils/constants"; 2 | 3 | import { LogLevels, consola } from "consola"; 4 | 5 | const logger = consola.create({ level: LogLevels.debug }).withTag(PLUGIN_NAME); 6 | 7 | export default logger; 8 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap 2 | 3 | This file provides an overview of the direction that this project is heading. These are features that need to be added at some point. 4 | 5 | - Initialize the Git repository for the user 6 | - Set the upstream branch for the user 7 | - Ensure that the user is authenticated using `ssh` and not `https` 8 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "yet-another-obsidian-synchronizer", 3 | "name": "Yet Another Obsidian Synchronizer", 4 | "description": "This is yet another obsidian synchronizer that uses Git to synchronize your vault contents across devices.", 5 | "version": "0.7.0", 6 | "minAppVersion": "0.15.0", 7 | "author": "Mahyar Mirrashed", 8 | "authorUrl": "https://github.com/mahyarmirrashed", 9 | "isDesktopOnly": true 10 | } -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "after:bump": "yarn bump" 4 | }, 5 | "git": { 6 | "requireBranch": "main", 7 | "commitMessage": "chore(release): ${version}", 8 | "tagName": "${version}", 9 | "tagAnnotation": "Release v${version}" 10 | }, 11 | "github": { 12 | "releaseName": "${version}", 13 | "preRelease": true, 14 | "draft": true 15 | }, 16 | "npm": { 17 | "publish": false 18 | }, 19 | "plugins": { 20 | "@release-it/conventional-changelog": { 21 | "infile": "CHANGELOG.md", 22 | "preset": "angular" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "env": { "node": true }, 5 | "plugins": ["@typescript-eslint", "no-loops"], 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/eslint-recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parserOptions": { 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | "no-console": "error", 16 | "no-unused-vars": "off", 17 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 18 | "no-prototype-builtins": "off", 19 | "no-loops/no-loops": "error" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Language and Environment */ 4 | "target": "ESNext", 5 | "lib": ["DOM", "ES5", "ES6", "ES7"], 6 | 7 | /* Modules */ 8 | "module": "ESNext", 9 | "moduleResolution": "node", 10 | "baseUrl": ".", 11 | "paths": { 12 | "@/*": ["src/*"] 13 | }, 14 | 15 | /* Emit */ 16 | "newLine": "lf", 17 | 18 | /* Interop Constraints */ 19 | "esModuleInterop": true, 20 | "forceConsistentCasingInFileNames": true, 21 | 22 | /* Type Checking */ 23 | "strict": true, 24 | 25 | /* Completeness */ 26 | "skipLibCheck": true 27 | }, 28 | "include": ["**/*.ts"] 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature]: " 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Flake for github:mahyarmirrashed/yaos"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = 10 | { 11 | self, 12 | nixpkgs, 13 | flake-utils, 14 | }: 15 | flake-utils.lib.eachDefaultSystem ( 16 | system: 17 | let 18 | pkgs = nixpkgs.legacyPackages.${system}; 19 | in 20 | { 21 | devShells.default = pkgs.mkShell { 22 | packages = with pkgs; [ 23 | nodejs-slim_22 24 | pnpm 25 | ]; 26 | shellHook = '' 27 | export PATH="$PATH:$(pnpm bin)" 28 | ''; 29 | }; 30 | } 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/services/gitignoreService.ts: -------------------------------------------------------------------------------- 1 | import { GitService } from "@/services/gitService"; 2 | import logger from "@/utils/logger"; 3 | 4 | import { resolve } from "path"; 5 | 6 | const GITIGNORE_FILE_NAME = ".gitignore"; 7 | 8 | export class GitignoreService { 9 | private gitignorePath: string; 10 | 11 | constructor(basePath: string, private gitService: GitService) { 12 | logger.debug("Initializing GitignoreService..."); 13 | this.gitignorePath = resolve(basePath, GITIGNORE_FILE_NAME); 14 | logger.debug("GitignoreService initialized."); 15 | } 16 | 17 | private async stageCommitAndPushGitignore(message: string) { 18 | await this.gitService.gitStage(GITIGNORE_FILE_NAME); 19 | await this.gitService.gitCommit(message); 20 | await this.gitService.gitPush(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scripts/update-project-version.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "fs"; 2 | import { dirname, resolve } from "path"; 3 | import { fileURLToPath } from "url"; 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)); 6 | 7 | const packageJsonPath = resolve(__dirname, "..", "package.json"); 8 | const manifestJsonPath = resolve(__dirname, "..", "manifest.json"); 9 | const versionsJsonPath = resolve(__dirname, "..", "versions.json"); 10 | 11 | const packageJson = JSON.parse(readFileSync(packageJsonPath)); 12 | let manifestJson = JSON.parse(readFileSync(manifestJsonPath)); 13 | let versionsJson = JSON.parse(readFileSync(versionsJsonPath)); 14 | 15 | const { version: newVersion } = packageJson; 16 | const { minAppVersion } = manifestJson; 17 | 18 | manifestJson.version = newVersion; 19 | versionsJson[newVersion] = minAppVersion; 20 | 21 | writeFileSync(manifestJsonPath, JSON.stringify(manifestJson, null, " ")); 22 | writeFileSync(versionsJsonPath, JSON.stringify(versionsJson, null, " ")); 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help remove pesky bugs 4 | title: "[Bug]: " 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | It is really helpful in diagnosing the issue if you can share the YAOS logs from the developer settings inside of Obsidian. This keyboard shortcut to opening it should be . 27 | 28 | **Desktop (please complete the following information):** 29 | 30 | - OS: [e.g. Windows, MacOS, Linux] 31 | - Obsidian version: 32 | - YAOS version: 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Mahyar Mirrashed 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup NodeJS 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: "16" 21 | 22 | - name: Install dependencies 23 | run: yarn install 24 | 25 | - name: Build production artifacts 26 | run: yarn dev production 27 | 28 | - name: Get version 29 | id: package 30 | run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV 31 | 32 | - name: Create Release and Upload Assets 33 | uses: softprops/action-gh-release@v1 34 | with: 35 | token: ${{ secrets.PAT }} 36 | name: ${{ env.VERSION }} 37 | draft: true 38 | prerelease: false 39 | files: | 40 | ./main.js 41 | ./manifest.json 42 | ./styles.css 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yaos", 3 | "version": "0.7.0", 4 | "description": "Yet Another Obsidian Synchronizer", 5 | "main": "main.js", 6 | "repository": "git@github.com:mahyarmirrashed/yaos.git", 7 | "author": "Mahyar Mirrashed ", 8 | "license": "MIT", 9 | "private": false, 10 | "scripts": { 11 | "bump": "node scripts/update-project-version.mjs", 12 | "dev": "node scripts/esbuild.config.mjs", 13 | "format": "npx prettier --write scripts/ src/", 14 | "release": "release-it" 15 | }, 16 | "devDependencies": { 17 | "@release-it/conventional-changelog": "^5.1.1", 18 | "@types/node": "^20.3.3", 19 | "@typescript-eslint/eslint-plugin": "^5.60.1", 20 | "@typescript-eslint/parser": "^5.60.1", 21 | "builtin-modules": "^3.3.0", 22 | "esbuild": "^0.18.11", 23 | "eslint": "^8.44.0", 24 | "eslint-plugin-no-loops": "^0.3.0", 25 | "obsidian": "^1.2.8", 26 | "prettier": "^2.8.8", 27 | "prettier-plugin-organize-imports": "^3.2.2", 28 | "release-it": "^15.11.0", 29 | "tslib": "^2.6.0", 30 | "typescript": "^5.1.6" 31 | }, 32 | "dependencies": { 33 | "consola": "^3.2.2", 34 | "dayjs": "^1.11.9", 35 | "simple-git": "^3.19.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/views/modals/unmergedFilesView.ts: -------------------------------------------------------------------------------- 1 | import { GitService } from "@/services/gitService"; 2 | 3 | import { App, Modal } from "obsidian"; 4 | 5 | export default class UnmergedFilesView extends Modal { 6 | constructor(app: App, private gitService?: GitService) { 7 | super(app); 8 | } 9 | 10 | onOpen(): void { 11 | this.contentEl.empty(); 12 | 13 | this.contentEl.createEl("h3", { 14 | text: "Loading...", 15 | cls: "yaos-title", 16 | }); 17 | 18 | this.gitService?.getConflictingFiles().then((files) => { 19 | this.contentEl.empty(); 20 | 21 | if (files.length > 0) { 22 | this.contentEl.createEl("h3", { 23 | text: "Please resolve the conflicts in the following files:", 24 | cls: "yaos-title", 25 | }); 26 | 27 | const list = this.contentEl.createEl("ul"); 28 | 29 | files.forEach((file) => 30 | list 31 | .createEl("li") 32 | .createEl("strong", { text: file, cls: "yaos-conflicting-files" }) 33 | ); 34 | } else { 35 | this.contentEl.createEl("h3", { 36 | text: "No unmerged files to show!", 37 | cls: "yaos-title", 38 | }); 39 | } 40 | }); 41 | } 42 | 43 | onClose(): void { 44 | this.contentEl.empty(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /scripts/esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import builtins from "builtin-modules"; 2 | import esbuild from "esbuild"; 3 | import { dirname, resolve } from "path"; 4 | import process from "process"; 5 | import { fileURLToPath } from "url"; 6 | 7 | const __dirname = dirname(fileURLToPath(import.meta.url)); 8 | 9 | const PRODUCTION = "production"; 10 | 11 | const buildingForProduction = process.argv[2] === PRODUCTION; 12 | 13 | const entryPoint = resolve(__dirname, "..", "src", "main.ts"); 14 | const outFile = resolve(__dirname, "..", "main.js"); 15 | 16 | const context = await esbuild.context({ 17 | entryPoints: [entryPoint], 18 | bundle: true, 19 | external: [ 20 | "obsidian", 21 | "electron", 22 | "@codemirror/autocomplete", 23 | "@codemirror/collab", 24 | "@codemirror/commands", 25 | "@codemirror/language", 26 | "@codemirror/lint", 27 | "@codemirror/search", 28 | "@codemirror/state", 29 | "@codemirror/view", 30 | "@lezer/common", 31 | "@lezer/highlight", 32 | "@lezer/lr", 33 | ...builtins, 34 | ], 35 | format: "cjs", 36 | target: "es2018", 37 | logLevel: "info", 38 | sourcemap: buildingForProduction ? false : "inline", 39 | treeShaking: true, 40 | outfile: outFile, 41 | }); 42 | 43 | if (buildingForProduction) { 44 | await context.rebuild(); 45 | process.exit(0); 46 | } else { 47 | await context.watch(); 48 | } 49 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1757545623, 24 | "narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixos-25.05", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions (FAQ) 2 | 3 | ## How do I install Git on my machine? 4 | 5 | ### Windows 6 | 7 | 1. Download the official Git for Windows installer from the [Git website](https://git-scm.com/download/win); and 8 | 2. Run the installer and follow the prompts to install Git. 9 | 10 | ### macOS 11 | 12 | 1. If you have installed Xcode (or its Command Line Tools), Git may already be installed. You can check by opening Terminal and typing `git --version`; 13 | 2. If Git is not installed, you can install it using Homebrew. First, install Homebrew by following the instructions on the [Homebrew website](https://brew.sh). Then, install Git by typing: brew `install git`. 14 | 15 | ### Linux 16 | 17 | 1. Open a terminal; 18 | 2. Install Git using your distribution's package manager. For example, on Ubuntu or Debian, you would type `sudo apt install git`. 19 | 20 | ## How do I create a new Git Repository? 21 | 22 | 1. Open a terminal; 23 | 2. Navigate to the directory where you want to create the repository; and 24 | 3. Type `git init` to initialize a new Git repository. 25 | 26 | You can find more detailed instructions in the [Git Documentation](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository#_git_cloning) or [GitHub's Help Pages](https://docs.github.com/en/get-started/quickstart/create-a-repo). 27 | 28 | ## How do I clone an existing Git Repository? 29 | 30 | 1. Open a terminal; 31 | 2. Navigate to the directory where you want to clone the repository; 32 | 3. Type `git clone [url]`, replacing `[url]` with the URL of the Git repository you want to clone. 33 | 34 | You can find more detailed instructions in the [Git Documentation](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository#_git_cloning) or [GitHub's Help Pages](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository). 35 | 36 | ## How do I use YAOS with my Obsidian Vault? 37 | 38 | After you have installed Git and initialized or cloned a Git repository for your Obsidian vault, you can use YAOS to sync your vault with the repository. Simply press the "Sync" button in Obsidian to sync your vault. If there are conflicts between your local vault and the remote repository, YAOS will prompt you to resolve these conflicts. 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YAOS (Yet Another Obsidian Synchronizer) :arrows_clockwise: 2 | 3 | ![Obsidian Downloads](https://img.shields.io/badge/dynamic/json?logo=obsidian&color=%23483699&label=downloads&query=%24%5B%22yet-another-obsidian-synchronizer%22%5D.downloads&url=https%3A%2F%2Fraw.githubusercontent.com%2Fobsidianmd%2Fobsidian-releases%2Fmaster%2Fcommunity-plugin-stats.json) 4 | 5 | YAOS is a minimalist Obsidian plugin designed to provide a simple and intuitive Git synchronization experience for your Obsidian vault. It's inspired by the simplicity of Obsidian's paid sync functionality and aims to bring that same ease of use to users who prefer to privately manage their vault with Git. 6 | 7 | ## Features 8 | 9 | - **Single Sync Button**: YAOS provides a single sync button that handles all your Git operations. No need to worry about multiple commands or complex workflows. 10 | - **Conflict Resolution**: If YAOS detects conflicts between your local vault and the remote repository, it will prompt you to resolve these conflicts before proceeding with the sync. 11 | - **Minimalist Design**: YAOS is designed with minimalism in mind. It provides just the features that you need to manage your vault with Git, without any added unnecessary complexity. 12 | 13 | ## Prerequisites 14 | 15 | YAOS assumes that you have a basic understanding of Git and that you have initialized and cloned a Git repository for your Obsidian vault on your local machine. 16 | 17 | ## Usage 18 | 19 | After installing the plugin, you'll find a new "Sync" button in Obsidian. Simply press this button to sync your vault with your remote Git repository. 20 | 21 | If there are conflicts between your local vault and the remote repository that cannot be resolved automagically with Git, YAOS will prompt you to resolve these conflicts manually. Once you're resolved the conflicts, press the "Sync" button again to complete the sync process. 22 | 23 | ## Installation 24 | 25 | To install YAOS, follow these steps: 26 | 27 | 1. In Obsidian, open "Settings" → "Community plugins". 28 | 2. Click "Browse" and search for "Yet Another Obsidian Synchronizer". 29 | 3. Click "Install". 30 | 4. In the "Installed plugins" section, enable "Yet Another Obsidian Synchronizer". 31 | 32 | ## Contributing 33 | 34 | Contributions to YAOS are welcome! Please see the [CONTRIBUTING.md](./CONTRIBUTING.md) file for more information. 35 | 36 | ## License 37 | 38 | YAOS is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information. 39 | 40 | ## Support 41 | 42 | If you encounter any issues or have any questions about YAOS, please open an issue on the GitHub repository. 43 | 44 | --- 45 | 46 | I hope you find YAOS useful in your Obsidian workflow. Happy writing! 47 | -------------------------------------------------------------------------------- /src/controllers/syncController.ts: -------------------------------------------------------------------------------- 1 | import { GitService } from "@/services/gitService"; 2 | import { GitignoreService } from "@/services/gitignoreService"; 3 | import logger from "@/utils/logger"; 4 | import { notifyUserAboutFailure } from "@/utils/notifier"; 5 | 6 | import { Notice } from "obsidian"; 7 | 8 | export default class SyncController { 9 | constructor( 10 | private gitService: GitService, 11 | private gitignoreService: GitignoreService 12 | ) { 13 | logger.debug("Initialized sync controller."); 14 | } 15 | 16 | private async createVaultBackup() { 17 | if (await this.gitService.unstagedChangesExist()) { 18 | await this.handleUnstagedChanges(); 19 | } else { 20 | logger.info("No changes in vault detected for backup."); 21 | } 22 | 23 | if (await this.gitService.isLocalAhead()) { 24 | await this.handleLocalAhead(); 25 | } 26 | } 27 | 28 | private async handleLocalAhead() { 29 | try { 30 | await this.gitService.gitPush(); 31 | 32 | this.notifyUserAboutBackup(); 33 | } catch { 34 | logger.warn("Remote and local have conflicting changes!"); 35 | logger.info("Starting rebase process..."); 36 | 37 | try { 38 | await this.gitService.gitPullWithRebase(); 39 | await this.gitService.gitPush(); 40 | 41 | this.notifyUserAboutBackup(); 42 | 43 | logger.success("Created vault backup."); 44 | } catch { 45 | this.notifyUserAboutConflicts(); 46 | 47 | logger.error("Automatic rebasing failed."); 48 | } 49 | } 50 | } 51 | 52 | private async handleUnstagedChanges() { 53 | logger.info("Unstaged changes detected. Creating backup..."); 54 | 55 | await this.gitService.gitStageAll(); 56 | await this.gitService.gitCommit(); 57 | } 58 | 59 | private notifyUserAboutBackup() { 60 | new Notice("Successfully backed up vault!"); 61 | } 62 | 63 | private notifyUserAboutConflicts() { 64 | new Notice("Your local and remote repositories had conflicting changes."); 65 | new Notice("Please fix the changes and then click the sync button again."); 66 | } 67 | 68 | async sync() { 69 | if (await this.gitService.isRepo()) { 70 | logger.debug("Vault is initialized as a Git repository."); 71 | } else { 72 | logger.fatal("Vault is not initialized as a Git repository."); 73 | notifyUserAboutFailure("Vault is not a Git repository."); 74 | return; 75 | } 76 | 77 | if (await this.gitService.isRemoteConfigured()) { 78 | logger.debug("Remote repository is configured."); 79 | } else { 80 | logger.fatal("Remote repository is not configured."); 81 | notifyUserAboutFailure("Remote repository is not configured."); 82 | return; 83 | } 84 | 85 | if (await this.gitService.isRebasing()) { 86 | logger.debug("Stopping in progress rebase."); 87 | 88 | await this.gitService.stopRebasing(); 89 | } 90 | 91 | await this.createVaultBackup(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | # obsidian 133 | data.json 134 | 135 | # macOS finder view states 136 | .DS_Store 137 | 138 | # vscode 139 | .vscode 140 | 141 | # exclude compiled main.js file 142 | main.js 143 | 144 | # exclude sourcemaps 145 | *.map 146 | 147 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import SyncController from "@/controllers/syncController"; 2 | import { GitService, SimpleGitService } from "@/services/gitService"; 3 | import { GitignoreService } from "@/services/gitignoreService"; 4 | 5 | import { PLUGIN_NAME } from "@/utils/constants"; 6 | import logger from "@/utils/logger"; 7 | import { notifyUserAboutFailure } from "@/utils/notifier"; 8 | 9 | import UnmergedFilesView from "@/views/modals/unmergedFilesView"; 10 | import YaosSettingTab, { 11 | DEFAULT_YAOS_SETTINGS, 12 | YaosSettings, 13 | } from "@/views/settingsTab"; 14 | 15 | import { FileSystemAdapter, Notice, Plugin } from "obsidian"; 16 | 17 | const PLUGIN_ICON = "sync"; 18 | 19 | export default class YaosPlugin extends Plugin { 20 | private gitService?: GitService; 21 | private gitignoreService?: GitignoreService; 22 | private syncController?: SyncController; 23 | 24 | settings: YaosSettings = DEFAULT_YAOS_SETTINGS; 25 | 26 | async onload() { 27 | logger.debug("Initializing plugin..."); 28 | 29 | try { 30 | await this.loadSettings(); 31 | 32 | logger.info("Loaded settings."); 33 | } catch { 34 | logger.error("Failed to load settings."); 35 | } 36 | 37 | const adapter = this.app.vault.adapter; 38 | 39 | if (adapter instanceof FileSystemAdapter) { 40 | const basePath = adapter.getBasePath(); 41 | 42 | this.gitService = new SimpleGitService(basePath, this.settings); 43 | this.gitignoreService = new GitignoreService(basePath, this.gitService); 44 | this.syncController = new SyncController( 45 | this.gitService, 46 | this.gitignoreService 47 | ); 48 | 49 | this.addCommand({ 50 | id: "show-unmerged", 51 | name: "Show unmerged/conflicting files", 52 | callback: () => new UnmergedFilesView(this.app, this.gitService).open(), 53 | }); 54 | this.addCommand({ 55 | id: "sync", 56 | name: "Sync your vault", 57 | callback: async () => { 58 | await this.syncVault(); 59 | }, 60 | }); 61 | this.addRibbonIcon(PLUGIN_ICON, PLUGIN_NAME, this.syncVault.bind(this)); 62 | this.addSettingTab(new YaosSettingTab(this.app, this)); 63 | 64 | logger.debug("Plugin initialized."); 65 | } else { 66 | notifyUserAboutFailure("Adapter type is not recognized."); 67 | 68 | logger.fatal("Logger type was not FileSystemAdapter."); 69 | } 70 | } 71 | 72 | async loadSettings() { 73 | this.settings = Object.assign( 74 | {}, 75 | DEFAULT_YAOS_SETTINGS, 76 | await this.loadData() 77 | ); 78 | 79 | if (this.settings.deviceName === "") 80 | this.settings.deviceName = DEFAULT_YAOS_SETTINGS.deviceName; 81 | } 82 | 83 | async saveSettings(settings = this.settings) { 84 | await this.saveData(settings); 85 | } 86 | 87 | private async syncVault(_evt?: MouseEvent) { 88 | if (!this.gitService || !this.gitignoreService) { 89 | logger.fatal("Services were not initialized."); 90 | } else if (!this.syncController) { 91 | logger.fatal("Sync controller was not initialized."); 92 | } else { 93 | try { 94 | await this.syncController.sync(); 95 | } catch (e) { 96 | new Notice("Unknown error occurred. Please create an issue."); 97 | 98 | logger.fatal("Unknown error:", e); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/services/gitService.ts: -------------------------------------------------------------------------------- 1 | import logger from "@/utils/logger"; 2 | import { YaosSettings } from "@/views/settingsTab"; 3 | 4 | import dayjs from "dayjs"; 5 | import simpleGit, { SimpleGit } from "simple-git"; 6 | 7 | const DEFAULT_REMOTE = "origin"; 8 | const DEFAULT_BRANCH = "main"; 9 | 10 | const CURRENT_TIME = () => dayjs().format("YYYY-MM-DDTHH:mm"); 11 | const COMMIT_MESSAGE = (deviceName: string) => 12 | `chore: vault backup from ${deviceName} at ${CURRENT_TIME()}`; 13 | 14 | export interface GitService { 15 | settings: YaosSettings; 16 | 17 | gitCommit(message?: string): Promise; 18 | gitPullWithRebase(): Promise; 19 | gitPush(forcePush?: boolean): Promise; 20 | gitStage(...files: string[]): Promise; 21 | gitStageAll(): Promise; 22 | 23 | isLocalAhead(): Promise; 24 | isRebasing(): Promise; 25 | isRemoteConfigured(): Promise; 26 | isRepo(): Promise; 27 | 28 | getConflictingFiles(): Promise; 29 | stopRebasing(): Promise; 30 | unstagedChangesExist(): Promise; 31 | } 32 | 33 | export class SimpleGitService implements GitService { 34 | private gitProvider: SimpleGit; 35 | 36 | constructor(repoPath: string, public settings: YaosSettings) { 37 | logger.debug("Initializing SimpleGitService..."); 38 | this.gitProvider = simpleGit(repoPath); 39 | logger.debug("SimpleGitService initialized."); 40 | } 41 | 42 | async gitCommit(message = COMMIT_MESSAGE(this.settings.deviceName)) { 43 | logger.info(`Committing... ${message}`); 44 | 45 | await this.gitProvider.commit(message); 46 | } 47 | 48 | async gitPullWithRebase() { 49 | await this.gitProvider.pull(["--rebase"]); 50 | } 51 | 52 | async gitPush(forcePush = false) { 53 | const options = forcePush ? ["-f"] : []; 54 | 55 | await this.gitProvider.push(DEFAULT_REMOTE, DEFAULT_BRANCH, options); 56 | 57 | logger.info(`Pushed changes to ${DEFAULT_REMOTE}/${DEFAULT_BRANCH}.`); 58 | } 59 | 60 | async gitStage(...files: string[]) { 61 | await Promise.all(files.map((file) => this.gitProvider.add(file))); 62 | } 63 | 64 | async gitStageAll() { 65 | await this.gitProvider.add("./*"); 66 | } 67 | 68 | async isLocalAhead() { 69 | return this.gitProvider.status().then((status) => status.ahead > 0); 70 | } 71 | 72 | async isRebasing() { 73 | return this.gitProvider 74 | .raw(["status"]) 75 | .then((status) => status.includes("rebase")); 76 | } 77 | 78 | async isRemoteConfigured() { 79 | let remoteConfigured = false; 80 | 81 | try { 82 | const remotes = await this.gitProvider.listRemote(["--get-url"]); 83 | remoteConfigured = !!remotes; 84 | } catch { 85 | remoteConfigured = false; 86 | } 87 | 88 | return remoteConfigured; 89 | } 90 | 91 | async isRepo() { 92 | return this.gitProvider.checkIsRepo(); 93 | } 94 | 95 | async getConflictingFiles() { 96 | return this.gitProvider.status().then((status) => status.conflicted); 97 | } 98 | 99 | async stopRebasing() { 100 | process.env.GIT_EDITOR = "true"; 101 | 102 | await this.gitStageAll(); 103 | await this.gitProvider.rebase(["--continue"]); 104 | await this.gitPush(); 105 | 106 | process.env.GIT_EDITOR = undefined; 107 | } 108 | 109 | async unstagedChangesExist() { 110 | return this.gitProvider 111 | .status() 112 | .then((status) => status.files.length > 0 || status.not_added.length > 0); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/views/settingsTab.ts: -------------------------------------------------------------------------------- 1 | import YaosPlugin from "@/main"; 2 | import { BooleanKeys } from "@/types"; 3 | 4 | import { App, PluginSettingTab, Setting } from "obsidian"; 5 | 6 | import os from "os"; 7 | 8 | const GITHUB_ISSUE_LINK = 9 | "https://github.com/mahyarmirrashed/yaos/issues/new/choose"; 10 | 11 | export interface YaosSettings { 12 | deviceName: string; 13 | syncImages: boolean; 14 | syncAudio: boolean; 15 | syncVideos: boolean; 16 | syncPdfs: boolean; 17 | syncOtherFiles: boolean; 18 | syncMainSettings: boolean; 19 | syncAppearanceSettings: boolean; 20 | syncThemesAndSnippets: boolean; 21 | syncHotkeys: boolean; 22 | syncCorePluginSettings: boolean; 23 | syncCommunityPluginSettings: boolean; 24 | } 25 | 26 | type YaosSettingOptions> = { 27 | propertyName: K; 28 | settingName: string; 29 | settingDesc: string; 30 | }; 31 | 32 | export const DEFAULT_YAOS_SETTINGS: YaosSettings = { 33 | deviceName: os.hostname(), 34 | syncImages: false, 35 | syncAudio: false, 36 | syncVideos: false, 37 | syncPdfs: false, 38 | syncOtherFiles: false, 39 | syncMainSettings: false, 40 | syncAppearanceSettings: false, 41 | syncThemesAndSnippets: false, 42 | syncHotkeys: false, 43 | syncCorePluginSettings: false, 44 | syncCommunityPluginSettings: false, 45 | }; 46 | 47 | export default class YaosSettingTab extends PluginSettingTab { 48 | plugin: YaosPlugin; 49 | 50 | constructor(app: App, plugin: YaosPlugin) { 51 | super(app, plugin); 52 | this.plugin = plugin; 53 | } 54 | 55 | display() { 56 | const { containerEl } = this; 57 | 58 | containerEl.empty(); 59 | 60 | this.addGeneralSection(containerEl); 61 | } 62 | 63 | private addGeneralSection(el: HTMLElement) { 64 | el.createEl("h2", { text: "General" }); 65 | this.addDeviceNameSetting(el); 66 | this.addCreateIssueSetting(el); 67 | } 68 | 69 | private addSelectiveSection(el: HTMLElement) { 70 | el.createEl("h2", { text: "Selective sync" }); 71 | this.addToggleSetting(el, { 72 | propertyName: "syncImages", 73 | settingName: "Sync images", 74 | settingDesc: 75 | "Sync image files with these extensions: bmp, png, jpg, jpeg, gif, svg, webp.", 76 | }); 77 | this.addToggleSetting(el, { 78 | propertyName: "syncAudio", 79 | settingName: "Sync audio", 80 | settingDesc: 81 | "Sync audio files with these extensions: mp3, wav, m4a, 3gp, flac, ogg, oga, opus.", 82 | }); 83 | this.addToggleSetting(el, { 84 | propertyName: "syncVideos", 85 | settingName: "Sync videos", 86 | settingDesc: 87 | "Sync video files with these extensions: mp4, webm, ogv, mov, mkv.", 88 | }); 89 | this.addToggleSetting(el, { 90 | propertyName: "syncPdfs", 91 | settingName: "Sync PDFs", 92 | settingDesc: "Sync PDF files.", 93 | }); 94 | this.addToggleSetting(el, { 95 | propertyName: "syncOtherFiles", 96 | settingName: "Sync all other types", 97 | settingDesc: "Sync unsupported file types.", 98 | }); 99 | } 100 | 101 | private addVaultSection(el: HTMLElement) { 102 | el.createEl("h2", { text: "Vault configuration sync" }); 103 | this.addDisabledToggleSetting(el, { 104 | propertyName: "syncMainSettings", 105 | settingName: "Sync main settings", 106 | settingDesc: "Sync editor settings, files, link settings, and others.", 107 | }); 108 | this.addDisabledToggleSetting(el, { 109 | propertyName: "syncAppearanceSettings", 110 | settingName: "Sync appearance settings", 111 | settingDesc: 112 | "Sync appearance settings like dark mode, active theme, and enabled snippets.", 113 | }); 114 | this.addDisabledToggleSetting(el, { 115 | propertyName: "syncThemesAndSnippets", 116 | settingName: "Sync themes and snippets", 117 | settingDesc: 118 | "Sync downloaded themes and snippets. Whether they are enabled depends on the previous setting.", 119 | }); 120 | this.addDisabledToggleSetting(el, { 121 | propertyName: "syncHotkeys", 122 | settingName: "Sync hotkeys", 123 | settingDesc: "Sync custom hotkeys.", 124 | }); 125 | this.addDisabledToggleSetting(el, { 126 | propertyName: "syncCorePluginSettings", 127 | settingName: "Sync core plugin settings", 128 | settingDesc: "Sync core plugin settings.", 129 | }); 130 | this.addDisabledToggleSetting(el, { 131 | propertyName: "syncCommunityPluginSettings", 132 | settingName: "Sync community plugin settings", 133 | settingDesc: "Sync core plugin settings.", 134 | }); 135 | } 136 | 137 | private addDeviceNameSetting(el: HTMLElement) { 138 | new Setting(el) 139 | .setName("Device name") 140 | .setDesc( 141 | "This name will be displayed in the commit messages to indicate the sync source. Leave empty to use the default name." 142 | ) 143 | .addText((text) => 144 | text 145 | .setPlaceholder(DEFAULT_YAOS_SETTINGS.deviceName) 146 | .setValue(this.plugin.settings.deviceName) 147 | .onChange(async (deviceName) => { 148 | this.plugin.settings.deviceName = deviceName; 149 | await this.plugin.saveSettings(); 150 | }) 151 | ); 152 | } 153 | 154 | private addCreateIssueSetting(el: HTMLElement) { 155 | new Setting(el) 156 | .setName("Contact support") 157 | .setDesc( 158 | "If you run into any issues working with this plugin, please let us know by creating an issue on our GitHub page." 159 | ) 160 | .addButton((button) => 161 | button 162 | .setButtonText("Create issue") 163 | .setTooltip("Create an issue on GitHub") 164 | .setCta() 165 | .onClick(() => self.open(GITHUB_ISSUE_LINK, "_blank", "norefferrer")) 166 | ); 167 | } 168 | 169 | private addDisabledToggleSetting>( 170 | el: HTMLElement, 171 | options: YaosSettingOptions 172 | ) { 173 | this.addToggleSetting(el, options, true); 174 | } 175 | 176 | private addToggleSetting>( 177 | el: HTMLElement, 178 | options: YaosSettingOptions, 179 | disabled = false 180 | ) { 181 | const { propertyName, settingName, settingDesc } = options; 182 | 183 | new Setting(el) 184 | .setName(settingName) 185 | .setDesc(settingDesc) 186 | .addToggle((toggle) => 187 | toggle 188 | .setValue(disabled ? true : this.plugin.settings[propertyName]) 189 | .setDisabled(disabled) 190 | .onChange(async (value) => { 191 | this.plugin.settings[propertyName] = value; 192 | await this.plugin.saveSettings(); 193 | }) 194 | ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contributing to YAOS 4 | 5 | First off, thanks for taking the time to contribute! :heart: 6 | 7 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It makes it a lot easier for me and smooth out the experience for all involved. The community looks forward to your contributions. :tada: 8 | 9 | > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which I would also be very happy about: 10 | > 11 | > - Star the project :star: 12 | > - Tweet about it :bird: 13 | > - Refer this project in your project's `README.md` 14 | > - Mention the project at local meetups and tell your friends/colleagues 15 | 16 | 17 | 18 | ## Table of Contents 19 | 20 | - [I Have a Question](#i-have-a-question) 21 | - [I Want To Contribute](#i-want-to-contribute) 22 | - [Reporting Bugs](#reporting-bugs) 23 | - [Suggesting Enhancements](#suggesting-enhancements) 24 | - [Your First Code Contribution](#your-first-code-contribution) 25 | - [Improving The Documentation](#improving-the-documentation) 26 | - [Styleguides](#styleguides) 27 | - [Commit Messages](#commit-messages) 28 | - [Join The Project Team](#join-the-project-team) 29 | 30 | ## I Have a Question 31 | 32 | > If you want to ask a question, I assume that you have read the available [FAQ](./FAQ.md). 33 | 34 | Before you ask a question, it is best to search for existing [Issues](https://github.com/mahyarmirrashed/yaos/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. 35 | 36 | If you then still feel the need to ask a question and need clarification, I recommend the following: 37 | 38 | - Open an [Issue](https://github.com/mahyarmirrashed/yaos/issues/new); 39 | - Provide as much context as you can about what you're running into; and 40 | - Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant. 41 | 42 | I will then take care of the issue as soon as possible. 43 | 44 | ## I Want To Contribute 45 | 46 | > ### Legal Notice 47 | > 48 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project licence. 49 | 50 | ### Reporting Bugs 51 | 52 | 53 | 54 | #### Before Submitting a Bug Report 55 | 56 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, please investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help me fix any potential bug as fast as possible. 57 | 58 | - Make sure that you are using the latest version; 59 | - Determine if your bug is really a bug and not an error on your side (e.g. using incompatible environment components/versions); 60 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/mahyarmirrashed/yaos/issues?q=label%3Abug); 61 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue; 62 | - Collect information about the bug: 63 | - Stack trace; 64 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM); 65 | - Git version, NodeJS version, and repository provider; 66 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions? 67 | 68 | 69 | 70 | #### How Do I Submit a Good Bug Report? 71 | 72 | > You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . 73 | 74 | 75 | 76 | I use GitHub issues to track bugs and errors. If you run into an issue with the project: 77 | 78 | - Open an [Issue](https://github.com/mahyarmirrashed/yaos/issues/new); 79 | - Explain the behaviour you would expect and the actual behaviour; 80 | - Please provide as much context as possible and describe the _reproduction steps_ that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case; and 81 | - Provide the information you collected in the previous section. 82 | 83 | Once it's filed: 84 | 85 | - I'll label the issue accordingly; 86 | - A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. 87 | - If I'm able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution). 88 | 89 | 90 | 91 | ### Suggesting Enhancements 92 | 93 | This section guides you through submitting an enhancement suggestion for YAOS, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help me and the community to understand your suggestion and find related suggestions. 94 | 95 | 96 | 97 | #### Before Submitting an Enhancement 98 | 99 | - Make sure that you are using the latest version. 100 | - Read the [FAQ](./FAQ.md) carefully and find out if the functionality is already covered, maybe by an individual configuration; 101 | - Perform a [search](https://github.com/mahyarmirrashed/yaos/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one; and 102 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince users of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider forking this repository instead. 103 | 104 | 105 | 106 | #### How Do I Submit a Good Enhancement Suggestion? 107 | 108 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/mahyarmirrashed/yaos/issues). 109 | 110 | - Use a **clear and descriptive title** for the issue to identify the suggestion; 111 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible; 112 | - **Describe the current behaviour** and **explain which behaviour you expected to see instead** and why. At this point you can also tell which alternatives do not work for you; and 113 | - **Explain why this enhancement would be useful** to most YAOS users. You may also want to point out the other projects that solved it better and which could serve as inspiration. 114 | 115 | ### Your First Code Contribution 116 | 117 | There is an `.editorconfig` included in this repository. Please adhere to its styles. I also use Prettier's default settings to format most documents in this repository. Like most Obsidian plugin development, I recommend enabling the [Hot Reload Plugin](https://github.com/pjeby/hot-reload) to speed up development. Run `yarn dev` to launch ESBuild in hot reload (development) mode and you're off to the races! 118 | 119 | ### Improving The Documentation 120 | 121 | If good questions are asked, or if you have steps that you think would make this plugin more accessible to users, feel free to add edits to the [FAQ](./FAQ.md) page. 122 | 123 | ### Commit Messages 124 | 125 | Use [Commitizen](https://github.com/commitizen/cz-cli) with the [Conventional Changelog](https://github.com/conventional-changelog/conventional-changelog) adapter and make commits _often_. 126 | 127 | 128 | 129 | ## Attribution 130 | 131 | This guide is based on the **Contributing Generator**. [Make your own](https://github.com/bttger/contributing-gen)! 132 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [0.7.0](https://github.com/mahyarmirrashed/yaos/compare/0.6.3...0.7.0) (2023-11-02) 4 | 5 | 6 | ### Features 7 | 8 | * add command for sync action ([a0b03ec](https://github.com/mahyarmirrashed/yaos/commit/a0b03ec667d4ae7c5fc463aea1e50bcf187f6f62)) 9 | 10 | ## [0.6.3](https://github.com/mahyarmirrashed/yaos/compare/0.6.2...0.6.3) (2023-10-28) 11 | 12 | ## [0.6.2](https://github.com/mahyarmirrashed/yaos/compare/0.6.1...0.6.2) (2023-10-28) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * await async stage all command ([3dd9554](https://github.com/mahyarmirrashed/yaos/commit/3dd9554e61ec950adf2da79d12508c44b5226b3b)) 18 | 19 | ## [0.6.1](https://github.com/mahyarmirrashed/yaos/compare/0.6.0...0.6.1) (2023-07-22) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * conform to more universal standard of delimiting with `T` ([5644953](https://github.com/mahyarmirrashed/yaos/commit/564495301ebc075b24d9327cafa80c3ae61a1a82)) 25 | 26 | # [0.6.0](https://github.com/mahyarmirrashed/yaos/compare/0.5.0...0.6.0) (2023-07-22) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * properly detect if is rebasing ([cd8ba77](https://github.com/mahyarmirrashed/yaos/commit/cd8ba779b4b99c2d95992a6d65eb12204d6a8323)) 32 | * properly format commit message template datetime ([724ad4e](https://github.com/mahyarmirrashed/yaos/commit/724ad4e0dc1321e4446ee9d326b98bddc507c87b)) 33 | * use default device name if not set ([23a9dd4](https://github.com/mahyarmirrashed/yaos/commit/23a9dd4f8319e109936fec3fe196d3773f90c04c)) 34 | 35 | 36 | ### Features 37 | 38 | * add create issue setting for users to report issues ([18f9592](https://github.com/mahyarmirrashed/yaos/commit/18f95927a31f749a7e15c1009d1e7e48837e50e8)) 39 | * add debugging message for when rebasing ([6ed0c37](https://github.com/mahyarmirrashed/yaos/commit/6ed0c37f728067770508e25f11d154d6aacdd031)) 40 | * add device name setting with ability to set/unset it ([45ee678](https://github.com/mahyarmirrashed/yaos/commit/45ee6784dcdfbabb1fa594d05def30762d6f42c1)) 41 | * add settings for git service to access ([7f194d2](https://github.com/mahyarmirrashed/yaos/commit/7f194d21e502cecf6dd61ba35518452a21524a0f)) 42 | * add settings tab with option to set device name ([3261db9](https://github.com/mahyarmirrashed/yaos/commit/3261db95c2fbfd9398448d4431b260c8a9c206c8)) 43 | * add toggle settings for file sync and configuration sync ([70d2c55](https://github.com/mahyarmirrashed/yaos/commit/70d2c55190191f1832bcc8d6cfee49cc0c57f34d)) 44 | * add type for specifying keys whose values must be boolean ([06357d3](https://github.com/mahyarmirrashed/yaos/commit/06357d322581f8835423dd7d001c847c541aceac)) 45 | * handle errors when sync button is pressed and log the error ([1279939](https://github.com/mahyarmirrashed/yaos/commit/1279939e0654c53b2bc863ab10a5977ff4608ad2)) 46 | * use device name in backup commit message ([d914f2d](https://github.com/mahyarmirrashed/yaos/commit/d914f2db94c21526497ee2ee58158c752d5e20c7)) 47 | 48 | # [0.5.0](https://github.com/mahyarmirrashed/yaos/compare/0.4.2...0.5.0) (2023-07-11) 49 | 50 | 51 | ### Bug Fixes 52 | 53 | * perform to check adapter is file system adapter ([57d7b8b](https://github.com/mahyarmirrashed/yaos/commit/57d7b8b08b63390f0a03efa3fb4b2b10f4cc65b0)) 54 | * remove process that ignores obsidian directory ([28a3856](https://github.com/mahyarmirrashed/yaos/commit/28a3856a626ef9d09ce8971b52d8142652ea90d7)) 55 | 56 | 57 | ### Features 58 | 59 | * notify user about failure when unexpected adapter is found ([f8a1b32](https://github.com/mahyarmirrashed/yaos/commit/f8a1b32a232d1d51b8dc76fb6f941f71dd709f3f)) 60 | 61 | ## [0.4.2](https://github.com/mahyarmirrashed/yaos/compare/0.4.1...0.4.2) (2023-07-03) 62 | 63 | 64 | ### Bug Fixes 65 | 66 | * automatically run `yarn bump` before pushing release ([2f5c429](https://github.com/mahyarmirrashed/yaos/commit/2f5c429e6a1bf82fa366dab7b7efeb5df01603d3)) 67 | * update release name to match obsidian's desired name ([eeb8626](https://github.com/mahyarmirrashed/yaos/commit/eeb8626cc89ed383c3bfa621e3b43408c78b730c)) 68 | 69 | ## 0.4.1 (2023-07-03) 70 | 71 | 72 | ### Bug Fixes 73 | 74 | * add notices for when repository has not been setup ([6b538bf](https://github.com/mahyarmirrashed/yaos/commit/6b538bf418c4c3d322fd7a77660f223b8edeed24)) 75 | 76 | # [0.4.0](https://github.com/mahyarmirrashed/yaos/compare/0.3.0...0.4.0) (2023-07-03) 77 | 78 | 79 | ### Features 80 | 81 | * add command to show conflicting files modal ([76fcd60](https://github.com/mahyarmirrashed/yaos/commit/76fcd6021ac437252a7e69d240a8bbf1231552da)) 82 | * add modal for displaying conflicting files ([a3bf15f](https://github.com/mahyarmirrashed/yaos/commit/a3bf15f0ed918621244a5086909cba69f4cd8c2f)) 83 | * add notice when vault successfully backs up ([104c154](https://github.com/mahyarmirrashed/yaos/commit/104c15453038f925c94242a6ca0c8cb3aad57c99)) 84 | * use simple git api to get list of conflicting files ([763774f](https://github.com/mahyarmirrashed/yaos/commit/763774f46b9028ab2c486931ca6bb392537c4a5f)) 85 | 86 | # [0.3.0](https://github.com/mahyarmirrashed/yaos/compare/0.2.0...0.3.0) (2023-07-03) 87 | 88 | 89 | ### Features 90 | 91 | * add basic synchronizer method on controller for performing sync action ([918974e](https://github.com/mahyarmirrashed/yaos/commit/918974ec80fdb74cf7025f3a7899f532f164dc3c)) 92 | * add method to check if local branch is ahead of remote branch ([c51d8a7](https://github.com/mahyarmirrashed/yaos/commit/c51d8a716412789377f2061d8739b8a039f6ae35)) 93 | * add method to check if remote branch is ahead of local branch ([ce44aa9](https://github.com/mahyarmirrashed/yaos/commit/ce44aa9e2463a3df6b3294da4abebd31002f3626)) 94 | * add method to force stop of rebase ([bee9df1](https://github.com/mahyarmirrashed/yaos/commit/bee9df18422fe48a82b553e6030591dc8230cb3c)) 95 | * add method to list unmerged files in the repository ([002530e](https://github.com/mahyarmirrashed/yaos/commit/002530e402e95469bc8fde9ce3dbe8a1d405004c)) 96 | * add method to name if currently rebasing repository ([c1a9f3d](https://github.com/mahyarmirrashed/yaos/commit/c1a9f3d408b5abb25f7db3ea9e1039bc45970f3f)) 97 | * add method to pull with rebase merge conflict resolution ([82c9723](https://github.com/mahyarmirrashed/yaos/commit/82c9723a2da95748125359f6b2ba6d1fd3fb0087)) 98 | * complete vault backup process ([90eef2b](https://github.com/mahyarmirrashed/yaos/commit/90eef2bc8156271843ba50e606a0859d5b19b581)) 99 | * **sync:** add sync controller to manage logic and process ([fe5b771](https://github.com/mahyarmirrashed/yaos/commit/fe5b7717f22d70d948a346ec68ccf9a4008692b7)) 100 | 101 | # [0.2.0](https://github.com/mahyarmirrashed/yaos/compare/0.1.0...0.2.0) (2023-07-03) 102 | 103 | 104 | ### Bug Fixes 105 | 106 | * check again if path is still being tracked after removing from history ([692c450](https://github.com/mahyarmirrashed/yaos/commit/692c450a2b0040f00c87407306682167ca49e0d2)) 107 | * pass correct path to git service ([3b340d8](https://github.com/mahyarmirrashed/yaos/commit/3b340d8d1cf2638cb5c3444bb248135940001261)) 108 | * properly check if path is being ignored ([576b6c4](https://github.com/mahyarmirrashed/yaos/commit/576b6c4ef55e4d42d7daa3d72063ec5fc89b53f7)) 109 | * specify in gitservice interface optional push parameter ([f9cf3b4](https://github.com/mahyarmirrashed/yaos/commit/f9cf3b48c5c0051e91f5dc38577159f793fe1337)) 110 | * specify in interface that argument is optional ([5034699](https://github.com/mahyarmirrashed/yaos/commit/503469996726f4a8ce8cbdee514edb623cbbd3e2)) 111 | * ungeneralize method ([c229223](https://github.com/mahyarmirrashed/yaos/commit/c229223a37b141b2380f74e0c3f2dd746b903748)) 112 | * use different message on stage, commit, and push process ([4fd6707](https://github.com/mahyarmirrashed/yaos/commit/4fd6707da0528ca4aafd8f3b7c43fc873a0cfb41)) 113 | 114 | 115 | ### Features 116 | 117 | * add basic implementation of git service interface with simple git backend ([dcae94f](https://github.com/mahyarmirrashed/yaos/commit/dcae94f462ef9dafbea75b2e90734761cd0ff290)) 118 | * add common git operations ([9ab8763](https://github.com/mahyarmirrashed/yaos/commit/9ab87636cef4d14c26bfe1765c1dca9324f91018)) 119 | * add git operation for commiting changes ([7a57d77](https://github.com/mahyarmirrashed/yaos/commit/7a57d77f2bfbb0d5dccb0a3db9029dd0c45a1964)) 120 | * add gitignore service ([d512a3d](https://github.com/mahyarmirrashed/yaos/commit/d512a3dedfe23ad71b691587b1ce6d9435162508)) 121 | * add interface for git service implementations ([32a6c9f](https://github.com/mahyarmirrashed/yaos/commit/32a6c9f2dc7a28c0a69554d5e347e801bd4c7556)) 122 | * add logger for debugging purposes ([987dd02](https://github.com/mahyarmirrashed/yaos/commit/987dd028b6a9b7de6c96d4f6807d0f9b5bb6ae3a)) 123 | * add logging message when making commits ([0b4017d](https://github.com/mahyarmirrashed/yaos/commit/0b4017de0e3bbf3f98fd1e710732a8083bbe2596)) 124 | * add logging messages when pushing to remote ([f89a93f](https://github.com/mahyarmirrashed/yaos/commit/f89a93f673fcb2673541a14ddbf1979dcd8cadb0)) 125 | * add method to check if a remote address is configured for the repository ([7a8776f](https://github.com/mahyarmirrashed/yaos/commit/7a8776f97c37e4cf764332c9f3462a5bdc2cd4ea)) 126 | * add method to check if path is being tracked ([460b7c8](https://github.com/mahyarmirrashed/yaos/commit/460b7c8b12c6cf3f5c29a7243690df1df2ea84fc)) 127 | * add method to check if unstaged files exist ([b0b0dbf](https://github.com/mahyarmirrashed/yaos/commit/b0b0dbfea4d1ae584cb4b0d51a821e05e6890296)) 128 | * add method to delete tracks to certain git ([22717b6](https://github.com/mahyarmirrashed/yaos/commit/22717b646cb2ca6e806f0bac9df352cea885c4f8)) 129 | * add, commit, and push newly created `.gitignore` files ([92ec554](https://github.com/mahyarmirrashed/yaos/commit/92ec554a3a7d77c8e7d8fd98250ccdc1ea8cfd5f)) 130 | * check if remote repository is configured ([98894a9](https://github.com/mahyarmirrashed/yaos/commit/98894a9cd398e0db39f64d28d56fb41a92f2f60d)) 131 | * consolidate logic directly into single try-catch ([7124933](https://github.com/mahyarmirrashed/yaos/commit/7124933398eb8029dd91abaa4bb1006601458acd)) 132 | * create distinction between currently and previously tracking files ([768b8d5](https://github.com/mahyarmirrashed/yaos/commit/768b8d5560e8a755e328792911b99fef9db40b74)) 133 | * force push after rewriting git commit history ([42281c1](https://github.com/mahyarmirrashed/yaos/commit/42281c11a758f66619f7efed47e1f33daa521b89)) 134 | * initialize `.gitignore` with necessary contents to ignore `.obsidian/` ([55cb3f4](https://github.com/mahyarmirrashed/yaos/commit/55cb3f4693b6b38b0de2125c0cc992cca19f340f)) 135 | * trigger vault backup every time sync button is pressed ([80d16cd](https://github.com/mahyarmirrashed/yaos/commit/80d16cd22cb5c1e0b24c2e9f9c4c2e7767c8807d)) 136 | * use git service to check if current vault is configured as a git repository ([3c935d4](https://github.com/mahyarmirrashed/yaos/commit/3c935d4b03a9adf2677d4da0364c4da92b5231d2)) 137 | * use gitignore service to ensure obsidian folder is ignored ([9e1b4fc](https://github.com/mahyarmirrashed/yaos/commit/9e1b4fc449254cbeb341307dfdb41ebfe34a4b8a)) 138 | 139 | # 0.1.0 (2023-07-02) 140 | 141 | 142 | ### Bug Fixes 143 | 144 | * fix variable naming error for `versionsJson` ([c451bcc](https://github.com/mahyarmirrashed/yaos/commit/c451bccc0f59bf2f1a2a8b77c92b17a10ef27c7e)) 145 | * only allow release from `main` branch ([f15a5ee](https://github.com/mahyarmirrashed/yaos/commit/f15a5ee333974a2c03fa517242b303b0e7c11be8)) 146 | * properly retrive dirname in es module ([132f614](https://github.com/mahyarmirrashed/yaos/commit/132f614cbf4fb299d6cba4a46d4aa76ba5948ed3)) 147 | 148 | 149 | ### Features 150 | 151 | * add ribbon icon for plugin ([9015ff4](https://github.com/mahyarmirrashed/yaos/commit/9015ff4e9064c58eb06d9131ce3307337cffedcf)) --------------------------------------------------------------------------------