From 2c60f3517609825873003cde0d4367c6cc2cd74c Mon Sep 17 00:00:00 2001 From: jiriks74 Date: Sun, 15 Dec 2024 19:23:00 +0100 Subject: [PATCH] feat: Handle API URLs for Forgejo, Gitea, GitLab and GitHub --- dist/index.js | 40 ++++++++++++++++++++++++++++-------- src/create-pull-request.ts | 6 ++++-- src/github-helper.ts | 42 ++++++++++++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/dist/index.js b/dist/index.js index 8cbfb72..3665fd4 100644 --- a/dist/index.js +++ b/dist/index.js @@ -405,8 +405,10 @@ function createPullRequest(inputs) { core.startGroup('Determining the base and head repositories'); const baseRemote = gitConfigHelper.getGitRemote(); // Init the GitHub clients - const ghBranch = new github_helper_1.GitHubHelper(baseRemote.hostname, inputs.branchToken); - const ghPull = new github_helper_1.GitHubHelper(baseRemote.hostname, inputs.token); + const apiUrl = yield github_helper_1.GitHubHelper.determineApiPath(baseRemote.hostname); + core.info(`Using API base URL: ${apiUrl}`); + const ghBranch = new github_helper_1.GitHubHelper(apiUrl, inputs.branchToken); + const ghPull = new github_helper_1.GitHubHelper(apiUrl, inputs.token); // Determine the head repository; the target for the pull request branch const branchRemoteName = inputs.pushToFork ? 'fork' : 'origin'; const branchRepository = inputs.pushToFork @@ -1278,20 +1280,40 @@ const ERROR_PR_REVIEW_TOKEN_SCOPE = 'Validation Failed: "Could not resolve to a const ERROR_PR_FORK_COLLAB = `Fork collab can't be granted by someone without permission`; const blobCreationLimit = (0, p_limit_1.default)(8); class GitHubHelper { - constructor(githubServerHostname, token) { + constructor(apiUrl, token) { const options = {}; if (token) { options.auth = `${token}`; } - if (githubServerHostname !== 'github.com') { - options.baseUrl = `https://${githubServerHostname}/api/v1`; - } - else { - options.baseUrl = 'https://api.github.com'; - } + options.baseUrl = apiUrl; options.throttle = octokit_client_1.throttleOptions; this.octokit = new octokit_client_1.Octokit(options); } + static determineApiPath(hostname) { + return __awaiter(this, void 0, void 0, function* () { + if (hostname === 'github.com') { + return "https://api.github.com"; + } + const baseUrl = `https://${hostname}`; + const possiblePaths = ['/api/v4/version', '/api/forgejo/v1/version', '/api/v1/version']; + for (const path of possiblePaths) { + try { + const url = `${baseUrl}${path}`; + const response = yield fetch(url, { method: 'GET', redirect: 'manual' }); + const contentType = response.headers.get('Content-Type') || ''; + if ((response.ok || [401, 403].includes(response.status)) && // Check for valid API status codes + contentType.includes('application/json') // Ensure it's returning JSON + ) { + return path.includes('/version') ? url.replace('/version', '') : url; + } + } + catch (error) { + // Ignore errors and try the next path + } + } + throw new Error(`Unable to determine API base URL for hostname: ${hostname}`); + }); + } parseRepository(repository) { const [owner, repo] = repository.split('/'); return { diff --git a/src/create-pull-request.ts b/src/create-pull-request.ts index 9076b7d..a8971a7 100644 --- a/src/create-pull-request.ts +++ b/src/create-pull-request.ts @@ -51,8 +51,10 @@ export async function createPullRequest(inputs: Inputs): Promise { core.startGroup('Determining the base and head repositories') const baseRemote = gitConfigHelper.getGitRemote() // Init the GitHub clients - const ghBranch = new GitHubHelper(baseRemote.hostname, inputs.branchToken) - const ghPull = new GitHubHelper(baseRemote.hostname, inputs.token) + const apiUrl = await GitHubHelper.determineApiUrl(baseRemote.hostname); + core.info(`Using API base URL: ${apiUrl}`); + const ghBranch = new GitHubHelper(apiUrl, inputs.branchToken) + const ghPull = new GitHubHelper(apiUrl, inputs.token) // Determine the head repository; the target for the pull request branch const branchRemoteName = inputs.pushToFork ? 'fork' : 'origin' const branchRepository = inputs.pushToFork diff --git a/src/github-helper.ts b/src/github-helper.ts index 82a9296..c0eb2ac 100644 --- a/src/github-helper.ts +++ b/src/github-helper.ts @@ -41,20 +41,49 @@ type TreeObject = { export class GitHubHelper { private octokit: InstanceType - constructor(githubServerHostname: string, token: string) { + constructor(apiUrl: string, token: string) { const options: OctokitOptions = {} if (token) { options.auth = `${token}` } - if (githubServerHostname !== 'github.com') { - options.baseUrl = `https://${githubServerHostname}/api/v3` - } else { - options.baseUrl = 'https://api.github.com' - } + options.baseUrl = apiUrl; options.throttle = throttleOptions this.octokit = new Octokit(options) } + static async determineApiUrl(hostname: string): Promise { + if (hostname === 'github.com') { + return "https://api.github.com"; + } + + const baseUrl = `https://${hostname}`; + const possiblePaths = ['/api/v4/version', '/api/forgejo/v1/version', '/api/v1/version']; + + for (const path of possiblePaths) { + try { + const url = `${baseUrl}${path}`; + const response = await fetch(url, { method: 'GET', redirect: 'manual' }); // GitLab redirects + // invalid API paths + // to login prompt + // which returns 200 + + const contentType = response.headers.get('Content-Type') || ''; + if ( + (response.ok || [401, 403].includes(response.status)) && // We might get 401, 403 + // as we're unauthorised + contentType.includes('application/json') + ) { + return path.includes('/version') ? url.replace('/version', '') : url; + } + + } catch (error) { + // Ignore errors and try the next path + } + } + + throw new Error(`Unable to determine API base URL for hostname: ${hostname}`); + } + private parseRepository(repository: string): Repository { const [owner, repo] = repository.split('/') return { @@ -403,3 +432,4 @@ export class GitHubHelper { }) } } +