mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-29 12:03:09 +00:00
Add one-click install/sign-in to sentry-triage setup gate (#2831)
The sentry-triage canvas's setup gate previously told users to ask
Copilot to install its optional `sentry` dependency and reload
extensions, and to run `sentry auth login` from a terminal. Both
flows were unreliable or high-friction in practice.
This adds real one-click buttons for both steps:
- sentryClient.mjs — installPackage() runs npm install (async, via
execFile so the shared extension process's event loop isn't
blocked) rooted at the file's own directory via import.meta.url, so
the path is never guessed. loadFactory() falls back to importing
the package's resolved entry file by absolute path when the bare
import('sentry') fails, since Node caches a negative resolution for
a bare specifier for the life of the process — while still
distinguishing a genuinely missing package from a transitive-
dependency defect so the latter isn't masked as "package missing."
login() fails fast with a clear message when SENTRY_AUTH_TOKEN/
SENTRY_TOKEN is active in the environment (which takes precedence
over the OAuth login this button drives), and treats an empty/
falsy auth.login() result as a failed sign-in instead of silently
re-probing.
- preflight.mjs — installDependencies() runs the install and
re-probes the connection; authenticate() classifies the new error
codes for gate messaging.
- server.mjs / extension.mjs — wire POST /api/install-dependencies
and POST /api/auth-login routes (through the existing CSRF/Host
gate) to the new preflight functions, publishing refreshed
connection state to the canvas.
- components/page.mjs / styles.mjs — the install and sign-in
buttons, with loading/success/failure states and accessible live-
region status updates; a live multi-org <select> dropdown that
rebuilds in place once org discovery completes post-signin,
without requiring a canvas reopen.
- README.md — documents the one-click flow; manual npm install /
sentry auth login kept as a fallback.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { execSync } from 'node:child_process'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { startServer } from './server.mjs'
|
||||
import { scanIssues, listOrgs, listProjects, findProject } from './sentry.mjs'
|
||||
import { checkConnections, checkConnectionsOnce } from './preflight.mjs'
|
||||
import { checkConnections, checkConnectionsOnce, installDependencies, authenticate } from './preflight.mjs'
|
||||
import { sanitizeForPrompt } from './escape.mjs'
|
||||
|
||||
// A Sentry-derived URL is safe to pass through only if it PARSES as a real
|
||||
@@ -1309,6 +1309,55 @@ async function onRecheckConnections(entry) {
|
||||
return connections
|
||||
}
|
||||
|
||||
// "Install dependencies" button handler on the package-missing setup gate.
|
||||
// Delegates to preflight's installDependencies (npm install + re-probe, rooted
|
||||
// at the extension's own directory) and publishes the fresh connection state so
|
||||
// the gate updates live. On success (Sentry now reachable and an org already
|
||||
// committed) kicks a scan, mirroring onRecheckConnections's post-recovery path.
|
||||
async function onInstallDependencies(entry) {
|
||||
if (entry.closed) return entry.state.getConnections?.() || { sentry: { reachable: false } }
|
||||
const connections = await installDependencies()
|
||||
if (entry.closed) return connections
|
||||
entry.state.setConnections(connections)
|
||||
entry.notifyClients()
|
||||
if (connections.sentry.reachable && entry.state.getOrg()) {
|
||||
triageSentry(entry).catch((err) => {
|
||||
console.error('[sentry-triage] post-install scan failed:', err instanceof Error ? err.message : err)
|
||||
})
|
||||
} else if (connections.sentry.reachable) {
|
||||
discoverOrgs(entry).catch((err) => {
|
||||
console.error('[sentry-triage] post-install org discovery failed:', err instanceof Error ? err.message : err)
|
||||
})
|
||||
}
|
||||
return connections
|
||||
}
|
||||
|
||||
// "Sign in with Sentry" button handler on the not-authenticated setup gate
|
||||
// (shown only once the package is installed — see components/page.mjs). Runs
|
||||
// the SDK's OAuth device-code login (opens the user's browser directly, no
|
||||
// terminal) and publishes the fresh connection state so the gate updates
|
||||
// live, mirroring onInstallDependencies's post-recovery path. Unlike install,
|
||||
// a failed/cancelled login is allowed to propagate so the server route can
|
||||
// report the specific reason instead of a generic "still signed out".
|
||||
async function onAuthenticate(entry) {
|
||||
if (entry.closed) return entry.state.getConnections?.() || { sentry: { reachable: false } }
|
||||
const connections = await authenticate()
|
||||
if (entry.closed) return connections
|
||||
entry.state.setConnections(connections)
|
||||
entry.notifyClients()
|
||||
if (connections.sentry.reachable && entry.state.getOrg()) {
|
||||
triageSentry(entry).catch((err) => {
|
||||
console.error('[sentry-triage] post-auth scan failed:', err instanceof Error ? err.message : err)
|
||||
})
|
||||
} else if (connections.sentry.reachable) {
|
||||
discoverOrgs(entry).catch((err) => {
|
||||
console.error('[sentry-triage] post-auth org discovery failed:', err instanceof Error ? err.message : err)
|
||||
})
|
||||
}
|
||||
return connections
|
||||
}
|
||||
|
||||
|
||||
async function onWorkSelected(entry, issueKeys, modelByKey, assignCopilot) {
|
||||
if (entry.closed) return
|
||||
const uniqueKeys = [...new Set(issueKeys)].filter(Boolean)
|
||||
@@ -1982,6 +2031,8 @@ const session = await joinSession({
|
||||
onRefresh: () => refreshAll(entry),
|
||||
onWorkSelected: (keys, modelByKey, assignCopilot) => onWorkSelected(entry, keys, modelByKey, assignCopilot),
|
||||
onRecheck: () => onRecheckConnections(entry),
|
||||
onInstallDependencies: () => onInstallDependencies(entry),
|
||||
onAuthenticate: () => onAuthenticate(entry),
|
||||
onListProjects: (org) => discoverProjects(entry, org, { force: true }),
|
||||
onResolveProject: (org, slug) => resolveProject(entry, org, slug),
|
||||
onInvalidateEnrichment: () => {
|
||||
|
||||
Reference in New Issue
Block a user