Skip to content

fix(bitbucket): scan repos as they're discovered and fix dead My-PR endpoint - #31240

Open
sunnywty wants to merge 12 commits into
raycast:mainfrom
sunnywty:bitbucket-limit-stale-repo-scans
Open

sunnywty wants to merge 12 commits into
raycast:mainfrom
sunnywty:bitbucket-limit-stale-repo-scans

Conversation

@sunnywty

Copy link
Copy Markdown
Contributor

Summary

  • Cache the repository list and start fetching pull requests for a repo as soon as it's discovered, instead of waiting for the entire workspace's repo list to finish paginating first. This overlaps repo-list pagination with PR fetching so results start streaming in sooner.
  • Fix "Search My Open Pull Requests", which was consistently returning a 404. Bitbucket has removed the workspace-wide "list PRs for a user" endpoint it depended on. It now reuses the same per-repo scan as "Search All Open Pull Requests", filtered server-side to the current user's PRs via the q query param.
  • Add an optional, per-command "Max Repository Age (days)" preference (default 0 = no limit) that skips repositories not updated within N days.

Why the age-limit preference matters

I have 100+ repositories in my workspace, and most of them are stale (no recent activity, no open PRs). Both search commands have to make one API call per repository (Bitbucket has no workspace-wide "list PRs" endpoint), so a full scan across all of them sometimes triggers Bitbucket's rate limiting, causing failed/incomplete results. Letting a workspace skip long-dormant repos entirely cuts both the number of API calls and the chance of getting rate-limited, while defaulting to 0 (scan everything) keeps existing behavior unchanged for anyone who doesn't set it.

Test plan

  • npm run build / ray lint pass with no errors
  • Manually tested in ray develop: both "Search All Open Pull Requests" and "Search My Open Pull Requests" load results correctly, including with the age preference set and unset
  • Reviewer: verify with your own Bitbucket workspace/credentials since this depends on live API behavior

🤖 Generated with Claude Code

…ndpoint

Cache the repo list and start fetching pull requests as repo pages arrive
instead of waiting for the full workspace scan to finish, cutting cold-start
time for large workspaces. "Search My Open Pull Requests" was returning 404s
because Bitbucket removed the workspace-wide "PRs for a user" endpoint it
depended on; it now reuses the same per-repo scan as Search All Open Pull
Requests, filtered server-side to the current user.

Also adds an optional per-command "Max Repository Age (days)" preference
(0 = no limit) so workspaces with many stale repos can skip them entirely,
reducing the risk of hitting Bitbucket's rate limit during a full scan.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sunnywty
sunnywty requested a review from 0xdhrv as a code owner September 17, 2026 17:44
@raycastbot raycastbot added extension fix / improvement Label for PRs with extension's fix improvements extension: bitbucket Issues related to the bitbucket extension platform: macOS platform: Windows labels Sep 17, 2026
@raycastbot

Copy link
Copy Markdown
Collaborator

Thank you for your contribution! 🎉

🔔 @Lp-Francois @esarmstrong @itsrifat @skuio @arpitdalal @LitoMore @AaronMoat @0xdhrv @Siuhinnn you might want to have a look.

You can use this guide to learn how to check out the Pull Request locally in order to test it.

📋 Quick checkout commands
BRANCH="bitbucket-limit-stale-repo-scans"
FORK_URL="https://github.com/sunnywty/raycast_extensions.git"
EXTENSION_NAME="bitbucket"
REPO_NAME="raycast_extensions"

git clone -n --depth=1 --filter=tree:0 -b $BRANCH $FORK_URL
cd $REPO_NAME
git sparse-checkout set --no-cone "extensions/$EXTENSION_NAME"
git checkout
cd "extensions/$EXTENSION_NAME"
npm install && npm run dev

We're currently experiencing a high volume of incoming requests. As a result, the initial review may take up to 15 business days.

@greptile-apps

greptile-apps Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge. No new issue was found in the latest changes.

