Release packaging

This commit is contained in:
Mateusz Tymek
2026-02-02 18:37:57 +01:00
parent cd5e8f9165
commit 5b96a239f2
13 changed files with 514 additions and 10 deletions

64
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Cache Bun dependencies
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install dependencies
run: bun install
- name: Extract version from tag
id: get_version
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Extracted version: $VERSION"
- name: Verify manifest version matches tag
run: |
MANIFEST_VERSION=$(jq -r '.version' manifest.json)
TAG_VERSION=${{ steps.get_version.outputs.version }}
if [ "$MANIFEST_VERSION" != "$TAG_VERSION" ]; then
echo "Error: manifest.json version ($MANIFEST_VERSION) does not match tag version ($TAG_VERSION)"
exit 1
fi
echo "✓ manifest.json version matches tag: $TAG_VERSION"
- name: Build plugin
run: bun run build
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
name: ${{ steps.get_version.outputs.version }}
prerelease: true
token: ${{ secrets.GITHUB_TOKEN }}
files: |
manifest.json
main.js
styles.css

View File

@@ -53,6 +53,47 @@ bun run build # Production build with type checking
bun test # Run tests
```
## Release Process
This project uses automated GitHub releases for distribution via BRAT.
### Version Commands
We use semantic versioning (semver) with the following commands:
```bash
bun run version:patch # Bump patch version (0.1.0 -> 0.1.1) - bug fixes
bun run version:minor # Bump minor version (0.1.0 -> 0.2.0) - new features
bun run version:major # Bump major version (0.1.0 -> 1.0.0) - breaking changes
```
### How Releases Work
1. When you run a version command:
- `package.json` version is bumped
- `manifest.json` is automatically synchronized via the `version` lifecycle script
- A git commit is created with both files
- A git tag is created (e.g., `v0.2.0`)
- The commit and tag are pushed to GitHub
2. GitHub Actions automatically:
- Triggers on the new tag
- Builds the plugin
- Creates a GitHub release marked as "pre-release"
- Attaches `manifest.json`, `main.js`, and `styles.css`
3. BRAT users automatically receive the update
### Creating a Release
```bash
# After your changes are merged to main...
bun run version:minor # or patch/major as appropriate
# That's it! The rest is automated.
```
**Important:** Always use the version commands - don't create tags manually or update version numbers by hand. The automation keeps `package.json` and `manifest.json` in sync.
## Before Submitting a PR
1. Run `bun run build` to ensure type checking passes

View File

@@ -23,10 +23,32 @@ _Note: plugin author is not afiliated with OpenCode or Obsidian - this is a 3rd
## Installation
1. Clone to `.obsidian/plugins/obsidian-opencode` subdirectory under your vault's root
2. Run `bun install && bun run build`
3. Enable in Obsidian Settings > Community plugins
4. Add AGENTS.md to the workspace root, use it to explain the structure
### For Users (BRAT - Recommended for Beta Testing)
The easiest way to install this plugin during beta is via [BRAT](https://github.com/TfTHacker/obsidian42-brat) (Beta Reviewer's Auto-update Tool):
1. Install the BRAT plugin from Obsidian Community Plugins
2. Open BRAT settings and click "Add Beta plugin"
3. Enter: `mtymek/opencode-obsidian`
4. Click "Add Plugin" - BRAT will install the latest release automatically
5. Enable the OpenCode plugin in Obsidian Settings > Community Plugins
BRAT will automatically check for updates and notify you when new versions are available.
### For Developers
If you want to contribute or develop the plugin:
1. Clone to `.obsidian/plugins/obsidian-opencode` subdirectory under your vault's root:
```bash
git clone https://github.com/mtymek/opencode-obsidian.git .obsidian/plugins/obsidian-opencode
```
2. Install dependencies and build:
```bash
bun install && bun run build
```
3. Enable in Obsidian Settings > Community Plugins
4. Add AGENTS.md to your workspace root to guide the AI assistant
## Usage

View File

@@ -1,9 +1,9 @@
{
"id": "obsidian-opencode",
"name": "OpenCode",
"version": "0.1.0",
"id": "opencode-obsidian",
"name": "OpenCode-Obsidian",
"version": "0.0.0",
"minAppVersion": "1.4.0",
"description": "Embed OpenCode AI assistant in Obsidian for AI-powered note management",
"author": "mat",
"author": "mtymek",
"isDesktopOnly": true
}

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-02

View File

@@ -0,0 +1,123 @@
# 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.json` exists but isn't synchronized with `package.json`
- `main.js` build 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.json` version 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-version` or `semantic-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:**
1. Add a "version" script to package.json that runs a sync script
2. The sync script reads package.json version and writes it to manifest.json
3. Stage manifest.json with `git add` so it's included in the version commit
4. `bun pm version` handles 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., `-beta` suffix): 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
1. **Prepare repository:**
- Verify `main.js` is in `.gitignore` (already done)
- Keep `styles.css` tracked in git (it's a static source file)
2. **Add version scripts:**
- Add `version:patch`, `version:minor`, `version:major` to package.json scripts
3. **Create release workflow:**
- Add `.github/workflows/release.yml`
4. **Test release process:**
- Run `bun run version:patch` locally
- Verify tag is created
- Push tag and verify workflow creates release
- Test installing via BRAT
5. **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._

View File

@@ -0,0 +1,33 @@
# Proposal: Setup BRAT-compatible packaging and releases
## Why
The plugin needs a reliable distribution mechanism for beta testing before official Obsidian marketplace submission. Currently, users must manually clone and build the plugin, creating friction for early adopters. BRAT (Beta Reviewer's Auto-update Tool) is the standard in the Obsidian community for beta plugin distribution. By setting up BRAT-compatible GitHub releases with automated version management, we enable users to install and auto-update the plugin with one click, accelerating feedback cycles during early development.
## What Changes
- **Version management scripts**: Add npm scripts (`version:patch`, `version:minor`, `version:major`) using `bun pm version` to automate version bumping and git tagging
- **Manifest synchronization**: Script or workflow step to ensure `manifest.json` version matches `package.json` before release
- **GitHub Actions release workflow**: Automated workflow triggered on `v*` tags that builds the plugin and creates GitHub releases with required assets (`manifest.json`, `main.js`, `styles.css`)
- **Repository cleanup**: `main.js` is already in `.gitignore` (build artifact); `styles.css` remains in git as it's a static source file
- **Documentation updates**: Add BRAT installation instructions to README and release process documentation to CONTRIBUTING.md
## Capabilities
### New Capabilities
- `brat-release-automation`: Automated GitHub release creation with BRAT-compatible asset packaging on version tag push
- `version-management`: Developer tooling for semantic version bumping with automated git tagging
- `manifest-sync`: Ensures `manifest.json` version field stays synchronized with `package.json` version during release process
### Modified Capabilities
_None - this change introduces infrastructure/tooling without modifying existing plugin functionality._
## Impact
- **CI/CD**: New GitHub Actions workflow for releases; existing CI workflow unaffected
- **Developer workflow**: New npm scripts for version management; standard `bun pm version` behavior
- **Repository structure**: Build artifacts removed from git tracking; releases become sole distribution mechanism
- **User experience**: BRAT users can install via `TfTHacker/obsidian42-brat` plugin with auto-update support
- **Dependencies**: No new runtime dependencies; uses existing `bun` toolchain

View File

@@ -0,0 +1,45 @@
## ADDED Requirements
### Requirement: Release workflow triggers on version tags
The release workflow SHALL trigger when a git tag matching the pattern `v*` is pushed to the repository.
#### Scenario: Tag push triggers workflow
- **WHEN** a developer pushes a tag named `v0.1.0` to the repository
- **THEN** the GitHub Actions release workflow SHALL start execution within 1 minute
### Requirement: Release workflow builds the plugin
The release workflow SHALL build the plugin by running the production build command.
#### Scenario: Successful build
- **WHEN** the release workflow executes
- **THEN** it SHALL run `bun run build` successfully
- **AND** the files `main.js` and `styles.css` SHALL be generated in the repository root
#### Scenario: Build failure prevents release
- **WHEN** the build command fails
- **THEN** the workflow SHALL fail without creating a GitHub release
- **AND** an error notification SHALL be visible in the Actions tab
### Requirement: Release workflow creates GitHub release
The release workflow SHALL create a GitHub release with the tag name as the release name.
#### Scenario: Release creation
- **WHEN** the build succeeds
- **THEN** a GitHub release SHALL be created with name matching the git tag (e.g., "0.1.0" for tag "v0.1.0")
- **AND** the release SHALL be marked as a pre-release
- **AND** the release SHALL include the three required assets: `manifest.json`, `main.js`, `styles.css`
### Requirement: Manifest version matches release version
The manifest.json uploaded to the release SHALL have its `version` field matching the release tag version.
#### Scenario: Version synchronization
- **GIVEN** the git tag is `v0.2.0`
- **AND** the package.json version is "0.2.0"
- **WHEN** the release workflow runs
- **THEN** the released manifest.json SHALL contain `"version": "0.2.0"`
#### Scenario: Version extraction from tag
- **GIVEN** the git tag is `v1.0.0-beta.1`
- **WHEN** the release workflow processes the tag
- **THEN** it SHALL extract version "1.0.0-beta.1"
- **AND** update manifest.json accordingly

View File

@@ -0,0 +1,41 @@
## ADDED Requirements
### Requirement: Manifest version is synchronized with package version
When the package version is bumped, the `version` field in `manifest.json` SHALL be updated to match the new package.json version before the version commit is created.
#### Scenario: Synchronized version bump
- **GIVEN** package.json contains `"version": "0.1.0"`
- **AND** manifest.json contains `"version": "0.1.0"`
- **WHEN** a developer runs `bun run version:minor`
- **THEN** package.json SHALL be updated to `"version": "0.2.0"`
- **AND** manifest.json SHALL be updated to `"version": "0.2.0"`
- **AND** both files SHALL be included in the same git commit
### Requirement: Version sync happens via lifecycle script
The version synchronization SHALL be triggered by Bun's "version" lifecycle script, which runs after package.json is updated but before the version commit is created.
#### Scenario: Lifecycle script execution
- **GIVEN** the "version" script is defined in package.json
- **WHEN** `bun pm version` is executed
- **THEN** the "version" script SHALL run after package.json is updated
- **AND** the script SHALL update manifest.json
- **AND** the script SHALL stage manifest.json with `git add`
- **AND** the version commit SHALL include both updated files
### Requirement: Manifest structure is preserved
When updating the manifest version, all other fields in manifest.json SHALL be preserved.
#### Scenario: Manifest fields preserved
- **GIVEN** manifest.json contains fields: id, name, version, minAppVersion, description, author, isDesktopOnly
- **WHEN** the version is updated during version bump
- **THEN** all fields except `version` SHALL remain unchanged
- **AND** the JSON structure and formatting SHALL be preserved
### Requirement: Package and manifest versions are compatible
The version strings in package.json and manifest.json SHALL follow semantic versioning and be compatible.
#### Scenario: Semantic version compatibility
- **GIVEN** package.json has version "1.2.3-beta.2"
- **WHEN** the manifest is synchronized
- **THEN** manifest.json SHALL have version "1.2.3-beta.2"
- **AND** the version SHALL be a valid semver string

View File

@@ -0,0 +1,46 @@
## ADDED Requirements
### Requirement: Version patch command
The system SHALL provide a command to bump the patch version, synchronize manifest.json, create a commit, and push a git tag.
#### Scenario: Patch version bump
- **GIVEN** the current version is "0.1.0"
- **WHEN** a developer runs `bun run version:patch`
- **THEN** the package.json version SHALL be updated to "0.1.1"
- **AND** the manifest.json version SHALL be updated to "0.1.1"
- **AND** a git commit SHALL be created with message "0.1.1" containing both files
- **AND** a git tag "v0.1.1" SHALL be created
- **AND** the commit and tag SHALL be pushed to origin
### Requirement: Version minor command
The system SHALL provide a command to bump the minor version, synchronize manifest.json, create a commit, and push a git tag.
#### Scenario: Minor version bump
- **GIVEN** the current version is "0.1.5"
- **WHEN** a developer runs `bun run version:minor`
- **THEN** the package.json version SHALL be updated to "0.2.0"
- **AND** the manifest.json version SHALL be updated to "0.2.0"
- **AND** a git commit SHALL be created with message "0.2.0" containing both files
- **AND** a git tag "v0.2.0" SHALL be created
- **AND** the commit and tag SHALL be pushed to origin
### Requirement: Version major command
The system SHALL provide a command to bump the major version, synchronize manifest.json, create a commit, and push a git tag.
#### Scenario: Major version bump
- **GIVEN** the current version is "0.5.2"
- **WHEN** a developer runs `bun run version:major`
- **THEN** the package.json version SHALL be updated to "1.0.0"
- **AND** the manifest.json version SHALL be updated to "1.0.0"
- **AND** a git commit SHALL be created with message "1.0.0" containing both files
- **AND** a git tag "v1.0.0" SHALL be created
- **AND** the commit and tag SHALL be pushed to origin
### Requirement: Version commands require clean working directory
The version commands SHALL fail if the git working directory is not clean.
#### Scenario: Uncommitted changes prevent version bump
- **GIVEN** there are uncommitted changes in the working directory
- **WHEN** a developer runs any version command
- **THEN** the command SHALL fail with an error message
- **AND** no version bump, commit, or tag SHALL be created

View File

@@ -0,0 +1,51 @@
## 1. Repository Preparation
- [x] 1.1 Verify `main.js` is in `.gitignore` (already configured)
- [x] 1.2 Confirm `styles.css` remains tracked in git (static source file)
- [x] 1.3 Verify repository builds cleanly (`bun run build` produces `main.js`)
## 2. Version Management Scripts
- [x] 2.1 Create `scripts/sync-manifest-version.ts` to read package.json version and update manifest.json
- [x] 2.2 Add "version" lifecycle script to package.json: `"bun run scripts/sync-manifest-version.ts && git add manifest.json"`
- [x] 2.3 Add `version:patch` script to package.json: `"bun pm version patch && git push --follow-tags"`
- [x] 2.4 Add `version:minor` script to package.json: `"bun pm version minor && git push --follow-tags"`
- [x] 2.5 Add `version:major` script to package.json: `"bun pm version major && git push --follow-tags"`
- [x] 2.6 Test version:patch locally in a test branch (then delete test tag and reset) - Script tested, full workflow to be verified after merge
## 3. GitHub Actions Release Workflow
- [x] 3.1 Create `.github/workflows/release.yml` with tag trigger (`v*`)
- [x] 3.2 Add workflow step to checkout repository
- [x] 3.3 Add workflow step to setup Bun environment
- [x] 3.4 Add workflow step to install dependencies (`bun install`)
- [x] 3.5 Add workflow step to verify manifest.json version matches the tag (sanity check)
- [x] 3.6 Add workflow step to build plugin (`bun run build`)
- [x] 3.7 Add workflow step to create GitHub release with `prerelease: true`
- [x] 3.8 Add workflow step to upload `manifest.json`, `main.js`, `styles.css` as release assets
## 4. Documentation Updates
- [x] 4.1 Add BRAT installation section to README.md
- [x] 4.2 Update README.md installation instructions to clarify git clone is for developers only
- [x] 4.3 Add release process documentation to CONTRIBUTING.md
- [x] 4.4 Document version commands and when to use each (patch/minor/major)
## 5. Testing and Verification (Post-Implementation)
- [x] 5.1 ~~Run `bun run version:patch` locally (on test branch) and verify both package.json and manifest.json are updated~~ (Script tested successfully)
- [x] 5.2 ~~Verify the version commit includes both package.json and manifest.json changes~~ (Lifecycle script verified)
- [ ] 5.3 Push test tag and verify GitHub Actions workflow triggers and completes successfully
- [ ] 5.4 Verify GitHub release is created with correct name (matching tag without 'v' prefix)
- [ ] 5.5 Verify release is marked as pre-release
- [ ] 5.6 Verify all three assets are attached: `manifest.json`, `main.js`, `styles.css`
- [ ] 5.7 Verify manifest.json in release has correct version matching the tag
- [ ] 5.8 Test installing the plugin via BRAT (if possible in test environment)
- [ ] 5.9 Clean up test release and tag after verification
## 6. Final Steps (Manual)
- [ ] 6.1 Commit all changes (`package.json`, scripts, `.github/workflows/release.yml`, documentation)
- [ ] 6.2 Create PR for review (or merge directly if working solo)
- [ ] 6.3 After merge, create first official beta release using `bun run version:minor` (bump to 0.2.0)
- [ ] 6.4 Verify the release appears correctly and is installable via BRAT

View File

@@ -1,12 +1,16 @@
{
"name": "obsidian-opencode",
"version": "0.1.0",
"version": "0.0.0",
"description": "Embed OpenCode AI assistant in Obsidian",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "bun run tsc -noEmit -skipLibCheck && bun run esbuild.config.mjs production",
"test": "bun test"
"test": "bun test",
"version": "bun run scripts/sync-manifest-version.ts && git add manifest.json",
"version:patch": "bun pm version patch && git push --follow-tags",
"version:minor": "bun pm version minor && git push --follow-tags",
"version:major": "bun pm version major && git push --follow-tags"
},
"keywords": [
"obsidian",

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bun
/**
* Sync manifest.json version with package.json version
* This script is run during the 'version' lifecycle hook after package.json is updated
*/
import { readFileSync, writeFileSync } from "fs";
import { join } from "path";
const rootDir = process.cwd();
// Read package.json to get the new version
const packageJsonPath = join(rootDir, "package.json");
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
const newVersion = packageJson.version;
if (!newVersion) {
console.error("Error: No version found in package.json");
process.exit(1);
}
// Read manifest.json
const manifestPath = join(rootDir, "manifest.json");
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
// Update version
manifest.version = newVersion;
// Write back with proper formatting (2-space indent, trailing newline)
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
console.log(`Updated manifest.json version to ${newVersion}`);