Compare commits

..
Author SHA1 Message Date
eric sciple c455122937 . 2020-03-19 00:43:38 -04:00
eric sciple 605b7eb961 . 2020-03-19 00:37:38 -04:00
eric sciple 036772999d . 2020-03-19 00:14:00 -04:00
eric sciple 46054cf00b . 2020-03-18 23:34:02 -04:00
eric sciple 85a425b582 use token for workflow repo 2020-03-18 23:33:26 -04:00
10 changed files with 286 additions and 78 deletions
+3 -1
View File
@@ -725,6 +725,7 @@ async function setup(testName: string): Promise<void> {
setEnvironmentVariable: jest.fn((name: string, value: string) => {
git.env[name] = value
}),
setRemoteUrl: jest.fn(),
submoduleForeach: jest.fn(async () => {
return ''
}),
@@ -748,7 +749,7 @@ async function setup(testName: string): Promise<void> {
}
),
tryDisableAutomaticGarbageCollection: jest.fn(),
tryGetFetchUrl: jest.fn(),
tryGetRemoteUrl: jest.fn(),
tryReset: jest.fn()
}
@@ -757,6 +758,7 @@ async function setup(testName: string): Promise<void> {
clean: true,
commit: '',
fetchDepth: 1,
isWorkflowRepository: true,
lfs: false,
submodules: false,
nestedSubmodules: false,
+57 -18
View File
@@ -7,7 +7,8 @@ import {IGitCommandManager} from '../lib/git-command-manager'
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
let repositoryPath: string
let repositoryUrl: string
let httpsUrl: string
let sshUrl: string
let clean: boolean
let git: IGitCommandManager
@@ -40,7 +41,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -62,7 +64,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -87,7 +90,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -108,7 +112,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -136,7 +141,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -155,14 +161,15 @@ describe('git-directory-helper tests', () => {
await setup(removesContentsWhenDifferentRepositoryUrl)
clean = false
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
const differentRepositoryUrl =
const differentRemoteUrl =
'https://github.com/my-different-org/my-different-repo'
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
differentRepositoryUrl,
differentRemoteUrl,
[differentRemoteUrl],
clean
)
@@ -186,7 +193,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -211,7 +219,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -235,7 +244,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
undefined,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -259,7 +269,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -289,7 +300,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -319,7 +331,8 @@ describe('git-directory-helper tests', () => {
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
repositoryUrl,
httpsUrl,
[httpsUrl, sshUrl],
clean
)
@@ -329,6 +342,30 @@ describe('git-directory-helper tests', () => {
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1')
expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2')
})
const updatesRemoteUrl = 'updates remote URL'
it(updatesRemoteUrl, async () => {
// Arrange
await setup(updatesRemoteUrl)
await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
// Act
await gitDirectoryHelper.prepareExistingDirectory(
git,
repositoryPath,
sshUrl,
[sshUrl, httpsUrl],
clean
)
// Assert
const files = await fs.promises.readdir(repositoryPath)
expect(files.sort()).toEqual(['.git', 'my-file'])
expect(git.isDetached).toHaveBeenCalled()
expect(git.branchList).toHaveBeenCalled()
expect(core.warning).not.toHaveBeenCalled()
expect(git.setRemoteUrl).toHaveBeenCalledWith(sshUrl)
})
})
async function setup(testName: string): Promise<void> {
@@ -338,8 +375,9 @@ async function setup(testName: string): Promise<void> {
repositoryPath = path.join(testWorkspace, testName)
await fs.promises.mkdir(path.join(repositoryPath, '.git'), {recursive: true})
// Repository URL
repositoryUrl = 'https://github.com/my-org/my-repo'
// Remote URLs
httpsUrl = 'https://github.com/my-org/my-repo'
sshUrl = 'git@github.com:my-org/my-repo'
// Clean
clean = true
@@ -365,6 +403,7 @@ async function setup(testName: string): Promise<void> {
remoteAdd: jest.fn(),
removeEnvironmentVariable: jest.fn(),
setEnvironmentVariable: jest.fn(),
setRemoteUrl: jest.fn(),
submoduleForeach: jest.fn(),
submoduleSync: jest.fn(),
submoduleUpdate: jest.fn(),
@@ -374,10 +413,10 @@ async function setup(testName: string): Promise<void> {
}),
tryConfigUnset: jest.fn(),
tryDisableAutomaticGarbageCollection: jest.fn(),
tryGetFetchUrl: jest.fn(async () => {
tryGetRemoteUrl: jest.fn(async () => {
// Sanity check - this function shouldn't be called when the .git directory doesn't exist
await fs.promises.stat(path.join(repositoryPath, '.git'))
return repositoryUrl
return httpsUrl
}),
tryReset: jest.fn(async () => {
return true
+7 -29
View File
@@ -29,29 +29,14 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: >
Personal access token (PAT) used to fetch the repository. The PAT is configured
with the local git config, which enables your scripts to run authenticated git
commands. The post-job step removes the PAT.
We recommend using a service account with the least permissions necessary.
Also when generating a new PAT, select the least scopes necessary.
[Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
commands. The post-job step removes the PAT. [Learn more about creating and using
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
default: ${{ github.token }}
ssh-key:
description: >
The SSH key to use. The SSH key is configured with the local git config, which enables
your scripts to run authenticated git commands. The post-job step removes the SSH key.
When fetching the repository the workflow is running in, the auth token will be used
and configured instead. This enables the SSH key to be used only for submodules.
We recommend using a service account with the least permissions necessary.
[Learn more about creating and using
SSH key used to fetch the repository. SSH key is configured with the local
git config, which enables your scripts to run authenticated git commands.
The post-job step removes the SSH key. [Learn more about creating and using
encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
ssh-known-hosts:
description: >
@@ -59,10 +44,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
SSH keys for a host may be obtained using the utility `ssh-keyscan`. For example,
`ssh-keyscan github.com`. The public key for github.com is always implicitly added.
ssh-strict:
description: >
Whether to perform strict host key checking. When true, adds the options `StrictHostKeyChecking=yes`
and `CheckHostIP=no` to the SSH command line. Use the input `ssh-known-hosts` to
configure additional hosts.
description: 'Whether to perform strict host key checking'
default: true
persist-credentials:
description: 'Whether to configure the token or SSH key with the local git config'
@@ -82,11 +64,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: >
Whether to checkout submodules: `true` to checkout submodules or `recursive` to
recursively checkout submodules.
When the `ssh-key` input is not provided, SSH URLs beginning with `git@github.com:` are
converted to HTTPS.
default: false
default: 'false'
```
Note:
+69 -13
View File
@@ -5225,7 +5225,7 @@ class GitAuthHelper {
}
removeGlobalAuth() {
return __awaiter(this, void 0, void 0, function* () {
core.info(`Unsetting HOME override`);
core.debug(`Unsetting HOME override`);
this.git.removeEnvironmentVariable('HOME');
yield io.rmRF(this.temporaryHomePath);
});
@@ -5581,6 +5581,11 @@ class GitCommandManager {
setEnvironmentVariable(name, value) {
this.gitEnv[name] = value;
}
setRemoteUrl(value) {
return __awaiter(this, void 0, void 0, function* () {
yield this.config('git.remote.url', value);
});
}
submoduleForeach(command, recursive) {
return __awaiter(this, void 0, void 0, function* () {
const args = ['submodule', 'foreach'];
@@ -5643,7 +5648,7 @@ class GitCommandManager {
return output.exitCode === 0;
});
}
tryGetFetchUrl() {
tryGetRemoteUrl() {
return __awaiter(this, void 0, void 0, function* () {
const output = yield this.execGit(['config', '--local', '--get', 'remote.origin.url'], true);
if (output.exitCode !== 0) {
@@ -5800,11 +5805,12 @@ const stateHelper = __importStar(__webpack_require__(153));
const hostname = 'github.com';
function getSource(settings) {
return __awaiter(this, void 0, void 0, function* () {
// Repository URL
core.info(`Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}`);
const repositoryUrl = settings.sshKey
? `git@${hostname}:${encodeURIComponent(settings.repositoryOwner)}/${encodeURIComponent(settings.repositoryName)}.git`
: `https://${hostname}/${encodeURIComponent(settings.repositoryOwner)}/${encodeURIComponent(settings.repositoryName)}`;
// Remote URL
const httpsUrl = `https://${hostname}/${encodeURIComponent(settings.repositoryOwner)}/${encodeURIComponent(settings.repositoryName)}`;
const sshUrl = `git@${hostname}:${encodeURIComponent(settings.repositoryOwner)}/${encodeURIComponent(settings.repositoryName)}.git`;
// Always fetch the workflow repository using the token, not the SSH key
const initialRemoteUrl = !settings.sshKey || settings.isWorkflowRepository ? httpsUrl : sshUrl;
// Remove conflicting file path
if (fsHelper.fileExistsSync(settings.repositoryPath)) {
yield io.rmRF(settings.repositoryPath);
@@ -5816,10 +5822,12 @@ function getSource(settings) {
yield io.mkdirP(settings.repositoryPath);
}
// Git command manager
core.startGroup('Getting Git version info');
const git = yield getGitCommandManager(settings);
core.endGroup();
// Prepare existing directory, otherwise recreate
if (isExisting) {
yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean);
yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, initialRemoteUrl, [httpsUrl, sshUrl], settings.clean);
}
if (!git) {
// Downloading using REST API
@@ -5838,46 +5846,72 @@ function getSource(settings) {
stateHelper.setRepositoryPath(settings.repositoryPath);
// Initialize the repository
if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) {
core.startGroup('Initializing the repository');
yield git.init();
yield git.remoteAdd('origin', repositoryUrl);
yield git.remoteAdd('origin', initialRemoteUrl);
core.endGroup();
}
// Disable automatic garbage collection
core.startGroup('Disabling automatic garbage collection');
if (!(yield git.tryDisableAutomaticGarbageCollection())) {
core.warning(`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`);
}
core.endGroup();
const authHelper = gitAuthHelper.createAuthHelper(git, settings);
try {
// Configure auth
core.startGroup('Setting up auth');
yield authHelper.configureAuth();
core.endGroup();
// LFS install
if (settings.lfs) {
yield git.lfsInstall();
}
// Fetch
core.startGroup('Fetching the repository');
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
yield git.fetch(settings.fetchDepth, refSpec);
core.endGroup();
// Checkout info
core.startGroup('Determining the checkout info');
const checkoutInfo = yield refHelper.getCheckoutInfo(git, settings.ref, settings.commit);
core.endGroup();
// LFS fetch
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
// Explicit lfs fetch will fetch lfs objects in parallel.
if (settings.lfs) {
core.startGroup('Fetching LFS objects');
yield git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref);
core.endGroup();
}
// Fix URL when using SSH
if (settings.sshKey && initialRemoteUrl !== sshUrl) {
core.startGroup('Updating the remote URL');
yield git.setRemoteUrl(sshUrl);
core.endGroup();
}
// Checkout
core.startGroup('Checking out the ref');
yield git.checkout(checkoutInfo.ref, checkoutInfo.startPoint);
core.endGroup();
// Submodules
if (settings.submodules) {
try {
// Temporarily override global config
core.startGroup('Setting up auth for fetching submodules');
yield authHelper.configureGlobalAuth();
core.endGroup();
// Checkout submodules
core.startGroup('Fetching submodules');
yield git.submoduleSync(settings.nestedSubmodules);
yield git.submoduleUpdate(settings.fetchDepth, settings.nestedSubmodules);
yield git.submoduleForeach('git config --local gc.auto 0', settings.nestedSubmodules);
core.endGroup();
// Persist credentials
if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules');
yield authHelper.configureSubmoduleAuth();
core.endGroup();
}
}
finally {
@@ -5891,7 +5925,9 @@ function getSource(settings) {
finally {
// Remove auth
if (!settings.persistCredentials) {
core.startGroup('Removing auth');
yield authHelper.removeAuth();
core.endGroup();
}
}
});
@@ -7191,21 +7227,29 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const assert = __importStar(__webpack_require__(357));
const core = __importStar(__webpack_require__(470));
const fs = __importStar(__webpack_require__(747));
const fsHelper = __importStar(__webpack_require__(618));
const io = __importStar(__webpack_require__(1));
const path = __importStar(__webpack_require__(622));
function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
function prepareExistingDirectory(git, repositoryPath, preferredRemoteUrl, allowedRemoteUrls, clean) {
return __awaiter(this, void 0, void 0, function* () {
assert.ok(repositoryPath, 'Expected repositoryPath to be defined');
assert.ok(preferredRemoteUrl, 'Expected preferredRemoteUrl to be defined');
assert.ok(allowedRemoteUrls, 'Expected allowedRemoteUrls to be defined');
assert.ok(allowedRemoteUrls.length, 'Expected allowedRemoteUrls to have at least one value');
// Indicates whether to delete the directory contents
let remove = false;
// The remote URL
let remoteUrl;
// Check whether using git or REST API
if (!git) {
remove = true;
}
// Fetch URL does not match
else if (!fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) ||
repositoryUrl !== (yield git.tryGetFetchUrl())) {
allowedRemoteUrls.indexOf((remoteUrl = yield git.tryGetRemoteUrl())) < 0) {
remove = true;
}
else {
@@ -7223,6 +7267,7 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
}
}
try {
core.startGroup('Removing previously created refs, to avoid conflicts');
// Checkout detached HEAD
if (!(yield git.isDetached())) {
yield git.checkoutDetach();
@@ -7237,8 +7282,10 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
for (const branch of branches) {
yield git.branchDelete(true, branch);
}
core.endGroup();
// Clean
if (clean) {
core.startGroup('Cleaning the repository');
if (!(yield git.tryClean())) {
core.debug(`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`);
remove = true;
@@ -7246,10 +7293,17 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
else if (!(yield git.tryReset())) {
remove = true;
}
core.endGroup();
if (remove) {
core.warning(`Unable to clean or reset the repository. The repository will be recreated instead.`);
}
}
// Update to the preferred remote URL
if (remoteUrl !== preferredRemoteUrl) {
core.startGroup('Updating the remote URL');
yield git.setRemoteUrl(preferredRemoteUrl);
core.endGroup();
}
}
catch (error) {
core.warning(`Unable to prepare the existing repository. The repository will be recreated instead.`);
@@ -13989,6 +14043,7 @@ const core = __importStar(__webpack_require__(470));
const fsHelper = __importStar(__webpack_require__(618));
const github = __importStar(__webpack_require__(469));
const path = __importStar(__webpack_require__(622));
const hostname = 'github.com';
function getInputs() {
const result = {};
// GitHub workspace
@@ -14018,12 +14073,13 @@ function getInputs() {
throw new Error(`Repository path '${result.repositoryPath}' is not under '${githubWorkspacePath}'`);
}
// Workflow repository?
const isWorkflowRepository = qualifiedRepository.toUpperCase() ===
`${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase();
result.isWorkflowRepository =
qualifiedRepository.toUpperCase() ===
`${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase();
// Source branch, source version
result.ref = core.getInput('ref');
if (!result.ref) {
if (isWorkflowRepository) {
if (result.isWorkflowRepository) {
result.ref = github.context.ref;
result.commit = github.context.sha;
// Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event),
+1 -1
View File
@@ -167,7 +167,7 @@ class GitAuthHelper {
}
async removeGlobalAuth(): Promise<void> {
core.info(`Unsetting HOME override`)
core.debug(`Unsetting HOME override`)
this.git.removeEnvironmentVariable('HOME')
await io.rmRF(this.temporaryHomePath)
}
+7 -2
View File
@@ -33,6 +33,7 @@ export interface IGitCommandManager {
remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
removeEnvironmentVariable(name: string): void
setEnvironmentVariable(name: string, value: string): void
setRemoteUrl(url: string): Promise<void>
submoduleForeach(command: string, recursive: boolean): Promise<string>
submoduleSync(recursive: boolean): Promise<void>
submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
@@ -40,7 +41,7 @@ export interface IGitCommandManager {
tryClean(): Promise<boolean>
tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
tryDisableAutomaticGarbageCollection(): Promise<boolean>
tryGetFetchUrl(): Promise<string>
tryGetRemoteUrl(): Promise<string>
tryReset(): Promise<boolean>
}
@@ -241,6 +242,10 @@ class GitCommandManager {
this.gitEnv[name] = value
}
async setRemoteUrl(value: string): Promise<void> {
await this.config('git.remote.url', value)
}
async submoduleForeach(command: string, recursive: boolean): Promise<string> {
const args = ['submodule', 'foreach']
if (recursive) {
@@ -309,7 +314,7 @@ class GitCommandManager {
return output.exitCode === 0
}
async tryGetFetchUrl(): Promise<string> {
async tryGetRemoteUrl(): Promise<string> {
const output = await this.execGit(
['config', '--local', '--get', 'remote.origin.url'],
true
+28 -2
View File
@@ -1,18 +1,33 @@
import * as assert from 'assert'
import * as core from '@actions/core'
import * as fs from 'fs'
import * as fsHelper from './fs-helper'
import * as io from '@actions/io'
import * as path from 'path'
import {IGitCommandManager} from './git-command-manager'
import {IGitSourceSettings} from './git-source-settings'
export async function prepareExistingDirectory(
git: IGitCommandManager | undefined,
repositoryPath: string,
repositoryUrl: string,
preferredRemoteUrl: string,
allowedRemoteUrls: string[],
clean: boolean
): Promise<void> {
assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
assert.ok(preferredRemoteUrl, 'Expected preferredRemoteUrl to be defined')
assert.ok(allowedRemoteUrls, 'Expected allowedRemoteUrls to be defined')
assert.ok(
allowedRemoteUrls.length,
'Expected allowedRemoteUrls to have at least one value'
)
// Indicates whether to delete the directory contents
let remove = false
// The remote URL
let remoteUrl: string
// Check whether using git or REST API
if (!git) {
remove = true
@@ -20,7 +35,7 @@ export async function prepareExistingDirectory(
// Fetch URL does not match
else if (
!fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) ||
repositoryUrl !== (await git.tryGetFetchUrl())
allowedRemoteUrls.indexOf((remoteUrl = await git.tryGetRemoteUrl())) < 0
) {
remove = true
} else {
@@ -38,6 +53,7 @@ export async function prepareExistingDirectory(
}
try {
core.startGroup('Removing previously created refs, to avoid conflicts')
// Checkout detached HEAD
if (!(await git.isDetached())) {
await git.checkoutDetach()
@@ -54,9 +70,11 @@ export async function prepareExistingDirectory(
for (const branch of branches) {
await git.branchDelete(true, branch)
}
core.endGroup()
// Clean
if (clean) {
core.startGroup('Cleaning the repository')
if (!(await git.tryClean())) {
core.debug(
`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`
@@ -65,6 +83,7 @@ export async function prepareExistingDirectory(
} else if (!(await git.tryReset())) {
remove = true
}
core.endGroup()
if (remove) {
core.warning(
@@ -72,6 +91,13 @@ export async function prepareExistingDirectory(
)
}
}
// Update to the preferred remote URL
if (remoteUrl !== preferredRemoteUrl) {
core.startGroup('Updating the remote URL')
await git.setRemoteUrl(preferredRemoteUrl)
core.endGroup()
}
} catch (error) {
core.warning(
`Unable to prepare the existing repository. The repository will be recreated instead.`
+46 -10
View File
@@ -14,17 +14,21 @@ import {IGitSourceSettings} from './git-source-settings'
const hostname = 'github.com'
export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Repository URL
core.info(
`Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}`
)
const repositoryUrl = settings.sshKey
? `git@${hostname}:${encodeURIComponent(
settings.repositoryOwner
)}/${encodeURIComponent(settings.repositoryName)}.git`
: `https://${hostname}/${encodeURIComponent(
settings.repositoryOwner
)}/${encodeURIComponent(settings.repositoryName)}`
// Remote URL
const httpsUrl = `https://${hostname}/${encodeURIComponent(
settings.repositoryOwner
)}/${encodeURIComponent(settings.repositoryName)}`
const sshUrl = `git@${hostname}:${encodeURIComponent(
settings.repositoryOwner
)}/${encodeURIComponent(settings.repositoryName)}.git`
// Always fetch the workflow repository using the token, not the SSH key
const initialRemoteUrl =
!settings.sshKey || settings.isWorkflowRepository ? httpsUrl : sshUrl
// Remove conflicting file path
if (fsHelper.fileExistsSync(settings.repositoryPath)) {
@@ -39,14 +43,17 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
}
// Git command manager
core.startGroup('Getting Git version info')
const git = await getGitCommandManager(settings)
core.endGroup()
// Prepare existing directory, otherwise recreate
if (isExisting) {
await gitDirectoryHelper.prepareExistingDirectory(
git,
settings.repositoryPath,
repositoryUrl,
initialRemoteUrl,
[httpsUrl, sshUrl],
settings.clean
)
}
@@ -85,21 +92,27 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
if (
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
) {
core.startGroup('Initializing the repository')
await git.init()
await git.remoteAdd('origin', repositoryUrl)
await git.remoteAdd('origin', initialRemoteUrl)
core.endGroup()
}
// Disable automatic garbage collection
core.startGroup('Disabling automatic garbage collection')
if (!(await git.tryDisableAutomaticGarbageCollection())) {
core.warning(
`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`
)
}
core.endGroup()
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
try {
// Configure auth
core.startGroup('Setting up auth')
await authHelper.configureAuth()
core.endGroup()
// LFS install
if (settings.lfs) {
@@ -107,33 +120,51 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
}
// Fetch
core.startGroup('Fetching the repository')
const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
await git.fetch(settings.fetchDepth, refSpec)
core.endGroup()
// Checkout info
core.startGroup('Determining the checkout info')
const checkoutInfo = await refHelper.getCheckoutInfo(
git,
settings.ref,
settings.commit
)
core.endGroup()
// LFS fetch
// Explicit lfs-fetch to avoid slow checkout (fetches one lfs object at a time).
// Explicit lfs fetch will fetch lfs objects in parallel.
if (settings.lfs) {
core.startGroup('Fetching LFS objects')
await git.lfsFetch(checkoutInfo.startPoint || checkoutInfo.ref)
core.endGroup()
}
// Fix URL when using SSH
if (settings.sshKey && initialRemoteUrl !== sshUrl) {
core.startGroup('Updating the remote URL')
await git.setRemoteUrl(sshUrl)
core.endGroup()
}
// Checkout
core.startGroup('Checking out the ref')
await git.checkout(checkoutInfo.ref, checkoutInfo.startPoint)
core.endGroup()
// Submodules
if (settings.submodules) {
try {
// Temporarily override global config
core.startGroup('Setting up auth for fetching submodules')
await authHelper.configureGlobalAuth()
core.endGroup()
// Checkout submodules
core.startGroup('Fetching submodules')
await git.submoduleSync(settings.nestedSubmodules)
await git.submoduleUpdate(
settings.fetchDepth,
@@ -143,10 +174,13 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
'git config --local gc.auto 0',
settings.nestedSubmodules
)
core.endGroup()
// Persist credentials
if (settings.persistCredentials) {
core.startGroup('Persisting credentials for submodules')
await authHelper.configureSubmoduleAuth()
core.endGroup()
}
} finally {
// Remove temporary global config override
@@ -159,7 +193,9 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
} finally {
// Remove auth
if (!settings.persistCredentials) {
core.startGroup('Removing auth')
await authHelper.removeAuth()
core.endGroup()
}
}
}
+64
View File
@@ -1,17 +1,81 @@
export interface IGitSourceSettings {
/**
* The location on disk where the repository will be placed
*/
repositoryPath: string
/**
* The repository owner
*/
repositoryOwner: string
/**
* The repository name
*/
repositoryName: string
/**
* Indicates whether the repository is main workflow repository
*/
isWorkflowRepository: boolean
/**
* The ref to fetch
*/
ref: string
/**
* The commit to checkout
*/
commit: string
/**
* Indicates whether to clean the repository
*/
clean: boolean
/**
* The depth when fetching
*/
fetchDepth: number
/**
* Indicates whether to fetch LFS objects
*/
lfs: boolean
/**
* Indicates whether to checkout submodules
*/
submodules: boolean
/**
* Indicates whether to recursively checkout submodules
*/
nestedSubmodules: boolean
/**
* The auth token to use when fetching the repository
*/
authToken: string
/**
* The SSH key to configure
*/
sshKey: string
/**
* Additional SSH known hosts
*/
sshKnownHosts: string
/**
* Indicates whether the server must be a known host
*/
sshStrict: boolean
/**
* Indicates whether to persist the credentials on disk to enable scripting authenticated git commands
*/
persistCredentials: boolean
}
+4 -2
View File
@@ -4,6 +4,8 @@ import * as github from '@actions/github'
import * as path from 'path'
import {IGitSourceSettings} from './git-source-settings'
const hostname = 'github.com'
export function getInputs(): IGitSourceSettings {
const result = ({} as unknown) as IGitSourceSettings
@@ -51,14 +53,14 @@ export function getInputs(): IGitSourceSettings {
}
// Workflow repository?
const isWorkflowRepository =
result.isWorkflowRepository =
qualifiedRepository.toUpperCase() ===
`${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase()
// Source branch, source version
result.ref = core.getInput('ref')
if (!result.ref) {
if (isWorkflowRepository) {
if (result.isWorkflowRepository) {
result.ref = github.context.ref
result.commit = github.context.sha