Source code for repomatic.github.urls
# Copyright Kevin Deldycke <[email protected]> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""Read a GitHub repository out of the forms that name it.
A repository reaches this package in several spellings: the `owner/name` slug
of `GITHUB_REPOSITORY`, an HTTPS remote, an SSH remote, and a `project_urls`
entry that points into the repository rather than at it. This module is the one
reader of all of them, so every consumer derives the same `(owner, name)` pair
from the same input.
A leaf module with no project-internal imports, so any module can import it
without the risk of an import cycle.
"""
from __future__ import annotations
import re
GITHUB_REPO_RE = re.compile(
r"(?:^|[/@])(?:www\.)?github\.com[:/]+(?P<owner>[^/\s:?#]+)/(?P<name>[^/\s?#]+)"
)
"""A repository on `github.com`, in a URL or in a remote.
The host must start the string or follow a `/` (a URL scheme) or an `@` (an
SSH user), so a host that only ends in `github.com` does not match, and neither
does `api.github.com`, whose first path segment is not an owner. After the host
comes a `/` in a URL, or a `:` in an SSH remote such as
`[email protected]:owner/name.git`. The match stops after the name, so a deep link
like `https://github.com/owner/name/issues/1` reads as its repository.
"""
[docs]
def parse_github_repo(url: str) -> tuple[str, str] | None:
"""Read the `(owner, name)` pair of the GitHub repository *url* points at.
Accepts an HTTPS URL, an SSH remote (`[email protected]:owner/name.git` and
`ssh://[email protected]/owner/name`), and a deep link into the repository
(`https://github.com/owner/name/blob/main/readme.md`). A trailing `.git`,
slash, query or fragment is dropped.
:param url: A URL or a git remote.
:return: The `(owner, name)` pair, or `None` when *url* names no repository
on `github.com`: another host, or a URL with an owner and no name.
"""
match = GITHUB_REPO_RE.search(url.strip())
if match is None:
return None
name = match["name"].removesuffix(".git")
if not name:
return None
return match["owner"], name
[docs]
def split_slug(slug: str) -> tuple[str, str]:
"""Split an `owner/name` slug into its two halves.
:param slug: A repository slug, like the value of `GITHUB_REPOSITORY`.
:return: The `(owner, name)` pair. A slug with no `/` is all name, with an
empty owner.
"""
owner, _, name = slug.rpartition("/")
return owner, name
[docs]
def repo_name(slug: str) -> str:
"""The name half of an `owner/name` slug.
It is also the package name an `owner/name` upstream implies: the inline
`package==X.Y.Z` pins of a workflow name the package by its repository.
:param slug: A repository slug.
:return: The repository name. A slug with no `/` is returned whole.
"""
return split_slug(slug)[1]