diff --git a/.github/workflows/publish-installer.yaml b/.github/workflows/publish-installer.yaml new file mode 100644 index 0000000..c77b03d --- /dev/null +++ b/.github/workflows/publish-installer.yaml @@ -0,0 +1,57 @@ +name: Publish Installer to NPM + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to publish (e.g., 0.0.2)' + required: true + type: string + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: bun install + + - name: Update version + run: | + cd packages/installer + npm version ${{ github.event.inputs.version }} --no-git-tag-version + + - name: Build installer + run: | + cd packages/installer + bun run build + + - name: Publish to NPM + run: | + cd packages/installer + npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Commit version bump + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add packages/installer/package.json + git commit -m "chore: bump installer version to ${{ github.event.inputs.version }}" + git push diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 0000000..a5df72b --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,218 @@ +# Publishing Guide + +This guide covers how to publish releases of the Vync plugin and the installer package. + +## Table of Contents + +- [Publishing the Plugin](#publishing-the-plugin) +- [Publishing the Installer to NPM](#publishing-the-installer-to-npm) +- [First-Time Setup](#first-time-setup) + +## Publishing the Plugin + +### Automatic Release (via GitHub Actions) + +1. **Create a release branch** with the version number: + ```bash + git checkout -b release/v1.0.0 + git push origin release/v1.0.0 + ``` + +2. **GitHub Actions will automatically**: + - Run tests to ensure everything works + - Build the plugin (`main.js`, `manifest.json`, `styles.css`) + - Create a Git tag for the version + - Create a GitHub release with the built files attached + - Update version numbers in `package.json` and `manifest.json` + +3. **Review and publish the release**: + - Go to [Releases](https://github.com/techsavvyash/vync/releases) + - Find the draft release + - Edit release notes if needed + - Click "Publish release" + +### Manual Release (if needed) + +```bash +# Build the plugin +cd packages/plugin +bun install +bun test +bun run build + +# Create and push tag +git tag v1.0.0 +git push origin v1.0.0 + +# Create GitHub release manually with main.js, manifest.json, styles.css +``` + +## Publishing the Installer to NPM + +The installer package (`@techsavvyash/vync-installer`) allows users to install the plugin via `npx` or `bunx`. + +### Prerequisites (First-Time Only) + +1. **Create NPM Account**: Sign up at [npmjs.com](https://www.npmjs.com/) + +2. **Generate NPM Token**: + - Log in to npmjs.com + - Go to Account Settings → Access Tokens + - Click "Generate New Token" → Select "Automation" + - Copy the token (starts with `npm_...`) + +3. **Add Token to GitHub Secrets**: + - Go to your repository on GitHub + - Navigate to Settings → Secrets and variables → Actions + - Click "New repository secret" + - Name: `NPM_TOKEN` + - Value: Paste your token + - Click "Add secret" + +4. **Setup GitHub Actions Workflow** (First-Time Only): + - Copy `packages/installer/publish-installer.yaml.template` to `.github/workflows/publish-installer.yaml` + - This step must be done manually by a repository owner due to workflow permissions + ```bash + cp packages/installer/publish-installer.yaml.template .github/workflows/publish-installer.yaml + git add .github/workflows/publish-installer.yaml + git commit -m "chore: add installer publishing workflow" + git push + ``` + +### Publishing via GitHub Actions (Recommended) + +1. **Navigate to Actions**: + - Go to your repository's Actions tab + - Select "Publish Installer to NPM" workflow + +2. **Run the workflow**: + - Click "Run workflow" + - Select the branch (usually `main`) + - Enter the version number (e.g., `0.0.3`) + - Click "Run workflow" + +3. **Wait for completion**: + - The workflow will: + - Update `packages/installer/package.json` with the new version + - Build the installer + - Publish to NPM + - Commit the version bump back to the repository + +4. **Verify publication**: + - Check [npmjs.com/package/@techsavvyash/vync-installer](https://www.npmjs.com/package/@techsavvyash/vync-installer) + - Test installation: `npx @techsavvyash/vync-installer@latest --help` + +### Manual Publishing + +If you prefer to publish manually or need to troubleshoot: + +```bash +# Navigate to the installer package +cd packages/installer + +# Install dependencies (first time only) +bun install + +# Update version number +npm version 0.0.3 + +# Build the installer +bun run build + +# Login to NPM (first time only) +npm login + +# Publish to NPM +npm publish --access public + +# Commit version bump +git add package.json +git commit -m "chore: bump installer version to 0.0.3" +git push +``` + +### Verifying the Published Package + +After publishing, verify that users can install it: + +```bash +# Test with npx +npx @techsavvyash/vync-installer --help + +# Test with bunx +bunx @techsavvyash/vync-installer --help +``` + +## First-Time Setup + +### For Plugin Distribution + +The plugin repository already has GitHub Actions set up. No additional configuration needed. + +### For NPM Publishing + +Follow the [Prerequisites](#prerequisites-first-time-only) section above to set up NPM token. + +## Version Numbering + +Follow [Semantic Versioning](https://semver.org/): + +- **MAJOR** version (1.0.0): Breaking changes +- **MINOR** version (0.1.0): New features, backwards-compatible +- **PATCH** version (0.0.1): Bug fixes, backwards-compatible + +### Keeping Versions in Sync + +The installer version should generally match the plugin version, but it's not strictly required: + +- When releasing a new plugin version: Create a plugin release first +- Then publish the installer with the same version number +- The installer will always fetch the latest plugin release + +## Troubleshooting + +### NPM Publish Fails with "403 Forbidden" + +- Check that your `NPM_TOKEN` secret is set correctly +- Verify the token has "Automation" or "Publish" permissions +- Make sure the package name `@techsavvyash/vync-installer` is available + +### GitHub Actions Workflow Fails + +- Check the Actions logs for specific errors +- Verify all secrets are set correctly +- Ensure the workflow file syntax is correct + +### Users Can't Install via npx + +- Wait a few minutes after publishing (NPM propagation) +- Check the package exists: https://www.npmjs.com/package/@techsavvyash/vync-installer +- Verify the `bin` field in `package.json` is correct +- Ensure the built file has execute permissions + +## Release Checklist + +### Plugin Release + +- [ ] Update CHANGELOG.md +- [ ] Run tests: `cd packages/plugin && bun test` +- [ ] Create release branch: `git checkout -b release/vX.Y.Z` +- [ ] Push branch: `git push origin release/vX.Y.Z` +- [ ] Wait for GitHub Actions to complete +- [ ] Publish the draft release on GitHub + +### Installer Release + +- [ ] Ensure NPM_TOKEN is set in GitHub Secrets +- [ ] Go to Actions → "Publish Installer to NPM" +- [ ] Run workflow with version number +- [ ] Wait for workflow completion +- [ ] Verify on npmjs.com +- [ ] Test: `npx @techsavvyash/vync-installer --help` + +## Additional Resources + +- [NPM Publishing Guide](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Semantic Versioning](https://semver.org/) +- [Obsidian Plugin Publishing](https://docs.obsidian.md/Plugins/Releasing/Submit+your+plugin) diff --git a/README.md b/README.md index 6dc8692..704a3d3 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,32 @@ A local-first Obsidian plugin for seamless vault synchronization with Google Dri 3. Click Install 4. Enable the plugin -#### Option B: Manual Installation +#### Option B: Quick Install from Latest Release (Recommended) +Use the installer script to automatically download and install from the latest GitHub release: + +```bash +# Using npx (Node.js) +npx @techsavvyash/vync-installer /path/to/your/vault + +# Using bunx (Bun) +bunx @techsavvyash/vync-installer /path/to/your/vault +``` + +**Examples:** +```bash +# Linux/macOS +npx @techsavvyash/vync-installer ~/Documents/MyVault + +# Windows +npx @techsavvyash/vync-installer "C:\Users\YourName\Documents\MyVault" +``` + +The installer will: +- Fetch the latest release from GitHub +- Download `main.js`, `manifest.json`, and `styles.css` +- Install them to `.obsidian/plugins/vync/` in your vault + +#### Option C: Manual Installation ```bash # Clone and build git clone @@ -150,6 +175,67 @@ When ready to publish: - Add your plugin to `community-plugins.json` - Submit PR +### Publishing the Installer to NPM + +The installer package (`@techsavvyash/vync-installer`) can be published to NPM for easy installation via `npx` or `bunx`. + +#### Prerequisites + +1. **NPM Account** - Create an account at [npmjs.com](https://www.npmjs.com/) +2. **NPM Token** - Generate an automation token: + - Go to npmjs.com → Account Settings → Access Tokens + - Generate New Token → Automation + - Copy the token + +3. **Add Token to GitHub Secrets** + - Go to GitHub Repository → Settings → Secrets and variables → Actions + - Add new secret: `NPM_TOKEN` with your token value + +#### Publishing Steps + +**Option 1: Using GitHub Actions (Recommended)** + +1. Go to Actions → "Publish Installer to NPM" +2. Click "Run workflow" +3. Enter the version number (e.g., `0.0.2`) +4. Click "Run workflow" + +The workflow will: +- Update the version in `package.json` +- Build the installer +- Publish to NPM +- Commit the version bump + +**Option 2: Manual Publishing** + +```bash +# Navigate to installer package +cd packages/installer + +# Login to NPM (first time only) +npm login + +# Update version +npm version 0.0.2 + +# Build +bun run build + +# Publish +npm publish --access public +``` + +#### After Publishing + +Users can install the plugin using: +```bash +npx @techsavvyash/vync-installer /path/to/vault +``` + +**Note:** It may take a few minutes for the package to be available on NPM after publishing. + +**📖 Detailed Publishing Guide:** See [PUBLISHING.md](PUBLISHING.md) for complete step-by-step instructions. + ## 📊 How It Works ### Sync Flow diff --git a/packages/installer/.gitignore b/packages/installer/.gitignore new file mode 100644 index 0000000..7535211 --- /dev/null +++ b/packages/installer/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +*.log +.DS_Store diff --git a/packages/installer/README.md b/packages/installer/README.md new file mode 100644 index 0000000..eac1dee --- /dev/null +++ b/packages/installer/README.md @@ -0,0 +1,89 @@ +# Vync Installer + +CLI tool to install the Vync Obsidian plugin from the latest GitHub release. + +## Usage + +### Using npx (recommended) + +```bash +npx @techsavvyash/vync-installer +``` + +### Using bunx + +```bash +bunx @techsavvyash/vync-installer +``` + +## Examples + +```bash +# Linux/macOS +npx @techsavvyash/vync-installer ~/Documents/MyVault + +# Windows +npx @techsavvyash/vync-installer "C:\Users\YourName\Documents\MyVault" +``` + +## What it does + +The installer will: + +1. Fetch the latest release from the [Vync GitHub repository](https://github.com/techsavvyash/vync) +2. Download `main.js`, `manifest.json`, and `styles.css` +3. Install them to `/.obsidian/plugins/vync/` + +## After Installation + +1. Open Obsidian +2. Go to Settings → Community Plugins +3. Enable "Vync" +4. Configure your Google Drive credentials + +## Requirements + +- Node.js 18+ or Bun +- A valid Obsidian vault (must contain `.obsidian` directory) + +## Troubleshooting + +**Error: Vault path does not exist** +- Make sure you provide the correct path to your vault directory + +**Error: Not a valid Obsidian vault** +- The provided path must be the root directory of your vault +- It should contain a `.obsidian` folder + +**Error: Required file not found in release** +- The latest release might not have all required files +- Check the [releases page](https://github.com/techsavvyash/vync/releases) + +## Development + +To build the installer locally: + +```bash +cd packages/installer +bun install +bun run build +``` + +To test locally: + +```bash +node dist/index.js /path/to/vault +``` + +## Publishing + +This package is published to npm. To publish a new version: + +1. Update version in `package.json` +2. Run `npm publish --access public` + +Or use the automated GitHub Actions workflow (see main README). + +## License + +MIT diff --git a/packages/installer/package.json b/packages/installer/package.json new file mode 100644 index 0000000..eeba92b --- /dev/null +++ b/packages/installer/package.json @@ -0,0 +1,31 @@ +{ + "name": "@techsavvyash/vync-installer", + "version": "0.0.2", + "description": "CLI installer for Vync Obsidian plugin", + "type": "module", + "bin": { + "vync-install": "./dist/index.js" + }, + "scripts": { + "build": "bun build src/index.ts --outdir dist --target node --format esm", + "dev": "bun build src/index.ts --outdir dist --target node --format esm --watch", + "prepublishOnly": "bun run build" + }, + "keywords": [ + "obsidian", + "vync", + "installer", + "cli" + ], + "author": "techsavvyash", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/techsavvyash/vync.git", + "directory": "packages/installer" + }, + "files": [ + "dist" + ], + "dependencies": {} +} diff --git a/packages/installer/src/index.ts b/packages/installer/src/index.ts new file mode 100644 index 0000000..ba7a62e --- /dev/null +++ b/packages/installer/src/index.ts @@ -0,0 +1,148 @@ +#!/usr/bin/env node + +import { existsSync, mkdirSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { argv, exit } from 'process'; + +interface GithubRelease { + tag_name: string; + assets: Array<{ + name: string; + browser_download_url: string; + }>; +} + +const REPO_OWNER = 'techsavvyash'; +const REPO_NAME = 'vync'; +const REQUIRED_FILES = ['main.js', 'manifest.json', 'styles.css']; + +async function fetchLatestRelease(): Promise { + const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`; + + try { + const response = await fetch(url, { + headers: { + 'Accept': 'application/vnd.github.v3+json', + 'User-Agent': 'vync-installer' + } + }); + + if (!response.ok) { + throw new Error(`Failed to fetch latest release: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + throw new Error(`Failed to fetch latest release: ${error}`); + } +} + +async function downloadFile(url: string): Promise { + try { + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Failed to download ${url}: ${response.statusText}`); + } + + return await response.text(); + } catch (error) { + throw new Error(`Failed to download file: ${error}`); + } +} + +async function installPlugin(vaultPath: string): Promise { + console.log('🔍 Fetching latest Vync release...'); + + // Fetch latest release + const release = await fetchLatestRelease(); + console.log(`✅ Found latest release: ${release.tag_name}`); + + // Create plugin directory + const pluginDir = join(vaultPath, '.obsidian', 'plugins', 'vync'); + + if (!existsSync(pluginDir)) { + console.log(`📁 Creating plugin directory: ${pluginDir}`); + mkdirSync(pluginDir, { recursive: true }); + } + + // Download and install required files + for (const fileName of REQUIRED_FILES) { + const asset = release.assets.find(a => a.name === fileName); + + if (!asset) { + throw new Error(`Required file '${fileName}' not found in release ${release.tag_name}`); + } + + console.log(`⬇️ Downloading ${fileName}...`); + const content = await downloadFile(asset.browser_download_url); + + const filePath = join(pluginDir, fileName); + writeFileSync(filePath, content, 'utf-8'); + console.log(`✅ Installed ${fileName}`); + } + + console.log('\n🎉 Vync plugin installed successfully!'); + console.log('\nNext steps:'); + console.log('1. Open Obsidian'); + console.log('2. Go to Settings → Community Plugins'); + console.log('3. Enable "Vync"'); + console.log('4. Configure your Google Drive credentials'); +} + +function printUsage(): void { + console.log(` +Vync Installer - Install Vync Obsidian plugin from latest release + +Usage: + npx @techsavvyash/vync-installer + bunx @techsavvyash/vync-installer + +Arguments: + Path to your Obsidian vault directory + +Examples: + npx @techsavvyash/vync-installer ~/Documents/MyVault + bunx @techsavvyash/vync-installer "C:\\Users\\YourName\\Documents\\MyVault" + +The installer will: + - Fetch the latest release from GitHub + - Download main.js, manifest.json, and styles.css + - Install them to /.obsidian/plugins/vync/ + `); +} + +async function main(): Promise { + // Parse arguments (skip first two: node and script path) + const args = argv.slice(2); + + if (args.length === 0 || args.includes('--help') || args.includes('-h')) { + printUsage(); + exit(args.length === 0 ? 1 : 0); + } + + const vaultPath = args[0]; + + // Validate vault path + if (!existsSync(vaultPath)) { + console.error(`❌ Error: Vault path does not exist: ${vaultPath}`); + exit(1); + } + + const obsidianDir = join(vaultPath, '.obsidian'); + if (!existsSync(obsidianDir)) { + console.error(`❌ Error: Not a valid Obsidian vault. .obsidian directory not found in: ${vaultPath}`); + console.error(' Make sure you provide the path to your vault root directory.'); + exit(1); + } + + try { + await installPlugin(vaultPath); + exit(0); + } catch (error) { + console.error(`\n❌ Installation failed: ${error}`); + exit(1); + } +} + +main(); diff --git a/packages/installer/tsconfig.json b/packages/installer/tsconfig.json new file mode 100644 index 0000000..d67f6de --- /dev/null +++ b/packages/installer/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "module": "ESNext", + "target": "ES2022", + "lib": ["ES2022"], + "moduleResolution": "bundler", + "types": ["node"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}