Skip to content

Commit d933bfa

Browse files
committed
improve git clone url parsing
1 parent eff5f23 commit d933bfa

File tree

2 files changed

+41
-23
lines changed

2 files changed

+41
-23
lines changed

components/dashboard/src/projects/new-project/NewProjectCreateFromURL.tsx

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,57 @@ type Props = {
1818
};
1919
export const NewProjectCreateFromURL: FC<Props> = ({ repoSearchFilter, isCreating, onCreateProject }) => {
2020
const { toast } = useToast();
21+
22+
const normalizedURL = useMemo(() => {
23+
let url = repoSearchFilter.toLowerCase().trim();
24+
25+
// Just parse out the origin/pathname to remove any query params or hash
26+
try {
27+
const parsedURL = new URL(url);
28+
const { origin, pathname } = parsedURL;
29+
url = `${origin}${pathname}`;
30+
} catch (e) {
31+
return url;
32+
}
33+
34+
return url;
35+
}, [repoSearchFilter]);
36+
2137
const showCreateFromURL = useMemo(() => {
2238
// TODO: Only accounts for https urls, need to account for ssh clone urls too?
23-
const looksLikeURL = isURL(repoSearchFilter, {
39+
const looksLikeURL = isURL(normalizedURL, {
2440
require_protocol: true,
2541
protocols: ["https"],
2642
});
2743

28-
const hasTrailingGit = /\.git$/.test(repoSearchFilter);
44+
const hasTrailingGit = /\.git$/.test(normalizedURL);
2945

3046
return looksLikeURL && hasTrailingGit;
31-
}, [repoSearchFilter]);
32-
33-
const normalizedURL = useMemo(() => {
34-
let url = repoSearchFilter.toLowerCase().trim();
35-
36-
return url;
37-
}, [repoSearchFilter]);
47+
}, [normalizedURL]);
3848

3949
const handleCreate = useCallback(() => {
4050
let name = "";
4151
let slug = "";
4252

4353
try {
4454
// try and parse the url for owner/repo path parts
45-
console.log("url: ", normalizedURL.substring(0, normalizedURL.length - 4));
46-
const [owner, repo] = new URL(normalizedURL.substring(0, normalizedURL.length - 4)).pathname.split("/");
55+
const segments = new URL(normalizedURL.substring(0, normalizedURL.length - ".git".length)).pathname
56+
.split("/")
57+
.filter(Boolean);
58+
59+
// Repo is last segment
60+
const repo = segments.pop();
61+
// owner is everything else
62+
const owner = segments.join("/");
63+
4764
if (!owner && !repo) {
4865
throw new Error();
4966
}
67+
5068
name = repo || owner;
51-
slug = repo || owner;
69+
slug = (repo || owner).toLowerCase();
5270
} catch (e) {
53-
toast("Sorry, it looks like we can't handle that URL. Is it a valid git clone url?");
71+
toast("Sorry, it looks like we can't handle that URL. Is it a valid git clone URL?");
5472
return;
5573
}
5674

components/dashboard/src/projects/new-project/NewProjectRepoSelection.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const NewProjectRepoSelection: FC<Props> = ({ selectedProviderHost, onPro
3232
const createProject = useCreateProject();
3333

3434
// Component state managed by this component
35-
const [selectedAccount, _setSelectedAccount] = useState<string>();
35+
const [selectedAccount, setSelectedAccount] = useState<string>();
3636
const [repoSearchFilter, setRepoSearchFilter] = useState("");
3737
const [installationId, setInstallationId] = useState<string>();
3838

@@ -43,8 +43,8 @@ export const NewProjectRepoSelection: FC<Props> = ({ selectedProviderHost, onPro
4343
});
4444

4545
// Wrap setting selected account so we can clear the repo search filter at the same time
46-
const updateSelectedAccount = useCallback((account?: string) => {
47-
_setSelectedAccount(account);
46+
const setSelectedAccountAndClearSearch = useCallback((account?: string) => {
47+
setSelectedAccount(account);
4848
setRepoSearchFilter("");
4949
}, []);
5050

@@ -117,22 +117,22 @@ export const NewProjectRepoSelection: FC<Props> = ({ selectedProviderHost, onPro
117117
[onCreateProject],
118118
);
119119

120-
// Adjusts selectedAccount when repos change
120+
// Adjusts selectedAccount when repos change if we don't have a selected account
121121
useEffect(() => {
122122
if (reposInAccounts?.length === 0) {
123-
updateSelectedAccount(undefined);
124-
} else {
123+
setSelectedAccountAndClearSearch(undefined);
124+
} else if (!selectedAccount) {
125125
const first = reposInAccounts?.[0];
126126
if (!!first?.installationUpdatedAt) {
127127
const mostRecent = reposInAccounts?.reduce((prev, current) =>
128128
(prev.installationUpdatedAt || 0) > (current.installationUpdatedAt || 0) ? prev : current,
129129
);
130-
updateSelectedAccount(mostRecent?.account);
130+
setSelectedAccountAndClearSearch(mostRecent?.account);
131131
} else {
132-
updateSelectedAccount(first?.account);
132+
setSelectedAccountAndClearSearch(first?.account);
133133
}
134134
}
135-
}, [reposInAccounts, updateSelectedAccount]);
135+
}, [reposInAccounts, selectedAccount, setSelectedAccountAndClearSearch]);
136136

137137
if (isLoading) {
138138
return <ReposLoading />;
@@ -150,7 +150,7 @@ export const NewProjectRepoSelection: FC<Props> = ({ selectedProviderHost, onPro
150150
accounts={accounts}
151151
selectedAccount={selectedAccount}
152152
selectedProviderHost={selectedProviderHost}
153-
onAccountSelected={updateSelectedAccount}
153+
onAccountSelected={setSelectedAccountAndClearSearch}
154154
onAddGitHubAccount={reconfigure}
155155
onSelectGitProvider={onChangeGitProvider}
156156
/>

0 commit comments

Comments
 (0)