Findings

  1. P2 cacheConfig also wraps repository search, whose getRepositoriesLazy fetcher calls Bitbucket directly. Setting shouldRetryOnError to false removes SWR's retry there too. A temporary network, rate-limit, or server error can now leave that command failed with no refresh action. Scope this setting to the pull-request scans, or add retry behavior to repository search. ▶
Fix with agent prompt
### Issue 1
extensions/bitbucket/src/helpers/cache.ts:undefined-40
`cacheConfig` also wraps repository search, whose `getRepositoriesLazy` fetcher calls Bitbucket directly. Setting `shouldRetryOnError` to `false` removes SWR's retry there too. A temporary network, rate-limit, or server error can now leave that command failed with no refresh action. Scope this setting to the pull-request scans, or add retry behavior to repository search.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Bitbucket pull-request searches now scan repositories through a shared, cached worker queue, so results can appear while the workspace is still being read. “Search My Open Pull Requests” now uses per-repository filtering instead of the removed workspace-wide endpoint, and both commands can skip old repositories with a per-command age limit.

  • Repository pages feed up to ten pull-request workers as soon as they arrive.
  • Repository lists are cached for ten minutes and requests retry rate-limit and server errors.
  • “Search My Open Pull Requests” filters by the current user’s author or reviewer UUID.
  • Both scan commands add “Max Repository Age (Days)”, defaulting to 0 for no limit.

Reviews (10) · Last reviewed commit: "fix(bitbucket): scope shouldRetryOnError..."

Comment thread extensions/bitbucket/src/queries/index.ts Outdated
Comment thread extensions/bitbucket/src/components/pullRequests/searchMyPullRequests.tsx Outdated
Comment thread extensions/bitbucket/src/queries/index.ts Outdated
Comment thread extensions/bitbucket/CHANGELOG.md Outdated
Comment thread extensions/bitbucket/package.json Outdated
Comment thread extensions/bitbucket/src/helpers/preferences.ts Outdated
Sunny Wong and others added 2 commits September 18, 2026 01:59
…caching

- Scope the internal repo-list cache key by account email too, so switching
  Bitbucket accounts doesn't reuse another account's cached repository list
- Include maxRepoAgeDays in both PR-result SWR cache keys so changing the
  preference invalidates the previous scope instead of showing a stale result
- Don't fail the whole scan if persisting the repo-list cache fails; log and
  continue, since the actual API work already succeeded
- Use Title Case for the "Max Repository Age (Days)" preference title
- Use {PR_MERGE_DATE} in CHANGELOG.md per repo convention
- Replace the hand-written Preferences interface with Raycast's generated
  per-command types (maxRepoAgeDays only exists on the two commands that
  scan repos, not on the shared extension preferences)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
zod was only used to validate the now-removed dead "PRs for a user"
endpoint response; nothing in src imports it anymore.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pernielsentikaer pernielsentikaer self-assigned this Sep 19, 2026

@pernielsentikaer pernielsentikaer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Lp-Francois 👋

We're reviewing PR #31240 and I'd like your take as the extension author before we move ahead — do you consider any of this breaking for existing users?

The main change is Search My Open Pull Requests: Bitbucket removed the workspace-wide .../pullrequests/{user} endpoint it relied on (that was the 404), so it now scans each repository and filters per repo with q=author.uuid="…". Same command name and UI, but results now depend on every repo scan succeeding, and it honours the new repo-age setting.

It also adds a per-command Max Repository Age (Days) preference (default 0 = unchanged) and a 10-minute repository-list cache, and the PR cache keys changed, so the first run after upgrading re-scans.

Command names/IDs, existing preferences, and the author/contributors metadata are unchanged. Does that look safe to you as a non-breaking fix, or is there behaviour here (e.g. what the old "my PRs" endpoint actually returned) that would break existing setups? 🙂


I’ll convert this PR into a draft after submitting this review. Please press Ready for review when it’s ready and we’ll have a look 😊

