Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions packages/myst-frontmatter/src/socials/validators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,27 +226,37 @@ describe('validateGitHub', () => {
expect(result).toBe('https://github.com/orgs/org');
});

it('should validate a valid GitHub Enterprise org URL', ({ opts }) => {
const result = validateGitHub('https://github.enterprise.com/orgs/myorg', opts);
expect(result).toBe('https://github.enterprise.com/orgs/myorg');
});

it('should validate a GitHub Enterprise URL with trailing slash', ({ opts }) => {
const result = validateGitHub('https://gh.enterprise.com/team/project/', opts);
expect(result).toBe('https://gh.enterprise.com/team/project/');
});

it('should return an error for an invalid GitHub username', ({ opts }) => {
const result = validateGitHub('@asdfg#', opts);
expect(result).toBeUndefined();
expect(opts.messages.errors?.at(0)?.message).toContain(
`GitHub social identity must be a valid username, org/repo, or org URL`,
`GitHub social identity must be a valid username, org/repo, GitHub URL, or GitHub Enterprise URL`,
);
});

it('should return an error for a non-org GitHub URL', ({ opts }) => {
const result = validateGitHub('https://github.com/user', opts);
expect(result).toBeUndefined();
expect(opts.messages.errors?.at(0)?.message).toContain(
`GitHub social identity must be a valid username, org/repo, or org URL`,
`GitHub social identity must be a valid username, org/repo, GitHub URL, or GitHub Enterprise URL`,
);
});

it('should return an error for a non-GitHub URL', ({ opts }) => {
const result = validateGitHub('https:/example.com', opts);
expect(result).toBeUndefined();
expect(opts.messages.errors?.at(0)?.message).toContain(
`GitHub social identity must be a valid username, org/repo, or org URL`,
`GitHub social identity must be a valid username, org/repo, GitHub URL, or GitHub Enterprise URL`,
);
});
});
21 changes: 15 additions & 6 deletions packages/myst-frontmatter/src/socials/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const TWITTER_URL_REGEX = /^https:\/\/(?:twitter\.com|x\.com)\/@?([a-zA-Z0-9_]{4
// Match a basic identifier (letters, numbers, underscores, full-stops)
const GITHUB_USERNAME_REGEX = /^@?([a-zA-Z0-9_.-]+)$/;
const GITHUB_ORG_URL_REGEX = /^https:\/\/github\.com\/orgs\/[a-zA-Z0-9_.-]+$/;
// Match GitHub Enterprise URLs with custom domains
const GITHUB_ENTERPRISE_URL_REGEX = /^https:\/\/gh\.[^./]+\.com\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)\/?$/;
// Match a basic identifier (letters, numbers, underscores, between 4 and 15 characters)
const TELEGRAM_REGEX = /^@?([a-zA-Z0-9_]{5,})$/;
const TELEGRAM_URL_REGEX = /^https:\/\/(?:t\.me|telegram\.me)\/?([a-zA-Z0-9_]{5,})$/;
Expand Down Expand Up @@ -155,23 +157,30 @@ export function validateYouTube(input: any, opts: ValidationOptions) {
}

/**
* Validate value is valid GitHub URL,
* Validate value is valid GitHub URL, including GitHub Enterprise domains
*/
export function validateGitHub(input: any, opts: ValidationOptions) {
const value = validateString(input, opts);
if (value === undefined) return undefined;

let match: ReturnType<typeof value.match>;
// URL
if ((match = value.match(GITHUB_USERNAME_REGEX))) {
return match[1];
} else if ((match = value.match(GITHUB_USERNAME_REPO_REGEX))) {

// Check for full URLs first (both github.com and GitHub Enterprise)
if ((match = value.match(GITHUB_ENTERPRISE_URL_REGEX))) {
// For GitHub Enterprise URLs, return the full URL
return match[0];
} else if ((match = value.match(GITHUB_ORG_URL_REGEX))) {
// Standard GitHub.com org URL
return match[0];
} else if ((match = value.match(GITHUB_USERNAME_REPO_REGEX))) {
// org/repo format (without domain)
return match[0];
} else if ((match = value.match(GITHUB_USERNAME_REGEX))) {
// username only
return match[1];
} else {
return validationError(
`GitHub social identity must be a valid username, org/repo, or org URL: ${value}`,
`GitHub social identity must be a valid username, org/repo, GitHub URL, or GitHub Enterprise URL: ${value}`,
opts,
);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/myst-frontmatter/src/utils/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export function validateGithubUrl(value: any, opts: ValidationOptions) {
github = `https://github.com/${repo}`;
}
}

// For GitHub Enterprise URLs (gh.something.com), just validate as a URL without domain restriction
if (typeof github === 'string' && github.startsWith('https://gh.') && github.includes('.com/')) {
return validateUrl(github, incrementOptions('github', opts));
}

// For standard GitHub URLs, keep the existing validation
return validateUrl(github, {
...incrementOptions('github', opts),
includes: 'github',
Expand Down
Loading