Compare commits

...

3 Commits

@ -49,19 +49,19 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
# with the local git config, which enables your scripts to run authenticated git # with the local git config, which enables your scripts to run authenticated git
# commands. The post-job step removes the PAT. # commands. The post-job step removes the PAT.
# #
# We recommend creating a service account with the least permissions necessary. # We recommend using a service account with the least permissions necessary. Also
# Also when generating a new PAT, select the least scopes necessary. # 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) # [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 }} # Default: ${{ github.token }}
token: '' token: ''
# SSH key used to fetch the repository. SSH key is configured with the local git # SSH key used to fetch the repository. The SSH key is configured with the local
# config, which enables your scripts to run authenticated git commands. The # git config, which enables your scripts to run authenticated git commands. The
# post-job step removes the SSH key. # post-job step removes the SSH key.
# #
# We recommend creating a service account with the least permissions necessary. # We recommend using a service account with the least permissions 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) # [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-key: '' ssh-key: ''

@ -320,6 +320,8 @@ describe('git-auth-helper tests', () => {
).toString() ).toString()
expect(actualSshKeyContent).toBe(settings.sshKey + '\n') expect(actualSshKeyContent).toBe(settings.sshKey + '\n')
if (!isWindows) { if (!isWindows) {
// Assert read/write for user, not group or others.
// Otherwise SSH client will error.
expect((await fs.promises.stat(actualSshKeyPath)).mode & 0o777).toBe( expect((await fs.promises.stat(actualSshKeyPath)).mode & 0o777).toBe(
0o600 0o600
) )
@ -437,15 +439,16 @@ describe('git-auth-helper tests', () => {
} }
) )
const configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeyNotSet = const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeyNotSet =
'configureSubmoduleAuth configures token when persist credentials true and SSH key not set' 'configureSubmoduleAuth configures submodules when persist credentials false and SSH key not set'
it( it(
configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeyNotSet, configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeyNotSet,
async () => { async () => {
// Arrange // Arrange
await setup( await setup(
configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeyNotSet configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeyNotSet
) )
settings.persistCredentials = false
settings.sshKey = '' settings.sshKey = ''
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth() await authHelper.configureAuth()
@ -456,31 +459,30 @@ describe('git-auth-helper tests', () => {
await authHelper.configureSubmoduleAuth() await authHelper.configureSubmoduleAuth()
// Assert // Assert
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) expect(mockSubmoduleForeach).toBeCalledTimes(1)
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( expect(mockSubmoduleForeach.mock.calls[0][0] as string).toMatch(
/unset-all.*insteadOf/ /unset-all.*insteadOf/
) )
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/url.*insteadOf/)
} }
) )
const configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet = const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet =
'configureSubmoduleAuth configures token when persist credentials true and SSH key set' 'configureSubmoduleAuth configures submodules when persist credentials false and SSH key set'
it( it(
configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet, configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet,
async () => { async () => {
if (!sshPath) { if (!sshPath) {
process.stdout.write( process.stdout.write(
`Skipped test "${configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet}". Executable 'ssh' not found in the PATH.\n` `Skipped test "${configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet}". Executable 'ssh' not found in the PATH.\n`
) )
return return
} }
// Arrange // Arrange
await setup( await setup(
configureSubmoduleAuth_configuresTokenWhenPersistCredentialsTrueAndSshKeySet configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsFalseAndSshKeySet
) )
settings.persistCredentials = false
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth() await authHelper.configureAuth()
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any> const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
@ -490,24 +492,23 @@ describe('git-auth-helper tests', () => {
await authHelper.configureSubmoduleAuth() await authHelper.configureSubmoduleAuth()
// Assert // Assert
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2) expect(mockSubmoduleForeach).toHaveBeenCalledTimes(1)
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
/unset-all.*insteadOf/ /unset-all.*insteadOf/
) )
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
} }
) )
const configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse = const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeyNotSet =
'configureSubmoduleAuth does not configure token when persist credentials false' 'configureSubmoduleAuth configures submodules when persist credentials true and SSH key not set'
it( it(
configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse, configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeyNotSet,
async () => { async () => {
// Arrange // Arrange
await setup( await setup(
configureSubmoduleAuth_doesNotConfigureTokenWhenPersistCredentialsFalse configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeyNotSet
) )
settings.persistCredentials = false settings.sshKey = ''
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth() await authHelper.configureAuth()
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any> const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
@ -517,28 +518,30 @@ describe('git-auth-helper tests', () => {
await authHelper.configureSubmoduleAuth() await authHelper.configureSubmoduleAuth()
// Assert // Assert
expect(mockSubmoduleForeach).toBeCalledTimes(1) expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
expect(mockSubmoduleForeach.mock.calls[0][0] as string).toMatch( expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
/unset-all.*insteadOf/ /unset-all.*insteadOf/
) )
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/url.*insteadOf/)
} }
) )
const configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet = const configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet =
'configureSubmoduleAuth does not configure URL insteadOf when persist credentials true and SSH key set' 'configureSubmoduleAuth configures submodules when persist credentials true and SSH key set'
it( it(
configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet, configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet,
async () => { async () => {
if (!sshPath) { if (!sshPath) {
process.stdout.write( process.stdout.write(
`Skipped test "${configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet}". Executable 'ssh' not found in the PATH.\n` `Skipped test "${configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet}". Executable 'ssh' not found in the PATH.\n`
) )
return return
} }
// Arrange // Arrange
await setup( await setup(
configureSubmoduleAuth_doesNotConfigureUrlInsteadOfWhenPersistCredentialsTrueAndSshKeySet configureSubmoduleAuth_configuresSubmodulesWhenPersistCredentialsTrueAndSshKeySet
) )
const authHelper = gitAuthHelper.createAuthHelper(git, settings) const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth() await authHelper.configureAuth()
@ -549,37 +552,12 @@ describe('git-auth-helper tests', () => {
await authHelper.configureSubmoduleAuth() await authHelper.configureSubmoduleAuth()
// Assert // Assert
expect(mockSubmoduleForeach).toHaveBeenCalledTimes(2) expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3)
expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch(
/unset-all.*insteadOf/ /unset-all.*insteadOf/
) )
expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/) expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/)
} expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/)
)
const configureSubmoduleAuth_removesUrlInsteadOfWhenPersistCredentialsFalse =
'configureSubmoduleAuth removes URL insteadOf when persist credentials false'
it(
configureSubmoduleAuth_removesUrlInsteadOfWhenPersistCredentialsFalse,
async () => {
// Arrange
await setup(
configureSubmoduleAuth_removesUrlInsteadOfWhenPersistCredentialsFalse
)
settings.persistCredentials = false
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
await authHelper.configureAuth()
const mockSubmoduleForeach = git.submoduleForeach as jest.Mock<any, any>
mockSubmoduleForeach.mockClear() // reset calls
// Act
await authHelper.configureSubmoduleAuth()
// Assert
expect(mockSubmoduleForeach).toBeCalledTimes(1)
expect(mockSubmoduleForeach.mock.calls[0][0] as string).toMatch(
/unset-all.*insteadOf/
)
} }
) )

