pub type GitHubRepoRef = {owner: string, name: string, full_name: string}
fn __github_strip_git_suffix(value) {
const text = trim(to_string(value ?? ""))
if ends_with(text, ".git") {
return substring(text, 0, len(text) - len(".git"))
}
return text
}
fn __github_slug_match(pattern, text) {
const captures = regex_captures(pattern, text)
if len(captures) == 0 {
return nil
}
const groups = captures[0].groups
if len(groups) < 2 {
return nil
}
const owner = trim(to_string(groups[0]))
const repo = __github_strip_git_suffix(groups[1])
if owner == "" || repo == "" || contains(owner, "/") || contains(repo, "/") {
return nil
}
return owner + "/" + repo
}
/**
* github_slug_from_remote returns `owner/repo` for common GitHub SSH and HTTPS remote URLs.
*
* @effects: []
* @errors: []
*/
pub fn github_slug_from_remote(url: string) -> string? {
const text = trim(to_string(url ?? ""))
if text == "" {
return nil
}
const url_slug = __github_slug_match(
"^(?:git\\+)?(?:https?|ssh|git)://(?:[^@/]+@)?github\\.com(?::[0-9]+)?/([^/]+)/([^/#?]+)(?:[?#].*)?$",
text,
)
if url_slug != nil {
return url_slug
}
return __github_slug_match("^(?:[^@/]+@)?github\\.com:([^/]+)/([^/#?]+)(?:[?#].*)?$", text)
}
/**
* github_repo normalizes an `owner/repo` slug, GitHub remote URL, or owner+repo pair.
*
* @effects: []
* @errors: []
*/
pub fn github_repo(repo: any, name: string? = nil) -> GitHubRepoRef? {
if name != nil {
const owner = trim(to_string(repo ?? ""))
const repo_name = __github_strip_git_suffix(name)
if owner == "" || repo_name == "" {
return nil
}
return {owner: owner, name: repo_name, full_name: owner + "/" + repo_name}
}
if type_of(repo) == "dict" {
const full_name = repo?.full_name
if full_name != nil {
return github_repo(full_name)
}
const owner = repo?.owner ?? repo?.owner_login
const repo_name = repo?.name ?? repo?.repo
if owner != nil && repo_name != nil {
return github_repo(owner, repo_name)
}
return nil
}
const raw = trim(to_string(repo ?? ""))
const slug = if contains(raw, "github.com") || contains(raw, "://") {
github_slug_from_remote(raw)
} else {
raw
}
if slug == nil || !contains(slug, "/") {
return nil
}
const parts = split(slug, "/")
if len(parts) != 2 {
return nil
}
return github_repo(parts[0], parts[1])
}
/**
* Parse a repository reference or raise the connector's canonical input error.
*
* @effects: []
* @errors: [runtime]
*/
pub fn github_repo_or_throw(repo, name = nil) -> GitHubRepoRef {
const parsed = github_repo(repo, name)
if parsed == nil {
throw "std/connectors/github: expected repo as owner/repo, GitHub remote URL, or owner + repo"
}
return parsed
}