Repository navigation
Releases: obsidianmd/obsidian-workflows
Release list
v1.2.3
Fixes a regression introduced in v1.2.2. No action is needed to pick it up if you reference the action as @v1.
Fixed
Valid Markdown images were no longer counted in the README check
v1.2.2 rewrote the Markdown image pattern so it could not backtrack, by excluding each opening delimiter from its own character class. That kept the pattern linear, but it also stopped it recognising valid Markdown:
.png)
![label with [brackets]](image.png)A balanced pair of parentheses in the destination is legal Markdown and appears in real filenames. The first form was previously matched, if truncated at the inner ); the second was already missed before v1.2.2.
An unrecognised image is neither counted toward the image total nor stripped from the prose total, so both halves of the readme-screenshots-only heuristic moved in the same wrong direction and a README of screenshots could avoid the finding.
A regular expression cannot match balanced delimiters at all, and the narrow character classes needed to keep one linear are exactly what dropped the valid syntax. The check now builds the matching [/] and (/) positions in two passes and reads them in a third, which stays linear while handling nesting and backslash escapes that the original pattern did not.
Images nested inside links, the common badge form [](link), are still counted as before. A README consisting of 400,000 ![ sequences scans in about 96 ms.
Full Changelog: v1.2.2...v1.2.3
v1.2.2
Hardening follow-up to v1.2.1. That release fixed one regular expression that could stall a job; this one audits every regular expression in the action and adds a lint rule so the class of bug cannot return.
Fixed
Manifest author check could stall on a large author field
The email detector anchored its local part with a leading \b. Because the character class contains non-word characters (., %, +, -), a word boundary exists part-way through a crafted author value, so the match restarted at each one and rescanned the remainder — quadratic. A 256 KB author field cost about 33 seconds.
A lookbehind now pins matching to run boundaries. The check also became slightly stricter as a side effect: local parts beginning with punctuation, such as .name@example.com, are now detected where the old \b skipped them. Verified across two million randomised inputs, the new form never misses an address the old one caught.
README checks could stall on crafted markdown
The markdown image, <img> and generic HTML tag patterns each permitted their own opening delimiter inside the character class. A run of ![![![ or <img<img therefore restarted the match at every offset and rescanned the rest of the README each time. Excluding the delimiter bounds each attempt.
Stylelint output parsing could backtrack
\s matches newlines, which made the surrounding ^ and $ anchors ambiguous under the m flag. Only horizontal whitespace can pad the JSON line, so the class is now explicit.
Unknown-field lookup rewritten
/unknown field: (.*)\.$/ was quadratic, and every regular-expression form of it remains so because the literal prefix itself re-matches. It is now a plain indexOf and slice, which is linear and additionally preserves field names containing a period.
Added
Lint gate for ReDoS
eslint-plugin-redos now runs as part of npm run lint, which CI already executes. It performs the same automata-based analysis as recheck, reporting the worst-case complexity class of each pattern. A regular expression that backtracks superlinearly now fails the build, instead of surfacing later as a job that hangs on a large enough input.
All 41 regular expressions in src/ are reported safe under this rule.
Full Changelog: v1.2.1...v1.2.2
v1.2.1
Fixed
Artifact preflight could stall a job for tens of minutes
The bundle-wasm-reference detector matched an unbounded character class before the .wasm literal:
/[^\s"'`\\)\]}>,;]+\.wasm\b/gi
That class excludes whitespace, quotes and brackets, but not base64 characters. A minified main.js that embeds a base64 payload — an inline WebAssembly module, font, image, or sourcemap — therefore contains one unbroken run of characters with no boundary in it. The engine began a match attempt at every offset inside that run, consumed to the end of the run, then backtracked across it looking for .wasm, giving quadratic behaviour.
On a real 2.9 MB plugin bundle holding a 568,136-character inline WebAssembly blob, that works out to roughly 3.6 × 10¹¹ backtracking steps — about 42 minutes of solid CPU with the Artifact preflight group open and no output written. Affected runs were being cancelled or hitting the job timeout, with nothing in the log to indicate why.
The detector now anchors on the .wasm literal and expands backwards to a path boundary, so there is no backtracking at all. The same bundle now completes in milliseconds and reports the same references.
Two things made this easy to hit and hard to diagnose:
- Any plugin bundling a large base64 payload was affected, whether or not it uses WebAssembly — the run of base64 characters is what triggers it, not the payload's contents.
- Artifact preflight runs unconditionally for plugins, so there was no input to disable it and no way to work around it.
The neighbouring bundle-inline-wasm check already flagged these bundles correctly in a few milliseconds; only the reference detector was affected. No other preflight check, and no other part of the action, shares the pattern.
Full Changelog: v1.2.0...v1.2.1
v1.2.0
Fixed
Repositories without a package-lock.json no longer fail
actions/setup-node throws when cache: npm cannot find a lockfile matching the selected package manager, so the reusable release workflow failed before this action ever ran.
This affected:
- Theme repositories, which commonly contain only
theme.cssandmanifest.json - Any repository using pnpm, since
pnpm-lock.yamlis not one of npm's recognized
lockfile patterns
cache: has been removed from the reusable workflow, and the package manager is now detected from the committed lockfile instead of assumed. pnpm, yarn and bun use their own frozen-install commands. If the detected tool is unavailable on the runner, the action warns and falls back to npm install rather than failing the job.
If you copied the release workflow from the README, remove the cache: npm line from your actions/setup-node step.
Closes #12
Stylelint findings were being discarded
Stylelint writes its JSON report to stderr. The action read only stdout, so every Stylelint finding was silently dropped and reported as inconclusive.
Plugins with no CSS no longer report a false error
Stylelint was invoked even when a repository contained no CSS files, and its "no matching files" exit was reported as a failure.
Added
Per-finding results
Previously an entire Stylelint or ESLint run collapsed into a single generic warning, with the real diagnostics visible only in raw log output.
Findings now carry a stable rule ID, severity, status and source location, and are emitted as annotations with file and line — so they appear directly on the diff.
Job summary
Every run writes a summary containing:
- Findings grouped by check, with rule IDs, locations and remediation links
- A coverage table showing what was checked here, what is partial, and what cannot run until a published release exists
- An explicit statement that passing here does not guarantee directory acceptance
Expanded community directory parity
New checks for plugin and theme manifests, README content, manifest URLs, dependency pinning, directory ID/name conflicts, and repository metadata, plus an advisory preflight over the locally built main.js.
Themes now require author and minAppVersion, matching the directory.
Obfuscation, funding and AI-generated detection are deliberately excluded.
strict input
- uses: obsidianmd/obsidian-workflows@v1
with:
strict: 'true' # default: 'false'Policy findings are advisory by default and reported as warnings. They will not fail your build. Set strict: true to enforce them.
Correctness failures remain hard errors in both modes: a failed build, an unparsable manifest.json, or missing release assets.
Upgrade notes
No action required. Because policy findings are advisory by default, existing workflows will not start failing.
You will see new warnings and a job summary. These reflect requirements the community directory already enforces — the action previously just didn't report them. We recommend reviewing them ahead of the end of the grandfathering period, after which they will apply to existing plugins and themes as well.
Plugin and theme IDs are exempt permanently: identifiers cannot change without breaking existing installations, so entries already in the directory keep theirs.
To pin to the previous behavior, use @v1.1.0.
v1.1.0
Changes since v1.0.0
Moved to obsidianmd organization
- All references updated from
saberzero1/obsidian-workflowstoobsidianmd/obsidian-workflows
Scanner parity
- Lint dependency isolation via temp directory (matches scanner architecture)
- Dynamic
minAppVersion→ Electron browser target mapping for stylelint - User dependencies installed before ESLint for type resolution
- Correct stylelint config values matching scanner's stylelint 17.6.0
manifest.jsonexcluded from ESLint (validated separately, matching scanner)- Build artifacts (
main.js,styles.css) excluded from ESLint
Attestation
- Matches
actions/attest@v4approach exactly - Auto-detects sigstore instance from repository visibility
- Per-subject attestation with SLSA provenance predicate
Dependencies
- Bumped
@actions/core,@actions/exec,@actions/glob,@actions/io - Bumped
actions/checkoutandactions/setup-nodeto v7 - Pinned prettier to 3.8.4 (matches super-linter)
Usage
- uses: obsidianmd/obsidian-workflows@v1See the readme for full documentation.
v1.0.0
Initial release of Obsidian Workflows — a GitHub Action that validates, lints, builds, and releases Obsidian plugins and themes.
Features
- Auto-detection of plugin vs theme projects via manifest.json and theme.css
- Manifest validation matching community directory schema expectations
- License and readme checks (error if missing, warn if non-OSI)
- Build auto-detection for plugins (build, build:plugin, compile scripts)
- Scanner lint rulesets — stylelint and eslint-plugin-obsidianmd configs matching the community scanner
- Release mode with asset validation, manifest consistency checks, build artifact attestation via @actions/attest, and draft release creation
- Reusable release workflow for single-file adoption
Usage
# PR checks
- uses: saberzero1/obsidian-workflows@v1
# Release
- uses: saberzero1/obsidian-workflows@v1
with:
mode: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}See the readme for full documentation.