bitsofchris_openaugi-obsidi.../docs/PUBLISHING.md
Chris Lettieri cc62c4d591 release tooling: scripts/release.sh + version-consistency guard test
Codify the release process so we stop drifting version files and stop
leaving the repo advertising a version with no published release (the
inconsistency that plausibly got the plugin auto-removed from the store).

- scripts/release.sh: one-shot, self-verifying release. Bumps manifest.json,
  package.json AND versions.json in lockstep, runs tests+build, pushes,
  tags (no v prefix), waits for CI, PUBLISHES the draft immediately, then
  verifies root manifest == released asset == tag. Includes a store-listing
  health check.
- tests/version-consistency.test.ts: guard test — fails the build if the
  three version files ever disagree (catches a forgotten versions.json bump
  before tagging).
- docs/PUBLISHING.md: rewritten script-first; adds versions.json (was
  omitted), the publish-immediately rule, and a 'if de-listed from the
  store' runbook (ask #plugin-dev, community.obsidian.md portal, the
  'entry already exists' gotcha).
- CLAUDE.md: quick-summary now points at the script and the three files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:04:53 -04:00

7.7 KiB
Raw Blame History

Publishing a New Release

name: publish-release description: How to cut and publish a new OpenAugi plugin version safely, keep it consistent with the Obsidian community store, and what to do if the plugin gets de-listed. when to use: Any time you ship a new version of the plugin (features, fixes) or need to relist it in the community store.


TL;DR — use the script

# 1. Write the release notes first (they become the GitHub release body):
#      docs/release-notes/X.Y.Z.md
# 2. Then run:
./scripts/release.sh X.Y.Z

release.sh does the whole thing and verifies it: checks preconditions → runs tests + build → bumps manifest.json, package.json, and versions.json in lockstep → commits, pushes, tags → waits for CI → publishes the draft immediately → verifies that the repo-root manifest, the released manifest asset, and the tag all equal X.Y.Z. It stops loudly on any mismatch.

Everything below explains why it does what it does, and the manual fallback.

The three things Obsidian actually checks

  1. manifest.json at the repo-root HEAD — Obsidian reads only the version field here to decide "what's the latest version."
  2. A GitHub release whose tag == that version (no v prefix), with main.js, manifest.json, and styles.css attached as assets. Obsidian downloads the files from the release, not from the repo tree.
  3. versions.json — maps each plugin version → minimum Obsidian version. Used to decide whether a given user's Obsidian is new enough to be offered the update.

If the root manifest advertises a version that has no matching published release, the plugin is in an inconsistent state — and Obsidian's automated validation can drop it from the store. Never leave that window open. (This is what bit us on 0.6.0.)

Version files — all THREE must move together

File Field Notes
manifest.json version source of truth for the release tag
package.json version keep in sync (npm/build hygiene)
versions.json add "X.Y.Z": "<minAppVersion>" easy to forget — it's why updates/relisting break

The tests/version-consistency.test.ts guard test fails the build if these ever disagree. npm test therefore catches a forgotten versions.json bump before you tag.

Historical note: earlier versions of this doc listed only manifest.json and package.json. versions.json was missing entirely, which broke the npm version script and left the store metadata incomplete. Don't regress.

Pre-release checklist

  • Features/fixes merged to master; git status clean; local == origin/master
  • npm test passes (includes the version-consistency guard) — see TESTING.md
  • npm run build clean (tsc + esbuild)
  • Manually tested in a real vault (npm run dev, reload plugin, exercise the change)
  • docs/CODEBASE_MAP.md / README.md updated for any behavior change
  • Release notes written to docs/release-notes/X.Y.Z.md

Release notes format

Write docs/release-notes/X.Y.Z.md before releasing. Claude: diff commits since the previous tag and focus on user-facing changes.

# X.Y.Z — <one-line theme>

## Added / Changed / Fixed / Deprecated
- <user-facing change>: <why the user cares>

