Skip to content

Commit e6d535c

Browse files
authored
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
1 parent 2541b12 commit e6d535c

13 files changed

+220
-67
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
__test__/_temp
22
_temp/
33
lib/
4-
node_modules/
4+
node_modules/
5+
.vscode/

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ When Git 2.18 or higher is not in your PATH, falls back to the REST API to downl
9797
# config --global --add safe.directory <path>`
9898
# Default: true
9999
set-safe-directory: ''
100+
101+
# The base URL for the GitHub instance that you are trying to clone from, will use
102+
# environment defaults to fetch from the same instance that the workflow is
103+
# running from unless specified. Example URLs are https://github.com or
104+
# https://my-ghes-server.example.com
105+
github-server-url: ''
100106
```
101107
<!-- end usage -->
102108

__test__/git-auth-helper.test.ts

+39-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ let tempHomedir: string
2020
let git: IGitCommandManager & {env: {[key: string]: string}}
2121
let settings: IGitSourceSettings
2222
let sshPath: string
23+
let githubServerUrl: string
2324

2425
describe('git-auth-helper tests', () => {
2526
beforeAll(async () => {
@@ -67,11 +68,18 @@ describe('git-auth-helper tests', () => {
6768
}
6869
})
6970

70-
const configureAuth_configuresAuthHeader =
71-
'configureAuth configures auth header'
72-
it(configureAuth_configuresAuthHeader, async () => {
71+
async function testAuthHeader(
72+
testName: string,
73+
serverUrl: string | undefined = undefined
74+
) {
7375
// Arrange
74-
await setup(configureAuth_configuresAuthHeader)
76+
let expectedServerUrl = 'https://github.com'
77+
if (serverUrl) {
78+
githubServerUrl = serverUrl
79+
expectedServerUrl = githubServerUrl
80+
}
81+
82+
await setup(testName)
7583
expect(settings.authToken).toBeTruthy() // sanity check
7684
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
7785

@@ -88,9 +96,33 @@ describe('git-auth-helper tests', () => {
8896
).toString('base64')
8997
expect(
9098
configContent.indexOf(
91-
`http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}`
99+
`http.${expectedServerUrl}/.extraheader AUTHORIZATION: basic ${basicCredential}`
92100
)
93101
).toBeGreaterThanOrEqual(0)
102+
}
103+
104+
const configureAuth_configuresAuthHeader =
105+
'configureAuth configures auth header'
106+
it(configureAuth_configuresAuthHeader, async () => {
107+
await testAuthHeader(configureAuth_configuresAuthHeader)
108+
})
109+
110+
const configureAuth_AcceptsGitHubServerUrl =
111+
'inject https://my-ghes-server.com as github server url'
112+
it(configureAuth_AcceptsGitHubServerUrl, async () => {
113+
await testAuthHeader(
114+
configureAuth_AcceptsGitHubServerUrl,
115+
'https://my-ghes-server.com'
116+
)
117+
})
118+
119+
const configureAuth_AcceptsGitHubServerUrlSetToGHEC =
120+
'inject https://github.com as github server url'
121+
it(configureAuth_AcceptsGitHubServerUrlSetToGHEC, async () => {
122+
await testAuthHeader(
123+
configureAuth_AcceptsGitHubServerUrl,
124+
'https://github.com'
125+
)
94126
})
95127

96128
const configureAuth_configuresAuthHeaderEvenWhenPersistCredentialsFalse =
@@ -778,7 +810,8 @@ async function setup(testName: string): Promise<void> {
778810
sshKnownHosts: '',
779811
sshStrict: true,
780812
workflowOrganizationId: 123456,
781-
setSafeDirectory: true
813+
setSafeDirectory: true,
814+
githubServerUrl: githubServerUrl
782815
}
783816
}
784817

action.yml

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ inputs:
7171
set-safe-directory:
7272
description: Add repository path as safe.directory for Git global config by running `git config --global --add safe.directory <path>`
7373
default: true
74+
github-server-url:
75+
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
76+
required: false
7477
runs:
7578
using: node16
7679
main: dist/index.js

dist/index.js

+87-27
Original file line numberDiff line numberDiff line change
@@ -1935,13 +1935,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
19351935
return result;
19361936
};
19371937
Object.defineProperty(exports, "__esModule", { value: true });
1938-
exports.getServerUrl = exports.getFetchUrl = void 0;
1938+
exports.isGhes = exports.getServerApiUrl = exports.getServerUrl = exports.getFetchUrl = void 0;
19391939
const assert = __importStar(__webpack_require__(357));
19401940
const url_1 = __webpack_require__(835);
19411941
function getFetchUrl(settings) {
19421942
assert.ok(settings.repositoryOwner, 'settings.repositoryOwner must be defined');
19431943
assert.ok(settings.repositoryName, 'settings.repositoryName must be defined');
1944-
const serviceUrl = getServerUrl();
1944+
const serviceUrl = getServerUrl(settings.githubServerUrl);
19451945
const encodedOwner = encodeURIComponent(settings.repositoryOwner);
19461946
const encodedName = encodeURIComponent(settings.repositoryName);
19471947
if (settings.sshKey) {
@@ -1951,13 +1951,27 @@ function getFetchUrl(settings) {
19511951
return `${serviceUrl.origin}/${encodedOwner}/${encodedName}`;
19521952
}
19531953
exports.getFetchUrl = getFetchUrl;
1954-
function getServerUrl() {
1955-
// todo: remove GITHUB_URL after support for GHES Alpha is no longer needed
1956-
return new url_1.URL(process.env['GITHUB_SERVER_URL'] ||
1957-
process.env['GITHUB_URL'] ||
1958-
'https://github.com');
1954+
function getServerUrl(url) {
1955+
let urlValue = url && url.trim().length > 0
1956+
? url
1957+
: process.env['GITHUB_SERVER_URL'] || 'https://github.com';
1958+
return new url_1.URL(urlValue);
19591959
}
19601960
exports.getServerUrl = getServerUrl;
1961+
function getServerApiUrl(url) {
1962+
let apiUrl = 'https://api.github.com';
1963+
if (isGhes(url)) {
1964+
const serverUrl = getServerUrl(url);
1965+
apiUrl = new url_1.URL(`${serverUrl.origin}/api/v3`).toString();
1966+
}
1967+
return apiUrl;
1968+
}
1969+
exports.getServerApiUrl = getServerApiUrl;
1970+
function isGhes(url) {
1971+
const ghUrl = getServerUrl(url);
1972+
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
1973+
}
1974+
exports.isGhes = isGhes;
19611975

19621976

19631977
/***/ }),
@@ -4066,6 +4080,51 @@ function authenticationPlugin(octokit, options) {
40664080
}
40674081

40684082

4083+
/***/ }),
4084+
4085+
/***/ 195:
4086+
/***/ (function(__unusedmodule, exports, __webpack_require__) {
4087+
4088+
"use strict";
4089+
4090+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4091+
if (k2 === undefined) k2 = k;
4092+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4093+
}) : (function(o, m, k, k2) {
4094+
if (k2 === undefined) k2 = k;
4095+
o[k2] = m[k];
4096+
}));
4097+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
4098+
Object.defineProperty(o, "default", { enumerable: true, value: v });
4099+
}) : function(o, v) {
4100+
o["default"] = v;
4101+
});
4102+
var __importStar = (this && this.__importStar) || function (mod) {
4103+
if (mod && mod.__esModule) return mod;
4104+
var result = {};
4105+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
4106+
__setModuleDefault(result, mod);
4107+
return result;
4108+
};
4109+
Object.defineProperty(exports, "__esModule", { value: true });
4110+
exports.getOctokit = exports.Octokit = void 0;
4111+
const github = __importStar(__webpack_require__(469));
4112+
const url_helper_1 = __webpack_require__(81);
4113+
// Centralize all Octokit references by re-exporting
4114+
var rest_1 = __webpack_require__(0);
4115+
Object.defineProperty(exports, "Octokit", { enumerable: true, get: function () { return rest_1.Octokit; } });
4116+
function getOctokit(authToken, opts) {
4117+
const options = {
4118+
baseUrl: (0, url_helper_1.getServerApiUrl)(opts.baseUrl)
4119+
};
4120+
if (opts.userAgent) {
4121+
options.userAgent = opts.userAgent;
4122+
}
4123+
return new github.GitHub(authToken, options);
4124+
}
4125+
exports.getOctokit = getOctokit;
4126+
4127+
40694128
/***/ }),
40704129

40714130
/***/ 197:
@@ -4279,9 +4338,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42794338
};
42804339
Object.defineProperty(exports, "__esModule", { value: true });
42814340
exports.checkCommitInfo = exports.testRef = exports.getRefSpec = exports.getRefSpecForAllHistory = exports.getCheckoutInfo = exports.tagsRefSpec = void 0;
4282-
const url_1 = __webpack_require__(835);
42834341
const core = __importStar(__webpack_require__(470));
42844342
const github = __importStar(__webpack_require__(469));
4343+
const octokit_provider_1 = __webpack_require__(195);
4344+
const url_helper_1 = __webpack_require__(81);
42854345
exports.tagsRefSpec = '+refs/tags/*:refs/tags/*';
42864346
function getCheckoutInfo(git, ref, commit) {
42874347
return __awaiter(this, void 0, void 0, function* () {
@@ -4431,12 +4491,12 @@ function testRef(git, ref, commit) {
44314491
});
44324492
}
44334493
exports.testRef = testRef;
4434-
function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref, commit) {
4494+
function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref, commit, baseUrl) {
44354495
var _a, _b;
44364496
return __awaiter(this, void 0, void 0, function* () {
44374497
try {
44384498
// GHES?
4439-
if (isGhes()) {
4499+
if ((0, url_helper_1.isGhes)(baseUrl)) {
44404500
return;
44414501
}
44424502
// Auth token?
@@ -4481,7 +4541,8 @@ function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref
44814541
const actualHeadSha = match[1];
44824542
if (actualHeadSha !== expectedHeadSha) {
44834543
core.debug(`Expected head sha ${expectedHeadSha}; actual head sha ${actualHeadSha}`);
4484-
const octokit = new github.GitHub(token, {
4544+
const octokit = (0, octokit_provider_1.getOctokit)(token, {
4545+
baseUrl: baseUrl,
44854546
userAgent: `actions-checkout-tracepoint/1.0 (code=STALE_MERGE;owner=${repositoryOwner};repo=${repositoryName};pr=${fromPayload('number')};run_id=${process.env['GITHUB_RUN_ID']};expected_head_sha=${expectedHeadSha};actual_head_sha=${actualHeadSha})`
44864547
});
44874548
yield octokit.repos.get({ owner: repositoryOwner, repo: repositoryName });
@@ -4507,10 +4568,6 @@ function select(obj, path) {
45074568
const key = path.substr(0, i);
45084569
return select(obj[key], path.substr(i + 1));
45094570
}
4510-
function isGhes() {
4511-
const ghUrl = new url_1.URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
4512-
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
4513-
}
45144571

45154572

45164573
/***/ }),
@@ -6561,7 +6618,7 @@ class GitAuthHelper {
65616618
this.git = gitCommandManager;
65626619
this.settings = gitSourceSettings || {};
65636620
// Token auth header
6564-
const serverUrl = urlHelper.getServerUrl();
6621+
const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl);
65656622
this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader`; // "origin" is SCHEME://HOSTNAME[:PORT]
65666623
const basicCredential = Buffer.from(`x-access-token:${this.settings.authToken}`, 'utf8').toString('base64');
65676624
core.setSecret(basicCredential);
@@ -7382,7 +7439,7 @@ function getSource(settings) {
73827439
else if (settings.sshKey) {
73837440
throw new Error(`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH.`);
73847441
}
7385-
yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
7442+
yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath, settings.githubServerUrl);
73867443
return;
73877444
}
73887445
// Save state for POST action
@@ -7415,7 +7472,7 @@ function getSource(settings) {
74157472
settings.ref = yield git.getDefaultBranch(repositoryUrl);
74167473
}
74177474
else {
7418-
settings.ref = yield githubApiHelper.getDefaultBranch(settings.authToken, settings.repositoryOwner, settings.repositoryName);
7475+
settings.ref = yield githubApiHelper.getDefaultBranch(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.githubServerUrl);
74197476
}
74207477
core.endGroup();
74217478
}
@@ -7481,7 +7538,7 @@ function getSource(settings) {
74817538
// Log commit sha
74827539
yield git.log1("--format='%H'");
74837540
// Check for incorrect pull request merge commit
7484-
yield refHelper.checkCommitInfo(settings.authToken, commitInfo, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit);
7541+
yield refHelper.checkCommitInfo(settings.authToken, commitInfo, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.githubServerUrl);
74857542
}
74867543
finally {
74877544
// Remove auth
@@ -10966,24 +11023,24 @@ exports.getDefaultBranch = exports.downloadRepository = void 0;
1096611023
const assert = __importStar(__webpack_require__(357));
1096711024
const core = __importStar(__webpack_require__(470));
1096811025
const fs = __importStar(__webpack_require__(747));
10969-
const github = __importStar(__webpack_require__(469));
1097011026
const io = __importStar(__webpack_require__(1));
1097111027
const path = __importStar(__webpack_require__(622));
1097211028
const retryHelper = __importStar(__webpack_require__(587));
1097311029
const toolCache = __importStar(__webpack_require__(533));
1097411030
const v4_1 = __importDefault(__webpack_require__(826));
11031+
const octokit_provider_1 = __webpack_require__(195);
1097511032
const IS_WINDOWS = process.platform === 'win32';
10976-
function downloadRepository(authToken, owner, repo, ref, commit, repositoryPath) {
11033+
function downloadRepository(authToken, owner, repo, ref, commit, repositoryPath, baseUrl) {
1097711034
return __awaiter(this, void 0, void 0, function* () {
1097811035
// Determine the default branch
1097911036
if (!ref && !commit) {
1098011037
core.info('Determining the default branch');
10981-
ref = yield getDefaultBranch(authToken, owner, repo);
11038+
ref = yield getDefaultBranch(authToken, owner, repo, baseUrl);
1098211039
}
1098311040
// Download the archive
1098411041
let archiveData = yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
1098511042
core.info('Downloading the archive');
10986-
return yield downloadArchive(authToken, owner, repo, ref, commit);
11043+
return yield downloadArchive(authToken, owner, repo, ref, commit, baseUrl);
1098711044
}));
1098811045
// Write archive to disk
1098911046
core.info('Writing archive to disk');
@@ -11027,12 +11084,12 @@ exports.downloadRepository = downloadRepository;
1102711084
/**
1102811085
* Looks up the default branch name
1102911086
*/
11030-
function getDefaultBranch(authToken, owner, repo) {
11087+
function getDefaultBranch(authToken, owner, repo, baseUrl) {
1103111088
return __awaiter(this, void 0, void 0, function* () {
1103211089
return yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
1103311090
var _a;
1103411091
core.info('Retrieving the default branch name');
11035-
const octokit = new github.GitHub(authToken);
11092+
const octokit = (0, octokit_provider_1.getOctokit)(authToken, { baseUrl: baseUrl });
1103611093
let result;
1103711094
try {
1103811095
// Get the default branch from the repo info
@@ -11062,9 +11119,9 @@ function getDefaultBranch(authToken, owner, repo) {
1106211119
});
1106311120
}
1106411121
exports.getDefaultBranch = getDefaultBranch;
11065-
function downloadArchive(authToken, owner, repo, ref, commit) {
11122+
function downloadArchive(authToken, owner, repo, ref, commit, baseUrl) {
1106611123
return __awaiter(this, void 0, void 0, function* () {
11067-
const octokit = new github.GitHub(authToken);
11124+
const octokit = (0, octokit_provider_1.getOctokit)(authToken, { baseUrl: baseUrl });
1106811125
const params = {
1106911126
owner: owner,
1107011127
repo: repo,
@@ -17330,6 +17387,9 @@ function getInputs() {
1733017387
// Set safe.directory in git global config.
1733117388
result.setSafeDirectory =
1733217389
(core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE';
17390+
// Determine the GitHub URL that the repository is being hosted from
17391+
result.githubServerUrl = core.getInput('github-server-url');
17392+
core.debug(`GitHub Host URL = ${result.githubServerUrl}`);
1733317393
return result;
1733417394
});
1733517395
}

src/git-auth-helper.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class GitAuthHelper {
5252
this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings)
5353

5454
// Token auth header
55-
const serverUrl = urlHelper.getServerUrl()
55+
const serverUrl = urlHelper.getServerUrl(this.settings.githubServerUrl)
5656
this.tokenConfigKey = `http.${serverUrl.origin}/.extraheader` // "origin" is SCHEME://HOSTNAME[:PORT]
5757
const basicCredential = Buffer.from(
5858
`x-access-token:${this.settings.authToken}`,

src/git-source-provider.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
9393
settings.repositoryName,
9494
settings.ref,
9595
settings.commit,
96-
settings.repositoryPath
96+
settings.repositoryPath,
97+
settings.githubServerUrl
9798
)
9899
return
99100
}
@@ -138,7 +139,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
138139
settings.ref = await githubApiHelper.getDefaultBranch(
139140
settings.authToken,
140141
settings.repositoryOwner,
141-
settings.repositoryName
142+
settings.repositoryName,
143+
settings.githubServerUrl
142144
)
143145
}
144146
core.endGroup()
@@ -232,7 +234,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
232234
settings.repositoryOwner,
233235
settings.repositoryName,
234236
settings.ref,
235-
settings.commit
237+
settings.commit,
238+
settings.githubServerUrl
236239
)
237240
} finally {
238241
// Remove auth

0 commit comments

Comments
 (0)