diff --git a/.github/workflows/obsidian-release.yaml b/.github/workflows/obsidian-release.yaml new file mode 100644 index 000000000..d4debb51e --- /dev/null +++ b/.github/workflows/obsidian-release.yaml @@ -0,0 +1,76 @@ +name: Stable Obsidian Release +on: + workflow_dispatch: + inputs: + version: + description: "Stable version to release, e.g. 1.6.0. Must match an existing Linear release version." + required: true + type: string + +permissions: + contents: write + +env: + VERSION: ${{ inputs.version }} + OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_PLUGIN_REPO_TOKEN }} + SUPABASE_URL: ${{ secrets.SUPABASE_URL }} + SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Validate version input + run: | + if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Invalid version format: '$VERSION'. Expected semver like 1.6.0." + exit 1 + fi + + - name: Install pnpm + uses: pnpm/action-setup@v6 + with: + version: 10.15.1 + run_install: false + + - name: Setup Node.js environment + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: "pnpm" + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Publish stable release + run: cd apps/obsidian && npx tsx scripts/publish.ts --version "$VERSION" + + - name: Commit version bump + run: | + if ! git diff --quiet -- apps/obsidian/package.json apps/obsidian/manifest.json; then + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add apps/obsidian/package.json apps/obsidian/manifest.json + git commit -m "chore: release obsidian ${VERSION} [skip ci]" + git push + fi + + - name: Sync Linear release + uses: linear/linear-release-action@v0 + with: + access_key: ${{ secrets.OBSIDIAN_LINEAR_RELEASE_KEY }} + command: sync + version: ${{ env.VERSION }} + include_paths: "apps/obsidian/**,packages/database/**,packages/utils/**" + + - name: Complete Linear release + uses: linear/linear-release-action@v0 + with: + access_key: ${{ secrets.OBSIDIAN_LINEAR_RELEASE_KEY }} + command: complete + version: ${{ env.VERSION }} diff --git a/apps/obsidian/README.md b/apps/obsidian/README.md index 22bb94f81..eb586cfbc 100644 --- a/apps/obsidian/README.md +++ b/apps/obsidian/README.md @@ -135,3 +135,12 @@ For more information about Discourse Graphs, check out [our website](https://dis 1. Join our growing community of academics, researchers, and thinkers on [Slack 💬](https://join.slack.com/t/discoursegraphs/shared_invite/zt-37xklatti-cpEjgPQC0YyKYQWPNgAkEg) 2. Are you a lab or researcher interested in piloting the plugin with some guidance from the team? Send us [an email](mailto:discoursegraphs@homeworld.bio) or DM on Slack! 3. Discourse Graphs is [open source](https://en.wikipedia.org/wiki/Open_source). See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to report bugs or propose changes. + +# Release process + +To ship an official stable release: + +1. Make sure a Linear release with the target version exists (e.g. `1.6.0`). +2. From the Actions tab, manually run **Stable Obsidian Release** (`.github/workflows/obsidian-release.yaml`) with the `version` input set to that version. + +That single run publishes the stable GitHub release, bumps `apps/obsidian/package.json` and `apps/obsidian/manifest.json`, pushes to the mirror repo, and syncs and completes the matching Linear release. diff --git a/apps/obsidian/manifest.json b/apps/obsidian/manifest.json index b05fa71f9..142ec82d9 100644 --- a/apps/obsidian/manifest.json +++ b/apps/obsidian/manifest.json @@ -1,7 +1,7 @@ { "id": "@discourse-graph/obsidian", "name": "Discourse Graph", - "version": "0.1.0", + "version": "1.5.2", "minAppVersion": "1.7.0", "description": "Add semantic structure to your notes with the Discourse Graph protocol.", "author": "Discourse Graphs", diff --git a/apps/obsidian/package.json b/apps/obsidian/package.json index 1b715d839..d7b167bc9 100644 --- a/apps/obsidian/package.json +++ b/apps/obsidian/package.json @@ -1,6 +1,6 @@ { "name": "@discourse-graphs/obsidian", - "version": "0.1.0", + "version": "1.5.2", "description": "Discourse Graph Plugin for obsidian.md", "main": "dist/main.js", "private": true, @@ -9,7 +9,7 @@ "build": "tsx scripts/build.ts", "lint": "eslint .", "lint:fix": "eslint . --fix", - "publish": "tsx scripts/publish.ts --version 0.1.0", + "publish": "tsx scripts/publish.ts", "check-types": "tsc --noEmit --skipLibCheck" }, "keywords": [], diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index c60bbad8b..7ea88d1a0 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -402,6 +402,23 @@ const sanitizePackageJsonForMirror = (tempDir: string): void => { } }; +const updateLocalVersion = (obsidianDir: string, version: string): void => { + const packageJsonPath = path.join(obsidianDir, "package.json"); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + packageJson.version = version; + fs.writeFileSync( + packageJsonPath, + JSON.stringify(packageJson, null, 2) + "\n", + ); + + const manifestPath = path.join(obsidianDir, "manifest.json"); + const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + manifest.version = version; + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n"); + + log(`Updated local package.json and manifest.json to version ${version}`); +}; + const updateMainBranch = async ( tempDir: string, version: string, @@ -613,6 +630,21 @@ const createGithubRelease = async ({ const releaseTempDir = path.join(os.tmpdir(), "temp-obsidian-release-assets"); + const existingRelease = await octokit + .request("GET /repos/{owner}/{repo}/releases/tags/{tag}", { + owner, + repo, + tag: tagName, + }) + .catch((err: { status?: number }) => + err.status === 404 ? null : Promise.reject(err), + ); + + if (existingRelease) { + log(`GitHub release for ${tagName} already exists, skipping creation.`); + return; + } + try { if (fs.existsSync(releaseTempDir)) { fs.rmSync(releaseTempDir, { recursive: true }); @@ -710,6 +742,7 @@ const publish = async (config: PublishConfig): Promise => { if (isExternal) { updateManifest(tempDir, version); await updateMainBranch(tempDir, version); + updateLocalVersion(obsidianDir, version); } else { log("Skipping main branch update for internal or pre-release"); }