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