## Notes
- <breaking changes or upgrade steps, if any>

Manual fallback (if you can't use the script)

Only if release.sh can't run. Do the steps in this order and don't stop before publishing:

# 1. Bump ALL THREE (or run: npm version X.Y.Z  — see note below)
#    manifest.json .version, package.json .version, versions.json add "X.Y.Z":"<minAppVersion>"
npm test && npm run build            # guard test must be green

git add manifest.json package.json versions.json docs/release-notes/X.Y.Z.md
git commit -m "Release X.Y.Z"
git push origin master

git tag -a X.Y.Z -m "X.Y.Z"          # annotated, no 'v' prefix
git push origin X.Y.Z                # triggers .github/workflows/release.yml → draft

# 2. Wait for the workflow, then PUBLISH RIGHT AWAY (don't leave it a draft):
gh run watch "$(gh run list --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId')" --exit-status
gh release edit X.Y.Z --draft=false --latest --notes-file docs/release-notes/X.Y.Z.md

# 3. Verify root manifest, released asset, and tag all say X.Y.Z:
curl -sL https://raw.githubusercontent.com/bitsofchris/openaugi-obsidian-plugin/master/manifest.json | grep version
curl -sL https://github.com/bitsofchris/openaugi-obsidian-plugin/releases/download/X.Y.Z/manifest.json | grep version

npm version X.Y.Z also works: .npmrc sets tag-version-prefix="" (so the tag has no v), and the version npm script runs version-bump.mjs which updates manifest.json + versions.json, while npm bumps package.json. It commits and tags for you — but it does not push or publish, so you still owe steps 13 above (push, wait, publish, verify). The script does all of it.

Community-store listing health

The plugin is listed in Obsidian's store via obsidianmd/obsidian-releases. That repo's community-plugins.json is now an automated mirror of Obsidian's internal list (commits read chore: Mirror community plugins and themes), so you can't fix listing by PR'ing that file — it gets overwritten.

Check whether we're currently listed:

curl -sL https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugins.json | grep -i openaugi
  • Listed → core Settings → Community plugins → "Check for updates" works for users.
  • Not listed → core update check can't see us; users must use BRAT or a manual install. See the relisting steps below.

If the plugin gets removed from the store

This happened on 2026-07-07 (removed in mirror commit 85db6f23, bundled with several unrelated plugins — an automated batch removal). The mirror commits carry no per-plugin reason and Obsidian doesn't file an issue on your repo, so:

  1. Fix consistency first (so a resubmit passes validation): run a clean release with release.sh — root manifest version must equal a published release with the three assets, versions.json present, README/LICENSE present. (All true as of 0.6.0.)
  2. Ask Obsidian why + request reinstatement — don't blindly resubmit an auto-removal. Post in the Obsidian Discord #plugin-dev channel (fastest; plugin-review team is active there) or the forum Developers: Plugin & API category. Cite: repo URL, removal date 2026-07-07, mirror commit 85db6f23.
  3. If they tell you to resubmit: the current path is the web portal at community.obsidian.md → sign in with your Obsidian account → link GitHub → Plugins → New plugin → enter the repo URL. (The old "PR to community-plugins.json" flow is dead now that the file is bot-mirrored.)
  4. Gotcha — "An entry already exists for this repository": because the old entry is archived rather than deleted, resubmitting the same repo URL can be rejected. Ask a moderator in #plugin-dev to delete the archived entry, then resubmit.

Sources: Obsidian plugin submission docs; obsidian-releases repo (mirror architecture); forum thread "Cannot resubmit plugin after archiving: 'An entry already exists for this repository'".

Troubleshooting

Tag already exists / wrong version tagged

git tag -d X.Y.Z
git push origin --delete X.Y.Z     # if already pushed
# fix versions, re-run ./scripts/release.sh X.Y.Z

Workflow failedgh run view <id>, fix, delete the tag, re-run the script.

Release stuck as draftgh release edit X.Y.Z --draft=false --latest.