mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-31 11:13:59 +08:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -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}`, | ||||
|   | ||||
| @@ -93,7 +93,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> { | ||||
|         settings.repositoryName, | ||||
|         settings.ref, | ||||
|         settings.commit, | ||||
|         settings.repositoryPath | ||||
|         settings.repositoryPath, | ||||
|         settings.githubServerUrl | ||||
|       ) | ||||
|       return | ||||
|     } | ||||
| @@ -138,7 +139,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> { | ||||
|         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<void> { | ||||
|       settings.repositoryOwner, | ||||
|       settings.repositoryName, | ||||
|       settings.ref, | ||||
|       settings.commit | ||||
|       settings.commit, | ||||
|       settings.githubServerUrl | ||||
|     ) | ||||
|   } finally { | ||||
|     // Remove auth | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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<void> { | ||||
|   // 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<string> { | ||||
|   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<Buffer> { | ||||
|   const octokit = new github.GitHub(authToken) | ||||
|   const octokit = getOctokit(authToken, {baseUrl: baseUrl}) | ||||
|   const params: Octokit.ReposGetArchiveLinkParams = { | ||||
|     owner: owner, | ||||
|     repo: repo, | ||||
|   | ||||
| @@ -125,5 +125,10 @@ export async function getInputs(): Promise<IGitSourceSettings> { | ||||
|   // 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 | ||||
| } | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/octokit-provider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/octokit-provider.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
| @@ -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<void> { | ||||
|   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' | ||||
| } | ||||
|   | ||||
| @@ -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' | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Peter Murray
					Peter Murray