mirror of
https://github.com/actions/checkout.git
synced 2026-07-11 19:23:49 +08:00
Compare commits
1 Commits
79d27f71d3
...
v5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93cb6efe18 |
19
README.md
19
README.md
@@ -2,11 +2,7 @@
|
|||||||
|
|
||||||
# Checkout V5
|
# Checkout V5
|
||||||
|
|
||||||
## What's new
|
Checkout v5 now supports Node.js 24
|
||||||
|
|
||||||
- Updated to the node24 runtime
|
|
||||||
- This requires a minimum Actions Runner version of [v2.327.1](https://github.com/actions/runner/releases/tag/v2.327.1) to run.
|
|
||||||
|
|
||||||
|
|
||||||
# Checkout V4
|
# Checkout V4
|
||||||
|
|
||||||
@@ -67,12 +63,6 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
# Default: ${{ github.token }}
|
# Default: ${{ github.token }}
|
||||||
token: ''
|
token: ''
|
||||||
|
|
||||||
# Github slug used to configure local user.name and user.email for git. This is
|
|
||||||
# required to push a commit from a Github Action Workflow. Set to '' to disable
|
|
||||||
# this configuration.
|
|
||||||
# Default: github-action[bot]
|
|
||||||
git-user: ''
|
|
||||||
|
|
||||||
# SSH key used to fetch the repository. The 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. 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.
|
||||||
@@ -164,10 +154,9 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||||||
# Scenarios
|
# Scenarios
|
||||||
|
|
||||||
- [Checkout V5](#checkout-v5)
|
- [Checkout V5](#checkout-v5)
|
||||||
- [What's new](#whats-new)
|
|
||||||
- [Checkout V4](#checkout-v4)
|
- [Checkout V4](#checkout-v4)
|
||||||
- [Note](#note)
|
- [Note](#note)
|
||||||
- [What's new](#whats-new-1)
|
- [What's new](#whats-new)
|
||||||
- [Usage](#usage)
|
- [Usage](#usage)
|
||||||
- [Scenarios](#scenarios)
|
- [Scenarios](#scenarios)
|
||||||
- [Fetch only the root files](#fetch-only-the-root-files)
|
- [Fetch only the root files](#fetch-only-the-root-files)
|
||||||
@@ -322,6 +311,8 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
date > generated.txt
|
date > generated.txt
|
||||||
# Note: the following account information will not work on GHES
|
# Note: the following account information will not work on GHES
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git add .
|
git add .
|
||||||
git commit -m "generated"
|
git commit -m "generated"
|
||||||
git push
|
git push
|
||||||
@@ -344,6 +335,8 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
date > generated.txt
|
date > generated.txt
|
||||||
# Note: the following account information will not work on GHES
|
# Note: the following account information will not work on GHES
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git add .
|
git add .
|
||||||
git commit -m "generated"
|
git commit -m "generated"
|
||||||
git push
|
git push
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as gitAuthHelper from '../src/git-auth-helper'
|
import * as gitAuthHelper from '../lib/git-auth-helper'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as stateHelper from '../src/state-helper'
|
import * as stateHelper from '../lib/state-helper'
|
||||||
import {IGitCommandManager} from '../src/git-command-manager'
|
import {IGitCommandManager} from '../lib/git-command-manager'
|
||||||
import {IGitSourceSettings} from '../src/git-source-settings'
|
import {IGitSourceSettings} from '../lib/git-source-settings'
|
||||||
|
|
||||||
const isWindows = process.platform === 'win32'
|
const isWindows = process.platform === 'win32'
|
||||||
const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper')
|
const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper')
|
||||||
@@ -675,6 +675,283 @@ describe('git-auth-helper tests', () => {
|
|||||||
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
expect(gitConfigContent.indexOf('http.')).toBeLessThan(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const removeAuth_removesV6StyleCredentials =
|
||||||
|
'removeAuth removes v6 style credentials'
|
||||||
|
it(removeAuth_removesV6StyleCredentials, async () => {
|
||||||
|
// Arrange
|
||||||
|
await setup(removeAuth_removesV6StyleCredentials)
|
||||||
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
|
await authHelper.configureAuth()
|
||||||
|
|
||||||
|
// Manually create v6-style credentials that would be left by v6
|
||||||
|
const credentialsFileName =
|
||||||
|
'git-credentials-12345678-1234-1234-1234-123456789abc.config'
|
||||||
|
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
||||||
|
const basicCredential = Buffer.from(
|
||||||
|
`x-access-token:${settings.authToken}`,
|
||||||
|
'utf8'
|
||||||
|
).toString('base64')
|
||||||
|
const credentialsContent = `[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic ${basicCredential}\n`
|
||||||
|
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
||||||
|
|
||||||
|
// Add includeIf entries to local git config (simulating v6 configuration)
|
||||||
|
const hostGitDir = path.join(workspace, '.git').replace(/\\/g, '/')
|
||||||
|
await fs.promises.appendFile(
|
||||||
|
localGitConfigPath,
|
||||||
|
`[includeIf "gitdir:${hostGitDir}/"]\n\tpath = ${credentialsFilePath}\n`
|
||||||
|
)
|
||||||
|
await fs.promises.appendFile(
|
||||||
|
localGitConfigPath,
|
||||||
|
`[includeIf "gitdir:/github/workspace/.git/"]\n\tpath = /github/runner_temp/${credentialsFileName}\n`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify v6 style config exists
|
||||||
|
let gitConfigContent = (
|
||||||
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
|
).toString()
|
||||||
|
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
||||||
|
expect(
|
||||||
|
gitConfigContent.indexOf(credentialsFilePath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
await fs.promises.stat(credentialsFilePath) // Verify file exists
|
||||||
|
|
||||||
|
// Mock the git methods to handle v6 cleanup
|
||||||
|
const mockTryGetConfigKeys = git.tryGetConfigKeys as jest.Mock<any, any>
|
||||||
|
mockTryGetConfigKeys.mockResolvedValue([
|
||||||
|
`includeIf.gitdir:${hostGitDir}/.path`,
|
||||||
|
'includeIf.gitdir:/github/workspace/.git/.path'
|
||||||
|
])
|
||||||
|
|
||||||
|
const mockTryGetConfigValues = git.tryGetConfigValues as jest.Mock<any, any>
|
||||||
|
mockTryGetConfigValues.mockImplementation(async (key: string) => {
|
||||||
|
if (key === `includeIf.gitdir:${hostGitDir}/.path`) {
|
||||||
|
return [credentialsFilePath]
|
||||||
|
}
|
||||||
|
if (key === 'includeIf.gitdir:/github/workspace/.git/.path') {
|
||||||
|
return [`/github/runner_temp/${credentialsFileName}`]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
|
const mockTryConfigUnsetValue = git.tryConfigUnsetValue as jest.Mock<
|
||||||
|
any,
|
||||||
|
any
|
||||||
|
>
|
||||||
|
mockTryConfigUnsetValue.mockImplementation(
|
||||||
|
async (
|
||||||
|
key: string,
|
||||||
|
value: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configPath?: string
|
||||||
|
) => {
|
||||||
|
const targetPath = configPath || localGitConfigPath
|
||||||
|
let content = await fs.promises.readFile(targetPath, 'utf8')
|
||||||
|
// Remove the includeIf section
|
||||||
|
const lines = content
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => !line.includes('includeIf') && !line.includes(value))
|
||||||
|
await fs.promises.writeFile(targetPath, lines.join('\n'))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await authHelper.removeAuth()
|
||||||
|
|
||||||
|
// Assert includeIf entries removed from local git config
|
||||||
|
gitConfigContent = (
|
||||||
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
|
).toString()
|
||||||
|
expect(gitConfigContent.indexOf('includeIf')).toBeLessThan(0)
|
||||||
|
expect(gitConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||||
|
|
||||||
|
// Assert credentials config file deleted
|
||||||
|
try {
|
||||||
|
await fs.promises.stat(credentialsFilePath)
|
||||||
|
throw new Error('Credentials file should have been deleted')
|
||||||
|
} catch (err) {
|
||||||
|
if ((err as any)?.code !== 'ENOENT') {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const removeAuth_removesV6StyleCredentialsFromSubmodules =
|
||||||
|
'removeAuth removes v6 style credentials from submodules'
|
||||||
|
it(removeAuth_removesV6StyleCredentialsFromSubmodules, async () => {
|
||||||
|
// Arrange
|
||||||
|
await setup(removeAuth_removesV6StyleCredentialsFromSubmodules)
|
||||||
|
|
||||||
|
// Create fake submodule config paths
|
||||||
|
const submodule1Dir = path.join(workspace, '.git', 'modules', 'submodule-1')
|
||||||
|
const submodule1ConfigPath = path.join(submodule1Dir, 'config')
|
||||||
|
await fs.promises.mkdir(submodule1Dir, {recursive: true})
|
||||||
|
await fs.promises.writeFile(submodule1ConfigPath, '')
|
||||||
|
|
||||||
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
|
await authHelper.configureAuth()
|
||||||
|
|
||||||
|
// Create v6-style credentials file
|
||||||
|
const credentialsFileName =
|
||||||
|
'git-credentials-abcdef12-3456-7890-abcd-ef1234567890.config'
|
||||||
|
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
||||||
|
const basicCredential = Buffer.from(
|
||||||
|
`x-access-token:${settings.authToken}`,
|
||||||
|
'utf8'
|
||||||
|
).toString('base64')
|
||||||
|
const credentialsContent = `[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic ${basicCredential}\n`
|
||||||
|
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
||||||
|
|
||||||
|
// Add includeIf entries to submodule config
|
||||||
|
const submodule1GitDir = submodule1Dir.replace(/\\/g, '/')
|
||||||
|
await fs.promises.appendFile(
|
||||||
|
submodule1ConfigPath,
|
||||||
|
`[includeIf "gitdir:${submodule1GitDir}/"]\n\tpath = ${credentialsFilePath}\n`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify submodule config has includeIf entry
|
||||||
|
let submoduleConfigContent = (
|
||||||
|
await fs.promises.readFile(submodule1ConfigPath)
|
||||||
|
).toString()
|
||||||
|
expect(submoduleConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
submoduleConfigContent.indexOf(credentialsFilePath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
|
// Mock getSubmoduleConfigPaths
|
||||||
|
const mockGetSubmoduleConfigPaths =
|
||||||
|
git.getSubmoduleConfigPaths as jest.Mock<any, any>
|
||||||
|
mockGetSubmoduleConfigPaths.mockResolvedValue([submodule1ConfigPath])
|
||||||
|
|
||||||
|
// Mock tryGetConfigKeys for submodule
|
||||||
|
const mockTryGetConfigKeys = git.tryGetConfigKeys as jest.Mock<any, any>
|
||||||
|
mockTryGetConfigKeys.mockImplementation(
|
||||||
|
async (pattern: string, globalConfig?: boolean, configPath?: string) => {
|
||||||
|
if (configPath === submodule1ConfigPath) {
|
||||||
|
return [`includeIf.gitdir:${submodule1GitDir}/.path`]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mock tryGetConfigValues for submodule
|
||||||
|
const mockTryGetConfigValues = git.tryGetConfigValues as jest.Mock<any, any>
|
||||||
|
mockTryGetConfigValues.mockImplementation(
|
||||||
|
async (key: string, globalConfig?: boolean, configPath?: string) => {
|
||||||
|
if (
|
||||||
|
configPath === submodule1ConfigPath &&
|
||||||
|
key === `includeIf.gitdir:${submodule1GitDir}/.path`
|
||||||
|
) {
|
||||||
|
return [credentialsFilePath]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mock tryConfigUnsetValue for submodule
|
||||||
|
const mockTryConfigUnsetValue = git.tryConfigUnsetValue as jest.Mock<
|
||||||
|
any,
|
||||||
|
any
|
||||||
|
>
|
||||||
|
mockTryConfigUnsetValue.mockImplementation(
|
||||||
|
async (
|
||||||
|
key: string,
|
||||||
|
value: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configPath?: string
|
||||||
|
) => {
|
||||||
|
const targetPath = configPath || localGitConfigPath
|
||||||
|
let content = await fs.promises.readFile(targetPath, 'utf8')
|
||||||
|
const lines = content
|
||||||
|
.split('\n')
|
||||||
|
.filter(line => !line.includes('includeIf') && !line.includes(value))
|
||||||
|
await fs.promises.writeFile(targetPath, lines.join('\n'))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await authHelper.removeAuth()
|
||||||
|
|
||||||
|
// Assert submodule includeIf entries removed
|
||||||
|
submoduleConfigContent = (
|
||||||
|
await fs.promises.readFile(submodule1ConfigPath)
|
||||||
|
).toString()
|
||||||
|
expect(submoduleConfigContent.indexOf('includeIf')).toBeLessThan(0)
|
||||||
|
expect(submoduleConfigContent.indexOf(credentialsFilePath)).toBeLessThan(0)
|
||||||
|
|
||||||
|
// Assert credentials file deleted
|
||||||
|
try {
|
||||||
|
await fs.promises.stat(credentialsFilePath)
|
||||||
|
throw new Error('Credentials file should have been deleted')
|
||||||
|
} catch (err) {
|
||||||
|
if ((err as any)?.code !== 'ENOENT') {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const removeAuth_skipsV6CleanupWhenEnvVarSet =
|
||||||
|
'removeAuth skips v6 cleanup when ACTIONS_CHECKOUT_SKIP_V6_CLEANUP is set'
|
||||||
|
it(removeAuth_skipsV6CleanupWhenEnvVarSet, async () => {
|
||||||
|
// Arrange
|
||||||
|
await setup(removeAuth_skipsV6CleanupWhenEnvVarSet)
|
||||||
|
|
||||||
|
// Set the skip environment variable
|
||||||
|
process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'] = '1'
|
||||||
|
|
||||||
|
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||||
|
await authHelper.configureAuth()
|
||||||
|
|
||||||
|
// Create v6-style credentials file in RUNNER_TEMP
|
||||||
|
const credentialsFileName = 'git-credentials-test-uuid-1234-5678.config'
|
||||||
|
const credentialsFilePath = path.join(runnerTemp, credentialsFileName)
|
||||||
|
const credentialsContent =
|
||||||
|
'[http "https://github.com/"]\n\textraheader = AUTHORIZATION: basic token\n'
|
||||||
|
await fs.promises.writeFile(credentialsFilePath, credentialsContent)
|
||||||
|
|
||||||
|
// Add includeIf section to local git config (separate from http.* config)
|
||||||
|
const includeIfSection = `\n[includeIf "gitdir:/some/path/.git/"]\n\tpath = ${credentialsFilePath}\n`
|
||||||
|
await fs.promises.appendFile(localGitConfigPath, includeIfSection)
|
||||||
|
|
||||||
|
// Verify v6 style config exists
|
||||||
|
let gitConfigContent = (
|
||||||
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
|
).toString()
|
||||||
|
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
||||||
|
await fs.promises.stat(credentialsFilePath) // Verify file exists
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await authHelper.removeAuth()
|
||||||
|
|
||||||
|
// Assert v5 cleanup still happened (http.* removed)
|
||||||
|
gitConfigContent = (
|
||||||
|
await fs.promises.readFile(localGitConfigPath)
|
||||||
|
).toString()
|
||||||
|
expect(
|
||||||
|
gitConfigContent.indexOf('http.https://github.com/.extraheader')
|
||||||
|
).toBeLessThan(0)
|
||||||
|
|
||||||
|
// Assert v6 cleanup was skipped - includeIf should still be present
|
||||||
|
expect(gitConfigContent.indexOf('includeIf')).toBeGreaterThanOrEqual(0)
|
||||||
|
expect(
|
||||||
|
gitConfigContent.indexOf(credentialsFilePath)
|
||||||
|
).toBeGreaterThanOrEqual(0)
|
||||||
|
|
||||||
|
// Assert credentials file still exists (wasn't deleted)
|
||||||
|
await fs.promises.stat(credentialsFilePath) // File should still exist
|
||||||
|
|
||||||
|
// Assert debug message was logged
|
||||||
|
expect(core.debug).toHaveBeenCalledWith(
|
||||||
|
'Skipping v6 style cleanup due to ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
delete process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP']
|
||||||
|
})
|
||||||
|
|
||||||
const removeGlobalConfig_removesOverride =
|
const removeGlobalConfig_removesOverride =
|
||||||
'removeGlobalConfig removes override'
|
'removeGlobalConfig removes override'
|
||||||
it(removeGlobalConfig_removesOverride, async () => {
|
it(removeGlobalConfig_removesOverride, async () => {
|
||||||
@@ -796,6 +1073,18 @@ async function setup(testName: string): Promise<void> {
|
|||||||
),
|
),
|
||||||
tryDisableAutomaticGarbageCollection: jest.fn(),
|
tryDisableAutomaticGarbageCollection: jest.fn(),
|
||||||
tryGetFetchUrl: jest.fn(),
|
tryGetFetchUrl: jest.fn(),
|
||||||
|
getSubmoduleConfigPaths: jest.fn(async () => {
|
||||||
|
return []
|
||||||
|
}),
|
||||||
|
tryConfigUnsetValue: jest.fn(async () => {
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
tryGetConfigValues: jest.fn(async () => {
|
||||||
|
return []
|
||||||
|
}),
|
||||||
|
tryGetConfigKeys: jest.fn(async () => {
|
||||||
|
return []
|
||||||
|
}),
|
||||||
tryReset: jest.fn(),
|
tryReset: jest.fn(),
|
||||||
version: jest.fn()
|
version: jest.fn()
|
||||||
}
|
}
|
||||||
@@ -824,8 +1113,7 @@ async function setup(testName: string): Promise<void> {
|
|||||||
sshUser: '',
|
sshUser: '',
|
||||||
workflowOrganizationId: 123456,
|
workflowOrganizationId: 123456,
|
||||||
setSafeDirectory: true,
|
setSafeDirectory: true,
|
||||||
githubServerUrl: githubServerUrl,
|
githubServerUrl: githubServerUrl
|
||||||
gitUser: 'github-action[bot]'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import * as fshelper from '../src/fs-helper'
|
import * as fshelper from '../lib/fs-helper'
|
||||||
import * as commandManager from '../src/git-command-manager'
|
import * as commandManager from '../lib/git-command-manager'
|
||||||
|
|
||||||
let git: commandManager.IGitCommandManager
|
let git: commandManager.IGitCommandManager
|
||||||
let mockExec = jest.fn()
|
let mockExec = jest.fn()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as gitDirectoryHelper from '../src/git-directory-helper'
|
import * as gitDirectoryHelper from '../lib/git-directory-helper'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {IGitCommandManager} from '../src/git-command-manager'
|
import {IGitCommandManager} from '../lib/git-command-manager'
|
||||||
|
|
||||||
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
|
const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
|
||||||
let repositoryPath: string
|
let repositoryPath: string
|
||||||
@@ -499,6 +499,18 @@ 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
|
||||||
}),
|
}),
|
||||||
|
getSubmoduleConfigPaths: jest.fn(async () => {
|
||||||
|
return []
|
||||||
|
}),
|
||||||
|
tryConfigUnsetValue: jest.fn(async () => {
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
tryGetConfigValues: jest.fn(async () => {
|
||||||
|
return []
|
||||||
|
}),
|
||||||
|
tryGetConfigKeys: jest.fn(async () => {
|
||||||
|
return []
|
||||||
|
}),
|
||||||
tryReset: jest.fn(async () => {
|
tryReset: jest.fn(async () => {
|
||||||
return true
|
return true
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as fsHelper from '../src/fs-helper'
|
import * as fsHelper from '../lib/fs-helper'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as inputHelper from '../src/input-helper'
|
import * as inputHelper from '../lib/input-helper'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as workflowContextHelper from '../src/workflow-context-helper'
|
import * as workflowContextHelper from '../lib/workflow-context-helper'
|
||||||
import {IGitSourceSettings} from '../src/git-source-settings'
|
import {IGitSourceSettings} from '../lib/git-source-settings'
|
||||||
|
|
||||||
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
|
const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE']
|
||||||
const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
|
const gitHubWorkspace = path.resolve('/checkout-tests/workspace')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as assert from 'assert'
|
import * as assert from 'assert'
|
||||||
import * as refHelper from '../src/ref-helper'
|
import * as refHelper from '../lib/ref-helper'
|
||||||
import {IGitCommandManager} from '../src/git-command-manager'
|
import {IGitCommandManager} from '../lib/git-command-manager'
|
||||||
|
|
||||||
const commit = '1234567890123456789012345678901234567890'
|
const commit = '1234567890123456789012345678901234567890'
|
||||||
let git: IGitCommandManager
|
let git: IGitCommandManager
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {RetryHelper} from '../src/retry-helper'
|
import {RetryHelper} from '../lib/retry-helper'
|
||||||
|
|
||||||
let info: string[]
|
let info: string[]
|
||||||
let retryHelper: any
|
let retryHelper: any
|
||||||
|
|||||||
@@ -22,12 +22,6 @@ inputs:
|
|||||||
|
|
||||||
[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 }}
|
||||||
git-user:
|
|
||||||
description: >
|
|
||||||
Github slug used to configure local user.name and user.email for git.
|
|
||||||
This is required to push a commit from a Github Action Workflow.
|
|
||||||
Set to '' to disable this configuration.
|
|
||||||
default: "github-action[bot]"
|
|
||||||
ssh-key:
|
ssh-key:
|
||||||
description: >
|
description: >
|
||||||
SSH key used to fetch the repository. The SSH key is configured with the local
|
SSH key used to fetch the repository. The SSH key is configured with the local
|
||||||
|
|||||||
172
dist/index.js
vendored
172
dist/index.js
vendored
@@ -411,8 +411,50 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
removeToken() {
|
removeToken() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// HTTP extra header
|
// Remove HTTP extra header from local git config and submodule configs
|
||||||
yield this.removeGitConfig(this.tokenConfigKey);
|
yield this.removeGitConfig(this.tokenConfigKey);
|
||||||
|
//
|
||||||
|
// Cleanup actions/checkout@v6 style credentials
|
||||||
|
//
|
||||||
|
const skipV6Cleanup = process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'];
|
||||||
|
if (skipV6Cleanup === '1' || (skipV6Cleanup === null || skipV6Cleanup === void 0 ? void 0 : skipV6Cleanup.toLowerCase()) === 'true') {
|
||||||
|
core.debug('Skipping v6 style cleanup due to ACTIONS_CHECKOUT_SKIP_V6_CLEANUP');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Collect credentials config paths that need to be removed
|
||||||
|
const credentialsPaths = new Set();
|
||||||
|
// Remove includeIf entries that point to git-credentials-*.config files
|
||||||
|
const mainCredentialsPaths = yield this.removeIncludeIfCredentials();
|
||||||
|
mainCredentialsPaths.forEach(path => credentialsPaths.add(path));
|
||||||
|
// Remove submodule includeIf entries that point to git-credentials-*.config files
|
||||||
|
try {
|
||||||
|
const submoduleConfigPaths = yield this.git.getSubmoduleConfigPaths(true);
|
||||||
|
for (const configPath of submoduleConfigPaths) {
|
||||||
|
const submoduleCredentialsPaths = yield this.removeIncludeIfCredentials(configPath);
|
||||||
|
submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.debug(`Unable to get submodule config paths: ${err}`);
|
||||||
|
}
|
||||||
|
// Remove credentials config files
|
||||||
|
for (const credentialsPath of credentialsPaths) {
|
||||||
|
// Only remove credentials config files if they are under RUNNER_TEMP
|
||||||
|
const runnerTemp = process.env['RUNNER_TEMP'];
|
||||||
|
if (runnerTemp && credentialsPath.startsWith(runnerTemp)) {
|
||||||
|
try {
|
||||||
|
yield io.rmRF(credentialsPath);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.debug(`Failed to remove credentials config '${credentialsPath}': ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
core.debug(`Failed to cleanup v6 style credentials: ${err}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
removeGitConfig(configKey_1) {
|
removeGitConfig(configKey_1) {
|
||||||
@@ -430,6 +472,49 @@ class GitAuthHelper {
|
|||||||
`sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true);
|
`sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Removes includeIf entries that point to git-credentials-*.config files.
|
||||||
|
* This handles cleanup of credentials configured by newer versions of the action.
|
||||||
|
* @param configPath Optional path to a specific git config file to operate on
|
||||||
|
* @returns Array of unique credentials config file paths that were found and removed
|
||||||
|
*/
|
||||||
|
removeIncludeIfCredentials(configPath) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const credentialsPaths = new Set();
|
||||||
|
try {
|
||||||
|
// Get all includeIf.gitdir keys
|
||||||
|
const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, // globalConfig?
|
||||||
|
configPath);
|
||||||
|
for (const key of keys) {
|
||||||
|
// Get all values for this key
|
||||||
|
const values = yield this.git.tryGetConfigValues(key, false, // globalConfig?
|
||||||
|
configPath);
|
||||||
|
if (values.length > 0) {
|
||||||
|
// Remove only values that match git-credentials-<uuid>.config pattern
|
||||||
|
for (const value of values) {
|
||||||
|
if (this.testCredentialsConfigPath(value)) {
|
||||||
|
credentialsPaths.add(value);
|
||||||
|
yield this.git.tryConfigUnsetValue(key, value, false, configPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
// Ignore errors - this is cleanup code
|
||||||
|
core.debug(`Error during includeIf cleanup${configPath ? ` for ${configPath}` : ''}: ${err}`);
|
||||||
|
}
|
||||||
|
return Array.from(credentialsPaths);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Tests if a path matches the git-credentials-*.config pattern used by newer versions.
|
||||||
|
* @param path The path to test
|
||||||
|
* @returns True if the path matches the credentials config pattern
|
||||||
|
*/
|
||||||
|
testCredentialsConfigPath(path) {
|
||||||
|
return /git-credentials-[0-9a-f-]+\.config$/i.test(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -706,6 +791,16 @@ class GitCommandManager {
|
|||||||
throw new Error('Unexpected output when retrieving default branch');
|
throw new Error('Unexpected output when retrieving default branch');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
getSubmoduleConfigPaths(recursive) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
// Get submodule config file paths.
|
||||||
|
// Use `--show-origin` to get the config file path for each submodule.
|
||||||
|
const output = yield this.submoduleForeach(`git config --local --show-origin --name-only --get-regexp remote.origin.url`, recursive);
|
||||||
|
// Extract config file paths from the output (lines starting with "file:").
|
||||||
|
const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [];
|
||||||
|
return configPaths;
|
||||||
|
});
|
||||||
|
}
|
||||||
getWorkingDirectory() {
|
getWorkingDirectory() {
|
||||||
return this.workingDirectory;
|
return this.workingDirectory;
|
||||||
}
|
}
|
||||||
@@ -836,6 +931,20 @@ class GitCommandManager {
|
|||||||
return output.exitCode === 0;
|
return output.exitCode === 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
tryConfigUnsetValue(configKey, configValue, globalConfig, configFile) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const args = ['config'];
|
||||||
|
if (configFile) {
|
||||||
|
args.push('--file', configFile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
args.push(globalConfig ? '--global' : '--local');
|
||||||
|
}
|
||||||
|
args.push('--unset', configKey, configValue);
|
||||||
|
const output = yield this.execGit(args, true);
|
||||||
|
return output.exitCode === 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
tryDisableAutomaticGarbageCollection() {
|
tryDisableAutomaticGarbageCollection() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true);
|
const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true);
|
||||||
@@ -855,6 +964,46 @@ class GitCommandManager {
|
|||||||
return stdout;
|
return stdout;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
tryGetConfigValues(configKey, globalConfig, configFile) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const args = ['config'];
|
||||||
|
if (configFile) {
|
||||||
|
args.push('--file', configFile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
args.push(globalConfig ? '--global' : '--local');
|
||||||
|
}
|
||||||
|
args.push('--get-all', configKey);
|
||||||
|
const output = yield this.execGit(args, true);
|
||||||
|
if (output.exitCode !== 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return output.stdout
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter(value => value.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tryGetConfigKeys(pattern, globalConfig, configFile) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const args = ['config'];
|
||||||
|
if (configFile) {
|
||||||
|
args.push('--file', configFile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
args.push(globalConfig ? '--global' : '--local');
|
||||||
|
}
|
||||||
|
args.push('--name-only', '--get-regexp', pattern);
|
||||||
|
const output = yield this.execGit(args, true);
|
||||||
|
if (output.exitCode !== 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return output.stdout
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter(key => key.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
tryReset() {
|
tryReset() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const output = yield this.execGit(['reset', '--hard', 'HEAD'], true);
|
const output = yield this.execGit(['reset', '--hard', 'HEAD'], true);
|
||||||
@@ -1357,15 +1506,6 @@ function getSource(settings) {
|
|||||||
core.setOutput('commit', commitSHA.trim());
|
core.setOutput('commit', commitSHA.trim());
|
||||||
// Check for incorrect pull request merge commit
|
// Check for incorrect pull request merge commit
|
||||||
yield refHelper.checkCommitInfo(settings.authToken, commitInfo, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.githubServerUrl);
|
yield refHelper.checkCommitInfo(settings.authToken, commitInfo, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.githubServerUrl);
|
||||||
if (settings.gitUser) {
|
|
||||||
if (!(yield git.configExists('user.name', true))) {
|
|
||||||
yield git.config('user.name', settings.gitUser, true);
|
|
||||||
}
|
|
||||||
if (!(yield git.configExists('user.email', true))) {
|
|
||||||
const userId = yield githubApiHelper.getUserId(settings.gitUser, settings.authToken, settings.githubServerUrl);
|
|
||||||
yield git.config('user.email', `${userId}+${settings.gitUser}@users.noreply.github.com`, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// Remove auth
|
// Remove auth
|
||||||
@@ -1555,7 +1695,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
exports.downloadRepository = downloadRepository;
|
exports.downloadRepository = downloadRepository;
|
||||||
exports.getDefaultBranch = getDefaultBranch;
|
exports.getDefaultBranch = getDefaultBranch;
|
||||||
exports.getUserId = getUserId;
|
|
||||||
const assert = __importStar(__nccwpck_require__(9491));
|
const assert = __importStar(__nccwpck_require__(9491));
|
||||||
const core = __importStar(__nccwpck_require__(2186));
|
const core = __importStar(__nccwpck_require__(2186));
|
||||||
const fs = __importStar(__nccwpck_require__(7147));
|
const fs = __importStar(__nccwpck_require__(7147));
|
||||||
@@ -1673,15 +1812,6 @@ function downloadArchive(authToken, owner, repo, ref, commit, baseUrl) {
|
|||||||
return Buffer.from(response.data); // response.data is ArrayBuffer
|
return Buffer.from(response.data); // response.data is ArrayBuffer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getUserId(username, authToken, baseUrl) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const octokit = github.getOctokit(authToken, {
|
|
||||||
baseUrl: (0, url_helper_1.getServerApiUrl)(baseUrl)
|
|
||||||
});
|
|
||||||
const user = yield octokit.rest.users.getByUsername({ username, });
|
|
||||||
return user.data.id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@@ -1832,8 +1962,6 @@ function getInputs() {
|
|||||||
core.debug(`recursive submodules = ${result.nestedSubmodules}`);
|
core.debug(`recursive submodules = ${result.nestedSubmodules}`);
|
||||||
// Auth token
|
// Auth token
|
||||||
result.authToken = core.getInput('token', { required: true });
|
result.authToken = core.getInput('token', { required: true });
|
||||||
// Git user
|
|
||||||
result.gitUser = core.getInput('git-user') || 'github-action[bot]';
|
|
||||||
// SSH
|
// SSH
|
||||||
result.sshKey = core.getInput('ssh-key');
|
result.sshKey = core.getInput('ssh-key');
|
||||||
result.sshKnownHosts = core.getInput('ssh-known-hosts');
|
result.sshKnownHosts = core.getInput('ssh-known-hosts');
|
||||||
|
|||||||
@@ -346,8 +346,58 @@ class GitAuthHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async removeToken(): Promise<void> {
|
private async removeToken(): Promise<void> {
|
||||||
// HTTP extra header
|
// Remove HTTP extra header from local git config and submodule configs
|
||||||
await this.removeGitConfig(this.tokenConfigKey)
|
await this.removeGitConfig(this.tokenConfigKey)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cleanup actions/checkout@v6 style credentials
|
||||||
|
//
|
||||||
|
const skipV6Cleanup = process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP']
|
||||||
|
if (skipV6Cleanup === '1' || skipV6Cleanup?.toLowerCase() === 'true') {
|
||||||
|
core.debug(
|
||||||
|
'Skipping v6 style cleanup due to ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Collect credentials config paths that need to be removed
|
||||||
|
const credentialsPaths = new Set<string>()
|
||||||
|
|
||||||
|
// Remove includeIf entries that point to git-credentials-*.config files
|
||||||
|
const mainCredentialsPaths = await this.removeIncludeIfCredentials()
|
||||||
|
mainCredentialsPaths.forEach(path => credentialsPaths.add(path))
|
||||||
|
|
||||||
|
// Remove submodule includeIf entries that point to git-credentials-*.config files
|
||||||
|
try {
|
||||||
|
const submoduleConfigPaths =
|
||||||
|
await this.git.getSubmoduleConfigPaths(true)
|
||||||
|
for (const configPath of submoduleConfigPaths) {
|
||||||
|
const submoduleCredentialsPaths =
|
||||||
|
await this.removeIncludeIfCredentials(configPath)
|
||||||
|
submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path))
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(`Unable to get submodule config paths: ${err}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove credentials config files
|
||||||
|
for (const credentialsPath of credentialsPaths) {
|
||||||
|
// Only remove credentials config files if they are under RUNNER_TEMP
|
||||||
|
const runnerTemp = process.env['RUNNER_TEMP']
|
||||||
|
if (runnerTemp && credentialsPath.startsWith(runnerTemp)) {
|
||||||
|
try {
|
||||||
|
await io.rmRF(credentialsPath)
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(
|
||||||
|
`Failed to remove credentials config '${credentialsPath}': ${err}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
core.debug(`Failed to cleanup v6 style credentials: ${err}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeGitConfig(
|
private async removeGitConfig(
|
||||||
@@ -371,4 +421,59 @@ class GitAuthHelper {
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes includeIf entries that point to git-credentials-*.config files.
|
||||||
|
* This handles cleanup of credentials configured by newer versions of the action.
|
||||||
|
* @param configPath Optional path to a specific git config file to operate on
|
||||||
|
* @returns Array of unique credentials config file paths that were found and removed
|
||||||
|
*/
|
||||||
|
private async removeIncludeIfCredentials(
|
||||||
|
configPath?: string
|
||||||
|
): Promise<string[]> {
|
||||||
|
const credentialsPaths = new Set<string>()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get all includeIf.gitdir keys
|
||||||
|
const keys = await this.git.tryGetConfigKeys(
|
||||||
|
'^includeIf\\.gitdir:',
|
||||||
|
false, // globalConfig?
|
||||||
|
configPath
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
// Get all values for this key
|
||||||
|
const values = await this.git.tryGetConfigValues(
|
||||||
|
key,
|
||||||
|
false, // globalConfig?
|
||||||
|
configPath
|
||||||
|
)
|
||||||
|
if (values.length > 0) {
|
||||||
|
// Remove only values that match git-credentials-<uuid>.config pattern
|
||||||
|
for (const value of values) {
|
||||||
|
if (this.testCredentialsConfigPath(value)) {
|
||||||
|
credentialsPaths.add(value)
|
||||||
|
await this.git.tryConfigUnsetValue(key, value, false, configPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore errors - this is cleanup code
|
||||||
|
core.debug(
|
||||||
|
`Error during includeIf cleanup${configPath ? ` for ${configPath}` : ''}: ${err}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(credentialsPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if a path matches the git-credentials-*.config pattern used by newer versions.
|
||||||
|
* @param path The path to test
|
||||||
|
* @returns True if the path matches the credentials config pattern
|
||||||
|
*/
|
||||||
|
private testCredentialsConfigPath(path: string): boolean {
|
||||||
|
return /git-credentials-[0-9a-f-]+\.config$/i.test(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export interface IGitCommandManager {
|
|||||||
}
|
}
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
getDefaultBranch(repositoryUrl: string): Promise<string>
|
getDefaultBranch(repositoryUrl: string): Promise<string>
|
||||||
|
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
|
||||||
getWorkingDirectory(): string
|
getWorkingDirectory(): string
|
||||||
init(): Promise<void>
|
init(): Promise<void>
|
||||||
isDetached(): Promise<boolean>
|
isDetached(): Promise<boolean>
|
||||||
@@ -59,8 +60,24 @@ export interface IGitCommandManager {
|
|||||||
tagExists(pattern: string): Promise<boolean>
|
tagExists(pattern: string): Promise<boolean>
|
||||||
tryClean(): Promise<boolean>
|
tryClean(): Promise<boolean>
|
||||||
tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
|
tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
|
||||||
|
tryConfigUnsetValue(
|
||||||
|
configKey: string,
|
||||||
|
configValue: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<boolean>
|
||||||
tryDisableAutomaticGarbageCollection(): Promise<boolean>
|
tryDisableAutomaticGarbageCollection(): Promise<boolean>
|
||||||
tryGetFetchUrl(): Promise<string>
|
tryGetFetchUrl(): Promise<string>
|
||||||
|
tryGetConfigValues(
|
||||||
|
configKey: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<string[]>
|
||||||
|
tryGetConfigKeys(
|
||||||
|
pattern: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<string[]>
|
||||||
tryReset(): Promise<boolean>
|
tryReset(): Promise<boolean>
|
||||||
version(): Promise<GitVersion>
|
version(): Promise<GitVersion>
|
||||||
}
|
}
|
||||||
@@ -323,6 +340,21 @@ class GitCommandManager {
|
|||||||
throw new Error('Unexpected output when retrieving default branch')
|
throw new Error('Unexpected output when retrieving default branch')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> {
|
||||||
|
// Get submodule config file paths.
|
||||||
|
// Use `--show-origin` to get the config file path for each submodule.
|
||||||
|
const output = await this.submoduleForeach(
|
||||||
|
`git config --local --show-origin --name-only --get-regexp remote.origin.url`,
|
||||||
|
recursive
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extract config file paths from the output (lines starting with "file:").
|
||||||
|
const configPaths =
|
||||||
|
output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []
|
||||||
|
|
||||||
|
return configPaths
|
||||||
|
}
|
||||||
|
|
||||||
getWorkingDirectory(): string {
|
getWorkingDirectory(): string {
|
||||||
return this.workingDirectory
|
return this.workingDirectory
|
||||||
}
|
}
|
||||||
@@ -455,6 +487,24 @@ class GitCommandManager {
|
|||||||
return output.exitCode === 0
|
return output.exitCode === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async tryConfigUnsetValue(
|
||||||
|
configKey: string,
|
||||||
|
configValue: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
const args = ['config']
|
||||||
|
if (configFile) {
|
||||||
|
args.push('--file', configFile)
|
||||||
|
} else {
|
||||||
|
args.push(globalConfig ? '--global' : '--local')
|
||||||
|
}
|
||||||
|
args.push('--unset', configKey, configValue)
|
||||||
|
|
||||||
|
const output = await this.execGit(args, true)
|
||||||
|
return output.exitCode === 0
|
||||||
|
}
|
||||||
|
|
||||||
async tryDisableAutomaticGarbageCollection(): Promise<boolean> {
|
async tryDisableAutomaticGarbageCollection(): Promise<boolean> {
|
||||||
const output = await this.execGit(
|
const output = await this.execGit(
|
||||||
['config', '--local', 'gc.auto', '0'],
|
['config', '--local', 'gc.auto', '0'],
|
||||||
@@ -481,6 +531,56 @@ class GitCommandManager {
|
|||||||
return stdout
|
return stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async tryGetConfigValues(
|
||||||
|
configKey: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<string[]> {
|
||||||
|
const args = ['config']
|
||||||
|
if (configFile) {
|
||||||
|
args.push('--file', configFile)
|
||||||
|
} else {
|
||||||
|
args.push(globalConfig ? '--global' : '--local')
|
||||||
|
}
|
||||||
|
args.push('--get-all', configKey)
|
||||||
|
|
||||||
|
const output = await this.execGit(args, true)
|
||||||
|
|
||||||
|
if (output.exitCode !== 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.stdout
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter(value => value.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
async tryGetConfigKeys(
|
||||||
|
pattern: string,
|
||||||
|
globalConfig?: boolean,
|
||||||
|
configFile?: string
|
||||||
|
): Promise<string[]> {
|
||||||
|
const args = ['config']
|
||||||
|
if (configFile) {
|
||||||
|
args.push('--file', configFile)
|
||||||
|
} else {
|
||||||
|
args.push(globalConfig ? '--global' : '--local')
|
||||||
|
}
|
||||||
|
args.push('--name-only', '--get-regexp', pattern)
|
||||||
|
|
||||||
|
const output = await this.execGit(args, true)
|
||||||
|
|
||||||
|
if (output.exitCode !== 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.stdout
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter(key => key.trim())
|
||||||
|
}
|
||||||
|
|
||||||
async tryReset(): Promise<boolean> {
|
async tryReset(): Promise<boolean> {
|
||||||
const output = await this.execGit(['reset', '--hard', 'HEAD'], true)
|
const output = await this.execGit(['reset', '--hard', 'HEAD'], true)
|
||||||
return output.exitCode === 0
|
return output.exitCode === 0
|
||||||
|
|||||||
@@ -274,23 +274,6 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
settings.commit,
|
settings.commit,
|
||||||
settings.githubServerUrl
|
settings.githubServerUrl
|
||||||
)
|
)
|
||||||
if (settings.gitUser) {
|
|
||||||
if (!(await git.configExists('user.name', true))) {
|
|
||||||
await git.config('user.name', settings.gitUser, true)
|
|
||||||
}
|
|
||||||
if (!(await git.configExists('user.email', true))) {
|
|
||||||
const userId = await githubApiHelper.getUserId(
|
|
||||||
settings.gitUser,
|
|
||||||
settings.authToken,
|
|
||||||
settings.githubServerUrl
|
|
||||||
)
|
|
||||||
await git.config(
|
|
||||||
'user.email',
|
|
||||||
`${userId}+${settings.gitUser}@users.noreply.github.com`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
// Remove auth
|
// Remove auth
|
||||||
if (authHelper) {
|
if (authHelper) {
|
||||||
|
|||||||
@@ -79,11 +79,6 @@ export interface IGitSourceSettings {
|
|||||||
*/
|
*/
|
||||||
authToken: string
|
authToken: string
|
||||||
|
|
||||||
/**
|
|
||||||
* A github user slug to set a default user name and email in the local git config
|
|
||||||
*/
|
|
||||||
gitUser: string
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SSH key to configure
|
* The SSH key to configure
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -143,15 +143,3 @@ async function downloadArchive(
|
|||||||
})
|
})
|
||||||
return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer
|
return Buffer.from(response.data as ArrayBuffer) // response.data is ArrayBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserId(
|
|
||||||
username: string,
|
|
||||||
authToken: string,
|
|
||||||
baseUrl?: string
|
|
||||||
): Promise<number> {
|
|
||||||
const octokit = github.getOctokit(authToken, {
|
|
||||||
baseUrl: getServerApiUrl(baseUrl)
|
|
||||||
})
|
|
||||||
const user = await octokit.rest.users.getByUsername({username})
|
|
||||||
return user.data.id
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -138,9 +138,6 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||||||
// Auth token
|
// Auth token
|
||||||
result.authToken = core.getInput('token', {required: true})
|
result.authToken = core.getInput('token', {required: true})
|
||||||
|
|
||||||
// Git user
|
|
||||||
result.gitUser = core.getInput('git-user') || 'github-action[bot]'
|
|
||||||
|
|
||||||
// SSH
|
// SSH
|
||||||
result.sshKey = core.getInput('ssh-key')
|
result.sshKey = core.getInput('ssh-key')
|
||||||
result.sshKnownHosts = core.getInput('ssh-known-hosts')
|
result.sshKnownHosts = core.getInput('ssh-known-hosts')
|
||||||
|
|||||||
Reference in New Issue
Block a user