@pernielsentikaer
pernielsentikaer marked this pull request as draft September 19, 2026 07:49
The removed Bitbucket "PRs for a user" endpoint returned PRs the user
authored OR was a requested reviewer on. The per-repo replacement query
only matched author.uuid, silently narrowing results for anyone who used
"Search My Open Pull Requests" to see PRs awaiting their review. Broaden
the q filter to (author.uuid OR reviewers.uuid) to preserve that scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sunnywty

Copy link
Copy Markdown
Contributor Author

Thanks for the review, @pernielsentikaer! I'm the one who made these changes (not the original extension author), so let me answer directly:

Not breaking, and here's why: "Search My Open Pull Requests" is currently returning a 404 for every user today, on the version already in the store. It calls /workspaces/{workspace}/pullrequests/{selected_user}, which Bitbucket has removed (the code even had a comment linking to the Atlassian community thread about this). So there's no currently-working behavior being changed here — this PR fixes a command that's already broken for everyone.

Good catch prompting a real fix: your question made me go verify the old endpoint's exact scope instead of assuming. That endpoint historically returned PRs the user authored or was a requested reviewer on (dashboard-style), but my per-repo replacement was only filtering on author.uuid, which would have silently narrowed results (missing PRs awaiting the user's review). I just pushed a fix broadening the query to (author.uuid OR reviewers.uuid) to match the original scope.

"Search All Open Pull Requests" behavior is unchanged by default: the new "Max Repository Age (Days)" preference defaults to 0 (no limit — same full scan as before), so nothing changes unless a user opts in.

Cache key changes are internal/ephemeral only: it's an in-memory + local-disk performance cache, not user data. Worst case after upgrading is one slightly slower first search while it repopulates — nothing is lost.

Command names/IDs and existing preferences are all unchanged, so this should be safe to review as a non-breaking fix + opt-in perf improvement. Happy to answer anything else.

Sunny Wong and others added 2 commits September 19, 2026 21:31
…-repo-scans

# Conflicts:
#	extensions/bitbucket/CHANGELOG.md
#	extensions/bitbucket/src/components/pullRequests/searchAllPullRequests.tsx
#	extensions/bitbucket/src/components/pullRequests/searchMyPullRequests.tsx
#	extensions/bitbucket/src/queries/index.ts
Sorting by -updated_on while paginating a live, actively-pushed workspace
can shift a repo's rank between page fetches, causing it to reappear on a
later page. That repo's PRs then get fetched and rendered twice (visible
as React duplicate-key warnings and duplicated list rows). De-dupe by
repo slug as pages arrive.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sunnywty
sunnywty marked this pull request as ready for review September 19, 2026 13:36
@pernielsentikaer

Copy link
Copy Markdown
Collaborator

I’d like to hear from @Lp-Francois, the original author. I’ll give him two days to check in before going further 🙂

