|
1 | 1 | import { execSync } from "node:child_process"; |
| 2 | +import { readFileSync } from "node:fs"; |
2 | 3 | import { |
3 | 4 | configFileName, |
4 | 5 | getWorkersCIBranchName, |
@@ -55,6 +56,165 @@ export function getHeadCommitMessage(): string | undefined { |
55 | 56 | } |
56 | 57 | } |
57 | 58 |
|
| 59 | +function normalizeRepositoryUrl(repositoryUrl: string): string | undefined { |
| 60 | + const trimmed = repositoryUrl.trim(); |
| 61 | + if (!trimmed) { |
| 62 | + return undefined; |
| 63 | + } |
| 64 | + |
| 65 | + const scpLikeSshMatch = trimmed.match(/^git@([^:]+):(.+)$/); |
| 66 | + if (scpLikeSshMatch) { |
| 67 | + const [, host, pathname] = scpLikeSshMatch; |
| 68 | + return `https://${host}/${pathname.replace(/\.git$/, "")}`; |
| 69 | + } |
| 70 | + |
| 71 | + try { |
| 72 | + const url = new URL(trimmed); |
| 73 | + if (url.protocol === "ssh:" && url.username === "git") { |
| 74 | + return `https://${url.host}${url.pathname.replace(/\.git$/, "")}`; |
| 75 | + } |
| 76 | + |
| 77 | + if (url.protocol !== "https:" && url.protocol !== "http:") { |
| 78 | + return undefined; |
| 79 | + } |
| 80 | + |
| 81 | + url.username = ""; |
| 82 | + url.password = ""; |
| 83 | + url.search = ""; |
| 84 | + url.hash = ""; |
| 85 | + url.pathname = url.pathname.replace(/\.git$/, ""); |
| 86 | + return url.toString().replace(/\/$/, ""); |
| 87 | + } catch { |
| 88 | + return undefined; |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +export function getRepositoryUrl(): string | undefined { |
| 93 | + const repositoryUrl = |
| 94 | + process.env.CI_PROJECT_URL ?? |
| 95 | + process.env.CI_REPOSITORY_URL ?? |
| 96 | + process.env.CIRCLE_REPOSITORY_URL ?? |
| 97 | + process.env.BUILDKITE_REPO ?? |
| 98 | + process.env.BITBUCKET_GIT_HTTP_ORIGIN ?? |
| 99 | + process.env.BITBUCKET_GIT_SSH_ORIGIN ?? |
| 100 | + process.env.REPOSITORY_URL; |
| 101 | + if (repositoryUrl) { |
| 102 | + return normalizeRepositoryUrl(repositoryUrl); |
| 103 | + } |
| 104 | + |
| 105 | + if (process.env.GITHUB_REPOSITORY) { |
| 106 | + const githubServerUrl = process.env.GITHUB_SERVER_URL ?? "https://github.com"; |
| 107 | + return normalizeRepositoryUrl( |
| 108 | + `${githubServerUrl.replace(/\/$/, "")}/${process.env.GITHUB_REPOSITORY}` |
| 109 | + ); |
| 110 | + } |
| 111 | + |
| 112 | + try { |
| 113 | + execSync(`git rev-parse --is-inside-work-tree`, { stdio: "ignore" }); |
| 114 | + return normalizeRepositoryUrl( |
| 115 | + execSync(`git config --get remote.origin.url`).toString() |
| 116 | + ); |
| 117 | + } catch { |
| 118 | + return undefined; |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +export type PullRequestMetadata = { |
| 123 | + number?: string; |
| 124 | + url?: string; |
| 125 | +}; |
| 126 | + |
| 127 | +function normalizePullRequestNumber(number: string | number | undefined) { |
| 128 | + if (number === undefined) { |
| 129 | + return undefined; |
| 130 | + } |
| 131 | + |
| 132 | + const normalizedNumber = String(number).trim(); |
| 133 | + return normalizedNumber ? normalizedNumber : undefined; |
| 134 | +} |
| 135 | + |
| 136 | +function getGitHubPullRequestMetadata(): PullRequestMetadata | undefined { |
| 137 | + if (process.env.GITHUB_EVENT_PATH) { |
| 138 | + try { |
| 139 | + const event = JSON.parse( |
| 140 | + readFileSync(process.env.GITHUB_EVENT_PATH, "utf8") |
| 141 | + ) as { |
| 142 | + pull_request?: { html_url?: string; number?: number }; |
| 143 | + }; |
| 144 | + const number = normalizePullRequestNumber(event.pull_request?.number); |
| 145 | + const url = event.pull_request?.html_url |
| 146 | + ? normalizeRepositoryUrl(event.pull_request.html_url) |
| 147 | + : undefined; |
| 148 | + if (number || url) { |
| 149 | + return { number, url }; |
| 150 | + } |
| 151 | + } catch { |
| 152 | + // Fall back to environment-derived metadata below. |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + const refPullRequestNumber = process.env.GITHUB_REF?.match( |
| 157 | + /^refs\/pull\/(\d+)\// |
| 158 | + )?.[1]; |
| 159 | + const number = normalizePullRequestNumber(refPullRequestNumber); |
| 160 | + if (!number || !process.env.GITHUB_REPOSITORY) { |
| 161 | + return undefined; |
| 162 | + } |
| 163 | + |
| 164 | + const githubServerUrl = process.env.GITHUB_SERVER_URL ?? "https://github.com"; |
| 165 | + return { |
| 166 | + number, |
| 167 | + url: normalizeRepositoryUrl( |
| 168 | + `${githubServerUrl.replace(/\/$/, "")}/${process.env.GITHUB_REPOSITORY}/pull/${number}` |
| 169 | + ), |
| 170 | + }; |
| 171 | +} |
| 172 | + |
| 173 | +function getGitLabPullRequestMetadata(): PullRequestMetadata | undefined { |
| 174 | + const number = normalizePullRequestNumber(process.env.CI_MERGE_REQUEST_IID); |
| 175 | + const projectUrl = |
| 176 | + process.env.CI_MERGE_REQUEST_PROJECT_URL ?? process.env.CI_PROJECT_URL; |
| 177 | + if (!number || !projectUrl) { |
| 178 | + return undefined; |
| 179 | + } |
| 180 | + |
| 181 | + const normalizedProjectUrl = projectUrl.replace(/\.git$/, "").replace(/\/$/, ""); |
| 182 | + return { |
| 183 | + number, |
| 184 | + url: normalizeRepositoryUrl( |
| 185 | + `${normalizedProjectUrl}/-/merge_requests/${number}` |
| 186 | + ), |
| 187 | + }; |
| 188 | +} |
| 189 | + |
| 190 | +function getDirectPullRequestMetadata(): PullRequestMetadata | undefined { |
| 191 | + const directUrl = |
| 192 | + process.env.PULL_REQUEST_URL ?? |
| 193 | + process.env.PR_URL ?? |
| 194 | + process.env.CHANGE_URL ?? |
| 195 | + process.env.CIRCLE_PULL_REQUEST; |
| 196 | + const number = normalizePullRequestNumber( |
| 197 | + process.env.PULL_REQUEST_NUMBER ?? |
| 198 | + process.env.PR_NUMBER ?? |
| 199 | + process.env.CHANGE_ID |
| 200 | + ); |
| 201 | + const url = directUrl ? normalizeRepositoryUrl(directUrl) : undefined; |
| 202 | + |
| 203 | + if (number || url) { |
| 204 | + return { number, url }; |
| 205 | + } |
| 206 | + |
| 207 | + return undefined; |
| 208 | +} |
| 209 | + |
| 210 | +export function getPullRequestMetadata(): PullRequestMetadata | undefined { |
| 211 | + return ( |
| 212 | + getDirectPullRequestMetadata() ?? |
| 213 | + getGitHubPullRequestMetadata() ?? |
| 214 | + getGitLabPullRequestMetadata() |
| 215 | + ); |
| 216 | +} |
| 217 | + |
58 | 218 | export function resolveWorkerName( |
59 | 219 | args: { workerName?: string; "worker-name"?: string }, |
60 | 220 | config: Config |
|
0 commit comments