Skip to content

Commit 3a2ceda

Browse files
committed
feat: store API keys in the system keyring
1 parent f8efda7 commit 3a2ceda

7 files changed

Lines changed: 1086 additions & 89 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
run: deno task check
2828

2929
- name: Run tests
30-
run: deno task test
30+
run: deno task test --ignore=test/keyring.integration.test.ts
3131

3232
- name: Install linear-cli for skill generation
3333
run: deno task install
@@ -43,3 +43,28 @@ jobs:
4343
git diff skills/
4444
exit 1
4545
fi
46+
47+
keyring-integration:
48+
strategy:
49+
matrix:
50+
include:
51+
- os: macos-latest
52+
- os: ubuntu-latest
53+
runs-on: ${{ matrix.os }}
54+
steps:
55+
- uses: actions/checkout@v3
56+
57+
- uses: denoland/setup-deno@v2
58+
with:
59+
deno-version: v2.x
60+
61+
- name: Set up Secret Service
62+
if: runner.os == 'Linux'
63+
run: |
64+
sudo apt-get update && sudo apt-get install -y gnome-keyring libsecret-tools dbus-x11
65+
eval "$(dbus-launch --sh-syntax)"
66+
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS" >> $GITHUB_ENV
67+
echo "test-password" | gnome-keyring-daemon --unlock --components=secrets
68+
69+
- name: Keyring Integration
70+
run: deno task test test/keyring.integration.test.ts

src/commands/auth/auth-list.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { Command } from "@cliffy/command"
22
import { unicodeWidth } from "@std/cli"
33
import { gql } from "../../__codegen__/gql.ts"
44
import {
5-
getAllCredentials,
5+
getApiKeyForWorkspace,
66
getDefaultWorkspace,
77
getWorkspaces,
88
} from "../../credentials.ts"
99
import { padDisplay } from "../../utils/display.ts"
10-
import { handleError } from "../../utils/errors.ts"
10+
import { handleError, isClientError } from "../../utils/errors.ts"
1111
import { createGraphQLClient } from "../../utils/graphql.ts"
1212

1313
const viewerQuery = gql(`
@@ -48,11 +48,22 @@ async function fetchWorkspaceInfo(
4848
userName: result.viewer.name,
4949
email: result.viewer.email,
5050
}
51-
} catch {
51+
} catch (error) {
52+
let errorMsg = "unknown error"
53+
if (isClientError(error)) {
54+
const status = error.response?.status
55+
if (status === 401 || status === 403) {
56+
errorMsg = "invalid credentials"
57+
} else {
58+
errorMsg = error.message
59+
}
60+
} else if (error instanceof Error) {
61+
errorMsg = error.message
62+
}
5263
return {
5364
workspace,
5465
isDefault,
55-
error: "invalid credentials",
66+
error: errorMsg,
5667
}
5768
}
5869
}
@@ -70,12 +81,19 @@ export const listCommand = new Command()
7081
return
7182
}
7283

73-
const credentials = getAllCredentials()
74-
7584
// Fetch info for all workspaces in parallel
76-
const infoPromises = workspaces.map((ws) =>
77-
fetchWorkspaceInfo(ws, credentials[ws]!)
78-
)
85+
const infoPromises = workspaces.map((ws) => {
86+
const apiKey = getApiKeyForWorkspace(ws)
87+
if (apiKey == null) {
88+
const info: WorkspaceInfo = {
89+
workspace: ws,
90+
isDefault: getDefaultWorkspace() === ws,
91+
error: "missing credentials",
92+
}
93+
return Promise.resolve(info)
94+
}
95+
return fetchWorkspaceInfo(ws, apiKey)
96+
})
7997
const infos = await Promise.all(infoPromises)
8098

8199
// Calculate column widths

0 commit comments

Comments
 (0)