@@ -59,8 +59,10 @@ def _github_number(env: Mapping[str, str]) -> int:
5959 return parse_pull_request_number (match .group (1 )) if match else 0
6060
6161
62- def _github_url (number : int , repo : Optional [str ], env : Mapping [str , str ]) -> Optional [str ]:
63- remote_host , remote_path = parse_git_remote (env .get ("BUILDKITE_REPO" ))
62+ def _github_repository_url (
63+ repo : Optional [str ], env : Mapping [str , str ], remote_url : Optional [str ] = None
64+ ) -> Optional [str ]:
65+ remote_host , remote_path = parse_git_remote (env .get ("BUILDKITE_REPO" ) or remote_url )
6466 # config.repo is only ever a bare repository name, so it cannot produce a
6567 # slug on its own; it is kept last for callers that pass a full owner/repo.
6668 repository = env .get ("GITHUB_REPOSITORY" ) or remote_path or repo
@@ -71,20 +73,82 @@ def _github_url(number: int, repo: Optional[str], env: Mapping[str, str]) -> Opt
7173 or (_http_url (f"https://{ remote_host } " ) if remote_host else None )
7274 or "https://github.com"
7375 )
74- return f"{ server } /{ repository .strip ('/' )} /pull/ { number } "
76+ return f"{ server } /{ repository .strip ('/' )} "
7577
7678
77- def _gitlab_url (number : int , repo : Optional [str ], env : Mapping [str , str ]) -> Optional [str ]:
79+ def _gitlab_project_url (
80+ repo : Optional [str ], env : Mapping [str , str ], remote_url : Optional [str ] = None
81+ ) -> Optional [str ]:
7882 project_url = _repository_url (env .get ("CI_PROJECT_URL" ))
7983 if not project_url :
80- remote_host , remote_path = parse_git_remote (env .get ("BUILDKITE_REPO" ))
84+ remote_host , remote_path = parse_git_remote (env .get ("BUILDKITE_REPO" ) or remote_url )
8185 project_path = env .get ("CI_PROJECT_PATH" ) or remote_path or repo
8286 server = (
8387 _http_url (env .get ("CI_SERVER_URL" ))
8488 or (_http_url (f"https://{ remote_host } " ) if remote_host else None )
8589 )
8690 if server and project_path and "/" in project_path :
8791 project_url = f"{ server } /{ project_path .strip ('/' )} "
92+ return project_url
93+
94+
95+ def _bitbucket_repository_url (
96+ repo : Optional [str ], env : Mapping [str , str ], remote_url : Optional [str ] = None
97+ ) -> Optional [str ]:
98+ remote_host , remote_path = parse_git_remote (remote_url )
99+ repository = env .get ("BITBUCKET_REPO_FULL_NAME" ) or remote_path or repo
100+ if not repository or "/" not in repository :
101+ return None
102+ server = (
103+ _http_url (env .get ("BITBUCKET_SERVER_URL" ))
104+ or (_http_url (f"https://{ remote_host } " ) if remote_host else None )
105+ or "https://bitbucket.org"
106+ )
107+ return f"{ server } /{ repository .strip ('/' )} "
108+
109+
110+ def resolve_repository_url (
111+ scm : Optional [str ],
112+ repo : Optional [str ] = None ,
113+ env : Optional [Mapping [str , str ]] = None ,
114+ remote_url : Optional [str ] = None ,
115+ ) -> Optional [str ]:
116+ """Return the web URL of the scanned repository, such as ``https://github.com/owner/repo``.
117+
118+ The CI environment wins, then the checkout's ``origin`` remote. ``None`` means the
119+ owner could not be determined: ``config.repo`` is the Socket repository slug,
120+ which has no owner, so a link built from it alone would point nowhere.
121+ """
122+ environment = env or {}
123+ provider = str (scm or "" ).lower ()
124+ if provider == "github" :
125+ return _github_repository_url (repo , environment , remote_url )
126+ if provider == "gitlab" :
127+ return _gitlab_project_url (repo , environment , remote_url )
128+ if provider == "bitbucket" :
129+ return _bitbucket_repository_url (repo , environment , remote_url )
130+ return None
131+
132+
133+ def repository_owner (repository_url : Optional [str ]) -> str :
134+ """Return everything before the repository name in ``repository_url``'s path.
135+
136+ That is the owner on GitHub and Bitbucket, and the full group path on GitLab,
137+ where projects can be nested under subgroups.
138+ """
139+ if not repository_url :
140+ return ""
141+ path = urlparse (repository_url ).path .strip ("/" )
142+ return path .rsplit ("/" , 1 )[0 ] if "/" in path else ""
143+
144+
145+ def _github_url (number : int , repo : Optional [str ], env : Mapping [str , str ]) -> Optional [str ]:
146+ repository_url = _github_repository_url (repo , env )
147+ return f"{ repository_url } /pull/{ number } " if repository_url else None
148+
149+
150+ def _gitlab_url (number : int , repo : Optional [str ], env : Mapping [str , str ]) -> Optional [str ]:
151+ project_url = _gitlab_project_url (repo , env )
88152 return f"{ project_url } /-/merge_requests/{ number } " if project_url else None
89153
90154
0 commit comments