mirror of
https://github.com/actions/checkout.git
synced 2026-05-23 20:40:15 +00:00
Fix checkout init for SHA-256 repositories
This commit is contained in:
@@ -318,6 +318,39 @@ describe('git-auth-helper tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const configureSshCommand_doesNotPersistSshCommand =
|
||||||
|
'configureSshCommand does not persist SSH command'
|
||||||
|
it(configureSshCommand_doesNotPersistSshCommand, async () => {
|
||||||
|
if (!sshPath) {
|
||||||
|
process.stdout.write(
|
||||||
|
`Skipped test "${configureSshCommand_doesNotPersistSshCommand}". Executable 'ssh' not found in the PATH.\n`
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
await setup(configureSshCommand_doesNotPersistSshCommand)
|
||||||
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await authHelper.configureSshCommand()
|
||||||
|
|
||||||
|
// Assert git env var
|
||||||
|
expect(git.setEnvironmentVariable).toHaveBeenCalledWith(
|
||||||
|
'GIT_SSH_COMMAND',
|
||||||
|
expect.any(String)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Assert the local SSH command was not persisted for the pre-init probe
|
||||||
|
expect(git.config).not.toHaveBeenCalledWith(
|
||||||
|
'core.sshCommand',
|
||||||
|
expect.any(String)
|
||||||
|
)
|
||||||
|
|
||||||
|
await authHelper.removeSshCommand()
|
||||||
|
expect(git.env['GIT_SSH_COMMAND']).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
const configureAuth_writesExplicitKnownHosts = 'writes explicit known hosts'
|
const configureAuth_writesExplicitKnownHosts = 'writes explicit known hosts'
|
||||||
it(configureAuth_writesExplicitKnownHosts, async () => {
|
it(configureAuth_writesExplicitKnownHosts, async () => {
|
||||||
if (!sshPath) {
|
if (!sshPath) {
|
||||||
@@ -1103,6 +1136,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
),
|
),
|
||||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||||
tryGetFetchUrl: jest.fn(),
|
tryGetFetchUrl: jest.fn(),
|
||||||
|
tryGetObjectFormat: jest.fn(async () => ({format: '', succeeded: true})),
|
||||||
tryGetConfigValues: jest.fn(
|
tryGetConfigValues: jest.fn(
|
||||||
async (
|
async (
|
||||||
key: string,
|
key: string,
|
||||||
|
|||||||
@@ -378,6 +378,152 @@ describe('Test fetchDepth and fetchTags options', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('repository object format', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
||||||
|
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects SHA-256 from ls-remote protocol output', async () => {
|
||||||
|
const calls: any[] = []
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
calls.push({args, env: {...options.env}})
|
||||||
|
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.includes('ls-remote')) {
|
||||||
|
options.listeners.stderr(
|
||||||
|
Buffer.from(
|
||||||
|
'packet: git< version 2\npacket: git< object-format=sha256\n'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
const objectFormat = await git.tryGetObjectFormat(
|
||||||
|
'https://github.com/example/repo'
|
||||||
|
)
|
||||||
|
await git.init()
|
||||||
|
|
||||||
|
expect(objectFormat).toEqual({format: 'sha256', succeeded: true})
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'ls-remote',
|
||||||
|
'--quiet',
|
||||||
|
'--exit-code',
|
||||||
|
'--symref',
|
||||||
|
'https://github.com/example/repo',
|
||||||
|
'HEAD'
|
||||||
|
],
|
||||||
|
expect.objectContaining({
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
calls.find(call => call.args.includes('ls-remote')).env.GIT_TRACE_PACKET
|
||||||
|
).toBe('1')
|
||||||
|
expect(
|
||||||
|
calls.find(call => call.args.includes('init')).env.GIT_TRACE_PACKET
|
||||||
|
).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an empty object format when the remote does not advertise one', async () => {
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.includes('ls-remote')) {
|
||||||
|
options.listeners.stderr(Buffer.from('packet: git< version 2\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
git.tryGetObjectFormat('https://github.com/example/repo')
|
||||||
|
).resolves.toEqual({format: '', succeeded: true})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reports failure when object format detection cannot reach the remote', async () => {
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 128
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
git.tryGetObjectFormat('https://github.com/example/repo')
|
||||||
|
).resolves.toEqual({format: '', succeeded: false})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('initializes SHA-256 repositories with the matching object format', async () => {
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
await git.init('sha256')
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
['init', '--object-format=sha256', 'test'],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('initializes SHA-1 repositories with existing default arguments', async () => {
|
||||||
|
mockExec.mockImplementation((path, args, options) => {
|
||||||
|
if (args.includes('version')) {
|
||||||
|
options.listeners.stdout(Buffer.from('git version 2.50.1'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
|
||||||
|
|
||||||
|
git = await commandManager.createCommandManager('test', false, false)
|
||||||
|
|
||||||
|
await git.init('sha1')
|
||||||
|
|
||||||
|
expect(mockExec).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
['init', 'test'],
|
||||||
|
expect.any(Object)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('git user-agent with orchestration ID', () => {
|
describe('git user-agent with orchestration ID', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
|
||||||
|
|||||||
@@ -501,6 +501,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
await fs.promises.stat(path.join(repositoryPath, '.git'))
|
await fs.promises.stat(path.join(repositoryPath, '.git'))
|
||||||
return repositoryUrl
|
return repositoryUrl
|
||||||
}),
|
}),
|
||||||
|
tryGetObjectFormat: jest.fn(async () => ({format: '', succeeded: true})),
|
||||||
tryGetConfigValues: jest.fn(),
|
tryGetConfigValues: jest.fn(),
|
||||||
tryGetConfigKeys: jest.fn(),
|
tryGetConfigKeys: jest.fn(),
|
||||||
tryReset: jest.fn(async () => {
|
tryReset: jest.fn(async () => {
|
||||||
|
|||||||
133
dist/index.js
vendored
133
dist/index.js
vendored
@@ -252,6 +252,11 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
configureSshCommand() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield this.configureSsh(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
configureSubmoduleAuth() {
|
configureSubmoduleAuth() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// Remove possible previous HTTPS instead of SSH
|
// Remove possible previous HTTPS instead of SSH
|
||||||
@@ -313,12 +318,18 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
removeSshCommand() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield this.removeSsh(false);
|
||||||
|
this.git.removeEnvironmentVariable('GIT_SSH_COMMAND');
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Configures SSH authentication by writing the SSH key and known hosts,
|
* Configures SSH authentication by writing the SSH key and known hosts,
|
||||||
* and setting up the GIT_SSH_COMMAND environment variable.
|
* and setting up the GIT_SSH_COMMAND environment variable.
|
||||||
*/
|
*/
|
||||||
configureSsh() {
|
configureSsh() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, arguments, void 0, function* (persistCredentials = this.settings.persistCredentials) {
|
||||||
if (!this.settings.sshKey) {
|
if (!this.settings.sshKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -368,7 +379,7 @@ class GitAuthHelper {
|
|||||||
core.info(`Temporarily overriding GIT_SSH_COMMAND=${this.sshCommand}`);
|
core.info(`Temporarily overriding GIT_SSH_COMMAND=${this.sshCommand}`);
|
||||||
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand);
|
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand);
|
||||||
// Configure core.sshCommand
|
// Configure core.sshCommand
|
||||||
if (this.settings.persistCredentials) {
|
if (persistCredentials) {
|
||||||
yield this.git.config(SSH_COMMAND_KEY, this.sshCommand);
|
yield this.git.config(SSH_COMMAND_KEY, this.sshCommand);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -454,7 +465,7 @@ class GitAuthHelper {
|
|||||||
* known hosts files, and SSH command configurations.
|
* known hosts files, and SSH command configurations.
|
||||||
*/
|
*/
|
||||||
removeSsh() {
|
removeSsh() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, arguments, void 0, function* (removeGitConfig = true) {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
// SSH key
|
// SSH key
|
||||||
const keyPath = this.sshKeyPath || stateHelper.SshKeyPath;
|
const keyPath = this.sshKeyPath || stateHelper.SshKeyPath;
|
||||||
@@ -480,10 +491,12 @@ class GitAuthHelper {
|
|||||||
core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`);
|
core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SSH command
|
if (removeGitConfig) {
|
||||||
core.info('Removing SSH command configuration');
|
// SSH command
|
||||||
yield this.removeGitConfig(SSH_COMMAND_KEY);
|
core.info('Removing SSH command configuration');
|
||||||
yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY);
|
yield this.removeGitConfig(SSH_COMMAND_KEY);
|
||||||
|
yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -896,9 +909,14 @@ class GitCommandManager {
|
|||||||
getWorkingDirectory() {
|
getWorkingDirectory() {
|
||||||
return this.workingDirectory;
|
return this.workingDirectory;
|
||||||
}
|
}
|
||||||
init() {
|
init(objectFormat) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
yield this.execGit(['init', this.workingDirectory]);
|
const args = ['init'];
|
||||||
|
if (objectFormat === 'sha256') {
|
||||||
|
args.push('--object-format=sha256');
|
||||||
|
}
|
||||||
|
args.push(this.workingDirectory);
|
||||||
|
yield this.execGit(args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isDetached() {
|
isDetached() {
|
||||||
@@ -1056,6 +1074,52 @@ class GitCommandManager {
|
|||||||
return stdout;
|
return stdout;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
tryGetObjectFormat(repositoryUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
var _a;
|
||||||
|
let stderr = '';
|
||||||
|
const listeners = {
|
||||||
|
stderr: (data) => {
|
||||||
|
stderr += data.toString();
|
||||||
|
},
|
||||||
|
errline: (data) => {
|
||||||
|
stderr += data.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const existingTracePacket = this.gitEnv['GIT_TRACE_PACKET'];
|
||||||
|
this.gitEnv['GIT_TRACE_PACKET'] = '1';
|
||||||
|
try {
|
||||||
|
const output = yield this.execGit([
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'ls-remote',
|
||||||
|
'--quiet',
|
||||||
|
'--exit-code',
|
||||||
|
'--symref',
|
||||||
|
repositoryUrl,
|
||||||
|
'HEAD'
|
||||||
|
], true, true, listeners);
|
||||||
|
if (output.exitCode !== 0) {
|
||||||
|
core.debug(`Unable to determine repository object format: git ls-remote exited with ${output.exitCode}`);
|
||||||
|
return { format: '', succeeded: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.debug(`Unable to determine repository object format: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
|
||||||
|
return { format: '', succeeded: false };
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (existingTracePacket === undefined) {
|
||||||
|
delete this.gitEnv['GIT_TRACE_PACKET'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.gitEnv['GIT_TRACE_PACKET'] = existingTracePacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const match = stderr.match(/object-format=(sha1|sha256)(?=\s|$)/);
|
||||||
|
return { format: match ? match[1] : '', succeeded: true };
|
||||||
|
});
|
||||||
|
}
|
||||||
tryGetConfigValues(configKey, globalConfig, configFile) {
|
tryGetConfigValues(configKey, globalConfig, configFile) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const args = ['config'];
|
const args = ['config'];
|
||||||
@@ -1449,6 +1513,10 @@ function getSource(settings) {
|
|||||||
const git = yield getGitCommandManager(settings);
|
const git = yield getGitCommandManager(settings);
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
let authHelper = null;
|
let authHelper = null;
|
||||||
|
let didStartConfigureAuth = false;
|
||||||
|
let didConfigureAuth = false;
|
||||||
|
let didConfigureSshCommand = false;
|
||||||
|
let didConfigureGlobalAuth = false;
|
||||||
try {
|
try {
|
||||||
if (git) {
|
if (git) {
|
||||||
authHelper = gitAuthHelper.createAuthHelper(git, settings);
|
authHelper = gitAuthHelper.createAuthHelper(git, settings);
|
||||||
@@ -1465,6 +1533,20 @@ function getSource(settings) {
|
|||||||
stateHelper.setSafeDirectory();
|
stateHelper.setSafeDirectory();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const configureGlobalAuth = () => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (!authHelper || didConfigureGlobalAuth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
yield authHelper.configureGlobalAuth();
|
||||||
|
didConfigureGlobalAuth = true;
|
||||||
|
});
|
||||||
|
const configureSshCommand = () => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (!authHelper || didConfigureSshCommand) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
yield authHelper.configureSshCommand();
|
||||||
|
didConfigureSshCommand = true;
|
||||||
|
});
|
||||||
// Prepare existing directory, otherwise recreate
|
// Prepare existing directory, otherwise recreate
|
||||||
if (isExisting) {
|
if (isExisting) {
|
||||||
yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean, settings.ref);
|
yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean, settings.ref);
|
||||||
@@ -1486,8 +1568,27 @@ function getSource(settings) {
|
|||||||
stateHelper.setRepositoryPath(settings.repositoryPath);
|
stateHelper.setRepositoryPath(settings.repositoryPath);
|
||||||
// Initialize the repository
|
// Initialize the repository
|
||||||
if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) {
|
if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) {
|
||||||
|
core.startGroup('Determining repository object format');
|
||||||
|
let objectFormatResult = yield git.tryGetObjectFormat(repositoryUrl);
|
||||||
|
if (!objectFormatResult.succeeded) {
|
||||||
|
if (settings.sshKey) {
|
||||||
|
yield configureSshCommand();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
yield configureGlobalAuth();
|
||||||
|
}
|
||||||
|
objectFormatResult = yield git.tryGetObjectFormat(repositoryUrl);
|
||||||
|
}
|
||||||
|
if (!objectFormatResult.succeeded) {
|
||||||
|
throw new Error('Unable to determine repository object format');
|
||||||
|
}
|
||||||
|
const objectFormat = objectFormatResult.format;
|
||||||
|
if (objectFormat === 'sha256') {
|
||||||
|
core.info('Detected SHA-256 repository object format');
|
||||||
|
}
|
||||||
|
core.endGroup();
|
||||||
core.startGroup('Initializing the repository');
|
core.startGroup('Initializing the repository');
|
||||||
yield git.init();
|
yield git.init(objectFormat);
|
||||||
yield git.remoteAdd('origin', repositoryUrl);
|
yield git.remoteAdd('origin', repositoryUrl);
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
}
|
}
|
||||||
@@ -1503,7 +1604,9 @@ function getSource(settings) {
|
|||||||
}
|
}
|
||||||
// Configure auth
|
// Configure auth
|
||||||
core.startGroup('Setting up auth');
|
core.startGroup('Setting up auth');
|
||||||
|
didStartConfigureAuth = true;
|
||||||
yield authHelper.configureAuth();
|
yield authHelper.configureAuth();
|
||||||
|
didConfigureAuth = true;
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
// Determine the default branch
|
// Determine the default branch
|
||||||
if (!settings.ref && !settings.commit) {
|
if (!settings.ref && !settings.commit) {
|
||||||
@@ -1599,7 +1702,7 @@ function getSource(settings) {
|
|||||||
if (settings.submodules) {
|
if (settings.submodules) {
|
||||||
// Temporarily override global config
|
// Temporarily override global config
|
||||||
core.startGroup('Setting up auth for fetching submodules');
|
core.startGroup('Setting up auth for fetching submodules');
|
||||||
yield authHelper.configureGlobalAuth();
|
yield configureGlobalAuth();
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
// Checkout submodules
|
// Checkout submodules
|
||||||
core.startGroup('Fetching submodules');
|
core.startGroup('Fetching submodules');
|
||||||
@@ -1625,12 +1728,16 @@ function getSource(settings) {
|
|||||||
finally {
|
finally {
|
||||||
// Remove auth
|
// Remove auth
|
||||||
if (authHelper) {
|
if (authHelper) {
|
||||||
if (!settings.persistCredentials) {
|
if (!settings.persistCredentials ||
|
||||||
|
(didStartConfigureAuth && !didConfigureAuth)) {
|
||||||
core.startGroup('Removing auth');
|
core.startGroup('Removing auth');
|
||||||
yield authHelper.removeAuth();
|
yield authHelper.removeAuth();
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
}
|
}
|
||||||
authHelper.removeGlobalConfig();
|
else if (didConfigureSshCommand && !didConfigureAuth) {
|
||||||
|
yield authHelper.removeSshCommand();
|
||||||
|
}
|
||||||
|
yield authHelper.removeGlobalConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ const SSH_COMMAND_KEY = 'core.sshCommand'
|
|||||||
export interface IGitAuthHelper {
|
export interface IGitAuthHelper {
|
||||||
configureAuth(): Promise<void>
|
configureAuth(): Promise<void>
|
||||||
configureGlobalAuth(): Promise<void>
|
configureGlobalAuth(): Promise<void>
|
||||||
|
configureSshCommand(): Promise<void>
|
||||||
configureSubmoduleAuth(): Promise<void>
|
configureSubmoduleAuth(): Promise<void>
|
||||||
configureTempGlobalConfig(): Promise<string>
|
configureTempGlobalConfig(): Promise<string>
|
||||||
removeAuth(): Promise<void>
|
removeAuth(): Promise<void>
|
||||||
removeGlobalConfig(): Promise<void>
|
removeGlobalConfig(): Promise<void>
|
||||||
|
removeSshCommand(): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAuthHelper(
|
export function createAuthHelper(
|
||||||
@@ -154,6 +156,10 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async configureSshCommand(): Promise<void> {
|
||||||
|
await this.configureSsh(false)
|
||||||
|
}
|
||||||
|
|
||||||
async configureSubmoduleAuth(): Promise<void> {
|
async configureSubmoduleAuth(): Promise<void> {
|
||||||
// Remove possible previous HTTPS instead of SSH
|
// Remove possible previous HTTPS instead of SSH
|
||||||
await this.removeSubmoduleGitConfig(this.insteadOfKey)
|
await this.removeSubmoduleGitConfig(this.insteadOfKey)
|
||||||
@@ -243,11 +249,18 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async removeSshCommand(): Promise<void> {
|
||||||
|
await this.removeSsh(false)
|
||||||
|
this.git.removeEnvironmentVariable('GIT_SSH_COMMAND')
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures SSH authentication by writing the SSH key and known hosts,
|
* Configures SSH authentication by writing the SSH key and known hosts,
|
||||||
* and setting up the GIT_SSH_COMMAND environment variable.
|
* and setting up the GIT_SSH_COMMAND environment variable.
|
||||||
*/
|
*/
|
||||||
private async configureSsh(): Promise<void> {
|
private async configureSsh(
|
||||||
|
persistCredentials = this.settings.persistCredentials
|
||||||
|
): Promise<void> {
|
||||||
if (!this.settings.sshKey) {
|
if (!this.settings.sshKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -313,7 +326,7 @@ class GitAuthHelper {
|
|||||||
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand)
|
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand)
|
||||||
|
|
||||||
// Configure core.sshCommand
|
// Configure core.sshCommand
|
||||||
if (this.settings.persistCredentials) {
|
if (persistCredentials) {
|
||||||
await this.git.config(SSH_COMMAND_KEY, this.sshCommand)
|
await this.git.config(SSH_COMMAND_KEY, this.sshCommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,7 +446,7 @@ class GitAuthHelper {
|
|||||||
* Removes SSH authentication configuration by cleaning up SSH keys,
|
* Removes SSH authentication configuration by cleaning up SSH keys,
|
||||||
* known hosts files, and SSH command configurations.
|
* known hosts files, and SSH command configurations.
|
||||||
*/
|
*/
|
||||||
private async removeSsh(): Promise<void> {
|
private async removeSsh(removeGitConfig = true): Promise<void> {
|
||||||
// SSH key
|
// SSH key
|
||||||
const keyPath = this.sshKeyPath || stateHelper.SshKeyPath
|
const keyPath = this.sshKeyPath || stateHelper.SshKeyPath
|
||||||
if (keyPath) {
|
if (keyPath) {
|
||||||
@@ -459,10 +472,12 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSH command
|
if (removeGitConfig) {
|
||||||
core.info('Removing SSH command configuration')
|
// SSH command
|
||||||
await this.removeGitConfig(SSH_COMMAND_KEY)
|
core.info('Removing SSH command configuration')
|
||||||
await this.removeSubmoduleGitConfig(SSH_COMMAND_KEY)
|
await this.removeGitConfig(SSH_COMMAND_KEY)
|
||||||
|
await this.removeSubmoduleGitConfig(SSH_COMMAND_KEY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ import {GitVersion} from './git-version'
|
|||||||
export const MinimumGitVersion = new GitVersion('2.18')
|
export const MinimumGitVersion = new GitVersion('2.18')
|
||||||
export const MinimumGitSparseCheckoutVersion = new GitVersion('2.28')
|
export const MinimumGitSparseCheckoutVersion = new GitVersion('2.28')
|
||||||
|
|
||||||
|
export interface GitObjectFormatResult {
|
||||||
|
format: string
|
||||||
|
succeeded: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface IGitCommandManager {
|
export interface IGitCommandManager {
|
||||||
branchDelete(remote: boolean, branch: string): Promise<void>
|
branchDelete(remote: boolean, branch: string): Promise<void>
|
||||||
branchExists(remote: boolean, pattern: string): Promise<boolean>
|
branchExists(remote: boolean, pattern: string): Promise<boolean>
|
||||||
@@ -43,7 +48,7 @@ export interface IGitCommandManager {
|
|||||||
getDefaultBranch(repositoryUrl: string): Promise<string>
|
getDefaultBranch(repositoryUrl: string): Promise<string>
|
||||||
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
|
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
|
||||||
getWorkingDirectory(): string
|
getWorkingDirectory(): string
|
||||||
init(): Promise<void>
|
init(objectFormat?: string): Promise<void>
|
||||||
isDetached(): Promise<boolean>
|
isDetached(): Promise<boolean>
|
||||||
lfsFetch(ref: string): Promise<void>
|
lfsFetch(ref: string): Promise<void>
|
||||||
lfsInstall(): Promise<void>
|
lfsInstall(): Promise<void>
|
||||||
@@ -68,6 +73,7 @@ export interface IGitCommandManager {
|
|||||||
): Promise<boolean>
|
): Promise<boolean>
|
||||||
tryDisableAutomaticGarbageCollection(): Promise<boolean>
|
tryDisableAutomaticGarbageCollection(): Promise<boolean>
|
||||||
tryGetFetchUrl(): Promise<string>
|
tryGetFetchUrl(): Promise<string>
|
||||||
|
tryGetObjectFormat(repositoryUrl: string): Promise<GitObjectFormatResult>
|
||||||
tryGetConfigValues(
|
tryGetConfigValues(
|
||||||
configKey: string,
|
configKey: string,
|
||||||
globalConfig?: boolean,
|
globalConfig?: boolean,
|
||||||
@@ -364,8 +370,14 @@ class GitCommandManager {
|
|||||||
return this.workingDirectory
|
return this.workingDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(objectFormat?: string): Promise<void> {
|
||||||
await this.execGit(['init', this.workingDirectory])
|
const args = ['init']
|
||||||
|
if (objectFormat === 'sha256') {
|
||||||
|
args.push('--object-format=sha256')
|
||||||
|
}
|
||||||
|
args.push(this.workingDirectory)
|
||||||
|
|
||||||
|
await this.execGit(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
async isDetached(): Promise<boolean> {
|
async isDetached(): Promise<boolean> {
|
||||||
@@ -536,6 +548,61 @@ class GitCommandManager {
|
|||||||
return stdout
|
return stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async tryGetObjectFormat(
|
||||||
|
repositoryUrl: string
|
||||||
|
): Promise<GitObjectFormatResult> {
|
||||||
|
let stderr = ''
|
||||||
|
const listeners = {
|
||||||
|
stderr: (data: Buffer) => {
|
||||||
|
stderr += data.toString()
|
||||||
|
},
|
||||||
|
errline: (data: Buffer) => {
|
||||||
|
stderr += data.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingTracePacket = this.gitEnv['GIT_TRACE_PACKET']
|
||||||
|
this.gitEnv['GIT_TRACE_PACKET'] = '1'
|
||||||
|
try {
|
||||||
|
const output = await this.execGit(
|
||||||
|
[
|
||||||
|
'-c',
|
||||||
|
'protocol.version=2',
|
||||||
|
'ls-remote',
|
||||||
|
'--quiet',
|
||||||
|
'--exit-code',
|
||||||
|
'--symref',
|
||||||
|
repositoryUrl,
|
||||||
|
'HEAD'
|
||||||
|
],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
listeners
|
||||||
|
)
|
||||||
|
|
||||||
|
if (output.exitCode !== 0) {
|
||||||
|
core.debug(
|
||||||
|
`Unable to determine repository object format: git ls-remote exited with ${output.exitCode}`
|
||||||
|
)
|
||||||
|
return {format: '', succeeded: false}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(
|
||||||
|
`Unable to determine repository object format: ${(err as any)?.message ?? err}`
|
||||||
|
)
|
||||||
|
return {format: '', succeeded: false}
|
||||||
|
} finally {
|
||||||
|
if (existingTracePacket === undefined) {
|
||||||
|
delete this.gitEnv['GIT_TRACE_PACKET']
|
||||||
|
} else {
|
||||||
|
this.gitEnv['GIT_TRACE_PACKET'] = existingTracePacket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = stderr.match(/object-format=(sha1|sha256)(?=\s|$)/)
|
||||||
|
return {format: match ? match[1] : '', succeeded: true}
|
||||||
|
}
|
||||||
|
|
||||||
async tryGetConfigValues(
|
async tryGetConfigValues(
|
||||||
configKey: string,
|
configKey: string,
|
||||||
globalConfig?: boolean,
|
globalConfig?: boolean,
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
let authHelper: gitAuthHelper.IGitAuthHelper | null = null
|
let authHelper: gitAuthHelper.IGitAuthHelper | null = null
|
||||||
|
let didStartConfigureAuth = false
|
||||||
|
let didConfigureAuth = false
|
||||||
|
let didConfigureSshCommand = false
|
||||||
|
let didConfigureGlobalAuth = false
|
||||||
try {
|
try {
|
||||||
if (git) {
|
if (git) {
|
||||||
authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
@@ -63,6 +67,24 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const configureGlobalAuth = async () => {
|
||||||
|
if (!authHelper || didConfigureGlobalAuth) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await authHelper.configureGlobalAuth()
|
||||||
|
didConfigureGlobalAuth = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const configureSshCommand = async () => {
|
||||||
|
if (!authHelper || didConfigureSshCommand) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await authHelper.configureSshCommand()
|
||||||
|
didConfigureSshCommand = true
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare existing directory, otherwise recreate
|
// Prepare existing directory, otherwise recreate
|
||||||
if (isExisting) {
|
if (isExisting) {
|
||||||
await gitDirectoryHelper.prepareExistingDirectory(
|
await gitDirectoryHelper.prepareExistingDirectory(
|
||||||
@@ -109,8 +131,27 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
if (
|
if (
|
||||||
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
|
||||||
) {
|
) {
|
||||||
|
core.startGroup('Determining repository object format')
|
||||||
|
let objectFormatResult = await git.tryGetObjectFormat(repositoryUrl)
|
||||||
|
if (!objectFormatResult.succeeded) {
|
||||||
|
if (settings.sshKey) {
|
||||||
|
await configureSshCommand()
|
||||||
|
} else {
|
||||||
|
await configureGlobalAuth()
|
||||||
|
}
|
||||||
|
objectFormatResult = await git.tryGetObjectFormat(repositoryUrl)
|
||||||
|
}
|
||||||
|
if (!objectFormatResult.succeeded) {
|
||||||
|
throw new Error('Unable to determine repository object format')
|
||||||
|
}
|
||||||
|
const objectFormat = objectFormatResult.format
|
||||||
|
if (objectFormat === 'sha256') {
|
||||||
|
core.info('Detected SHA-256 repository object format')
|
||||||
|
}
|
||||||
|
core.endGroup()
|
||||||
|
|
||||||
core.startGroup('Initializing the repository')
|
core.startGroup('Initializing the repository')
|
||||||
await git.init()
|
await git.init(objectFormat)
|
||||||
await git.remoteAdd('origin', repositoryUrl)
|
await git.remoteAdd('origin', repositoryUrl)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
@@ -130,7 +171,9 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
}
|
}
|
||||||
// Configure auth
|
// Configure auth
|
||||||
core.startGroup('Setting up auth')
|
core.startGroup('Setting up auth')
|
||||||
|
didStartConfigureAuth = true
|
||||||
await authHelper.configureAuth()
|
await authHelper.configureAuth()
|
||||||
|
didConfigureAuth = true
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
// Determine the default branch
|
// Determine the default branch
|
||||||
@@ -258,7 +301,7 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
if (settings.submodules) {
|
if (settings.submodules) {
|
||||||
// Temporarily override global config
|
// Temporarily override global config
|
||||||
core.startGroup('Setting up auth for fetching submodules')
|
core.startGroup('Setting up auth for fetching submodules')
|
||||||
await authHelper.configureGlobalAuth()
|
await configureGlobalAuth()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
// Checkout submodules
|
// Checkout submodules
|
||||||
@@ -299,12 +342,17 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
} finally {
|
} finally {
|
||||||
// Remove auth
|
// Remove auth
|
||||||
if (authHelper) {
|
if (authHelper) {
|
||||||
if (!settings.persistCredentials) {
|
if (
|
||||||
|
!settings.persistCredentials ||
|
||||||
|
(didStartConfigureAuth && !didConfigureAuth)
|
||||||
|
) {
|
||||||
core.startGroup('Removing auth')
|
core.startGroup('Removing auth')
|
||||||
await authHelper.removeAuth()
|
await authHelper.removeAuth()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
} else if (didConfigureSshCommand && !didConfigureAuth) {
|
||||||
|
await authHelper.removeSshCommand()
|
||||||
}
|
}
|
||||||
authHelper.removeGlobalConfig()
|
await authHelper.removeGlobalConfig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user