From e33cfa574212e8722034156757f8383979e76cb9 Mon Sep 17 00:00:00 2001 From: Peter Murray <681306+peter-murray@users.noreply.github.com> Date: Mon, 26 Sep 2022 17:34:52 +0100 Subject: Inject GitHub host to be able to clone from another GitHub instance (#922) * Adding the ability to specify the GitHub Server URL and allowing for it to differ from the Actions workflow host * Adding tests for injecting the GitHub URL * Addressing code review comments for PR #922 --- src/git-auth-helper.ts | 2 +- src/git-source-provider.ts | 9 ++++++--- src/git-source-settings.ts | 5 +++++ src/github-api-helper.ts | 20 +++++++++++--------- src/input-helper.ts | 5 +++++ src/octokit-provider.ts | 23 +++++++++++++++++++++++ src/ref-helper.ts | 18 +++++++----------- src/url-helper.ts | 34 +++++++++++++++++++++++++--------- 8 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 src/octokit-provider.ts (limited to 'src') diff --git a/src/git-auth-helper.ts b/src/git-auth-helper.ts index 8a1c7c3..3c6db8e 100644 --- a/src/git-auth-helper.ts +++ b/src/git-auth-helper.ts @@ -52,7 +52,7 @@ class GitAuthHelper { this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings) // Token auth header - const serverUrl = urlHelper.getServerUrl() + const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl) this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT] const basicCredential = Buffer.from( `x-access-token:${this.settings.authToken}`, diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts index 545a7a3..48f20da 100644 --- a/src/git-source-provider.ts +++ b/src/git-source-provider.ts @@ -93,7 +93,8 @@ export async function getSource(settings: IGitSourceSettings): Promise { settings.repositoryName, settings.ref, settings.commit, - settings.repositoryPath + settings.repositoryPath, + settings.githubServerUrl ) return } @@ -138,7 +139,8 @@ export async function getSource(settings: IGitSourceSettings): Promise { settings.ref = await githubApiHelper.getDefaultBranch( settings.authToken, settings.repositoryOwner, - settings.repositoryName + settings.repositoryName, + settings.githubServerUrl ) } core.endGroup() @@ -232,7 +234,8 @@ export async function getSource(settings: IGitSourceSettings): Promise { settings.repositoryOwner, settings.repositoryName, settings.ref, - settings.commit + settings.commit, + settings.githubServerUrl ) } finally { // Remove auth diff --git a/src/git-source-settings.ts b/src/git-source-settings.ts index 6fa3960..2da5622 100644 --- a/src/git-source-settings.ts +++ b/src/git-source-settings.ts @@ -83,4 +83,9 @@ export interface IGitSourceSettings { * Indicates whether to add repositoryPath as safe.directory in git global config */ setSafeDirectory: boolean + + /** + * User override on the GitHub Server/Host URL that hosts the repository to be cloned + */ + githubServerUrl: string | undefined } diff --git a/src/github-api-helper.ts b/src/github-api-helper.ts index 45b979a..2124a86 100644 --- a/src/github-api-helper.ts +++ b/src/github-api-helper.ts @@ -1,13 +1,12 @@ import * as assert from 'assert' import * as core from '@actions/core' import * as fs from 'fs' -import * as github from '@actions/github' import * as io from '@actions/io' import * as path from 'path' import * as retryHelper from './retry-helper' import * as toolCache from '@actions/tool-cache' import {default as uuid} from 'uuid/v4' -import {Octokit} from '@octokit/rest' +import {getOctokit, Octokit} from './octokit-provider' const IS_WINDOWS = process.platform === 'win32' @@ -17,18 +16,19 @@ export async function downloadRepository( repo: string, ref: string, commit: string, - repositoryPath: string + repositoryPath: string, + baseUrl?: string ): Promise { // Determine the default branch if (!ref && !commit) { core.info('Determining the default branch') - ref = await getDefaultBranch(authToken, owner, repo) + ref = await getDefaultBranch(authToken, owner, repo, baseUrl) } // Download the archive let archiveData = await retryHelper.execute(async () => { core.info('Downloading the archive') - return await downloadArchive(authToken, owner, repo, ref, commit) + return await downloadArchive(authToken, owner, repo, ref, commit, baseUrl) }) // Write archive to disk @@ -79,11 +79,12 @@ export async function downloadRepository( export async function getDefaultBranch( authToken: string, owner: string, - repo: string + repo: string, + baseUrl?: string ): Promise { return await retryHelper.execute(async () => { core.info('Retrieving the default branch name') - const octokit = new github.GitHub(authToken) + const octokit = getOctokit(authToken, {baseUrl: baseUrl}) let result: string try { // Get the default branch from the repo info @@ -121,9 +122,10 @@ async function downloadArchive( owner: string, repo: string, ref: string, - commit: string + commit: string, + baseUrl?: string ): Promise { - const octokit = new github.GitHub(authToken) + const octokit = getOctokit(authToken, {baseUrl: baseUrl}) const params: Octokit.ReposGetArchiveLinkParams = { owner: owner, repo: repo, diff --git a/src/input-helper.ts b/src/input-helper.ts index 8c2f901..237b06a 100644 --- a/src/input-helper.ts +++ b/src/input-helper.ts @@ -125,5 +125,10 @@ export async function getInputs(): Promise { // Set safe.directory in git global config. result.setSafeDirectory = (core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE' + + // Determine the GitHub URL that the repository is being hosted from + result.githubServerUrl = core.getInput('github-server-url') + core.debug(`GitHub Host URL = ${result.githubServerUrl}`) + return result } diff --git a/src/octokit-provider.ts b/src/octokit-provider.ts new file mode 100644 index 0000000..4fb1285 --- /dev/null +++ b/src/octokit-provider.ts @@ -0,0 +1,23 @@ +import * as github from '@actions/github' +import {Octokit} from '@octokit/rest' +import {getServerApiUrl} from './url-helper' + +// Centralize all Octokit references by re-exporting +export {Octokit} from '@octokit/rest' + +export type OctokitOptions = { + baseUrl?: string + userAgent?: string +} + +export function getOctokit(authToken: string, opts: OctokitOptions) { + const options: Octokit.Options = { + baseUrl: getServerApiUrl(opts.baseUrl) + } + + if (opts.userAgent) { + options.userAgent = opts.userAgent + } + + return new github.GitHub(authToken, options) +} diff --git a/src/ref-helper.ts b/src/ref-helper.ts index 209f49d..ce875fc 100644 --- a/src/ref-helper.ts +++ b/src/ref-helper.ts @@ -1,7 +1,8 @@ -import {URL} from 'url' import {IGitCommandManager} from './git-command-manager' import * as core from '@actions/core' import * as github from '@actions/github' +import {getOctokit} from './octokit-provider' +import {isGhes} from './url-helper' export const tagsRefSpec = '+refs/tags/*:refs/tags/*' @@ -183,11 +184,12 @@ export async function checkCommitInfo( repositoryOwner: string, repositoryName: string, ref: string, - commit: string + commit: string, + baseUrl?: string ): Promise { try { // GHES? - if (isGhes()) { + if (isGhes(baseUrl)) { return } @@ -243,7 +245,8 @@ export async function checkCommitInfo( core.debug( `Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}` ) - const octokit = new github.GitHub(token, { + const octokit = getOctokit(token, { + baseUrl: baseUrl, userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload( 'number' )};run_id=${ @@ -276,10 +279,3 @@ function select(obj: any, path: string): any { const key = path.substr(0, i) return select(obj[key], path.substr(i + 1)) } - -function isGhes(): boolean { - const ghUrl = new URL( - process.env['GITHUB_SERVER_URL'] || 'https://github.com' - ) - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' -} diff --git a/src/url-helper.ts b/src/url-helper.ts index 05f1cbd..6807b7f 100644 --- a/src/url-helper.ts +++ b/src/url-helper.ts @@ -1,6 +1,6 @@ import * as assert from 'assert' -import {IGitSourceSettings} from './git-source-settings' import {URL} from 'url' +import {IGitSourceSettings} from './git-source-settings' export function getFetchUrl(settings: IGitSourceSettings): string { assert.ok( @@ -8,7 +8,7 @@ export function getFetchUrl(settings: IGitSourceSettings): string { 'settings.repositoryOwner must be defined' ) assert.ok(settings.repositoryName, 'settings.repositoryName must be defined') - const serviceUrl = getServerUrl() + const serviceUrl = getServerUrl(settings.githubServerUrl) const encodedOwner = encodeURIComponent(settings.repositoryOwner) const encodedName = encodeURIComponent(settings.repositoryName) if (settings.sshKey) { @@ -19,11 +19,27 @@ export function getFetchUrl(settings: IGitSourceSettings): string { return `${serviceUrl.origin}/${encodedOwner}/${encodedName}` } -export function getServerUrl(): URL { - // todo: remove GITHUB_URL after support for GHES Alpha is no longer needed - return new URL( - process.env['GITHUB_SERVER_URL'] || - process.env['GITHUB_URL'] || - 'https://github.com' - ) +export function getServerUrl(url?: string): URL { + let urlValue = + url && url.trim().length > 0 + ? url + : process.env['GITHUB_SERVER_URL'] || 'https://github.com' + return new URL(urlValue) +} + +export function getServerApiUrl(url?: string): string { + let apiUrl = 'https://api.github.com' + + if (isGhes(url)) { + const serverUrl = getServerUrl(url) + apiUrl = new URL(`${serverUrl.origin}/api/v3`).toString() + } + + return apiUrl +} + +export function isGhes(url?: string): boolean { + const ghUrl = getServerUrl(url) + + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' } -- cgit v1.2.3