# Obsidian Deploy Integration -- Implementation Reference

## Overview

Publish Obsidian plugins to the community plugin directory via GitHub, including
automated release workflows, version bumping, and submission PR creation.

## Prerequisites

- GitHub repository for your plugin
- npm / Node.js 18+ build setup
- BRAT or direct install for beta testing

## Plugin Build Setup

```json
// package.json
{
    "scripts": {
        "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
        "dev": "node esbuild.config.mjs",
        "version": "node version-bump.mjs && git add manifest.json versions.json"
    },
    "devDependencies": {
        "@types/node": "^18.0.0",
        "obsidian": "latest",
        "typescript": "^5.0.0",
        "esbuild": "0.17.3",
        "builtin-modules": "3.3.0"
    }
}
```

```javascript
// esbuild.config.mjs
import esbuild from 'esbuild';
import process from 'process';
import builtins from 'builtin-modules';

const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;

const prod = (process.argv[2] === 'production');

const context = await esbuild.context({
    banner: { js: banner },
    entryPoints: ['main.ts'],
    bundle: true,
    external: [
        'obsidian',
        'electron',
        '@codemirror/autocomplete',
        '@codemirror/closebrackets',
        '@codemirror/commands',
        '@codemirror/fold',
        '@codemirror/gutter',
        '@codemirror/highlight',
        '@codemirror/history',
        '@codemirror/language',
        '@codemirror/lint',
        '@codemirror/matchbrackets',
        '@codemirror/panel',
        '@codemirror/rangeset',
        '@codemirror/rectangular-selection',
        '@codemirror/search',
        '@codemirror/state',
        '@codemirror/stream-parser',
        '@codemirror/text',
        '@codemirror/tooltip',
        '@codemirror/view',
        ...builtins,
    ],
    format: 'cjs',
    target: 'es2018',
    logLevel: 'info',
    sourcemap: prod ? false : 'inline',
    treeShaking: true,
    outfile: 'main.js',
});

if (prod) {
    await context.rebuild();
    process.exit(0);
} else {
    await context.watch();
}
```

## GitHub Actions Release Workflow

```yaml
# .github/workflows/release.yml
name: Release Obsidian Plugin

on:
  push:
    tags:
      - '*'

env:
  PLUGIN_NAME: my-obsidian-plugin  # Must match manifest.id

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm ci

      - name: Build plugin
        run: npm run build

      - name: Create release archive
        run: |
          mkdir "${{ env.PLUGIN_NAME }}"
          cp main.js manifest.json styles.css "${{ env.PLUGIN_NAME }}/" 2>/dev/null || true
          zip -r "${{ env.PLUGIN_NAME }}.zip" "${{ env.PLUGIN_NAME }}"

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            ${{ env.PLUGIN_NAME }}.zip
            main.js
            manifest.json
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

## Version Bump Script

```javascript
// version-bump.mjs
import { readFileSync, writeFileSync } from 'fs';

const targetVersion = process.env.npm_package_version;

// Read manifest and update version
const manifest = JSON.parse(readFileSync('manifest.json', 'utf8'));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync('manifest.json', JSON.stringify(manifest, null, '\t'));

// Add version to versions.json
const versions = JSON.parse(readFileSync('versions.json', 'utf8'));
versions[targetVersion] = minAppVersion;
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));

console.log(`Bumped to ${targetVersion} (minAppVersion: ${minAppVersion})`);
```

## Releasing a New Version

```bash
# 1. Update version in package.json
npm version patch   # or minor / major

# 2. This triggers version-bump.mjs (via package.json "version" script)
# which updates manifest.json and versions.json

# 3. Create and push the tag
git push
git push --tags

# GitHub Actions will create the release and attach artifacts automatically
```

## Community Plugin Submission Checklist

```markdown
## Pre-submission checklist

- [ ] Plugin passes all builds without errors
- [ ] manifest.json has correct id, name, version, minAppVersion
- [ ] README.md explains what the plugin does and how to use it
- [ ] Code does not access external network without user consent
- [ ] Settings are stored using Obsidian's data API (not raw localStorage)
- [ ] Plugin unloads cleanly (no dangling event listeners)
- [ ] Version history in versions.json is correct

## Submit via PR to obsidianmd/obsidian-releases
# Fork https://github.com/obsidianmd/obsidian-releases
# Add to community-plugins.json:
{
    "id": "my-plugin",
    "name": "My Plugin",
    "author": "Your Name",
    "description": "Short description",
    "repo": "yourname/obsidian-my-plugin"
}
# Open PR with title "Add plugin: My Plugin"
```

## BRAT Beta Testing

```markdown
## Install via BRAT for beta testers

1. Install BRAT plugin from community plugins
2. Open BRAT settings -> Add Beta Plugin
3. Enter your GitHub repo URL: https://github.com/yourname/obsidian-my-plugin
4. Enable "Auto-update plugins at startup"
```

## Resources

- [Obsidian Plugin Developer Docs](https://docs.obsidian.md/Plugins/Getting+started/Build+a+plugin)
- [obsidianmd/obsidian-releases](https://github.com/obsidianmd/obsidian-releases)
- [BRAT](https://github.com/TfTHacker/obsidian42-brat)
- [Obsidian Sample Plugin](https://github.com/obsidianmd/obsidian-sample-plugin)

---
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
