-
Notifications
You must be signed in to change notification settings - Fork 1k
fix(import): restore git-init for non-git folders on v2 folder import #5036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
36bdf63
70b131e
0cf36c3
b9f2526
06bf09a
fa9cfd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import { | ||
| AlertDialog, | ||
| AlertDialogContent, | ||
| AlertDialogDescription, | ||
| AlertDialogFooter, | ||
| AlertDialogHeader, | ||
| AlertDialogTitle, | ||
| } from "@superset/ui/alert-dialog"; | ||
| import { Button } from "@superset/ui/button"; | ||
| import { getBaseName } from "renderer/lib/pathBasename"; | ||
| import { useGitInitConfirmStore } from "renderer/stores/git-init-confirm"; | ||
|
|
||
| /** | ||
| * Confirms initializing git in a folder the user picked to import that isn't a | ||
| * git repo yet. Driven imperatively by `useGitInitConfirmStore.request()` from | ||
| * the folder-first import flow; mounted once via AddRepositoryModals. | ||
| */ | ||
| export function GitInitConfirmDialog() { | ||
| const isOpen = useGitInitConfirmStore((s) => s.isOpen); | ||
| const repoPath = useGitInitConfirmStore((s) => s.repoPath); | ||
| const resolve = useGitInitConfirmStore((s) => s.resolve); | ||
|
|
||
| const folderName = repoPath ? getBaseName(repoPath) : "this folder"; | ||
|
|
||
| return ( | ||
| <AlertDialog | ||
| open={isOpen} | ||
| onOpenChange={(open) => { | ||
| if (!open) resolve(false); | ||
| }} | ||
| > | ||
| <AlertDialogContent> | ||
| <AlertDialogHeader> | ||
| <AlertDialogTitle>Initialize git repository?</AlertDialogTitle> | ||
| <AlertDialogDescription asChild> | ||
| <p> | ||
| <span className="font-medium text-foreground select-text cursor-text"> | ||
| {folderName} | ||
| </span>{" "} | ||
| isn't a git repository yet. Initialize git here and import it? | ||
| </p> | ||
| </AlertDialogDescription> | ||
| </AlertDialogHeader> | ||
| <AlertDialogFooter> | ||
| <Button variant="outline" onClick={() => resolve(false)}> | ||
| Cancel | ||
| </Button> | ||
| <Button onClick={() => resolve(true)}>Initialize & import</Button> | ||
| </AlertDialogFooter> | ||
| </AlertDialogContent> | ||
| </AlertDialog> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { GitInitConfirmDialog } from "./GitInitConfirmDialog"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { create } from "zustand"; | ||
| import { devtools } from "zustand/middleware"; | ||
|
|
||
| interface GitInitConfirmState { | ||
| isOpen: boolean; | ||
| repoPath: string | null; | ||
| /** | ||
| * Opens the confirm dialog and resolves `true` if the user agrees to | ||
| * `git init` the folder, `false` if they cancel/dismiss. Only one request | ||
| * can be in flight at a time — a second call resolves the prior request to | ||
| * `false` before opening fresh. Safe today because there is a single global | ||
| * dialog instance (rendered by AddRepositoryModals). | ||
| */ | ||
| request: (repoPath: string) => Promise<boolean>; | ||
| resolve: (confirmed: boolean) => void; | ||
| } | ||
|
|
||
| // Module-level resolver so the pending promise isn't stored in zustand state. | ||
| // The store drives the dialog's open/close UI; the resolver bridges the | ||
| // imperative request() call back to its caller. | ||
| let pendingResolve: ((confirmed: boolean) => void) | null = null; | ||
|
|
||
| export const useGitInitConfirmStore = create<GitInitConfirmState>()( | ||
| devtools( | ||
| (set) => ({ | ||
| isOpen: false, | ||
| repoPath: null, | ||
| request: (repoPath) => { | ||
| pendingResolve?.(false); | ||
| return new Promise<boolean>((resolve) => { | ||
| pendingResolve = resolve; | ||
| set({ isOpen: true, repoPath }); | ||
| }); | ||
| }, | ||
| resolve: (confirmed) => { | ||
| const resolve = pendingResolve; | ||
| pendingResolve = null; | ||
| set({ isOpen: false, repoPath: null }); | ||
| resolve?.(confirmed); | ||
| }, | ||
| }), | ||
| { name: "git-init-confirm" }, | ||
| ), | ||
| ); | ||
|
|
||
| export const useRequestGitInitConfirm = () => | ||
| useGitInitConfirmStore((state) => state.request); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,8 @@ import { | |
| type ResolvedRepo, | ||
| resolveLocalRepo, | ||
| resolveMatchingSlug, | ||
| tryRevParseGitRoot, | ||
| validateDirectoryPath, | ||
| } from "./utils/resolve-repo"; | ||
|
|
||
| export const projectRouter = router({ | ||
|
|
@@ -162,7 +164,22 @@ export const projectRouter = router({ | |
| }), | ||
| ) | ||
| .query(async ({ ctx, input }) => { | ||
| const resolved = await resolveLocalRepo(input.repoPath); | ||
| // Detect "folder isn't a git repo yet" without throwing, so the import | ||
| // UI can offer to `git init` it (create importLocal + initIfNeeded) | ||
| // instead of dead-ending on a BAD_REQUEST. Additive optional field — | ||
| // repo paths never carry needsGitInit, so existing callers are | ||
| // unaffected. | ||
| const root = await tryRevParseGitRoot(input.repoPath); | ||
| if (root === null) { | ||
| validateDirectoryPath(input.repoPath, "Path"); // 400 on missing / not-a-dir | ||
| return { | ||
| candidates: [], | ||
| cloudErrors: [] as { url: string; message: string }[], | ||
| needsGitInit: true as const, | ||
| }; | ||
| } | ||
|
|
||
| const resolved = await resolveLocalRepo(root); | ||
|
Comment on lines
+172
to
+182
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate
🤖 Prompt for AI Agents |
||
| const gitRoot = resolved.repoPath; | ||
|
|
||
| const expectedParsed = | ||
|
|
@@ -391,6 +408,10 @@ export const projectRouter = router({ | |
| z.object({ | ||
| kind: z.literal("importLocal"), | ||
| repoPath: z.string().min(1), | ||
| // When set, `git init` a non-git folder in place before | ||
| // importing. The UI sets this only after confirming intent | ||
| // with the user (see findByPath's needsGitInit). | ||
| initIfNeeded: z.boolean().optional().default(false), | ||
| }), | ||
| z.object({ | ||
| kind: z.literal("template"), | ||
|
|
@@ -423,6 +444,7 @@ export const projectRouter = router({ | |
| return createFromImportLocal(ctx, { | ||
| name: input.name, | ||
| repoPath: input.mode.repoPath, | ||
| initIfNeeded: input.mode.initIfNeeded, | ||
| }); | ||
| } | ||
| }), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initIfNeedednow lets file paths slip through as repo imports.Before this change,
createFromImportLocalalways went throughresolveLocalRepo(repoPath), which rejected non-directories. With the new probe-first flow, passing/repo/src/file.tsplusinitIfNeeded: trueresolves/repoand imports it successfully. Please validate the originalrepoPathbeforetryRevParseGitRootso the mutation stays folder-only.Also applies to: 219-222
🤖 Prompt for AI Agents