6.2 KiB
Design: BRAT-compatible packaging and releases
Context
The obsidian-opencode plugin is currently in early development (v0.1.0). The repository contains main.js which is a build artifact, while styles.css is a static source file. Users must manually clone/build to install. This creates friction for beta testers and doesn't scale.
BRAT (Beta Reviewer's Auto-update Tool) is the community standard for beta plugin distribution. It works by monitoring GitHub releases and downloading manifest.json, main.js, and styles.css from release assets.
Current state:
- Uses Bun as package manager and build tool
- Has esbuild-based build system producing
main.js - Has CI workflow for testing but no release automation
manifest.jsonexists but isn't synchronized withpackage.jsonmain.jsbuild artifact is already in.gitignore
Goals / Non-Goals
Goals:
- Enable one-click installation via BRAT for beta testers
- Automate release creation on version tag push
- Ensure
manifest.jsonversion always matches release tag - Remove build artifacts from git repository
- Provide simple version bumping commands for developers
Non-Goals:
- Marketplace submission (out of scope for this change)
- Automated changelog generation
- Multi-platform release assets (only JS/CSS files, no binaries)
- Version pinning or rollback mechanisms
- Signing or verification of releases
Decisions
1. Use bun pm version for version management
Rationale: Bun's built-in version manager updates package.json, creates a git commit, and tags automatically. This is simpler than custom scripts and matches the existing Bun-based toolchain.
Alternatives considered:
- Custom Node.js script: More code to maintain, requires parsing JSON
standard-versionorsemantic-release: Too complex for early-stage project- Manual editing: Error-prone, inconsistent
2. Synchronized version updates: package.json + manifest.json
Approach: Use Bun's npm lifecycle scripts. When bun pm version runs, it executes the "version" script after updating package.json but before creating the commit. In this script, we read the new version from package.json and update manifest.json. Both files are then committed together with the same version.
Rationale: Keeps both files in sync at all times in the repository. The manifest.json always reflects the current package.json version, making it clear what version is being developed. This also simplifies the release workflow since manifest.json is already correct.
Implementation:
- Add a "version" script to package.json that runs a sync script
- The sync script reads package.json version and writes it to manifest.json
- Stage manifest.json with
git addso it's included in the version commit bun pm versionhandles the commit and tag automatically
Alternatives considered:
- Two-stage (release-time sync): Manifest in repo stays out of sync with package.json, confusing during development
- Custom version script: Would lose Bun's built-in lifecycle management
- Pre-commit hook: Would run on every commit, not just version bumps
3. Tag-triggered GitHub Actions workflow
Approach: Workflow triggers on v* tag push (e.g., v0.1.0). This is explicit and intentional - releases happen only when a developer decides to tag.
Rationale: Prevents accidental releases from every commit. Tag-based triggers are standard for GitHub releases and integrate well with bun pm version which creates tags.
Alternatives considered:
- Manual workflow dispatch: More steps, easier to forget
- Release on every push to main: Too frequent, no control
- Release on merged PR: Good for continuous deployment, but we want explicit versioning
4. Mark all releases as prerelease: true during beta
Approach: The GitHub Actions workflow will always set prerelease: true on releases. This is hardcoded until we're ready for marketplace.
Rationale: Signals to users and BRAT that these are beta versions. Once we submit to marketplace, we'll change this to prerelease: false for stable releases.
Alternatives considered:
- Detect from version string (e.g.,
-betasuffix): More complex, inconsistent with BRAT expectations - Manual toggle in workflow: Easy to forget
5. Build artifact (main.js) only in releases
Approach: main.js is already in .gitignore. The GitHub Actions workflow will build it and attach to releases. styles.css is a static source file and remains tracked in git.
Rationale: main.js is generated by esbuild and should not be in git. styles.css is hand-written and should be version controlled. Both are included in releases for BRAT distribution.
Trade-off: Repository won't have main.js after clone - developers must run bun install && bun run build. Acceptable since this is a development workflow.
Risks / Trade-offs
| Risk | Mitigation |
|---|---|
| Tag pushed but release fails | Workflow will fail visibly; developer can delete tag and retry, or manually create release |
| Version mismatch between package.json and manifest.json in release | Workflow enforces manifest.json version is updated from package.json before build |
| Users can't install from git clone anymore | Document that BRAT is the intended install method for users; git clone is for developers only |
| Build fails on CI but works locally | Existing CI already builds successfully; release workflow uses same build command |
Forgetting to use bun pm version vs manual tag |
Document in CONTRIBUTING.md; consider adding a check that verifies tag matches package.json version |
Migration Plan
-
Prepare repository:
- Verify
main.jsis in.gitignore(already done) - Keep
styles.csstracked in git (it's a static source file)
- Verify
-
Add version scripts:
- Add
version:patch,version:minor,version:majorto package.json scripts
- Add
-
Create release workflow:
- Add
.github/workflows/release.yml
- Add
-
Test release process:
- Run
bun run version:patchlocally - Verify tag is created
- Push tag and verify workflow creates release
- Test installing via BRAT
- Run
-
Update documentation:
- Add BRAT installation section to README
- Add release process to CONTRIBUTING.md
Open Questions
None at this time - design is ready for implementation.