@pernielsentikaer
pernielsentikaer marked this pull request as draft September 20, 2026 07:14
// away from just being a filter): https://community.atlassian.com/forums/Bitbucket-articles/Reminder-List-pull-requests-for-a-user-API-removal/ba-p/2935311
// so "my open PRs" reuses the same per-repo scan as getAllOpenPullRequests, with the
// author-or-reviewer filter applied server-side per repo via the `q` query param.
export async function getMyOpenPullRequests(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now N+1 API calls, could we hit rate limits because of this?

// repo older than the cutoff is seen instead of scanning the whole workspace. Yields pages
// as they arrive so callers can start PR-fetching before later pages have loaded.
async function* iterateAllRepositories(): AsyncGenerator<RepoWithSlug[]> {
const cutoff = maxRepoAgeMs();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can throw if preferences are invalid somehow

We should probably put a queue.close in a finally and wrap the rest in try{}. Wdyt?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this can indeed throw (bad maxRepoAgeDays, a failed repo-list API call, etc.) before every repo is queued, which would leave the worker pool awaiting queue.next() forever. Just pushed a fix: wrapped feedRepos's body in try/finally so queue.close() always runs regardless of where it throws (commit 6e1296f).

Sunny Wong and others added 2 commits September 22, 2026 11:12
If getCachedRepositories, the repo-listing API call, or preference
validation (maxRepoAgeMs) throws before every repo is queued, queue.close()
never ran — leaving the 10 worker loops awaiting queue.next() forever,
only cleaned up when the command process exits. Move queue.close() into a
finally so it always runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…g them

The N+1 per-repo scan (inherent to Bitbucket's API — no workspace-wide PR
endpoint exists) can hit rate limits on large workspaces. Previously a 429
on a single repo permanently dropped that repo's PRs from the results
(counted in failedRepoCount, never retried). Add retry-with-backoff for
429/5xx on both the repo-listing and per-repo PR-listing calls, honoring
Bitbucket's Retry-After header when present. Concurrency stays at 10,
unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread extensions/bitbucket/package.json
Comment thread extensions/bitbucket/package.json
…on hyperactive repos

Bitbucket's `page` param is an offset, not a stable cursor. On a repo with
PRs opened continuously during the scan, new PRs shift already-seen PRs
into the next page's offset window, re-returning them — observed as
duplicate PR rows (React duplicate-key warnings) and, in the worst case,
a pagination loop that never terminates (report of the dev process
running out of memory).

De-dupe by PR id within a repo's own pagination, track how many new PRs
each page actually contributes, and stop as soon as a page contributes
nothing new (we've caught up to already-seen data) or a 40-page cap is
hit, instead of relying solely on `!data.next`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sunnywty

Copy link
Copy Markdown
Contributor Author

Found and fixed a real bug while testing this: on a handful of very actively-developed repos in a large workspace, "Search My Open Pull Requests" was intermittently rendering duplicate PR rows (React duplicate-key warnings) and, in one case, causing the process to run out of memory.

Root cause: Bitbucket's page param on pullrequests.list is an offset, not a stable cursor. On a repo where new PRs are opened continuously during the scan, that shifts already-seen PRs into the next page's offset window, re-returning them — and in the worst case (PR creation rate matching or exceeding scan throughput) !data.next can effectively never terminate the loop.

Fix (commit e48a0bb): de-dupe by PR id within a repo's own pagination, track how many new PRs each page actually contributes, and stop as soon as a page contributes nothing new — instead of relying solely on !data.next. Also added a 40-page hard cap as a backstop. Verified via temporary diagnostic logging that this was in fact happening on our end (not just a rendering artifact) before landing the fix.

Sunny Wong and others added 2 commits September 22, 2026 11:46
The store guidelines ask every extension to declare at least one
category; this one had none.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ailure

SWR's default shouldRetryOnError retries a failed fetcher indefinitely in
the background. If the repo-listing call itself exhausts our own
withRetry attempts under sustained rate limiting, that rejects the whole
scan, and SWR would then silently restart it from scratch, compounding
with our per-request retries into a scan that "never stops." We already
retry transient errors per-request and surface a toast on failure, so
disable SWR's own retry-on-error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment thread extensions/bitbucket/src/helpers/cache.ts Outdated
Search Repositories shares the same cacheConfig but has no retry logic of
its own around its direct Bitbucket call, so disabling SWR's automatic
retry there would leave a transient network/rate-limit/server error stuck
with no way to recover. Split it into a separate scanCacheConfig used only
by Search All / Search My Open Pull Requests, which do retry per-request.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pernielsentikaer

Copy link
Copy Markdown
Collaborator

Do you want to check it again @Lp-Francois

@pernielsentikaer
pernielsentikaer marked this pull request as ready for review September 24, 2026 20:17
@0xdhrv 0xdhrv added the difficulty:medium Review effort suggested by Review Hub (medium). label Sep 27, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

difficulty:medium Review effort suggested by Review Hub (medium). extension: bitbucket Issues related to the bitbucket extension extension fix / improvement Label for PRs with extension's fix improvements platform: macOS platform: Windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants