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".
GitHub Actions workflows for Obsidian plugin development: build validation on every push, automated releases when you tag, version-bump scripting, manifest.json validation, and BRAT beta channel support.
Prerequisites
GitHub repository with an Obsidian plugin
Working local build (npm run build produces main.js)
Beta users install via BRAT by entering your GitHub repo URL. BRAT fetches the latest release (including pre-releases) automatically — no submission to the community repo needed.
Output
.github/workflows/build.yml — validates build on every push/PR
.github/workflows/release.yml — creates GitHub release with main.js, manifest.json, styles.css on tag push
.github/workflows/validate.yml — checks version consistency across manifest, package.json, and versions.json
version-bump.mjs — keeps manifest.json and versions.json in sync with package.json version
Optional beta-manifest.json for BRAT beta channel
Error Handling
Error
Cause
Solution
main.js not found
Build script doesn't output to root
Check esbuild outfile points to ./main.js
Release has no assets
Tag pushed before build
Let the release workflow handle the build, don't attach manually
Version mismatch
Forgot npm version
Run npm version patch instead of editing manifest by hand
BRAT not picking up beta
No pre-release on GitHub
Create release and check "pre-release" checkbox
npm ci fails
No lockfile
Commit package-lock.json to the repo
Permission denied on release
Missing contents: write
Add permissions block to release job
Examples
Tag and Release a New Version
set -euo pipefail
# Bump, commit, tag, push — release workflow fires automatically
npm version patch
git push origin main --tags
Manual Build Verification
set -euo pipefail
npm ci
npm run build
test -f main.js && echo "Build OK: $(wc -c < main.js) bytes" || echo "FAIL: main.js missing"
node -e "const m=require('./manifest.json'); console.log(m.id, 'v'+m.version)"
Release with Changelog
# In release.yml, replace generate_release_notes with a body:
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
main.js
manifest.json
styles.css
body: |
## Changes
- Feature: Added X
- Fix: Resolved Y