Skip to content

Releases: obsidianmd/obsidian-workflows

v1.2.3

Choose a tag to compare

@saberzero1 saberzero1 released this 22 Sep 20:53
v1.2.3
8167cae

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:

![diagram](images/build_(dark).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 [![badge](image)](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

Choose a tag to compare

@saberzero1 saberzero1 released this 22 Sep 20:32
v1.2.2
136ebd8

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

Choose a tag to compare

@saberzero1 saberzero1 released this 22 Sep 19:27
v1.2.1
23fb0ad

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

Choose a tag to compare

@saberzero1 saberzero1 released this 18 Sep 22:19
v1.2.0
611f291

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.css and manifest.json
  • Any repository using pnpm, since pnpm-lock.yaml is 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

Choose a tag to compare

@saberzero1 saberzero1 released this 19 Aug 18:48
v1.1.0
bd29a7e

Changes since v1.0.0

Moved to obsidianmd organization

  • All references updated from saberzero1/obsidian-workflows to obsidianmd/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.json excluded from ESLint (validated separately, matching scanner)
  • Build artifacts (main.js, styles.css) excluded from ESLint

Attestation

  • Matches actions/attest@v4 approach 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/checkout and actions/setup-node to v7
  • Pinned prettier to 3.8.4 (matches super-linter)

Usage

- uses: obsidianmd/obsidian-workflows@v1

See the readme for full documentation.

v1.0.0

Choose a tag to compare

@saberzero1 saberzero1 released this 18 Aug 11:27
v1.0.0
c5ac212

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.