From 50cdab17b902af5ae00ef9af45dbdae63af989d2 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 3 Jul 2026 16:38:31 -0700 Subject: [PATCH] FN-7474: add GitHub onboarding action choices Add first-run onboarding paths for GitHub OAuth and GitHub CLI setup while preserving existing GitHub warning behavior. - Add a GitHub onboarding action model for OAuth, authenticated gh CLI, unauthenticated gh CLI, missing gh CLI, and skip states. - Render in-flow GitHub OAuth connection, gh auth login guidance, and gh CLI install guidance with mobile styling and coverage. - Update onboarding docs, locale resources, and the release changeset as a minor user-facing feature. - Preserve the one-day GitHub warning-delay copy and dashboard OAuth browser behavior docs from main. Files changed: .changeset/fn-7474-github-onboarding-actions.md | 7 + docs/dashboard-guide.md | 2 +- docs/getting-started.md | 2 +- packages/cli/vitest.config.ts | 12 ++ .../app/components/ModelOnboardingModal.css | 68 ++++++++- .../app/components/ModelOnboardingModal.tsx | 153 +++++++++++++++++++-- .../__tests__/ModelOnboardingModal.test.tsx | 101 +++++++++++++- packages/dashboard/vitest.config.ts | 20 +++ packages/i18n/locales/en/app.json | 11 +- packages/i18n/locales/es/app.json | 11 +- packages/i18n/locales/fr/app.json | 11 +- packages/i18n/locales/ko/app.json | 11 +- packages/i18n/locales/zh-CN/app.json | 11 +- packages/i18n/locales/zh-TW/app.json | 11 +- packages/i18n/src/resources.d.ts | 11 +- 15 files changed, 413 insertions(+), 29 deletions(-) Fusion-Task-Id: FN-7474 Fusion-Task-Lineage: c06d3db5-da4b-43f7-8b74-a41f6a041029 Co-authored-by: Fusion (runfusion.ai) --- .../fn-7474-github-onboarding-actions.md | 7 + docs/dashboard-guide.md | 2 +- docs/getting-started.md | 2 +- .../app/components/ModelOnboardingModal.css | 68 +++++++- .../app/components/ModelOnboardingModal.tsx | 153 ++++++++++++++++-- .../__tests__/ModelOnboardingModal.test.tsx | 101 +++++++++++- packages/dashboard/vitest.config.ts | 8 + packages/i18n/locales/en/app.json | 11 +- packages/i18n/locales/es/app.json | 11 +- packages/i18n/locales/fr/app.json | 11 +- packages/i18n/locales/ko/app.json | 11 +- packages/i18n/locales/zh-CN/app.json | 11 +- packages/i18n/locales/zh-TW/app.json | 11 +- packages/i18n/src/resources.d.ts | 11 +- 14 files changed, 389 insertions(+), 29 deletions(-) create mode 100644 .changeset/fn-7474-github-onboarding-actions.md diff --git a/.changeset/fn-7474-github-onboarding-actions.md b/.changeset/fn-7474-github-onboarding-actions.md new file mode 100644 index 0000000000..effca3756f --- /dev/null +++ b/.changeset/fn-7474-github-onboarding-actions.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": minor +--- + +summary: Add GitHub OAuth and CLI setup actions to first-run onboarding. +category: feature +dev: GitHub onboarding now shows in-flow OAuth connect, gh auth login, and gh install guidance. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index a4c3880e82..4ddada50a1 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -793,7 +793,7 @@ Memory view provides a multi-file editor for project and daily memory files. Its ## Setup Wizard Project Registration -First-run setup checks the **GitHub (Optional)** step for the `git` executable on the Fusion server host before users continue into repository setup. If Git is installed, the step shows a low-noise prerequisite-ready note. If Git is missing, the step shows platform-aware install guidance for macOS, Windows, and Linux plus a Git downloads link, while still allowing users to skip optional GitHub authentication. The check reflects the machine or service container running Fusion, not the browser device. +First-run setup uses the **GitHub (Optional)** step for all GitHub prerequisites and auth choices. When dashboard GitHub OAuth is configured, the step offers an in-flow **Connect GitHub OAuth** action with Cancel/Retry feedback instead of sending users to Settings first. It also checks GitHub CLI (`gh`) on the Fusion server host: installed-but-unauthenticated hosts show `gh auth login` guidance, missing hosts show platform-aware GitHub CLI install guidance and a releases link, and authenticated `gh` still counts as GitHub-ready while OAuth remains optional. The same step checks the `git` executable before users continue into repository setup. If Git is installed, the step shows a low-noise prerequisite-ready note. If Git is missing, it shows platform-aware install guidance for macOS, Windows, and Linux plus a Git downloads link, while still allowing users to skip optional GitHub authentication. These checks reflect the machine or service container running Fusion, not the browser device; users can later manage OAuth from **Settings → Authentication**. First-run setup and embedded project setup both expose a **Repository setup** section before path entry: diff --git a/docs/getting-started.md b/docs/getting-started.md index 03e9856126..0436bb6002 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -93,7 +93,7 @@ fn dashboard On first launch, Fusion opens an onboarding wizard with guided setup steps: 1. **AI Setup** — choose a provider and authenticate (you only need one to start). Anthropic/Claude and OpenAI Codex use a pasted authorization-code OAuth flow in onboarding and Settings (sign in, then paste the final redirect URL or code back into Fusion), and Fusion warns before login so you remember to copy the browser address bar URL before the redirect tab appears to fail. After the initial Claude OAuth login, Fusion normally refreshes the OAuth credential automatically with the stored refresh token when the access token expires, so repeated manual re-login is not usually required. **Anthropic — via Claude CLI** remains available as a separate optional path. Deprecated Google Gemini CLI / Antigravity entries are hidden; Google/Gemini API key, Google Generative AI, Vertex, and Cloud Code options remain available. -2. **GitHub (Optional)** — connect GitHub for issue import and PR workflows. This step also checks whether the Fusion host can run `git`; if Git is missing, onboarding shows platform install guidance before you reach clone, init, or repository registration flows. Install Git on the machine or service container running Fusion, not just on the browser/client device. See [Git downloads](https://git-scm.com/downloads) for macOS, Windows, and Linux options. +2. **GitHub (Optional)** — connect GitHub for issue import and PR workflows. When dashboard OAuth is configured, this step includes an in-flow **Connect GitHub OAuth** action. It also shows whether the Fusion host has GitHub CLI (`gh`) available: run `gh auth login` on the host when `gh` is installed but unauthenticated, or use the GitHub CLI releases/install guidance when `gh` is missing. The step also checks whether the Fusion host can run `git`; if Git is missing, onboarding shows platform install guidance before you reach clone, init, or repository registration flows. Install Git and GitHub CLI on the machine or service container running Fusion, not just on the browser/client device. See [Git downloads](https://git-scm.com/downloads) and [GitHub CLI releases](https://github.com/cli/cli/releases/latest) for macOS, Windows, and Linux options. 3. **Project Setup** — choose how Fusion should prepare a repository: - **Use Existing Directory** registers a folder that is already a git repository or a workspace root with detected sub-repositories. - **Initialize New Repository** registers an existing local folder and lets the server run `git init` during registration when the folder is not already a git repository. diff --git a/packages/dashboard/app/components/ModelOnboardingModal.css b/packages/dashboard/app/components/ModelOnboardingModal.css index 0d22fdef22..528c69c1a8 100644 --- a/packages/dashboard/app/components/ModelOnboardingModal.css +++ b/packages/dashboard/app/components/ModelOnboardingModal.css @@ -819,6 +819,62 @@ margin: 0; } +.onboarding-github-setup-card { + display: flex; + flex-direction: column; + gap: var(--space-sm); + padding: var(--space-md); + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--surface); + color: var(--text-muted); + line-height: 1.5; +} + +.onboarding-github-setup-card--warning { + border-color: color-mix(in srgb, var(--color-warning) 40%, var(--border)); + border-left: var(--space-xs) solid var(--color-warning); + background: color-mix(in srgb, var(--color-warning) 10%, transparent); +} + +.onboarding-github-setup-card--info { + border-color: color-mix(in srgb, var(--color-info) 35%, var(--border)); + background: color-mix(in srgb, var(--color-info) 8%, transparent); +} + +.onboarding-github-setup-card__heading { + display: flex; + align-items: center; + gap: var(--space-xs); + color: var(--text); +} + +.onboarding-github-setup-card p { + margin: 0; +} + +.onboarding-github-setup-card a { + align-self: flex-start; +} + +.onboarding-github-setup-list { + display: flex; + flex-direction: column; + gap: var(--space-xxs); + margin: 0; + padding-left: var(--space-xl); +} + +.onboarding-github-command { + align-self: flex-start; + padding: var(--space-xs) var(--space-sm); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + background: var(--surface-raised); + color: var(--text); + font-family: var(--font-mono); +} + /* === GitHub Connection Status Feedback (FN-1934) === */ .onboarding-github-feedback { margin-top: var(--space-sm); @@ -1315,10 +1371,20 @@ min-height: 36px; } - .onboarding-github-git-prerequisite a { + .onboarding-github-git-prerequisite a, + .onboarding-github-setup-card a, + .onboarding-github-command { align-self: stretch; } + .onboarding-github-setup-card { + padding: var(--space-sm); + } + + .onboarding-github-setup-card__heading { + align-items: flex-start; + } + /* Compact provider card on mobile. Earlier rule forced flex-direction: column so the icon, body, and actions each took their own full-width row — combined with --space-lg diff --git a/packages/dashboard/app/components/ModelOnboardingModal.tsx b/packages/dashboard/app/components/ModelOnboardingModal.tsx index 4e2734fd3b..8cffb4953b 100644 --- a/packages/dashboard/app/components/ModelOnboardingModal.tsx +++ b/packages/dashboard/app/components/ModelOnboardingModal.tsx @@ -590,12 +590,85 @@ export type ProviderConnectionStatus = "connected" | "not-connected" | "skipped" /** GitHub-specific status variants for richer connection feedback */ type GitHubConnectionStatus = "connected" | "failed" | "pending" | "skipped" | "not-connected"; +type GitHubOAuthActionState = "unavailable" | "not-connected" | "connected" | "pending" | "failed" | "skipped"; +type GitHubCliActionState = "unknown" | "missing" | "unauthenticated" | "authenticated"; + interface GhCliStatus { available: boolean; authenticated: boolean; } +interface GitHubActionViewModel { + oauth: { + provider?: AuthProvider; + providerAvailable: boolean; + authenticated: boolean; + loginInProgress: boolean; + state: GitHubOAuthActionState; + }; + ghCli: { + status?: GhCliStatus; + state: GitHubCliActionState; + authenticated: boolean; + }; + ready: boolean; + readyVia: "oauth" | "gh-cli" | null; +} + const GIT_INSTALL_URL = "https://git-scm.com/downloads"; +const GH_CLI_INSTALL_URL = "https://github.com/cli/cli/releases/latest"; + +function buildGitHubActionViewModel(params: { + providers: AuthProvider[]; + ghCliStatus?: GhCliStatus; + loginOutcomes: Record; + skipped: boolean; +}): GitHubActionViewModel { + const githubProvider = params.providers.find((provider) => provider.id === "github"); + const oauthAuthenticated = githubProvider?.authenticated ?? false; + const oauthLoginInProgress = githubProvider?.loginInProgress ?? false; + const ghCliState: GitHubCliActionState = !params.ghCliStatus + ? "unknown" + : params.ghCliStatus.authenticated + ? "authenticated" + : params.ghCliStatus.available + ? "unauthenticated" + : "missing"; + const ghCliAuthenticated = ghCliState === "authenticated"; + let oauthState: GitHubOAuthActionState = "unavailable"; + + if (githubProvider) { + const outcome = params.loginOutcomes.github; + if (oauthAuthenticated) { + oauthState = "connected"; + } else if (outcome === "pending" || oauthLoginInProgress) { + oauthState = "pending"; + } else if (outcome === "failed" || outcome === "timeout") { + oauthState = "failed"; + } else if (params.skipped) { + oauthState = "skipped"; + } else { + oauthState = "not-connected"; + } + } + + return { + oauth: { + provider: githubProvider, + providerAvailable: !!githubProvider, + authenticated: oauthAuthenticated, + loginInProgress: oauthLoginInProgress, + state: oauthState, + }, + ghCli: { + status: params.ghCliStatus, + state: ghCliState, + authenticated: ghCliAuthenticated, + }, + ready: oauthAuthenticated || ghCliAuthenticated, + readyVia: oauthAuthenticated ? "oauth" : ghCliAuthenticated ? "gh-cli" : null, + }; +} /** Maximum number of poll cycles before timing out (150 × 2s = 5 minutes) */ const MAX_POLL_CYCLES = 150; @@ -915,19 +988,32 @@ export function ModelOnboardingModal({ return () => clearInterval(interval); }, [authProviders, loadAuthStatus]); + /* + FNXC:Onboarding 2026-07-03-17:04: + The GitHub onboarding step needs an explicit action model so OAuth login, GitHub CLI install/auth, and optional skip states stay independent. + GitHub readiness remains OAuth-authenticated OR `gh`-authenticated; GitHub-named AI providers such as `github-copilot` must never satisfy the GitHub integration provider check. + */ + const githubActionState = useMemo( + () => buildGitHubActionViewModel({ + providers: authProviders, + ghCliStatus, + loginOutcomes, + skipped: isGithubSkipped, + }), + [authProviders, ghCliStatus, loginOutcomes, isGithubSkipped], + ); // OAuth status for the GitHub provider (used for OAuth-specific controls like Connect/Disconnect). - const githubProvider = authProviders.find((p) => p.id === "github"); - const hasGithubProvider = !!githubProvider; - const isGithubAuthenticated = githubProvider?.authenticated ?? false; - const isGithubLoginInProgress = githubProvider?.loginInProgress ?? false; - const isGithubCliAuthenticated = ghCliStatus?.authenticated ?? false; + const hasGithubProvider = githubActionState.oauth.providerAvailable; + const isGithubAuthenticated = githubActionState.oauth.authenticated; + const isGithubLoginInProgress = githubActionState.oauth.loginInProgress; + const isGithubCliAuthenticated = githubActionState.ghCli.authenticated; const gitInstallUrl = gitCliStatus?.installUrl ?? GIT_INSTALL_URL; const gitVersionLabel = gitCliStatus?.version ? t("setup.gitPrerequisiteInstalledVersion", "Git is installed on the Fusion host ({{version}}).", { version: gitCliStatus.version }) : t("setup.gitPrerequisiteInstalled", "Git is installed on the Fusion host."); // Effective GitHub readiness (matches useSetupReadiness): OAuth OR authenticated gh CLI session. - const isGitHubReady = isGithubAuthenticated || isGithubCliAuthenticated; - const isGitHubReadyViaCli = !isGithubAuthenticated && isGithubCliAuthenticated; + const isGitHubReady = githubActionState.ready; + const isGitHubReadyViaCli = githubActionState.readyVia === "gh-cli"; // Get provider connection status for UI display const getProviderStatus = useCallback((provider: AuthProvider): ProviderConnectionStatus => { @@ -966,23 +1052,22 @@ export function ModelOnboardingModal({ } const getGitHubStatus = useCallback((): GitHubConnectionStatus => { - if (isGitHubReady) { + if (githubActionState.ready) { return "connected"; } - const githubOutcome = loginOutcomes["github"]; - if (githubOutcome === "pending") { + if (githubActionState.oauth.state === "pending") { return "pending"; } - if (githubOutcome === "failed" || githubOutcome === "timeout") { + if (githubActionState.oauth.state === "failed") { return "failed"; } - if (isGithubSkipped) { + if (githubActionState.oauth.state === "skipped") { return "skipped"; } return "not-connected"; - }, [isGitHubReady, loginOutcomes, isGithubSkipped]); + }, [githubActionState]); function GitHubStatusBadge({ status }: { status: GitHubConnectionStatus }) { const config: Record = { @@ -2759,6 +2844,44 @@ export function ModelOnboardingModal({

