From 2f7a0ce7d157ae22fce322c6fea15cb1806ea220 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Wed, 19 Aug 2026 10:46:55 -0400 Subject: [PATCH 01/12] ENG-2099 Stop beta versions publishing as official releases isExternalRelease() returned true for beta as well as stable, and that one boolean gates both the GitHub prerelease flag and the push to the publish repo's main branch. A beta therefore shipped as a full release and overwrote manifest.json on discourse-graph-obsidian's main branch, which is what the Obsidian community store reads to pick a version (ENG-2106). Only a bare x.y.z is external now, so betas and alphas stay pre-releases and leave the publish repo's main branch alone. Co-Authored-By: Claude Opus 5 --- apps/obsidian/scripts/publish.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index 7ea88d1a0..389fc675f 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -215,23 +215,15 @@ const validateVersion = (version: string): void => { }; const isExternalRelease = (version: string): boolean => { - // External releases are: - // 1. Stable releases (x.y.z) - // 2. Beta releases (x.y.z-beta.n) - - // Stable release pattern (x.y.z) + // Only stable releases (x.y.z) are external. Betas and alphas must stay + // pre-releases: the publish repo's main-branch manifest.json is what the + // Obsidian community store reads, so a beta landing there regresses the + // store to a pre-release (ENG-2106). const stablePattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; if (stablePattern.test(version)) { return true; } - // Beta release pattern (x.y.z-beta.n) - const betaPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)-beta(\.\d+)?$/; - if (betaPattern.test(version)) { - return true; - } - - // Everything else (including alpha releases) is internal return false; }; From 7b33e2d2d24bcf84dd18a6dfbcb8785f82b028d8 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Wed, 19 Aug 2026 10:46:56 -0400 Subject: [PATCH 02/12] ENG-2099 Fix Linear access key secret name in Obsidian workflows The workflows referenced secrets.OBSIDIAN_LINEAR_RELEASE_KEY, which does not exist. The repo secret is named LINEAR_RELEASES_ACCESS_KEY_OBSIDIAN, so the input resolved to an empty string and every run died at the Linear sync step with "access_key input is required". The name was correct on eng-1767 (which is why it passed there on 2026-05-24) and was changed on merge. obsidian-main.yaml has never once succeeded on main since: 6 failures, 3 cancelled, 0 successes. Note the sync step runs last, after publish has already written to the publish repo, so these red runs were still mutating production. Co-Authored-By: Claude Opus 5 --- .github/workflows/obsidian-main.yaml | 2 +- .github/workflows/obsidian-release.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/obsidian-main.yaml b/.github/workflows/obsidian-main.yaml index 84ff344c6..2707b8cf2 100644 --- a/.github/workflows/obsidian-main.yaml +++ b/.github/workflows/obsidian-main.yaml @@ -68,5 +68,5 @@ jobs: - name: Sync Linear release uses: linear/linear-release-action@v0 with: - access_key: ${{ secrets.OBSIDIAN_LINEAR_RELEASE_KEY }} + access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY_OBSIDIAN }} include_paths: "apps/obsidian/**,packages/database/**,packages/utils/**" diff --git a/.github/workflows/obsidian-release.yaml b/.github/workflows/obsidian-release.yaml index d4debb51e..437897e1a 100644 --- a/.github/workflows/obsidian-release.yaml +++ b/.github/workflows/obsidian-release.yaml @@ -63,7 +63,7 @@ jobs: - name: Sync Linear release uses: linear/linear-release-action@v0 with: - access_key: ${{ secrets.OBSIDIAN_LINEAR_RELEASE_KEY }} + access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY_OBSIDIAN }} command: sync version: ${{ env.VERSION }} include_paths: "apps/obsidian/**,packages/database/**,packages/utils/**" @@ -71,6 +71,6 @@ jobs: - name: Complete Linear release uses: linear/linear-release-action@v0 with: - access_key: ${{ secrets.OBSIDIAN_LINEAR_RELEASE_KEY }} + access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY_OBSIDIAN }} command: complete version: ${{ env.VERSION }} From 510f658467fc14375987efd51608f25dcdb37c2d Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Wed, 19 Aug 2026 10:52:06 -0400 Subject: [PATCH 03/12] ENG-2099 TEMP: point publish target at throwaway test repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not merge while this is in place. Retargets publish.ts at the private discourse-graph-obsidian-test repo so the full pipeline (GitHub release, manifest push, Linear sync) can be exercised from this branch via workflow_dispatch without touching the real publish repo. Running from a branch isolates which code runs, not where it writes, so the target constant is what actually sandboxes the test. Revert before merge — see TODO(ENG-2099) in scripts/publish.ts. Co-Authored-By: Claude Opus 5 --- apps/obsidian/scripts/publish.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index 389fc675f..b254a3ff0 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -54,9 +54,15 @@ const BLOB_UPLOAD_BATCH_SIZE = 10; const MAX_GITHUB_RETRIES = 5; const BASE_RETRY_DELAY_MS = 2_000; -const TARGET_REPO = "DiscourseGraphs/discourse-graph-obsidian"; +// TODO(ENG-2099): REVERT BEFORE MERGE — restore discourse-graph-obsidian. +// Temporarily pointed at the throwaway private test repo so the release +// pipeline can be exercised end-to-end (GitHub release + Linear sync) without +// writing to the real publish repo. Do not merge this branch while it points +// here: the Obsidian community store reads manifest.json from the real repo's +// main branch. +const TARGET_REPO = "DiscourseGraphs/discourse-graph-obsidian-test"; const OWNER = "DiscourseGraphs"; -const REPO = "discourse-graph-obsidian"; +const REPO = "discourse-graph-obsidian-test"; const log = (message: string): void => { console.log(`[Obsidian Publisher] ${message}`); From 8d68b05a860637cdac9f776f1ee6605228e73bb4 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Wed, 19 Aug 2026 11:15:57 -0400 Subject: [PATCH 04/12] ENG-2099 TEMP: point beta workflow at the test-repo token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not merge while this is in place. publish.ts reads its credential from the OBSIDIAN_PLUGIN_REPO_TOKEN env var, and the workflow is what maps a secret onto it, so repointing the env var is enough to keep a test run off the real publish repo without touching the script. Pairs with the temporary test-repo target in scripts/publish.ts. Requires a repo secret named OBSIDIAN_TEST_REPO_TOKEN, scoped to discourse-graph-obsidian-test with Contents: read and write. Revert before merge — see TODO(ENG-2099) in the workflow env block. Co-Authored-By: Claude Opus 5 --- .github/workflows/obsidian-main.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/obsidian-main.yaml b/.github/workflows/obsidian-main.yaml index 2707b8cf2..1e07bd8da 100644 --- a/.github/workflows/obsidian-main.yaml +++ b/.github/workflows/obsidian-main.yaml @@ -17,7 +17,11 @@ concurrency: cancel-in-progress: false env: - OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_PLUGIN_REPO_TOKEN }} + # TODO(ENG-2099): REVERT BEFORE MERGE — restore secrets.OBSIDIAN_PLUGIN_REPO_TOKEN. + # Paired with the temporary test-repo target in apps/obsidian/scripts/publish.ts. + # publish.ts reads the token from this env var, so pointing it at a token scoped + # only to discourse-graph-obsidian-test keeps the test run off the real repo. + OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_TEST_REPO_TOKEN }} SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} GH_TOKEN: ${{ github.token }} From 5eb67213fedb5cc0b4c2511283e2c238d8091559 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Fri, 21 Aug 2026 17:58:40 -0400 Subject: [PATCH 05/12] ENG-2099 Look up existing betas in the publish repo, not the monorepo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh release list had no --repo, so it queried the monorepo, which has zero releases and zero tags. LATEST_N was therefore always empty and the else branch always won, recomputing -beta.1 forever. Confirmed in run 32530653062: "next beta: 1.5.2-beta.1" followed by "already exists, skipping creation" and then "Publication completed successfully" — a green run that shipped nothing. Point the lookup at the publish repo, where releases actually live. The lookup also needs a credential that can read the target, since github.token cannot see a private repo; reuse whatever publish.ts is configured with so this works for both the real public repo and the private test target. Also drops the sed that stripped a -beta suffix from package.json. After the Bug A fix updateLocalVersion only runs for stable releases, so package.json only ever holds a bare x.y.z. Verified locally against both repos: latest_n=1 -> next=1.5.2-beta.2. Co-Authored-By: Claude Opus 5 --- .github/workflows/obsidian-main.yaml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/obsidian-main.yaml b/.github/workflows/obsidian-main.yaml index 1e07bd8da..121e03ae6 100644 --- a/.github/workflows/obsidian-main.yaml +++ b/.github/workflows/obsidian-main.yaml @@ -22,6 +22,8 @@ env: # publish.ts reads the token from this env var, so pointing it at a token scoped # only to discourse-graph-obsidian-test keeps the test run off the real repo. OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_TEST_REPO_TOKEN }} + # TODO(ENG-2099): REVERT BEFORE MERGE — restore DiscourseGraphs/discourse-graph-obsidian. + PUBLISH_REPO: DiscourseGraphs/discourse-graph-obsidian-test SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} GH_TOKEN: ${{ github.token }} @@ -52,17 +54,18 @@ jobs: - name: Compute next beta version id: version + env: + # Releases live in the publish repo, not the monorepo, so the lookup has + # to be pointed there explicitly. github.token cannot read a private + # target, so reuse whichever credential publish.ts is configured with. + GH_TOKEN: ${{ env.OBSIDIAN_PLUGIN_REPO_TOKEN }} run: | - STABLE_VERSION=$(node -p "require('./apps/obsidian/package.json').version" | sed 's/-beta\.[0-9]*//') - LATEST_N=$(gh release list --limit 50 --json tagName --jq '.[].tagName' \ + STABLE_VERSION=$(node -p "require('./apps/obsidian/package.json').version") + LATEST_N=$(gh release list --repo "$PUBLISH_REPO" --limit 100 --json tagName --jq '.[].tagName' \ | grep "^${STABLE_VERSION}-beta\." \ | sed "s/^${STABLE_VERSION}-beta\.//" \ | sort -n | tail -1) - if [ -n "$LATEST_N" ]; then - NEXT_BETA="${STABLE_VERSION}-beta.$((LATEST_N + 1))" - else - NEXT_BETA="${STABLE_VERSION}-beta.1" - fi + NEXT_BETA="${STABLE_VERSION}-beta.$(( ${LATEST_N:-0} + 1 ))" echo "Stable version: $STABLE_VERSION, next beta: $NEXT_BETA" echo "version=$NEXT_BETA" >> "$GITHUB_OUTPUT" From 571007ea92837a6133de0331be2cdc708f2510ce Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Fri, 21 Aug 2026 19:30:32 -0400 Subject: [PATCH 06/12] ENG-2099 TEMP: point stable workflow at the test-repo token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not merge while this is in place. Same treatment as obsidian-main.yaml so the stable release path can be exercised against the throwaway test repo. Revert before merge — see TODO(ENG-2099) in the workflow env block. Co-Authored-By: Claude Opus 5 --- .github/workflows/obsidian-release.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/obsidian-release.yaml b/.github/workflows/obsidian-release.yaml index 437897e1a..fbeecb4d6 100644 --- a/.github/workflows/obsidian-release.yaml +++ b/.github/workflows/obsidian-release.yaml @@ -12,7 +12,9 @@ permissions: env: VERSION: ${{ inputs.version }} - OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_PLUGIN_REPO_TOKEN }} + # TODO(ENG-2099): REVERT BEFORE MERGE — restore secrets.OBSIDIAN_PLUGIN_REPO_TOKEN. + # Paired with the temporary test-repo target in apps/obsidian/scripts/publish.ts. + OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_TEST_REPO_TOKEN }} SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} From 8776c607a99b37ab5ad6270499e3cbd7cfaf0bb7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 21 Aug 2026 23:33:03 +0000 Subject: [PATCH 07/12] chore: release obsidian 9.9.9 [skip ci] --- apps/obsidian/manifest.json | 2 +- apps/obsidian/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/obsidian/manifest.json b/apps/obsidian/manifest.json index 142ec82d9..64420a15d 100644 --- a/apps/obsidian/manifest.json +++ b/apps/obsidian/manifest.json @@ -1,7 +1,7 @@ { "id": "@discourse-graph/obsidian", "name": "Discourse Graph", - "version": "1.5.2", + "version": "9.9.9", "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 d7b167bc9..8c0ae70b0 100644 --- a/apps/obsidian/package.json +++ b/apps/obsidian/package.json @@ -1,6 +1,6 @@ { "name": "@discourse-graphs/obsidian", - "version": "1.5.2", + "version": "9.9.9", "description": "Discourse Graph Plugin for obsidian.md", "main": "dist/main.js", "private": true, From 1bb10ac7a4e715790068409e1500377c525cc46c Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Fri, 21 Aug 2026 20:14:23 -0400 Subject: [PATCH 08/12] ENG-2099 Match exclude patterns per path segment, not by substring shouldExclude tested patterns with relativePath.includes(), and turned glob patterns into unanchored regexes, so any file whose name merely contained an excluded word was silently dropped from the publish repo. "out" ate src/components/DatacoreCallout.tsx, and "*.log" becoming /.*.log/ ate src/utils/nativeJsonFileDialogs.ts via "diaLOGs". Confirmed on a real publish: 101 local src files, 99 published. Match whole path segments instead, and anchor globs to a single segment with regex metacharacters escaped. Verified against the full apps/obsidian tree: exactly those two files flip from excluded to included and nothing flips the other way. .env, .env.branch, .env.example, nested .env.local, *.pem, *.log, node_modules, dist, scripts and .git remain excluded; Logger.ts, Layout.tsx, distance.ts, environment.ts and buildIndex.ts are correctly kept. Co-Authored-By: Claude Opus 5 --- apps/obsidian/scripts/publish.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index b254a3ff0..f1257812b 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -300,17 +300,25 @@ const execCommand = async ( } }; +// Patterns match a whole path segment, never a substring of one. Substring +// matching silently dropped real source files whose names merely contained an +// excluded word — "out" ate DatacoreCallout.tsx, and the unanchored "*.log" +// regex ate nativeJsonFileDialogs.ts via "diaLOGs". +const segmentMatchesPattern = (segment: string, pattern: string): boolean => { + if (!pattern.includes("*")) return segment === pattern; + + const escaped = pattern + .split("*") + .map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) + .join("[^/]*"); + return new RegExp(`^${escaped}$`).test(segment); +}; + const shouldExclude = (filePath: string, baseDir: string): boolean => { - const relativePath = path.relative(baseDir, filePath); - return EXCLUDE_PATTERNS.some((pattern) => { - if (pattern.includes("*")) { - const regex = new RegExp(pattern.replace(/\*/g, ".*")); - return regex.test(relativePath) || regex.test(path.basename(filePath)); - } - return ( - relativePath.includes(pattern) || path.basename(filePath) === pattern - ); - }); + const segments = path.relative(baseDir, filePath).split(path.sep); + return segments.some((segment) => + EXCLUDE_PATTERNS.some((pattern) => segmentMatchesPattern(segment, pattern)), + ); }; const copyDirectory = ({ From 07180a4dfbfa673ca8eecdfd4b866e10c5fb6df2 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Fri, 21 Aug 2026 20:14:33 -0400 Subject: [PATCH 09/12] ENG-2099 Stage the release version into the published package.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updateManifest only rewrote manifest.json in the staging directory. package.json was copied at its pre-bump value, and updateLocalVersion — which bumps it — runs after updateMainBranch has already pushed. The published package.json was therefore always one release behind, permanently. Observed on both repos: the test repo showed manifest.json 9.9.9 alongside package.json 1.5.2 right after publishing 9.9.9, and the production repo shows manifest.json 1.5.2-beta.1 alongside package.json 1.5.2. Cosmetic for users, since Obsidian reads manifest.json, but the published repo was internally inconsistent and anything reading package.json got the wrong answer. Co-Authored-By: Claude Opus 5 --- apps/obsidian/scripts/publish.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index f1257812b..0fda05420 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -408,6 +408,18 @@ const sanitizePackageJsonForMirror = (tempDir: string): void => { } }; +// updateLocalVersion runs after the mirror push, so the staged copy would +// otherwise carry the previous release's version forever. +const updateStagedPackageVersion = (tempDir: string, version: string): void => { + const packageJsonPath = path.join(tempDir, "package.json"); + if (!fs.existsSync(packageJsonPath)) return; + + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + packageJson.version = version; + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); + log(`Updated staged package.json version to ${version}`); +}; + const updateLocalVersion = (obsidianDir: string, version: string): void => { const packageJsonPath = path.join(obsidianDir, "package.json"); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); @@ -747,6 +759,7 @@ const publish = async (config: PublishConfig): Promise => { if (isExternal) { updateManifest(tempDir, version); + updateStagedPackageVersion(tempDir, version); await updateMainBranch(tempDir, version); updateLocalVersion(obsidianDir, version); } else { From 2dc6982866fe52bc6079b76e0d5295df66d73ff0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Aug 2026 00:16:13 +0000 Subject: [PATCH 10/12] chore: release obsidian 9.9.10 [skip ci] --- apps/obsidian/manifest.json | 2 +- apps/obsidian/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/obsidian/manifest.json b/apps/obsidian/manifest.json index 64420a15d..bf8c37d00 100644 --- a/apps/obsidian/manifest.json +++ b/apps/obsidian/manifest.json @@ -1,7 +1,7 @@ { "id": "@discourse-graph/obsidian", "name": "Discourse Graph", - "version": "9.9.9", + "version": "9.9.10", "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 8c0ae70b0..3829df035 100644 --- a/apps/obsidian/package.json +++ b/apps/obsidian/package.json @@ -1,6 +1,6 @@ { "name": "@discourse-graphs/obsidian", - "version": "9.9.9", + "version": "9.9.10", "description": "Discourse Graph Plugin for obsidian.md", "main": "dist/main.js", "private": true, From c4a86fc5a021cd0a5f58bdf6a2de1437a68a9456 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Fri, 21 Aug 2026 23:43:31 -0400 Subject: [PATCH 11/12] ENG-2099 Disable the CI version-bump commit in the stable release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Commit version bump step can never succeed on main. The "Main Branch Protection" ruleset requires a pull request on the default branch and does not list github-actions[bot] as a bypass actor, so git push is rejected. Because the step sits after publishing, a real stable release would create the GitHub release and push the publish repo, then fail here and never reach the Linear sync/complete steps. Retrying does not help: updateMainBranch reports no changes, createGithubRelease skips the existing release, updateLocalVersion rewrites the same files, and the push is rejected again. The Linear half of the pipeline was unreachable for that version. The step's own guard gives us the way out. Bumping package.json and manifest.json by hand in the release PR means publish.ts writes values that already match, git diff --quiet passes, and nothing needs pushing from CI — so the remaining steps run. Commented out rather than deleted so the intent and the re-enable conditions stay visible. This also explains why the workflow's two green runs on 2026-05-24 proved nothing: they ran on a branch, where git push targets the branch, not main. Co-Authored-By: Claude Opus 5 --- .github/workflows/obsidian-release.yaml | 32 ++++++++++++++++++------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/obsidian-release.yaml b/.github/workflows/obsidian-release.yaml index fbeecb4d6..dc3ecc36d 100644 --- a/.github/workflows/obsidian-release.yaml +++ b/.github/workflows/obsidian-release.yaml @@ -52,15 +52,29 @@ jobs: - 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 + # Disabled: this step can never succeed on main. The "Main Branch Protection" + # ruleset requires a pull request on the default branch and does not list + # github-actions[bot] as a bypass actor, so the push is rejected. Because the + # step sits after publishing, a stable release would ship and then fail here, + # never reaching the Linear steps below — and every retry fails identically, + # so the Linear half of the pipeline was unreachable for that version. + # + # Instead, bump apps/obsidian/package.json and manifest.json by hand in the + # release PR before dispatching this workflow. publish.ts then writes values + # that already match, and nothing needs to be pushed from CI. + # + # To re-enable, add github-actions[bot] as a ruleset bypass actor, or change + # this to open a PR rather than pushing directly. See ENG-2099. + # + # - 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 From 409589dbf6688cb0bc40600e2bb573d480290b7a Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Sat, 22 Aug 2026 00:06:29 -0400 Subject: [PATCH 12/12] ENG-2099 Trim code comments to durable context only Comments that narrated what this PR fixes belong on the PR, not in the source. Removed the bug references and the specific file names that were dropped, and kept only what a future reader needs without this PR for context: - the community store reading manifest.json from the publish repo's main branch, which is why only finished releases are external - the segment-matching contract for exclude patterns - the ordering constraint that makes updateStagedPackageVersion necessary - releases existing only in the publish repo, which is why the lookup needs an explicit --repo - why the version-bump step is disabled and how to re-enable it TEMP revert markers reduced to a single line each. Co-Authored-By: Claude Opus 5 --- .github/workflows/obsidian-main.yaml | 12 ++++-------- .github/workflows/obsidian-release.yaml | 20 +++++--------------- apps/obsidian/scripts/publish.ts | 24 ++++++++---------------- 3 files changed, 17 insertions(+), 39 deletions(-) diff --git a/.github/workflows/obsidian-main.yaml b/.github/workflows/obsidian-main.yaml index 121e03ae6..bf05c5f59 100644 --- a/.github/workflows/obsidian-main.yaml +++ b/.github/workflows/obsidian-main.yaml @@ -17,12 +17,9 @@ concurrency: cancel-in-progress: false env: - # TODO(ENG-2099): REVERT BEFORE MERGE — restore secrets.OBSIDIAN_PLUGIN_REPO_TOKEN. - # Paired with the temporary test-repo target in apps/obsidian/scripts/publish.ts. - # publish.ts reads the token from this env var, so pointing it at a token scoped - # only to discourse-graph-obsidian-test keeps the test run off the real repo. + # TODO(ENG-2099): REVERT BEFORE MERGE, restore secrets.OBSIDIAN_PLUGIN_REPO_TOKEN. OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_TEST_REPO_TOKEN }} - # TODO(ENG-2099): REVERT BEFORE MERGE — restore DiscourseGraphs/discourse-graph-obsidian. + # TODO(ENG-2099): REVERT BEFORE MERGE, restore DiscourseGraphs/discourse-graph-obsidian. PUBLISH_REPO: DiscourseGraphs/discourse-graph-obsidian-test SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} @@ -55,9 +52,8 @@ jobs: - name: Compute next beta version id: version env: - # Releases live in the publish repo, not the monorepo, so the lookup has - # to be pointed there explicitly. github.token cannot read a private - # target, so reuse whichever credential publish.ts is configured with. + # Releases exist only in the publish repo, never in the monorepo, so the + # lookup needs an explicit --repo and a token that can read that repo. GH_TOKEN: ${{ env.OBSIDIAN_PLUGIN_REPO_TOKEN }} run: | STABLE_VERSION=$(node -p "require('./apps/obsidian/package.json').version") diff --git a/.github/workflows/obsidian-release.yaml b/.github/workflows/obsidian-release.yaml index dc3ecc36d..e2c9e56ef 100644 --- a/.github/workflows/obsidian-release.yaml +++ b/.github/workflows/obsidian-release.yaml @@ -12,8 +12,7 @@ permissions: env: VERSION: ${{ inputs.version }} - # TODO(ENG-2099): REVERT BEFORE MERGE — restore secrets.OBSIDIAN_PLUGIN_REPO_TOKEN. - # Paired with the temporary test-repo target in apps/obsidian/scripts/publish.ts. + # TODO(ENG-2099): REVERT BEFORE MERGE, restore secrets.OBSIDIAN_PLUGIN_REPO_TOKEN. OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_TEST_REPO_TOKEN }} SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} @@ -52,19 +51,10 @@ jobs: - name: Publish stable release run: cd apps/obsidian && npx tsx scripts/publish.ts --version "$VERSION" - # Disabled: this step can never succeed on main. The "Main Branch Protection" - # ruleset requires a pull request on the default branch and does not list - # github-actions[bot] as a bypass actor, so the push is rejected. Because the - # step sits after publishing, a stable release would ship and then fail here, - # never reaching the Linear steps below — and every retry fails identically, - # so the Linear half of the pipeline was unreachable for that version. - # - # Instead, bump apps/obsidian/package.json and manifest.json by hand in the - # release PR before dispatching this workflow. publish.ts then writes values - # that already match, and nothing needs to be pushed from CI. - # - # To re-enable, add github-actions[bot] as a ruleset bypass actor, or change - # this to open a PR rather than pushing directly. See ENG-2099. + # Disabled: the default-branch ruleset requires a pull request and does not + # grant github-actions[bot] a bypass, so this push is always rejected on main. + # Bump apps/obsidian/package.json and manifest.json in the release PR instead. + # To re-enable, add github-actions[bot] as a ruleset bypass actor. # # - name: Commit version bump # run: | diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index 0fda05420..4b98a2a1e 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -54,12 +54,7 @@ const BLOB_UPLOAD_BATCH_SIZE = 10; const MAX_GITHUB_RETRIES = 5; const BASE_RETRY_DELAY_MS = 2_000; -// TODO(ENG-2099): REVERT BEFORE MERGE — restore discourse-graph-obsidian. -// Temporarily pointed at the throwaway private test repo so the release -// pipeline can be exercised end-to-end (GitHub release + Linear sync) without -// writing to the real publish repo. Do not merge this branch while it points -// here: the Obsidian community store reads manifest.json from the real repo's -// main branch. +// TODO(ENG-2099): REVERT BEFORE MERGE, restore discourse-graph-obsidian. const TARGET_REPO = "DiscourseGraphs/discourse-graph-obsidian-test"; const OWNER = "DiscourseGraphs"; const REPO = "discourse-graph-obsidian-test"; @@ -221,10 +216,9 @@ const validateVersion = (version: string): void => { }; const isExternalRelease = (version: string): boolean => { - // Only stable releases (x.y.z) are external. Betas and alphas must stay - // pre-releases: the publish repo's main-branch manifest.json is what the - // Obsidian community store reads, so a beta landing there regresses the - // store to a pre-release (ENG-2106). + // The Obsidian community store reads manifest.json from the publish repo's + // main branch, so only a finished release may be external. Everything else + // ships as a GitHub pre-release and leaves that branch untouched. const stablePattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/; if (stablePattern.test(version)) { return true; @@ -300,10 +294,8 @@ const execCommand = async ( } }; -// Patterns match a whole path segment, never a substring of one. Substring -// matching silently dropped real source files whose names merely contained an -// excluded word — "out" ate DatacoreCallout.tsx, and the unanchored "*.log" -// regex ate nativeJsonFileDialogs.ts via "diaLOGs". +// Patterns match a whole path segment, never a substring of one, and globs are +// anchored to a single segment. const segmentMatchesPattern = (segment: string, pattern: string): boolean => { if (!pattern.includes("*")) return segment === pattern; @@ -408,8 +400,8 @@ const sanitizePackageJsonForMirror = (tempDir: string): void => { } }; -// updateLocalVersion runs after the mirror push, so the staged copy would -// otherwise carry the previous release's version forever. +// updateLocalVersion runs after the publish-repo push, so the release version +// has to be written into the staged copy here as well. const updateStagedPackageVersion = (tempDir: string, version: string): void => { const packageJsonPath = path.join(tempDir, "package.json"); if (!fs.existsSync(packageJsonPath)) return;