diff --git a/.github/workflows/obsidian-main.yaml b/.github/workflows/obsidian-main.yaml index 84ff344c6..bf05c5f59 100644 --- a/.github/workflows/obsidian-main.yaml +++ b/.github/workflows/obsidian-main.yaml @@ -17,7 +17,10 @@ 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. + 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 }} @@ -48,17 +51,17 @@ jobs: - name: Compute next beta version id: version + env: + # 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" | 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" @@ -68,5 +71,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..e2c9e56ef 100644 --- a/.github/workflows/obsidian-release.yaml +++ b/.github/workflows/obsidian-release.yaml @@ -12,7 +12,8 @@ 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. + OBSIDIAN_PLUGIN_REPO_TOKEN: ${{ secrets.OBSIDIAN_TEST_REPO_TOKEN }} SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_PUBLISHABLE_KEY: ${{ secrets.SUPABASE_PUBLISHABLE_KEY }} @@ -50,20 +51,25 @@ 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: 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: | + # 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 }} + access_key: ${{ secrets.LINEAR_RELEASES_ACCESS_KEY_OBSIDIAN }} command: sync version: ${{ env.VERSION }} include_paths: "apps/obsidian/**,packages/database/**,packages/utils/**" @@ -71,6 +77,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 }} diff --git a/apps/obsidian/manifest.json b/apps/obsidian/manifest.json index 142ec82d9..bf8c37d00 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.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 d7b167bc9..3829df035 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.10", "description": "Discourse Graph Plugin for obsidian.md", "main": "dist/main.js", "private": true, diff --git a/apps/obsidian/scripts/publish.ts b/apps/obsidian/scripts/publish.ts index 7ea88d1a0..4b98a2a1e 100644 --- a/apps/obsidian/scripts/publish.ts +++ b/apps/obsidian/scripts/publish.ts @@ -54,9 +54,10 @@ 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. +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}`); @@ -215,23 +216,14 @@ 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) + // 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; } - // 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; }; @@ -302,17 +294,23 @@ const execCommand = async ( } }; +// 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; + + 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 = ({ @@ -402,6 +400,18 @@ const sanitizePackageJsonForMirror = (tempDir: string): void => { } }; +// 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; + + 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")); @@ -741,6 +751,7 @@ const publish = async (config: PublishConfig): Promise => { if (isExternal) { updateManifest(tempDir, version); + updateStagedPackageVersion(tempDir, version); await updateMainBranch(tempDir, version); updateLocalVersion(obsidianDir, version); } else {