refactor: rename URL validators to looksLikeRepoUrl

This commit is contained in:
statxc 2026-04-01 23:21:22 +00:00
parent 6a7830b07e
commit 9d89d74d70
5 changed files with 16 additions and 16 deletions

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { import {
isGithubShorthand, isGithubShorthand,
isGithubUrl, looksLikeRepoUrl,
isHttpUrl, isHttpUrl,
normalizeGithubImportSource, normalizeGithubImportSource,
} from "../commands/client/company.js"; } from "../commands/client/company.js";
@ -21,17 +21,17 @@ describe("isHttpUrl", () => {
}); });
}); });
describe("isGithubUrl", () => { describe("looksLikeRepoUrl", () => {
it("matches GitHub URLs", () => { it("matches GitHub URLs", () => {
expect(isGithubUrl("https://github.com/org/repo")).toBe(true); expect(looksLikeRepoUrl("https://github.com/org/repo")).toBe(true);
}); });
it("rejects non-GitHub HTTP URLs", () => { it("rejects URLs without owner/repo path", () => {
expect(isGithubUrl("https://example.com/foo")).toBe(false); expect(looksLikeRepoUrl("https://example.com/foo")).toBe(false);
}); });
it("rejects local paths", () => { it("rejects local paths", () => {
expect(isGithubUrl("/tmp/my-company")).toBe(false); expect(looksLikeRepoUrl("/tmp/my-company")).toBe(false);
}); });
}); });

View file

@ -765,7 +765,7 @@ export function isHttpUrl(input: string): boolean {
return /^https?:\/\//i.test(input.trim()); return /^https?:\/\//i.test(input.trim());
} }
export function isGithubUrl(input: string): boolean { export function looksLikeRepoUrl(input: string): boolean {
try { try {
const url = new URL(input.trim()); const url = new URL(input.trim());
if (url.protocol !== "https:") return false; if (url.protocol !== "https:") return false;
@ -843,7 +843,7 @@ export function normalizeGithubImportSource(input: string, refOverride?: string)
}); });
} }
if (!isGithubUrl(trimmed)) { if (!looksLikeRepoUrl(trimmed)) {
throw new Error("GitHub source must be a GitHub or GitHub Enterprise URL, or owner/repo[/path] shorthand."); throw new Error("GitHub source must be a GitHub or GitHub Enterprise URL, or owner/repo[/path] shorthand.");
} }
if (!ref) { if (!ref) {
@ -1218,10 +1218,10 @@ export function registerCompanyCommands(program: Command): void {
| { type: "github"; url: string }; | { type: "github"; url: string };
const treatAsLocalPath = !isHttpUrl(from) && await pathExists(from); const treatAsLocalPath = !isHttpUrl(from) && await pathExists(from);
const isGithubSource = isGithubUrl(from) || (isGithubShorthand(from) && !treatAsLocalPath); const isGithubSource = looksLikeRepoUrl(from) || (isGithubShorthand(from) && !treatAsLocalPath);
if (isHttpUrl(from) || isGithubSource) { if (isHttpUrl(from) || isGithubSource) {
if (!isGithubUrl(from) && !isGithubShorthand(from)) { if (!looksLikeRepoUrl(from) && !isGithubShorthand(from)) {
throw new Error( throw new Error(
"Only GitHub URLs and local paths are supported for import. " + "Only GitHub URLs and local paths are supported for import. " +
"Generic HTTP URLs are not supported. Use a GitHub or GitHub Enterprise URL (https://github.com/... or https://ghe.example.com/...) or a local directory path.", "Generic HTTP URLs are not supported. Use a GitHub or GitHub Enterprise URL (https://github.com/... or https://ghe.example.com/...) or a local directory path.",

View file

@ -984,7 +984,7 @@ async function readUrlSkillImports(
): Promise<{ skills: ImportedSkill[]; warnings: string[] }> { ): Promise<{ skills: ImportedSkill[]; warnings: string[] }> {
const url = sourceUrl.trim(); const url = sourceUrl.trim();
const warnings: string[] = []; const warnings: string[] = [];
const isGitHubRepoUrl = (() => { try { const looksLikeRepoUrl = (() => { try {
const parsed = new URL(url); const parsed = new URL(url);
if (parsed.protocol !== "https:") return false; if (parsed.protocol !== "https:") return false;
const h = parsed.hostname.toLowerCase(); const h = parsed.hostname.toLowerCase();
@ -992,7 +992,7 @@ async function readUrlSkillImports(
const segments = parsed.pathname.split("/").filter(Boolean); const segments = parsed.pathname.split("/").filter(Boolean);
return segments.length >= 2 && !parsed.pathname.endsWith(".md"); return segments.length >= 2 && !parsed.pathname.endsWith(".md");
} catch { return false; } })(); } catch { return false; } })();
if (isGitHubRepoUrl) { if (looksLikeRepoUrl) {
const parsed = parseGitHubSourceUrl(url); const parsed = parseGitHubSourceUrl(url);
const apiBase = gitHubApiBase(parsed.hostname); const apiBase = gitHubApiBase(parsed.hostname);
const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed); const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed);

View file

@ -118,7 +118,7 @@ export function NewProjectDialog() {
const isAbsolutePath = (value: string) => value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value); const isAbsolutePath = (value: string) => value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value);
const isGitHubRepoUrl = (value: string) => { const looksLikeRepoUrl = (value: string) => {
try { try {
const parsed = new URL(value); const parsed = new URL(value);
if (parsed.protocol !== "https:") return false; if (parsed.protocol !== "https:") return false;
@ -155,7 +155,7 @@ export function NewProjectDialog() {
setWorkspaceError("Local folder must be a full absolute path."); setWorkspaceError("Local folder must be a full absolute path.");
return; return;
} }
if (repoUrl && !isGitHubRepoUrl(repoUrl)) { if (repoUrl && !looksLikeRepoUrl(repoUrl)) {
setWorkspaceError("Repo must use a valid GitHub or GitHub Enterprise repo URL."); setWorkspaceError("Repo must use a valid GitHub or GitHub Enterprise repo URL.");
return; return;
} }

View file

@ -343,7 +343,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
const isAbsolutePath = (value: string) => value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value); const isAbsolutePath = (value: string) => value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value);
const isGitHubRepoUrl = (value: string) => { const looksLikeRepoUrl = (value: string) => {
try { try {
const parsed = new URL(value); const parsed = new URL(value);
if (parsed.protocol !== "https:") return false; if (parsed.protocol !== "https:") return false;
@ -431,7 +431,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
persistCodebase({ repoUrl: null }); persistCodebase({ repoUrl: null });
return; return;
} }
if (!isGitHubRepoUrl(repoUrl)) { if (!looksLikeRepoUrl(repoUrl)) {
setWorkspaceError("Repo must use a valid GitHub or GitHub Enterprise repo URL."); setWorkspaceError("Repo must use a valid GitHub or GitHub Enterprise repo URL.");
return; return;
} }