+ {/* + FNXC:Onboarding 2026-07-03-17:22: + GitHub onboarding must give users in-flow paths for both supported auth modes instead of Settings-only explanatory copy. + The dashboard can start OAuth when the GitHub provider exists; `gh` CLI setup remains explicit host-side guidance because Fusion does not install third-party binaries automatically. + */} + {!isGithubCliAuthenticated && githubActionState.ghCli.state === "missing" && ( +
+
+ + {t("setup.githubCliInstallTitle", "Install GitHub CLI")} +
+

+ {t("setup.githubCliInstallBody", "Fusion could not find `gh` on the host running Fusion. Install GitHub CLI there to import issues and track pull requests with CLI authentication.")} +

+
    +
  • {t("setup.githubCliInstallMac", "macOS: `brew install gh` or download the installer from GitHub CLI releases.")}
  • +
  • {t("setup.githubCliInstallWindows", "Windows: install GitHub CLI with WinGet, Chocolatey, or the GitHub CLI installer, then restart the Fusion host shell or service.")}
  • +
  • {t("setup.githubCliInstallLinux", "Linux: install `gh` with your distribution package manager or the packages from cli.github.com.")}
  • +
+ + {t("setup.githubCliInstallLink", "Open GitHub CLI releases")} + +
+ )} + + {!isGithubCliAuthenticated && githubActionState.ghCli.state === "unauthenticated" && ( +
+
+ + {t("setup.githubCliAuthTitle", "Authenticate GitHub CLI")} +
+

+ {t("setup.githubCliAuthBody", "GitHub CLI is installed but not authenticated on the Fusion host. Run this command in the environment where Fusion is running, then return here and continue.")} +

+ gh auth login +
+ )} + {!hasGithubProvider ? (