obsidian-ci-integration
Set up GitHub Actions CI/CD for Obsidian plugin development. Use when automating builds, tests, and releases for your plugin, or setting up continuous integration for Obsidian projects. Trigger with phrases like "obsidian CI", "obsidian github actions", "obsidian automated build", "obsidian CI/CD". allowed-tools: Read, Write, Edit, Bash(npm:*) version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>
Allowed Tools
No tools specified
Provided by Plugin
obsidian-pack
Claude Code skill pack for Obsidian plugin development and vault management (24 skills)
Installation
This skill is included in the obsidian-pack plugin:
/plugin install obsidian-pack@claude-code-plugins-plus
Click to copy
Instructions
# Obsidian CI Integration
## Overview
Set up GitHub Actions for automated building, testing, and releasing Obsidian plugins.
## Prerequisites
- GitHub repository for your plugin
- Working local build (npm run build)
- Basic understanding of GitHub Actions
## Instructions
### Step 1: Create Build Workflow
```yaml
# .github/workflows/build.yml
name: Build Obsidian Plugin
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
continue-on-error: true
- name: Type check
run: npm run typecheck
if: always()
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: plugin-build
path: |
main.js
manifest.json
styles.css
if-no-files-found: error
```
### Step 2: Create Test Workflow
```yaml
# .github/workflows/test.yml
name: Test
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
fail_ci_if_error: false
```
### Step 3: Create Release Workflow
```yaml
# .github/workflows/release.yml
name: Release Obsidian Plugin
on:
push:
tags:
- '*'
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Get version
id: version
run: echo "version=$(jq -r '.version' manifest.json)" >> $GITHUB_OUTPUT
- name: Verify tag matches version
run: |
TAG_VERSION="${GITHUB_REF#refs/tags/}"
MANIFEST_VERSION="${{ steps.version.outputs.version }}"
if [ "$TAG_VERSION" != "$MANIFEST_VERSION" ]; then
echo "Tag ($TAG_VERSION) does not match manifest version ($MANIFEST_VERSION)"
exit 1
fi
- name: Create release archive
run: |
mkdir release
cp main.js manifest.json release/
[ -f styles.css ] && cp styles.css release/
cd release && zip -r ../my-plugin-${{ steps.version.outputs.version }}.zip *
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
main.js
manifest.json
styles.css
my-plugin-${{ steps.version.outputs.version }}.zip
generate_release_notes: true
draft: false
prerelease: false
```
### Step 4: Add Version Bump Script
```javascript
// version-bump.mjs
import { readFileSync, writeFileSync } from "fs";
const targetVersion = process.argv[2];
if (!targetVersion) {
console.error("Usage: node version-bump.mjs ");
process.exit(1);
}
// Validate semver
if (!/^\d+\.\d+\.\d+$/.test(targetVersion)) {
console.error("Invalid version format. Use semver: x.y.z");
process.exit(1);
}
// Update manifest.json
const manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
// Update versions.json
let versions = {};
try {
versions = JSON.parse(readFileSync("versions.json", "utf8"));
} catch {
// File doesn't exist, create new
}
versions[targetVersion] = minAppVersion;
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));
// Update package.json
const pkg = JSON.parse(readFileSync("package.json", "utf8"));
pkg.version = targetVersion;
writeFileSync("package.json", JSON.stringify(pkg, null, "\t"));
console.log(`Version bumped to ${targetVersion}`);
```
### Step 5: Configure package.json Scripts
```json
{
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "node esbuild.config.mjs production",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint src/ --ext .ts",
"typecheck": "tsc --noEmit",
"version": "node version-bump.mjs && git add manifest.json versions.json package.json",
"release": "npm run version && git commit -m 'Bump version' && git push && git push --tags"
}
}
```
### Step 6: Create Validation Workflow
```yaml
# .github/workflows/validate.yml
name: Validate Plugin
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Validate manifest.json
run: |
# Check required fields
for field in id name version minAppVersion description author; do
value=$(jq -r ".$field" manifest.json)
if [ "$value" == "null" ] || [ -z "$value" ]; then
echo "ERROR: Missing required field: $field"
exit 1
fi
done
# Validate version format
version=$(jq -r '.version' manifest.json)
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "ERROR: Invalid version format: $version"
exit 1
fi
echo "manifest.json is valid"
- name: Validate versions.json
run: |
if [ ! -f versions.json ]; then
echo "ERROR: versions.json not found"
exit 1
fi
# Validate JSON syntax
if ! jq empty versions.json; then
echo "ERROR: Invalid JSON in versions.json"
exit 1
fi
# Check current version is in versions.json
current=$(jq -r '.version' manifest.json)
if [ "$(jq -r ".[\"$current\"]" versions.json)" == "null" ]; then
echo "WARNING: Current version $current not in versions.json"
fi
echo "versions.json is valid"
- name: Check for console.log
run: |
count=$(grep -r "console.log" src/ --include="*.ts" | wc -l || true)
if [ "$count" -gt 5 ]; then
echo "WARNING: Found $count console.log statements (consider removing before release)"
fi
```
## Output
- Build workflow for CI validation
- Test workflow with coverage reporting
- Release workflow for automated GitHub releases
- Version bump script for consistent versioning
- Validation workflow for plugin standards
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| Build fails | Missing dependencies | Ensure package-lock.json is committed |
| Release fails | Version mismatch | Run version bump before tagging |
| Upload fails | File not found | Check build output paths |
| Permission denied | Token scope | Check workflow permissions |
## Examples
### Manual Release Process
```bash
# 1. Bump version
npm run version 1.0.1
# 2. Commit and tag
git add -A
git commit -m "Release v1.0.1"
git tag 1.0.1
# 3. Push (triggers release workflow)
git push && git push --tags
```
### Beta Release Workflow
```yaml
# .github/workflows/beta.yml
name: Beta Release
on:
push:
branches: [beta]
jobs:
beta:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Create beta release
uses: softprops/action-gh-release@v1
with:
tag_name: beta
prerelease: true
files: |
main.js
manifest.json
body: |
Beta release from ${{ github.sha }}
Install via BRAT: `username/repo-name` with beta tag
```
## Resources
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
- [Obsidian Plugin Releasing Guide](https://docs.obsidian.md/Plugins/Releasing/Release+your+plugin+with+GitHub+Actions)
## Next Steps
For publishing to community plugins, see `obsidian-deploy-integration`.