A GitHub Action that validates, lints, builds, and releases Obsidian plugins and themes. Matches the community directory's validation and scanner expectations so your submissions pass on the first try.
Create a file at .github/workflows/ci.yml:
name: CI
on:
pull_request:
branches: [main, master]
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: obsidianmd/obsidian-workflows@v1The action auto-detects whether your project is a plugin or theme.
Create a file at .github/workflows/release.yml:
name: Release
on:
push:
tags: ['*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
- uses: obsidianmd/obsidian-workflows@v1
with:
mode: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}This creates a draft release — after the workflow completes, go to your repository's Releases page to review and publish it.
Or use the reusable release workflow for a single-file setup at
.github/workflows/release.yml:
name: Release
on:
push:
tags: ['*']
jobs:
release:
uses: obsidianmd/obsidian-workflows/.github/workflows/release.yml@v1
permissions:
contents: write
id-token: write
attestations: writeThis also creates a draft release that you need to manually publish.
| Input | Default | Description |
|---|---|---|
type |
auto |
Project type: plugin, theme, or auto. Auto-detection checks manifest.json for an id field and theme.css presence. |
mode |
pr |
Run mode: pr (CI checks) or release (full release validation, attestation, draft release). |
build |
(empty) | Explicit build command override. For plugins, auto-detects build/build:plugin/compile. Set to false to disable. |
lint |
true |
Whether to run linting. |
scanner-lint |
false |
Use community scanner lint rulesets instead of project config. Always forced true in release mode. |
node-version |
24 |
Node.js version. Minimum enforced: 20. |
| Output | Description |
|---|---|
type |
Detected or specified project type (plugin/theme). |
validation-passed |
Whether all validation checks passed (true/false). |
release-url |
URL of the created draft release (release mode only). |
| Check | PR Mode | Release Mode |
|---|---|---|
manifest.json schema |
Yes | Yes |
versions.json format |
If present | If present |
| Readme exists and non-empty | Yes | Yes |
| License (error if missing, warn if non-OSI) | Yes | Yes |
| Build (auto-detect or explicit) | Plugins | Plugins |
| Lint (user's config) | Default | No |
| Lint (scanner rulesets) | Opt-in | Always |
| Release assets present | No | Yes |
| Manifest consistency (HEAD vs tag) | No | Yes |
| Build artifact attestation | No | Yes |
| Draft release creation | No | Yes |
The action detects your project type automatically:
manifest.jsonhas anidfield (string) → pluginmanifest.jsonhas noidfield andtheme.cssexists → theme- Neither condition met → error with instructions to set
typeexplicitly
In release mode (or when scanner-lint: true), the action installs and runs the
same lint configurations used by the Obsidian community directory scanner:
- Stylelint with the scanner's ruleset (both plugins and themes)
- ESLint with
eslint-plugin-obsidianmd(plugins only, with type-aware rules whentsconfig.jsonis present)
This ensures your release will pass the community directory's automated checks.
For plugins, the action checks package.json for the first available script:
buildbuild:plugincompile
If found, it runs npm ci (or npm install) followed by the detected script.
Set build: false to skip, or build: <command> to override.
npm install
npm test
npm run bundleMIT