fix: default to full URL mode and short-circuit SSH URL validation in repo dialog

RepositoryCreateDialog fixes:
- Change useAdvancedUrl default from false to true so full URL is the default
- Move isSshUrl helper before the effect that references it
- Short-circuit SSH URLs client-side in debounced validation so they always
  show as valid without depending on backend parseGitUrl behavior
- Keeps submit-time SSH key requirement: error shown if SSH URL without key

Tests:
- Update repositories-settings-tab tests for full-URL default mode
- Add SSH URL acceptance test with key selected (client-side short-circuit)
- Add SSH URL rejection test without key selected

Quality gates: tsc --noEmit pass, npm run build pass, 82/82 tests pass
This commit is contained in:
Developer
2026-06-09 14:07:02 +00:00
parent 680417a0a2
commit b2c84e2064
2 changed files with 169 additions and 19 deletions
@@ -37,7 +37,7 @@ export const RepositoryCreateDialog = ({
const [owner, setOwner] = useState("");
const [repoName, setRepoName] = useState("");
const [advancedUrl, setAdvancedUrl] = useState("");
const [useAdvancedUrl, setUseAdvancedUrl] = useState(false);
const [useAdvancedUrl, setUseAdvancedUrl] = useState(true);
const [formError, setFormError] = useState<string | null>(null);
const [urlValidation, setUrlValidation] = useState<{
status: UrlValidationStatus;
@@ -47,6 +47,11 @@ export const RepositoryCreateDialog = ({
const [selectedSshKey, setSelectedSshKey] = useState<string>("");
const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const isSshUrl = (url: string): boolean => {
const u = url.trim().toLowerCase();
return u.startsWith("git@") || u.startsWith("ssh://");
};
useEffect(() => {
if (!open && debounceTimer.current) {
clearTimeout(debounceTimer.current);
@@ -86,8 +91,26 @@ export const RepositoryCreateDialog = ({
setUrlValidation({ status: "validating", result: null });
debounceTimer.current = setTimeout(async () => {
const trimmed = advancedUrl.trim();
// Short-circuit SSH URLs — the backend parseGitUrl may not always
// recognise them, but they are valid clone URLs by definition.
if (isSshUrl(trimmed)) {
setUrlValidation({
status: "valid",
result: {
original_url: trimmed,
base_url: trimmed,
is_valid_clone_url: true,
needs_parsing: false,
host: null,
message: "Valid SSH git URL",
error_code: null,
},
});
return;
}
try {
const result = await parseGitUrl(advancedUrl.trim());
const result = await parseGitUrl(trimmed);
if (result.is_valid_clone_url) {
setUrlValidation({ status: "valid", result });
} else if (result.needs_parsing) {
@@ -124,11 +147,6 @@ export const RepositoryCreateDialog = ({
onClose();
};
const isSshUrl = (url: string): boolean => {
const u = url.trim().toLowerCase();
return u.startsWith("git@") || u.startsWith("ssh://");
};
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
setFormError(null);