From 546bcf9bbe651eaaa0129346536c9e7e98748555 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 15 Jun 2026 23:57:14 -0500 Subject: fix(api): clear auth-source's negative cache on pearl-clear-cache A key added to authinfo read as missing for up to 2h because auth-source caches the negative lookup and `pearl-clear-cache` only cleared pearl's own caches. It now also calls `auth-source-forget-all-cached`, and the auth-source miss tells the user to run `M-x pearl-clear-cache`. This is the 2026-06-01 live incident, where a freshly-synced Linear key kept erroring as unset. --- pearl.el | 11 +++++++---- tests/test-pearl-accounts.el | 9 +++++++++ tests/test-pearl-resolve.el | 9 +++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pearl.el b/pearl.el index 8540b9d..f66490c 100644 --- a/pearl.el +++ b/pearl.el @@ -521,7 +521,7 @@ lookup -- when the key cannot be found or SOURCE is malformed." (secret (and found (plist-get found :secret)))) (cond ((null secret) - (user-error "No API key for account %s: auth-source found no matching entry" + (user-error "No API key for account %s: auth-source found no matching entry (a key added in the last ~2h may be negatively cached; run M-x pearl-clear-cache)" account)) ((functionp secret) (funcall secret)) (t secret)))) @@ -2444,11 +2444,14 @@ none, returns nil. FORCE refreshes the collection cache first." ;;;###autoload (defun pearl-clear-cache () - "Clear the Linear lookup caches (teams, states, per-team collections, views). -Use after renaming things in Linear, or to force the next lookup to refetch." + "Clear the Linear lookup caches and auth-source's credential cache. +Use after renaming things in Linear, to force the next lookup to refetch, or +after adding an API key to authinfo so the new key is found without waiting +out auth-source's cache TTL (default 2h)." (interactive) (pearl--clear-account-scoped-state) - (message "Linear caches cleared")) + (auth-source-forget-all-cached) + (message "Linear caches cleared (including auth-source)")) (defun pearl-update-issue-state (issue-id state-name team-id) "Update the state of Linear issue with ISSUE-ID to STATE-NAME for TEAM-ID." diff --git a/tests/test-pearl-accounts.el b/tests/test-pearl-accounts.el index 4de9c2f..82f5e56 100644 --- a/tests/test-pearl-accounts.el +++ b/tests/test-pearl-accounts.el @@ -85,6 +85,15 @@ '(:auth-source :host "api.linear.app" :user "work") "work") :type 'user-error))) +(ert-deftest test-pearl-resolve-api-key-source-auth-miss-hints-at-cache () + "An auth-source miss hints at the negative cache, so a freshly added key is +recoverable via clear-cache rather than reading as permanently missing." + (cl-letf (((symbol-function 'auth-source-search) (lambda (&rest _) nil))) + (let ((err (should-error (pearl--resolve-api-key-source + '(:auth-source :host "api.linear.app" :user "work") "work") + :type 'user-error))) + (should (string-match-p "clear-cache" (error-message-string err)))))) + ;;; pearl--resolve-account (ert-deftest test-pearl-resolve-account-returns-full-context () diff --git a/tests/test-pearl-resolve.el b/tests/test-pearl-resolve.el index aefe27a..df75b6f 100644 --- a/tests/test-pearl-resolve.el +++ b/tests/test-pearl-resolve.el @@ -136,6 +136,15 @@ (should-not pearl--cache-states) (should-not pearl--cache-teams))) +(ert-deftest test-pearl-clear-cache-forgets-auth-source () + "Clearing the cache also drops auth-source's own cache, so a key just added +to authinfo is findable without waiting out the 2h auth-source TTL." + (let ((called nil)) + (cl-letf (((symbol-function 'auth-source-forget-all-cached) + (lambda (&rest _) (setq called t)))) + (pearl-clear-cache) + (should called)))) + ;;; failure / malformed cases (ert-deftest test-pearl-team-collection-malformed-returns-nil-no-cache () -- cgit v1.2.3