@ -16,7 +16,7 @@ inputs:
commands. The post-job step removes the PAT. commands. The post-job step removes the PAT.
We recommend creating a service account with the least permissions necessary. We recommend using a service account with the least permissions necessary.
Also when generating a new PAT, select the least scopes necessary. Also when generating a new PAT, select the least scopes necessary.
@ -24,12 +24,12 @@ inputs:
default: ${{ github.token }} default: ${{ github.token }}
ssh-key: ssh-key:
description: > description: >
SSH key used to fetch the repository. SSH key is configured with the local SSH key used to fetch the repository. The SSH key is configured with the local
git config, which enables your scripts to run authenticated git commands. git config, which enables your scripts to run authenticated git commands.
The post-job step removes the SSH key. The post-job step removes the SSH key.
We recommend creating a service account with the least permissions necessary. We recommend using a service account with the least permissions necessary.
[Learn more about creating and using [Learn more about creating and using

27
dist/index.js vendored

@ -5122,6 +5122,7 @@ class GitAuthHelper {
this.tokenConfigKey = `http.https://${HOSTNAME}/.extraheader`; this.tokenConfigKey = `http.https://${HOSTNAME}/.extraheader`;
this.insteadOfKey = `url.https://${HOSTNAME}/.insteadOf`; this.insteadOfKey = `url.https://${HOSTNAME}/.insteadOf`;
this.insteadOfValue = `git@${HOSTNAME}:`; this.insteadOfValue = `git@${HOSTNAME}:`;
this.sshCommand = '';
this.sshKeyPath = ''; this.sshKeyPath = '';
this.sshKnownHostsPath = ''; this.sshKnownHostsPath = '';
this.temporaryHomePath = ''; this.temporaryHomePath = '';
@ -5205,8 +5206,12 @@ class GitAuthHelper {
core.debug(`Replacing token placeholder in '${configPath}'`); core.debug(`Replacing token placeholder in '${configPath}'`);
this.replaceTokenPlaceholder(configPath); this.replaceTokenPlaceholder(configPath);
} }
// Configure HTTPS instead of SSH if (this.settings.sshKey) {
if (!this.settings.sshKey) { // Configure core.sshCommand
yield this.git.submoduleForeach(`git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`, this.settings.nestedSubmodules);
}
else {
// Configure HTTPS instead of SSH
yield this.git.submoduleForeach(`git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`, this.settings.nestedSubmodules); yield this.git.submoduleForeach(`git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`, this.settings.nestedSubmodules);
} }
} }
@ -5268,16 +5273,16 @@ class GitAuthHelper {
yield fs.promises.writeFile(this.sshKnownHostsPath, knownHosts); yield fs.promises.writeFile(this.sshKnownHostsPath, knownHosts);
// Configure GIT_SSH_COMMAND // Configure GIT_SSH_COMMAND
const sshPath = yield io.which('ssh', true); const sshPath = yield io.which('ssh', true);
let sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(this.sshKeyPath)}"`; this.sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(this.sshKeyPath)}"`;
if (this.settings.sshStrict) { if (this.settings.sshStrict) {
sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no'; this.sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no';
} }
sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(this.sshKnownHostsPath)}"`; this.sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(this.sshKnownHostsPath)}"`;
core.info(`Temporarily overriding GIT_SSH_COMMAND=${sshCommand}`); core.info(`Temporarily overriding GIT_SSH_COMMAND=${this.sshCommand}`);
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', sshCommand); this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand);
// Configure core.sshCommand // Configure core.sshCommand
if (this.settings.persistCredentials) { if (this.settings.persistCredentials) {
yield this.git.config(SSH_COMMAND_KEY, sshCommand); yield this.git.config(SSH_COMMAND_KEY, this.sshCommand);
} }
}); });
} }
@ -5820,6 +5825,12 @@ function getSource(settings) {
// Downloading using REST API // Downloading using REST API
core.info(`The repository will be downloaded using the GitHub REST API`); core.info(`The repository will be downloaded using the GitHub REST API`);
core.info(`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`); core.info(`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`);
if (settings.submodules) {
throw new Error(`Input 'submodules' 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.`);
}
else if (settings.sshKey) {
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.`);
}
yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath); yield githubApiHelper.downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath);
return; return;
} }

@ -37,6 +37,7 @@ class GitAuthHelper {
private readonly tokenPlaceholderConfigValue: string private readonly tokenPlaceholderConfigValue: string
private readonly insteadOfKey: string = `url.https://${HOSTNAME}/.insteadOf` private readonly insteadOfKey: string = `url.https://${HOSTNAME}/.insteadOf`
private readonly insteadOfValue: string = `git@${HOSTNAME}:` private readonly insteadOfValue: string = `git@${HOSTNAME}:`
private sshCommand = ''
private sshKeyPath = '' private sshKeyPath = ''
private sshKnownHostsPath = '' private sshKnownHostsPath = ''
private temporaryHomePath = '' private temporaryHomePath = ''
@ -144,8 +145,14 @@ class GitAuthHelper {
this.replaceTokenPlaceholder(configPath) this.replaceTokenPlaceholder(configPath)
} }
// Configure HTTPS instead of SSH if (this.settings.sshKey) {
if (!this.settings.sshKey) { // Configure core.sshCommand
await this.git.submoduleForeach(
`git config --local '${SSH_COMMAND_KEY}' '${this.sshCommand}'`,
this.settings.nestedSubmodules
)
} else {
// Configure HTTPS instead of SSH
await this.git.submoduleForeach( await this.git.submoduleForeach(
`git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`, `git config --local '${this.insteadOfKey}' '${this.insteadOfValue}'`,
this.settings.nestedSubmodules this.settings.nestedSubmodules
@ -218,21 +225,21 @@ class GitAuthHelper {
// Configure GIT_SSH_COMMAND // Configure GIT_SSH_COMMAND
const sshPath = await io.which('ssh', true) const sshPath = await io.which('ssh', true)
let sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename( this.sshCommand = `"${sshPath}" -i "$RUNNER_TEMP/${path.basename(
this.sshKeyPath this.sshKeyPath
)}"` )}"`
if (this.settings.sshStrict) { if (this.settings.sshStrict) {
sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no' this.sshCommand += ' -o StrictHostKeyChecking=yes -o CheckHostIP=no'
} }
sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename( this.sshCommand += ` -o "UserKnownHostsFile=$RUNNER_TEMP/${path.basename(
this.sshKnownHostsPath this.sshKnownHostsPath
)}"` )}"`
core.info(`Temporarily overriding GIT_SSH_COMMAND=${sshCommand}`) core.info(`Temporarily overriding GIT_SSH_COMMAND=${this.sshCommand}`)
this.git.setEnvironmentVariable('GIT_SSH_COMMAND', sshCommand) this.git.setEnvironmentVariable('GIT_SSH_COMMAND', this.sshCommand)
// Configure core.sshCommand // Configure core.sshCommand
if (this.settings.persistCredentials) { if (this.settings.persistCredentials) {
await this.git.config(SSH_COMMAND_KEY, sshCommand) await this.git.config(SSH_COMMAND_KEY, this.sshCommand)
} }
} }

@ -57,6 +57,16 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
core.info( core.info(
`To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH` `To create a local Git repository instead, add Git ${gitCommandManager.MinimumGitVersion} or higher to the PATH`
) )
if (settings.submodules) {
throw new Error(
`Input 'submodules' 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.`
)
} else if (settings.sshKey) {
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.`
)
}
await githubApiHelper.downloadRepository( await githubApiHelper.downloadRepository(
settings.authToken, settings.authToken,
settings.repositoryOwner, settings.repositoryOwner,

Loading…
Cancel
Save