mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-31 12:23:35 +00:00 
			
		
		
		
	more unit tests and corresponding refactoring (#174)
This commit is contained in:
		
							parent
							
								
									096e927750
								
							
						
					
					
						commit
						f219062370
					
				
							
								
								
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @ -19,8 +19,6 @@ jobs: | ||||
|       - run: npm run build | ||||
|       - run: npm run format-check | ||||
|       - run: npm run lint | ||||
|       - run: npm run pack | ||||
|       - run: npm run gendocs | ||||
|       - run: npm test | ||||
|       - name: Verify no unstaged changes | ||||
|         run: __test__/verify-no-unstaged-changes.sh | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,2 +1,3 @@ | ||||
| __test__/_temp | ||||
| lib/ | ||||
| node_modules/ | ||||
							
								
								
									
										200
									
								
								__test__/git-auth-helper.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								__test__/git-auth-helper.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | ||||
| import * as core from '@actions/core' | ||||
| import * as fs from 'fs' | ||||
| import * as gitAuthHelper from '../lib/git-auth-helper' | ||||
| import * as io from '@actions/io' | ||||
| import * as path from 'path' | ||||
| import {IGitCommandManager} from '../lib/git-command-manager' | ||||
| import {IGitSourceSettings} from '../lib/git-source-settings' | ||||
| 
 | ||||
| const testWorkspace = path.join(__dirname, '_temp', 'git-auth-helper') | ||||
| const originalRunnerTemp = process.env['RUNNER_TEMP'] | ||||
| let workspace: string | ||||
| let gitConfigPath: string | ||||
| let runnerTemp: string | ||||
| let git: IGitCommandManager | ||||
| let settings: IGitSourceSettings | ||||
| 
 | ||||
| describe('git-auth-helper tests', () => { | ||||
|   beforeAll(async () => { | ||||
|     // Clear test workspace
 | ||||
|     await io.rmRF(testWorkspace) | ||||
|   }) | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     // Mock setSecret
 | ||||
|     jest.spyOn(core, 'setSecret').mockImplementation((secret: string) => {}) | ||||
|   }) | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     // Unregister mocks
 | ||||
|     jest.restoreAllMocks() | ||||
|   }) | ||||
| 
 | ||||
|   afterAll(() => { | ||||
|     // Restore RUNNER_TEMP
 | ||||
|     delete process.env['RUNNER_TEMP'] | ||||
|     if (originalRunnerTemp) { | ||||
|       process.env['RUNNER_TEMP'] = originalRunnerTemp | ||||
|     } | ||||
|   }) | ||||
| 
 | ||||
|   const configuresAuthHeader = 'configures auth header' | ||||
|   it(configuresAuthHeader, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(configuresAuthHeader) | ||||
|     expect(settings.authToken).toBeTruthy() // sanity check
 | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await authHelper.configureAuth() | ||||
| 
 | ||||
|     // Assert config
 | ||||
|     const configContent = (await fs.promises.readFile(gitConfigPath)).toString() | ||||
|     const basicCredential = Buffer.from( | ||||
|       `x-access-token:${settings.authToken}`, | ||||
|       'utf8' | ||||
|     ).toString('base64') | ||||
|     expect( | ||||
|       configContent.indexOf( | ||||
|         `http.https://github.com/.extraheader AUTHORIZATION: basic ${basicCredential}` | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|   }) | ||||
| 
 | ||||
|   const configuresAuthHeaderEvenWhenPersistCredentialsFalse = | ||||
|     'configures auth header even when persist credentials false' | ||||
|   it(configuresAuthHeaderEvenWhenPersistCredentialsFalse, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(configuresAuthHeaderEvenWhenPersistCredentialsFalse) | ||||
|     expect(settings.authToken).toBeTruthy() // sanity check
 | ||||
|     settings.persistCredentials = false | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await authHelper.configureAuth() | ||||
| 
 | ||||
|     // Assert config
 | ||||
|     const configContent = (await fs.promises.readFile(gitConfigPath)).toString() | ||||
|     expect( | ||||
|       configContent.indexOf( | ||||
|         `http.https://github.com/.extraheader AUTHORIZATION` | ||||
|       ) | ||||
|     ).toBeGreaterThanOrEqual(0) | ||||
|   }) | ||||
| 
 | ||||
|   const registersBasicCredentialAsSecret = | ||||
|     'registers basic credential as secret' | ||||
|   it(registersBasicCredentialAsSecret, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(registersBasicCredentialAsSecret) | ||||
|     expect(settings.authToken).toBeTruthy() // sanity check
 | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await authHelper.configureAuth() | ||||
| 
 | ||||
|     // Assert secret
 | ||||
|     const setSecretSpy = core.setSecret as jest.Mock<any, any> | ||||
|     expect(setSecretSpy).toHaveBeenCalledTimes(1) | ||||
|     const expectedSecret = Buffer.from( | ||||
|       `x-access-token:${settings.authToken}`, | ||||
|       'utf8' | ||||
|     ).toString('base64') | ||||
|     expect(setSecretSpy).toHaveBeenCalledWith(expectedSecret) | ||||
|   }) | ||||
| 
 | ||||
|   const removesToken = 'removes token' | ||||
|   it(removesToken, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesToken) | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     await authHelper.configureAuth() | ||||
|     let gitConfigContent = ( | ||||
|       await fs.promises.readFile(gitConfigPath) | ||||
|     ).toString() | ||||
|     expect(gitConfigContent.indexOf('http.')).toBeGreaterThanOrEqual(0) // sanity check
 | ||||
| 
 | ||||
|     // Act
 | ||||
|     await authHelper.removeAuth() | ||||
| 
 | ||||
|     // Assert git config
 | ||||
|     gitConfigContent = (await fs.promises.readFile(gitConfigPath)).toString() | ||||
|     expect(gitConfigContent.indexOf('http.')).toBeLessThan(0) | ||||
|   }) | ||||
| }) | ||||
| 
 | ||||
| async function setup(testName: string): Promise<void> { | ||||
|   testName = testName.replace(/[^a-zA-Z0-9_]+/g, '-') | ||||
| 
 | ||||
|   // Directories
 | ||||
|   workspace = path.join(testWorkspace, testName, 'workspace') | ||||
|   runnerTemp = path.join(testWorkspace, testName, 'runner-temp') | ||||
|   await fs.promises.mkdir(workspace, {recursive: true}) | ||||
|   await fs.promises.mkdir(runnerTemp, {recursive: true}) | ||||
|   process.env['RUNNER_TEMP'] = runnerTemp | ||||
| 
 | ||||
|   // Create git config
 | ||||
|   gitConfigPath = path.join(workspace, '.git', 'config') | ||||
|   await fs.promises.mkdir(path.join(workspace, '.git'), {recursive: true}) | ||||
|   await fs.promises.writeFile(path.join(workspace, '.git', 'config'), '') | ||||
| 
 | ||||
|   git = { | ||||
|     branchDelete: jest.fn(), | ||||
|     branchExists: jest.fn(), | ||||
|     branchList: jest.fn(), | ||||
|     checkout: jest.fn(), | ||||
|     checkoutDetach: jest.fn(), | ||||
|     config: jest.fn(async (key: string, value: string) => { | ||||
|       await fs.promises.appendFile(gitConfigPath, `\n${key} ${value}`) | ||||
|     }), | ||||
|     configExists: jest.fn( | ||||
|       async (key: string): Promise<boolean> => { | ||||
|         const content = await fs.promises.readFile(gitConfigPath) | ||||
|         const lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|           .filter(x => x) | ||||
|         return lines.some(x => x.startsWith(key)) | ||||
|       } | ||||
|     ), | ||||
|     fetch: jest.fn(), | ||||
|     getWorkingDirectory: jest.fn(() => workspace), | ||||
|     init: jest.fn(), | ||||
|     isDetached: jest.fn(), | ||||
|     lfsFetch: jest.fn(), | ||||
|     lfsInstall: jest.fn(), | ||||
|     log1: jest.fn(), | ||||
|     remoteAdd: jest.fn(), | ||||
|     setEnvironmentVariable: jest.fn(), | ||||
|     tagExists: jest.fn(), | ||||
|     tryClean: jest.fn(), | ||||
|     tryConfigUnset: jest.fn( | ||||
|       async (key: string): Promise<boolean> => { | ||||
|         let content = await fs.promises.readFile(gitConfigPath) | ||||
|         let lines = content | ||||
|           .toString() | ||||
|           .split('\n') | ||||
|           .filter(x => x) | ||||
|           .filter(x => !x.startsWith(key)) | ||||
|         await fs.promises.writeFile(gitConfigPath, lines.join('\n')) | ||||
|         return true | ||||
|       } | ||||
|     ), | ||||
|     tryDisableAutomaticGarbageCollection: jest.fn(), | ||||
|     tryGetFetchUrl: jest.fn(), | ||||
|     tryReset: jest.fn() | ||||
|   } | ||||
| 
 | ||||
|   settings = { | ||||
|     authToken: 'some auth token', | ||||
|     clean: true, | ||||
|     commit: '', | ||||
|     fetchDepth: 1, | ||||
|     lfs: false, | ||||
|     persistCredentials: true, | ||||
|     ref: 'refs/heads/master', | ||||
|     repositoryName: 'my-repo', | ||||
|     repositoryOwner: 'my-org', | ||||
|     repositoryPath: '' | ||||
|   } | ||||
| } | ||||
							
								
								
									
										382
									
								
								__test__/git-directory-helper.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								__test__/git-directory-helper.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,382 @@ | ||||
| import * as core from '@actions/core' | ||||
| import * as fs from 'fs' | ||||
| import * as gitDirectoryHelper from '../lib/git-directory-helper' | ||||
| import * as io from '@actions/io' | ||||
| import * as path from 'path' | ||||
| import {IGitCommandManager} from '../lib/git-command-manager' | ||||
| 
 | ||||
| const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper') | ||||
| let repositoryPath: string | ||||
| let repositoryUrl: string | ||||
| let clean: boolean | ||||
| let git: IGitCommandManager | ||||
| 
 | ||||
| describe('git-directory-helper tests', () => { | ||||
|   beforeAll(async () => { | ||||
|     // Clear test workspace
 | ||||
|     await io.rmRF(testWorkspace) | ||||
|   }) | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     // Mock error/warning/info/debug
 | ||||
|     jest.spyOn(core, 'error').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'warning').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'info').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'debug').mockImplementation(jest.fn()) | ||||
|   }) | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     // Unregister mocks
 | ||||
|     jest.restoreAllMocks() | ||||
|   }) | ||||
| 
 | ||||
|   const cleansWhenCleanTrue = 'cleans when clean true' | ||||
|   it(cleansWhenCleanTrue, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(cleansWhenCleanTrue) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.tryClean).toHaveBeenCalled() | ||||
|     expect(git.tryReset).toHaveBeenCalled() | ||||
|     expect(core.warning).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const checkoutDetachWhenNotDetached = 'checkout detach when not detached' | ||||
|   it(checkoutDetachWhenNotDetached, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(checkoutDetachWhenNotDetached) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.checkoutDetach).toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const doesNotCheckoutDetachWhenNotAlreadyDetached = | ||||
|     'does not checkout detach when already detached' | ||||
|   it(doesNotCheckoutDetachWhenNotAlreadyDetached, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(doesNotCheckoutDetachWhenNotAlreadyDetached) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
|     const mockIsDetached = git.isDetached as jest.Mock<any, any> | ||||
|     mockIsDetached.mockImplementation(async () => { | ||||
|       return true | ||||
|     }) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.checkoutDetach).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const doesNotCleanWhenCleanFalse = 'does not clean when clean false' | ||||
|   it(doesNotCleanWhenCleanFalse, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(doesNotCleanWhenCleanFalse) | ||||
|     clean = false | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.isDetached).toHaveBeenCalled() | ||||
|     expect(git.branchList).toHaveBeenCalled() | ||||
|     expect(core.warning).not.toHaveBeenCalled() | ||||
|     expect(git.tryClean).not.toHaveBeenCalled() | ||||
|     expect(git.tryReset).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesContentsWhenCleanFails = 'removes contents when clean fails' | ||||
|   it(removesContentsWhenCleanFails, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesContentsWhenCleanFails) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
|     let mockTryClean = git.tryClean as jest.Mock<any, any> | ||||
|     mockTryClean.mockImplementation(async () => { | ||||
|       return false | ||||
|     }) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files).toHaveLength(0) | ||||
|     expect(git.tryClean).toHaveBeenCalled() | ||||
|     expect(core.warning).toHaveBeenCalled() | ||||
|     expect(git.tryReset).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesContentsWhenDifferentRepositoryUrl = | ||||
|     'removes contents when different repository url' | ||||
|   it(removesContentsWhenDifferentRepositoryUrl, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesContentsWhenDifferentRepositoryUrl) | ||||
|     clean = false | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
|     const differentRepositoryUrl = | ||||
|       'https://github.com/my-different-org/my-different-repo' | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       differentRepositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files).toHaveLength(0) | ||||
|     expect(core.warning).not.toHaveBeenCalled() | ||||
|     expect(git.isDetached).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesContentsWhenNoGitDirectory = | ||||
|     'removes contents when no git directory' | ||||
|   it(removesContentsWhenNoGitDirectory, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesContentsWhenNoGitDirectory) | ||||
|     clean = false | ||||
|     await io.rmRF(path.join(repositoryPath, '.git')) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files).toHaveLength(0) | ||||
|     expect(core.warning).not.toHaveBeenCalled() | ||||
|     expect(git.isDetached).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesContentsWhenResetFails = 'removes contents when reset fails' | ||||
|   it(removesContentsWhenResetFails, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesContentsWhenResetFails) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
|     let mockTryReset = git.tryReset as jest.Mock<any, any> | ||||
|     mockTryReset.mockImplementation(async () => { | ||||
|       return false | ||||
|     }) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files).toHaveLength(0) | ||||
|     expect(git.tryClean).toHaveBeenCalled() | ||||
|     expect(git.tryReset).toHaveBeenCalled() | ||||
|     expect(core.warning).toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesContentsWhenUndefinedGitCommandManager = | ||||
|     'removes contents when undefined git command manager' | ||||
|   it(removesContentsWhenUndefinedGitCommandManager, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesContentsWhenUndefinedGitCommandManager) | ||||
|     clean = false | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       undefined, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files).toHaveLength(0) | ||||
|     expect(core.warning).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesLocalBranches = 'removes local branches' | ||||
|   it(removesLocalBranches, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesLocalBranches) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
|     const mockBranchList = git.branchList as jest.Mock<any, any> | ||||
|     mockBranchList.mockImplementation(async (remote: boolean) => { | ||||
|       return remote ? [] : ['local-branch-1', 'local-branch-2'] | ||||
|     }) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-1') | ||||
|     expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-2') | ||||
|   }) | ||||
| 
 | ||||
|   const removesLockFiles = 'removes lock files' | ||||
|   it(removesLockFiles, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesLockFiles) | ||||
|     clean = false | ||||
|     await fs.promises.writeFile( | ||||
|       path.join(repositoryPath, '.git', 'index.lock'), | ||||
|       '' | ||||
|     ) | ||||
|     await fs.promises.writeFile( | ||||
|       path.join(repositoryPath, '.git', 'shallow.lock'), | ||||
|       '' | ||||
|     ) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     let files = await fs.promises.readdir(path.join(repositoryPath, '.git')) | ||||
|     expect(files).toHaveLength(0) | ||||
|     files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.isDetached).toHaveBeenCalled() | ||||
|     expect(git.branchList).toHaveBeenCalled() | ||||
|     expect(core.warning).not.toHaveBeenCalled() | ||||
|     expect(git.tryClean).not.toHaveBeenCalled() | ||||
|     expect(git.tryReset).not.toHaveBeenCalled() | ||||
|   }) | ||||
| 
 | ||||
|   const removesRemoteBranches = 'removes local branches' | ||||
|   it(removesRemoteBranches, async () => { | ||||
|     // Arrange
 | ||||
|     await setup(removesRemoteBranches) | ||||
|     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '') | ||||
|     const mockBranchList = git.branchList as jest.Mock<any, any> | ||||
|     mockBranchList.mockImplementation(async (remote: boolean) => { | ||||
|       return remote ? ['remote-branch-1', 'remote-branch-2'] : [] | ||||
|     }) | ||||
| 
 | ||||
|     // Act
 | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       repositoryPath, | ||||
|       repositoryUrl, | ||||
|       clean | ||||
|     ) | ||||
| 
 | ||||
|     // Assert
 | ||||
|     const files = await fs.promises.readdir(repositoryPath) | ||||
|     expect(files.sort()).toEqual(['.git', 'my-file']) | ||||
|     expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1') | ||||
|     expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2') | ||||
|   }) | ||||
| }) | ||||
| 
 | ||||
| async function setup(testName: string): Promise<void> { | ||||
|   testName = testName.replace(/[^a-zA-Z0-9_]+/g, '-') | ||||
| 
 | ||||
|   // Repository directory
 | ||||
|   repositoryPath = path.join(testWorkspace, testName) | ||||
|   await fs.promises.mkdir(path.join(repositoryPath, '.git'), {recursive: true}) | ||||
| 
 | ||||
|   // Repository URL
 | ||||
|   repositoryUrl = 'https://github.com/my-org/my-repo' | ||||
| 
 | ||||
|   // Clean
 | ||||
|   clean = true | ||||
| 
 | ||||
|   // Git command manager
 | ||||
|   git = { | ||||
|     branchDelete: jest.fn(), | ||||
|     branchExists: jest.fn(), | ||||
|     branchList: jest.fn(async () => { | ||||
|       return [] | ||||
|     }), | ||||
|     checkout: jest.fn(), | ||||
|     checkoutDetach: jest.fn(), | ||||
|     config: jest.fn(), | ||||
|     configExists: jest.fn(), | ||||
|     fetch: jest.fn(), | ||||
|     getWorkingDirectory: jest.fn(() => repositoryPath), | ||||
|     init: jest.fn(), | ||||
|     isDetached: jest.fn(), | ||||
|     lfsFetch: jest.fn(), | ||||
|     lfsInstall: jest.fn(), | ||||
|     log1: jest.fn(), | ||||
|     remoteAdd: jest.fn(), | ||||
|     setEnvironmentVariable: jest.fn(), | ||||
|     tagExists: jest.fn(), | ||||
|     tryClean: jest.fn(async () => { | ||||
|       return true | ||||
|     }), | ||||
|     tryConfigUnset: jest.fn(), | ||||
|     tryDisableAutomaticGarbageCollection: jest.fn(), | ||||
|     tryGetFetchUrl: jest.fn(async () => { | ||||
|       // Sanity check - this function shouldn't be called when the .git directory doesn't exist
 | ||||
|       await fs.promises.stat(path.join(repositoryPath, '.git')) | ||||
|       return repositoryUrl | ||||
|     }), | ||||
|     tryReset: jest.fn(async () => { | ||||
|       return true | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| @ -4,7 +4,7 @@ import * as fsHelper from '../lib/fs-helper' | ||||
| import * as github from '@actions/github' | ||||
| import * as inputHelper from '../lib/input-helper' | ||||
| import * as path from 'path' | ||||
| import {ISourceSettings} from '../lib/git-source-provider' | ||||
| import {IGitSourceSettings} from '../lib/git-source-settings' | ||||
| 
 | ||||
| const originalGitHubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||
| const gitHubWorkspace = path.resolve('/checkout-tests/workspace') | ||||
| @ -17,12 +17,18 @@ let originalContext = {...github.context} | ||||
| 
 | ||||
| describe('input-helper tests', () => { | ||||
|   beforeAll(() => { | ||||
|     // Mock @actions/core getInput()
 | ||||
|     // Mock getInput
 | ||||
|     jest.spyOn(core, 'getInput').mockImplementation((name: string) => { | ||||
|       return inputs[name] | ||||
|     }) | ||||
| 
 | ||||
|     // Mock @actions/github context
 | ||||
|     // Mock error/warning/info/debug
 | ||||
|     jest.spyOn(core, 'error').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'warning').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'info').mockImplementation(jest.fn()) | ||||
|     jest.spyOn(core, 'debug').mockImplementation(jest.fn()) | ||||
| 
 | ||||
|     // Mock github context
 | ||||
|     jest.spyOn(github.context, 'repo', 'get').mockImplementation(() => { | ||||
|       return { | ||||
|         owner: 'some-owner', | ||||
| @ -62,7 +68,7 @@ describe('input-helper tests', () => { | ||||
|   }) | ||||
| 
 | ||||
|   it('sets defaults', () => { | ||||
|     const settings: ISourceSettings = inputHelper.getInputs() | ||||
|     const settings: IGitSourceSettings = inputHelper.getInputs() | ||||
|     expect(settings).toBeTruthy() | ||||
|     expect(settings.authToken).toBeFalsy() | ||||
|     expect(settings.clean).toBe(true) | ||||
| @ -80,7 +86,7 @@ describe('input-helper tests', () => { | ||||
|     let originalRef = github.context.ref | ||||
|     try { | ||||
|       github.context.ref = 'some-unqualified-ref' | ||||
|       const settings: ISourceSettings = inputHelper.getInputs() | ||||
|       const settings: IGitSourceSettings = inputHelper.getInputs() | ||||
|       expect(settings).toBeTruthy() | ||||
|       expect(settings.commit).toBe('1234567890123456789012345678901234567890') | ||||
|       expect(settings.ref).toBe('refs/heads/some-unqualified-ref') | ||||
| @ -98,7 +104,7 @@ describe('input-helper tests', () => { | ||||
| 
 | ||||
|   it('roots path', () => { | ||||
|     inputs.path = 'some-directory/some-subdirectory' | ||||
|     const settings: ISourceSettings = inputHelper.getInputs() | ||||
|     const settings: IGitSourceSettings = inputHelper.getInputs() | ||||
|     expect(settings.repositoryPath).toBe( | ||||
|       path.join(gitHubWorkspace, 'some-directory', 'some-subdirectory') | ||||
|     ) | ||||
| @ -106,21 +112,21 @@ describe('input-helper tests', () => { | ||||
| 
 | ||||
|   it('sets correct default ref/sha for other repo', () => { | ||||
|     inputs.repository = 'some-owner/some-other-repo' | ||||
|     const settings: ISourceSettings = inputHelper.getInputs() | ||||
|     const settings: IGitSourceSettings = inputHelper.getInputs() | ||||
|     expect(settings.ref).toBe('refs/heads/master') | ||||
|     expect(settings.commit).toBeFalsy() | ||||
|   }) | ||||
| 
 | ||||
|   it('sets ref to empty when explicit sha', () => { | ||||
|     inputs.ref = '1111111111222222222233333333334444444444' | ||||
|     const settings: ISourceSettings = inputHelper.getInputs() | ||||
|     const settings: IGitSourceSettings = inputHelper.getInputs() | ||||
|     expect(settings.ref).toBeFalsy() | ||||
|     expect(settings.commit).toBe('1111111111222222222233333333334444444444') | ||||
|   }) | ||||
| 
 | ||||
|   it('sets sha to empty when explicit ref', () => { | ||||
|     inputs.ref = 'refs/heads/some-other-ref' | ||||
|     const settings: ISourceSettings = inputHelper.getInputs() | ||||
|     const settings: IGitSourceSettings = inputHelper.getInputs() | ||||
|     expect(settings.ref).toBe('refs/heads/some-other-ref') | ||||
|     expect(settings.commit).toBeFalsy() | ||||
|   }) | ||||
|  | ||||
							
								
								
									
										334
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										334
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							| @ -5051,6 +5051,98 @@ function coerce (version) { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 287: | ||||
| /***/ (function(__unusedmodule, exports, __webpack_require__) { | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||||
|     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||||
|     return new (P || (P = Promise))(function (resolve, reject) { | ||||
|         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||||
|         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||||
|         function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||||
|         step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||||
|     }); | ||||
| }; | ||||
| var __importStar = (this && this.__importStar) || function (mod) { | ||||
|     if (mod && mod.__esModule) return mod; | ||||
|     var result = {}; | ||||
|     if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||||
|     result["default"] = mod; | ||||
|     return result; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| const core = __importStar(__webpack_require__(470)); | ||||
| const fs = __importStar(__webpack_require__(747)); | ||||
| const path = __importStar(__webpack_require__(622)); | ||||
| const IS_WINDOWS = process.platform === 'win32'; | ||||
| const HOSTNAME = 'github.com'; | ||||
| const EXTRA_HEADER_KEY = `http.https://${HOSTNAME}/.extraheader`; | ||||
| function createAuthHelper(git, settings) { | ||||
|     return new GitAuthHelper(git, settings); | ||||
| } | ||||
| exports.createAuthHelper = createAuthHelper; | ||||
| class GitAuthHelper { | ||||
|     constructor(gitCommandManager, gitSourceSettings) { | ||||
|         this.git = gitCommandManager; | ||||
|         this.settings = gitSourceSettings || {}; | ||||
|     } | ||||
|     configureAuth() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             // Remove possible previous values
 | ||||
|             yield this.removeAuth(); | ||||
|             // Configure new values
 | ||||
|             yield this.configureToken(); | ||||
|         }); | ||||
|     } | ||||
|     removeAuth() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             yield this.removeToken(); | ||||
|         }); | ||||
|     } | ||||
|     configureToken() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             // Configure a placeholder value. This approach avoids the credential being captured
 | ||||
|             // by process creation audit events, which are commonly logged. For more information,
 | ||||
|             // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||
|             const placeholder = `AUTHORIZATION: basic ***`; | ||||
|             yield this.git.config(EXTRA_HEADER_KEY, placeholder); | ||||
|             // Determine the basic credential value
 | ||||
|             const basicCredential = Buffer.from(`x-access-token:${this.settings.authToken}`, 'utf8').toString('base64'); | ||||
|             core.setSecret(basicCredential); | ||||
|             // Replace the value in the config file
 | ||||
|             const configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config'); | ||||
|             let content = (yield fs.promises.readFile(configPath)).toString(); | ||||
|             const placeholderIndex = content.indexOf(placeholder); | ||||
|             if (placeholderIndex < 0 || | ||||
|                 placeholderIndex != content.lastIndexOf(placeholder)) { | ||||
|                 throw new Error('Unable to replace auth placeholder in .git/config'); | ||||
|             } | ||||
|             content = content.replace(placeholder, `AUTHORIZATION: basic ${basicCredential}`); | ||||
|             yield fs.promises.writeFile(configPath, content); | ||||
|         }); | ||||
|     } | ||||
|     removeToken() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             // HTTP extra header
 | ||||
|             yield this.removeGitConfig(EXTRA_HEADER_KEY); | ||||
|         }); | ||||
|     } | ||||
|     removeGitConfig(configKey) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             if ((yield this.git.configExists(configKey)) && | ||||
|                 !(yield this.git.tryConfigUnset(configKey))) { | ||||
|                 // Load the config contents
 | ||||
|                 core.warning(`Failed to remove '${configKey}' from the git config`); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 289: | ||||
| @ -5085,12 +5177,12 @@ const git_version_1 = __webpack_require__(559); | ||||
| // Auth header not supported before 2.9
 | ||||
| // Wire protocol v2 not supported before 2.18
 | ||||
| exports.MinimumGitVersion = new git_version_1.GitVersion('2.18'); | ||||
| function CreateCommandManager(workingDirectory, lfs) { | ||||
| function createCommandManager(workingDirectory, lfs) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         return yield GitCommandManager.createCommandManager(workingDirectory, lfs); | ||||
|     }); | ||||
| } | ||||
| exports.CreateCommandManager = CreateCommandManager; | ||||
| exports.createCommandManager = createCommandManager; | ||||
| class GitCommandManager { | ||||
|     // Private constructor; use createCommandManager()
 | ||||
|     constructor() { | ||||
| @ -5251,6 +5343,9 @@ class GitCommandManager { | ||||
|             yield this.execGit(['remote', 'add', remoteName, remoteUrl]); | ||||
|         }); | ||||
|     } | ||||
|     setEnvironmentVariable(name, value) { | ||||
|         this.gitEnv[name] = value; | ||||
|     } | ||||
|     tagExists(pattern) { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             const output = yield this.execGit(['tag', '--list', pattern]); | ||||
| @ -5420,21 +5515,21 @@ var __importStar = (this && this.__importStar) || function (mod) { | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| const core = __importStar(__webpack_require__(470)); | ||||
| const fs = __importStar(__webpack_require__(747)); | ||||
| const fsHelper = __importStar(__webpack_require__(618)); | ||||
| const gitAuthHelper = __importStar(__webpack_require__(287)); | ||||
| const gitCommandManager = __importStar(__webpack_require__(289)); | ||||
| const gitDirectoryHelper = __importStar(__webpack_require__(438)); | ||||
| const githubApiHelper = __importStar(__webpack_require__(464)); | ||||
| const io = __importStar(__webpack_require__(1)); | ||||
| const path = __importStar(__webpack_require__(622)); | ||||
| const refHelper = __importStar(__webpack_require__(227)); | ||||
| const stateHelper = __importStar(__webpack_require__(153)); | ||||
| const serverUrl = 'https://github.com/'; | ||||
| const authConfigKey = `http.${serverUrl}.extraheader`; | ||||
| const hostname = 'github.com'; | ||||
| function getSource(settings) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         // Repository URL
 | ||||
|         core.info(`Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}`); | ||||
|         const repositoryUrl = `https://github.com/${encodeURIComponent(settings.repositoryOwner)}/${encodeURIComponent(settings.repositoryName)}`; | ||||
|         const repositoryUrl = `https://${hostname}/${encodeURIComponent(settings.repositoryOwner)}/${encodeURIComponent(settings.repositoryName)}`; | ||||
|         // Remove conflicting file path
 | ||||
|         if (fsHelper.fileExistsSync(settings.repositoryPath)) { | ||||
|             yield io.rmRF(settings.repositoryPath); | ||||
| @ -5449,7 +5544,7 @@ function getSource(settings) { | ||||
|         const git = yield getGitCommandManager(settings); | ||||
|         // Prepare existing directory, otherwise recreate
 | ||||
|         if (isExisting) { | ||||
|             yield prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean); | ||||
|             yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean); | ||||
|         } | ||||
|         if (!git) { | ||||
|             // Downloading using REST API
 | ||||
| @ -5469,11 +5564,10 @@ function getSource(settings) { | ||||
|             if (!(yield git.tryDisableAutomaticGarbageCollection())) { | ||||
|                 core.warning(`Unable to turn off git automatic garbage collection. The git fetch operation may trigger garbage collection and cause a delay.`); | ||||
|             } | ||||
|             // Remove possible previous extraheader
 | ||||
|             yield removeGitConfig(git, authConfigKey); | ||||
|             const authHelper = gitAuthHelper.createAuthHelper(git, settings); | ||||
|             try { | ||||
|                 // Config extraheader
 | ||||
|                 yield configureAuthToken(git, settings.authToken); | ||||
|                 // Configure auth
 | ||||
|                 yield authHelper.configureAuth(); | ||||
|                 // LFS install
 | ||||
|                 if (settings.lfs) { | ||||
|                     yield git.lfsInstall(); | ||||
| @ -5495,8 +5589,9 @@ function getSource(settings) { | ||||
|                 yield git.log1(); | ||||
|             } | ||||
|             finally { | ||||
|                 // Remove auth
 | ||||
|                 if (!settings.persistCredentials) { | ||||
|                     yield removeGitConfig(git, authConfigKey); | ||||
|                     yield authHelper.removeAuth(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @ -5512,22 +5607,22 @@ function cleanup(repositoryPath) { | ||||
|         } | ||||
|         let git; | ||||
|         try { | ||||
|             git = yield gitCommandManager.CreateCommandManager(repositoryPath, false); | ||||
|             git = yield gitCommandManager.createCommandManager(repositoryPath, false); | ||||
|         } | ||||
|         catch (_a) { | ||||
|             return; | ||||
|         } | ||||
|         // Remove extraheader
 | ||||
|         yield removeGitConfig(git, authConfigKey); | ||||
|         // Remove auth
 | ||||
|         const authHelper = gitAuthHelper.createAuthHelper(git); | ||||
|         yield authHelper.removeAuth(); | ||||
|     }); | ||||
| } | ||||
| exports.cleanup = cleanup; | ||||
| function getGitCommandManager(settings) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         core.info(`Working directory is '${settings.repositoryPath}'`); | ||||
|         let git = null; | ||||
|         try { | ||||
|             return yield gitCommandManager.CreateCommandManager(settings.repositoryPath, settings.lfs); | ||||
|             return yield gitCommandManager.createCommandManager(settings.repositoryPath, settings.lfs); | ||||
|         } | ||||
|         catch (err) { | ||||
|             // Git is required for LFS
 | ||||
| @ -5535,108 +5630,7 @@ function getGitCommandManager(settings) { | ||||
|                 throw err; | ||||
|             } | ||||
|             // Otherwise fallback to REST API
 | ||||
|             return null; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         let remove = false; | ||||
|         // Check whether using git or REST API
 | ||||
|         if (!git) { | ||||
|             remove = true; | ||||
|         } | ||||
|         // Fetch URL does not match
 | ||||
|         else if (!fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) || | ||||
|             repositoryUrl !== (yield git.tryGetFetchUrl())) { | ||||
|             remove = true; | ||||
|         } | ||||
|         else { | ||||
|             // Delete any index.lock and shallow.lock left by a previously canceled run or crashed git process
 | ||||
|             const lockPaths = [ | ||||
|                 path.join(repositoryPath, '.git', 'index.lock'), | ||||
|                 path.join(repositoryPath, '.git', 'shallow.lock') | ||||
|             ]; | ||||
|             for (const lockPath of lockPaths) { | ||||
|                 try { | ||||
|                     yield io.rmRF(lockPath); | ||||
|                 } | ||||
|                 catch (error) { | ||||
|                     core.debug(`Unable to delete '${lockPath}'. ${error.message}`); | ||||
|                 } | ||||
|             } | ||||
|             try { | ||||
|                 // Checkout detached HEAD
 | ||||
|                 if (!(yield git.isDetached())) { | ||||
|                     yield git.checkoutDetach(); | ||||
|                 } | ||||
|                 // Remove all refs/heads/*
 | ||||
|                 let branches = yield git.branchList(false); | ||||
|                 for (const branch of branches) { | ||||
|                     yield git.branchDelete(false, branch); | ||||
|                 } | ||||
|                 // Remove all refs/remotes/origin/* to avoid conflicts
 | ||||
|                 branches = yield git.branchList(true); | ||||
|                 for (const branch of branches) { | ||||
|                     yield git.branchDelete(true, branch); | ||||
|                 } | ||||
|                 // Clean
 | ||||
|                 if (clean) { | ||||
|                     if (!(yield git.tryClean())) { | ||||
|                         core.debug(`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`); | ||||
|                         remove = true; | ||||
|                     } | ||||
|                     else if (!(yield git.tryReset())) { | ||||
|                         remove = true; | ||||
|                     } | ||||
|                     if (remove) { | ||||
|                         core.warning(`Unable to clean or reset the repository. The repository will be recreated instead.`); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (error) { | ||||
|                 core.warning(`Unable to prepare the existing repository. The repository will be recreated instead.`); | ||||
|                 remove = true; | ||||
|             } | ||||
|         } | ||||
|         if (remove) { | ||||
|             // Delete the contents of the directory. Don't delete the directory itself
 | ||||
|             // since it might be the current working directory.
 | ||||
|             core.info(`Deleting the contents of '${repositoryPath}'`); | ||||
|             for (const file of yield fs.promises.readdir(repositoryPath)) { | ||||
|                 yield io.rmRF(path.join(repositoryPath, file)); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| function configureAuthToken(git, authToken) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         // Configure a placeholder value. This approach avoids the credential being captured
 | ||||
|         // by process creation audit events, which are commonly logged. For more information,
 | ||||
|         // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||
|         const placeholder = `AUTHORIZATION: basic ***`; | ||||
|         yield git.config(authConfigKey, placeholder); | ||||
|         // Determine the basic credential value
 | ||||
|         const basicCredential = Buffer.from(`x-access-token:${authToken}`, 'utf8').toString('base64'); | ||||
|         core.setSecret(basicCredential); | ||||
|         // Replace the value in the config file
 | ||||
|         const configPath = path.join(git.getWorkingDirectory(), '.git', 'config'); | ||||
|         let content = (yield fs.promises.readFile(configPath)).toString(); | ||||
|         const placeholderIndex = content.indexOf(placeholder); | ||||
|         if (placeholderIndex < 0 || | ||||
|             placeholderIndex != content.lastIndexOf(placeholder)) { | ||||
|             throw new Error('Unable to replace auth placeholder in .git/config'); | ||||
|         } | ||||
|         content = content.replace(placeholder, `AUTHORIZATION: basic ${basicCredential}`); | ||||
|         yield fs.promises.writeFile(configPath, content); | ||||
|     }); | ||||
| } | ||||
| function removeGitConfig(git, configKey) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         if ((yield git.configExists(configKey)) && | ||||
|             !(yield git.tryConfigUnset(configKey))) { | ||||
|             // Load the config contents
 | ||||
|             core.warning(`Failed to remove '${configKey}' from the git config`); | ||||
|             return undefined; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| @ -6874,6 +6868,108 @@ function escape(s) { | ||||
| } | ||||
| //# sourceMappingURL=command.js.map
 | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 438: | ||||
| /***/ (function(__unusedmodule, exports, __webpack_require__) { | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||||
|     function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||||
|     return new (P || (P = Promise))(function (resolve, reject) { | ||||
|         function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||||
|         function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||||
|         function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||||
|         step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||||
|     }); | ||||
| }; | ||||
| var __importStar = (this && this.__importStar) || function (mod) { | ||||
|     if (mod && mod.__esModule) return mod; | ||||
|     var result = {}; | ||||
|     if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||||
|     result["default"] = mod; | ||||
|     return result; | ||||
| }; | ||||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||||
| const core = __importStar(__webpack_require__(470)); | ||||
| const fs = __importStar(__webpack_require__(747)); | ||||
| const fsHelper = __importStar(__webpack_require__(618)); | ||||
| const io = __importStar(__webpack_require__(1)); | ||||
| const path = __importStar(__webpack_require__(622)); | ||||
| function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) { | ||||
|     return __awaiter(this, void 0, void 0, function* () { | ||||
|         let remove = false; | ||||
|         // Check whether using git or REST API
 | ||||
|         if (!git) { | ||||
|             remove = true; | ||||
|         } | ||||
|         // Fetch URL does not match
 | ||||
|         else if (!fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) || | ||||
|             repositoryUrl !== (yield git.tryGetFetchUrl())) { | ||||
|             remove = true; | ||||
|         } | ||||
|         else { | ||||
|             // Delete any index.lock and shallow.lock left by a previously canceled run or crashed git process
 | ||||
|             const lockPaths = [ | ||||
|                 path.join(repositoryPath, '.git', 'index.lock'), | ||||
|                 path.join(repositoryPath, '.git', 'shallow.lock') | ||||
|             ]; | ||||
|             for (const lockPath of lockPaths) { | ||||
|                 try { | ||||
|                     yield io.rmRF(lockPath); | ||||
|                 } | ||||
|                 catch (error) { | ||||
|                     core.debug(`Unable to delete '${lockPath}'. ${error.message}`); | ||||
|                 } | ||||
|             } | ||||
|             try { | ||||
|                 // Checkout detached HEAD
 | ||||
|                 if (!(yield git.isDetached())) { | ||||
|                     yield git.checkoutDetach(); | ||||
|                 } | ||||
|                 // Remove all refs/heads/*
 | ||||
|                 let branches = yield git.branchList(false); | ||||
|                 for (const branch of branches) { | ||||
|                     yield git.branchDelete(false, branch); | ||||
|                 } | ||||
|                 // Remove all refs/remotes/origin/* to avoid conflicts
 | ||||
|                 branches = yield git.branchList(true); | ||||
|                 for (const branch of branches) { | ||||
|                     yield git.branchDelete(true, branch); | ||||
|                 } | ||||
|                 // Clean
 | ||||
|                 if (clean) { | ||||
|                     if (!(yield git.tryClean())) { | ||||
|                         core.debug(`The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.`); | ||||
|                         remove = true; | ||||
|                     } | ||||
|                     else if (!(yield git.tryReset())) { | ||||
|                         remove = true; | ||||
|                     } | ||||
|                     if (remove) { | ||||
|                         core.warning(`Unable to clean or reset the repository. The repository will be recreated instead.`); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (error) { | ||||
|                 core.warning(`Unable to prepare the existing repository. The repository will be recreated instead.`); | ||||
|                 remove = true; | ||||
|             } | ||||
|         } | ||||
|         if (remove) { | ||||
|             // Delete the contents of the directory. Don't delete the directory itself
 | ||||
|             // since it might be the current working directory.
 | ||||
|             core.info(`Deleting the contents of '${repositoryPath}'`); | ||||
|             for (const file of yield fs.promises.readdir(repositoryPath)) { | ||||
|                 yield io.rmRF(path.join(repositoryPath, file)); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| exports.prepareExistingDirectory = prepareExistingDirectory; | ||||
| 
 | ||||
| 
 | ||||
| /***/ }), | ||||
| 
 | ||||
| /***/ 453: | ||||
|  | ||||
| @ -4,14 +4,11 @@ | ||||
|   "description": "checkout action", | ||||
|   "main": "lib/main.js", | ||||
|   "scripts": { | ||||
|     "build": "tsc", | ||||
|     "build": "tsc && ncc build && node lib/misc/generate-docs.js", | ||||
|     "format": "prettier --write **/*.ts", | ||||
|     "format-check": "prettier --check **/*.ts", | ||||
|     "lint": "eslint src/**/*.ts", | ||||
|     "pack": "ncc build", | ||||
|     "gendocs": "node lib/misc/generate-docs.js", | ||||
|     "test": "jest", | ||||
|     "all": "npm run build && npm run format && npm run lint && npm run pack && npm run gendocs && npm test" | ||||
|     "test": "jest" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|  | ||||
							
								
								
									
										102
									
								
								src/git-auth-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/git-auth-helper.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | ||||
| import * as assert from 'assert' | ||||
| import * as core from '@actions/core' | ||||
| import * as exec from '@actions/exec' | ||||
| import * as fs from 'fs' | ||||
| import * as io from '@actions/io' | ||||
| import * as os from 'os' | ||||
| import * as path from 'path' | ||||
| import * as stateHelper from './state-helper' | ||||
| import {default as uuid} from 'uuid/v4' | ||||
| import {IGitCommandManager} from './git-command-manager' | ||||
| import {IGitSourceSettings} from './git-source-settings' | ||||
| 
 | ||||
| const IS_WINDOWS = process.platform === 'win32' | ||||
| const HOSTNAME = 'github.com' | ||||
| const EXTRA_HEADER_KEY = `http.https://${HOSTNAME}/.extraheader` | ||||
| 
 | ||||
| export interface IGitAuthHelper { | ||||
|   configureAuth(): Promise<void> | ||||
|   removeAuth(): Promise<void> | ||||
| } | ||||
| 
 | ||||
| export function createAuthHelper( | ||||
|   git: IGitCommandManager, | ||||
|   settings?: IGitSourceSettings | ||||
| ): IGitAuthHelper { | ||||
|   return new GitAuthHelper(git, settings) | ||||
| } | ||||
| 
 | ||||
| class GitAuthHelper { | ||||
|   private git: IGitCommandManager | ||||
|   private settings: IGitSourceSettings | ||||
| 
 | ||||
|   constructor( | ||||
|     gitCommandManager: IGitCommandManager, | ||||
|     gitSourceSettings?: IGitSourceSettings | ||||
|   ) { | ||||
|     this.git = gitCommandManager | ||||
|     this.settings = gitSourceSettings || (({} as unknown) as IGitSourceSettings) | ||||
|   } | ||||
| 
 | ||||
|   async configureAuth(): Promise<void> { | ||||
|     // Remove possible previous values
 | ||||
|     await this.removeAuth() | ||||
| 
 | ||||
|     // Configure new values
 | ||||
|     await this.configureToken() | ||||
|   } | ||||
| 
 | ||||
|   async removeAuth(): Promise<void> { | ||||
|     await this.removeToken() | ||||
|   } | ||||
| 
 | ||||
|   private async configureToken(): Promise<void> { | ||||
|     // Configure a placeholder value. This approach avoids the credential being captured
 | ||||
|     // by process creation audit events, which are commonly logged. For more information,
 | ||||
|     // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||
|     const placeholder = `AUTHORIZATION: basic ***` | ||||
|     await this.git.config(EXTRA_HEADER_KEY, placeholder) | ||||
| 
 | ||||
|     // Determine the basic credential value
 | ||||
|     const basicCredential = Buffer.from( | ||||
|       `x-access-token:${this.settings.authToken}`, | ||||
|       'utf8' | ||||
|     ).toString('base64') | ||||
|     core.setSecret(basicCredential) | ||||
| 
 | ||||
|     // Replace the value in the config file
 | ||||
|     const configPath = path.join( | ||||
|       this.git.getWorkingDirectory(), | ||||
|       '.git', | ||||
|       'config' | ||||
|     ) | ||||
|     let content = (await fs.promises.readFile(configPath)).toString() | ||||
|     const placeholderIndex = content.indexOf(placeholder) | ||||
|     if ( | ||||
|       placeholderIndex < 0 || | ||||
|       placeholderIndex != content.lastIndexOf(placeholder) | ||||
|     ) { | ||||
|       throw new Error('Unable to replace auth placeholder in .git/config') | ||||
|     } | ||||
|     content = content.replace( | ||||
|       placeholder, | ||||
|       `AUTHORIZATION: basic ${basicCredential}` | ||||
|     ) | ||||
|     await fs.promises.writeFile(configPath, content) | ||||
|   } | ||||
| 
 | ||||
|   private async removeToken(): Promise<void> { | ||||
|     // HTTP extra header
 | ||||
|     await this.removeGitConfig(EXTRA_HEADER_KEY) | ||||
|   } | ||||
| 
 | ||||
|   private async removeGitConfig(configKey: string): Promise<void> { | ||||
|     if ( | ||||
|       (await this.git.configExists(configKey)) && | ||||
|       !(await this.git.tryConfigUnset(configKey)) | ||||
|     ) { | ||||
|       // Load the config contents
 | ||||
|       core.warning(`Failed to remove '${configKey}' from the git config`) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -26,6 +26,7 @@ export interface IGitCommandManager { | ||||
|   lfsInstall(): Promise<void> | ||||
|   log1(): Promise<void> | ||||
|   remoteAdd(remoteName: string, remoteUrl: string): Promise<void> | ||||
|   setEnvironmentVariable(name: string, value: string): void | ||||
|   tagExists(pattern: string): Promise<boolean> | ||||
|   tryClean(): Promise<boolean> | ||||
|   tryConfigUnset(configKey: string): Promise<boolean> | ||||
| @ -34,7 +35,7 @@ export interface IGitCommandManager { | ||||
|   tryReset(): Promise<boolean> | ||||
| } | ||||
| 
 | ||||
| export async function CreateCommandManager( | ||||
| export async function createCommandManager( | ||||
|   workingDirectory: string, | ||||
|   lfs: boolean | ||||
| ): Promise<IGitCommandManager> { | ||||
| @ -207,6 +208,10 @@ class GitCommandManager { | ||||
|     await this.execGit(['remote', 'add', remoteName, remoteUrl]) | ||||
|   } | ||||
| 
 | ||||
|   setEnvironmentVariable(name: string, value: string): void { | ||||
|     this.gitEnv[name] = value | ||||
|   } | ||||
| 
 | ||||
|   async tagExists(pattern: string): Promise<boolean> { | ||||
|     const output = await this.execGit(['tag', '--list', pattern]) | ||||
|     return !!output.stdout.trim() | ||||
|  | ||||
							
								
								
									
										91
									
								
								src/git-directory-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/git-directory-helper.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | ||||
| import * as core from '@actions/core' | ||||
| import * as fs from 'fs' | ||||
| import * as fsHelper from './fs-helper' | ||||
| import * as io from '@actions/io' | ||||
| import * as path from 'path' | ||||
| import {IGitCommandManager} from './git-command-manager' | ||||
| 
 | ||||
| export async function prepareExistingDirectory( | ||||
|   git: IGitCommandManager | undefined, | ||||
|   repositoryPath: string, | ||||
|   repositoryUrl: string, | ||||
|   clean: boolean | ||||
| ): Promise<void> { | ||||
|   let remove = false | ||||
| 
 | ||||
|   // Check whether using git or REST API
 | ||||
|   if (!git) { | ||||
|     remove = true | ||||
|   } | ||||
|   // Fetch URL does not match
 | ||||
|   else if ( | ||||
|     !fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) || | ||||
|     repositoryUrl !== (await git.tryGetFetchUrl()) | ||||
|   ) { | ||||
|     remove = true | ||||
|   } else { | ||||
|     // Delete any index.lock and shallow.lock left by a previously canceled run or crashed git process
 | ||||
|     const lockPaths = [ | ||||
|       path.join(repositoryPath, '.git', 'index.lock'), | ||||
|       path.join(repositoryPath, '.git', 'shallow.lock') | ||||
|     ] | ||||
|     for (const lockPath of lockPaths) { | ||||
|       try { | ||||
|         await io.rmRF(lockPath) | ||||
|       } catch (error) { | ||||
|         core.debug(`Unable to delete '${lockPath}'. ${error.message}`) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       // Checkout detached HEAD
 | ||||
|       if (!(await git.isDetached())) { | ||||
|         await git.checkoutDetach() | ||||
|       } | ||||
| 
 | ||||
|       // Remove all refs/heads/*
 | ||||
|       let branches = await git.branchList(false) | ||||
|       for (const branch of branches) { | ||||
|         await git.branchDelete(false, branch) | ||||
|       } | ||||
| 
 | ||||
|       // Remove all refs/remotes/origin/* to avoid conflicts
 | ||||
|       branches = await git.branchList(true) | ||||
|       for (const branch of branches) { | ||||
|         await git.branchDelete(true, branch) | ||||
|       } | ||||
| 
 | ||||
|       // Clean
 | ||||
|       if (clean) { | ||||
|         if (!(await git.tryClean())) { | ||||
|           core.debug( | ||||
|             `The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.` | ||||
|           ) | ||||
|           remove = true | ||||
|         } else if (!(await git.tryReset())) { | ||||
|           remove = true | ||||
|         } | ||||
| 
 | ||||
|         if (remove) { | ||||
|           core.warning( | ||||
|             `Unable to clean or reset the repository. The repository will be recreated instead.` | ||||
|           ) | ||||
|         } | ||||
|       } | ||||
|     } catch (error) { | ||||
|       core.warning( | ||||
|         `Unable to prepare the existing repository. The repository will be recreated instead.` | ||||
|       ) | ||||
|       remove = true | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (remove) { | ||||
|     // Delete the contents of the directory. Don't delete the directory itself
 | ||||
|     // since it might be the current working directory.
 | ||||
|     core.info(`Deleting the contents of '${repositoryPath}'`) | ||||
|     for (const file of await fs.promises.readdir(repositoryPath)) { | ||||
|       await io.rmRF(path.join(repositoryPath, file)) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,36 +1,24 @@ | ||||
| import * as core from '@actions/core' | ||||
| import * as fs from 'fs' | ||||
| import * as fsHelper from './fs-helper' | ||||
| import * as gitAuthHelper from './git-auth-helper' | ||||
| import * as gitCommandManager from './git-command-manager' | ||||
| import * as gitDirectoryHelper from './git-directory-helper' | ||||
| import * as githubApiHelper from './github-api-helper' | ||||
| import * as io from '@actions/io' | ||||
| import * as path from 'path' | ||||
| import * as refHelper from './ref-helper' | ||||
| import * as stateHelper from './state-helper' | ||||
| import {IGitCommandManager} from './git-command-manager' | ||||
| import {IGitSourceSettings} from './git-source-settings' | ||||
| 
 | ||||
| const serverUrl = 'https://github.com/' | ||||
| const authConfigKey = `http.${serverUrl}.extraheader` | ||||
| const hostname = 'github.com' | ||||
| 
 | ||||
| export interface ISourceSettings { | ||||
|   repositoryPath: string | ||||
|   repositoryOwner: string | ||||
|   repositoryName: string | ||||
|   ref: string | ||||
|   commit: string | ||||
|   clean: boolean | ||||
|   fetchDepth: number | ||||
|   lfs: boolean | ||||
|   authToken: string | ||||
|   persistCredentials: boolean | ||||
| } | ||||
| 
 | ||||
| export async function getSource(settings: ISourceSettings): Promise<void> { | ||||
| export async function getSource(settings: IGitSourceSettings): Promise<void> { | ||||
|   // Repository URL
 | ||||
|   core.info( | ||||
|     `Syncing repository: ${settings.repositoryOwner}/${settings.repositoryName}` | ||||
|   ) | ||||
|   const repositoryUrl = `https://github.com/${encodeURIComponent( | ||||
|   const repositoryUrl = `https://${hostname}/${encodeURIComponent( | ||||
|     settings.repositoryOwner | ||||
|   )}/${encodeURIComponent(settings.repositoryName)}` | ||||
| 
 | ||||
| @ -51,7 +39,7 @@ export async function getSource(settings: ISourceSettings): Promise<void> { | ||||
| 
 | ||||
|   // Prepare existing directory, otherwise recreate
 | ||||
|   if (isExisting) { | ||||
|     await prepareExistingDirectory( | ||||
|     await gitDirectoryHelper.prepareExistingDirectory( | ||||
|       git, | ||||
|       settings.repositoryPath, | ||||
|       repositoryUrl, | ||||
| @ -92,12 +80,10 @@ export async function getSource(settings: ISourceSettings): Promise<void> { | ||||
|       ) | ||||
|     } | ||||
| 
 | ||||
|     // Remove possible previous extraheader
 | ||||
|     await removeGitConfig(git, authConfigKey) | ||||
| 
 | ||||
|     const authHelper = gitAuthHelper.createAuthHelper(git, settings) | ||||
|     try { | ||||
|       // Config extraheader
 | ||||
|       await configureAuthToken(git, settings.authToken) | ||||
|       // Configure auth
 | ||||
|       await authHelper.configureAuth() | ||||
| 
 | ||||
|       // LFS install
 | ||||
|       if (settings.lfs) { | ||||
| @ -128,8 +114,9 @@ export async function getSource(settings: ISourceSettings): Promise<void> { | ||||
|       // Dump some info about the checked out commit
 | ||||
|       await git.log1() | ||||
|     } finally { | ||||
|       // Remove auth
 | ||||
|       if (!settings.persistCredentials) { | ||||
|         await removeGitConfig(git, authConfigKey) | ||||
|         await authHelper.removeAuth() | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @ -146,22 +133,22 @@ export async function cleanup(repositoryPath: string): Promise<void> { | ||||
| 
 | ||||
|   let git: IGitCommandManager | ||||
|   try { | ||||
|     git = await gitCommandManager.CreateCommandManager(repositoryPath, false) | ||||
|     git = await gitCommandManager.createCommandManager(repositoryPath, false) | ||||
|   } catch { | ||||
|     return | ||||
|   } | ||||
| 
 | ||||
|   // Remove extraheader
 | ||||
|   await removeGitConfig(git, authConfigKey) | ||||
|   // Remove auth
 | ||||
|   const authHelper = gitAuthHelper.createAuthHelper(git) | ||||
|   await authHelper.removeAuth() | ||||
| } | ||||
| 
 | ||||
| async function getGitCommandManager( | ||||
|   settings: ISourceSettings | ||||
| ): Promise<IGitCommandManager> { | ||||
|   settings: IGitSourceSettings | ||||
| ): Promise<IGitCommandManager | undefined> { | ||||
|   core.info(`Working directory is '${settings.repositoryPath}'`) | ||||
|   let git = (null as unknown) as IGitCommandManager | ||||
|   try { | ||||
|     return await gitCommandManager.CreateCommandManager( | ||||
|     return await gitCommandManager.createCommandManager( | ||||
|       settings.repositoryPath, | ||||
|       settings.lfs | ||||
|     ) | ||||
| @ -172,138 +159,6 @@ async function getGitCommandManager( | ||||
|     } | ||||
| 
 | ||||
|     // Otherwise fallback to REST API
 | ||||
|     return (null as unknown) as IGitCommandManager | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| async function prepareExistingDirectory( | ||||
|   git: IGitCommandManager, | ||||
|   repositoryPath: string, | ||||
|   repositoryUrl: string, | ||||
|   clean: boolean | ||||
| ): Promise<void> { | ||||
|   let remove = false | ||||
| 
 | ||||
|   // Check whether using git or REST API
 | ||||
|   if (!git) { | ||||
|     remove = true | ||||
|   } | ||||
|   // Fetch URL does not match
 | ||||
|   else if ( | ||||
|     !fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) || | ||||
|     repositoryUrl !== (await git.tryGetFetchUrl()) | ||||
|   ) { | ||||
|     remove = true | ||||
|   } else { | ||||
|     // Delete any index.lock and shallow.lock left by a previously canceled run or crashed git process
 | ||||
|     const lockPaths = [ | ||||
|       path.join(repositoryPath, '.git', 'index.lock'), | ||||
|       path.join(repositoryPath, '.git', 'shallow.lock') | ||||
|     ] | ||||
|     for (const lockPath of lockPaths) { | ||||
|       try { | ||||
|         await io.rmRF(lockPath) | ||||
|       } catch (error) { | ||||
|         core.debug(`Unable to delete '${lockPath}'. ${error.message}`) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       // Checkout detached HEAD
 | ||||
|       if (!(await git.isDetached())) { | ||||
|         await git.checkoutDetach() | ||||
|       } | ||||
| 
 | ||||
|       // Remove all refs/heads/*
 | ||||
|       let branches = await git.branchList(false) | ||||
|       for (const branch of branches) { | ||||
|         await git.branchDelete(false, branch) | ||||
|       } | ||||
| 
 | ||||
|       // Remove all refs/remotes/origin/* to avoid conflicts
 | ||||
|       branches = await git.branchList(true) | ||||
|       for (const branch of branches) { | ||||
|         await git.branchDelete(true, branch) | ||||
|       } | ||||
| 
 | ||||
|       // Clean
 | ||||
|       if (clean) { | ||||
|         if (!(await git.tryClean())) { | ||||
|           core.debug( | ||||
|             `The clean command failed. This might be caused by: 1) path too long, 2) permission issue, or 3) file in use. For futher investigation, manually run 'git clean -ffdx' on the directory '${repositoryPath}'.` | ||||
|           ) | ||||
|           remove = true | ||||
|         } else if (!(await git.tryReset())) { | ||||
|           remove = true | ||||
|         } | ||||
| 
 | ||||
|         if (remove) { | ||||
|           core.warning( | ||||
|             `Unable to clean or reset the repository. The repository will be recreated instead.` | ||||
|           ) | ||||
|         } | ||||
|       } | ||||
|     } catch (error) { | ||||
|       core.warning( | ||||
|         `Unable to prepare the existing repository. The repository will be recreated instead.` | ||||
|       ) | ||||
|       remove = true | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (remove) { | ||||
|     // Delete the contents of the directory. Don't delete the directory itself
 | ||||
|     // since it might be the current working directory.
 | ||||
|     core.info(`Deleting the contents of '${repositoryPath}'`) | ||||
|     for (const file of await fs.promises.readdir(repositoryPath)) { | ||||
|       await io.rmRF(path.join(repositoryPath, file)) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| async function configureAuthToken( | ||||
|   git: IGitCommandManager, | ||||
|   authToken: string | ||||
| ): Promise<void> { | ||||
|   // Configure a placeholder value. This approach avoids the credential being captured
 | ||||
|   // by process creation audit events, which are commonly logged. For more information,
 | ||||
|   // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | ||||
|   const placeholder = `AUTHORIZATION: basic ***` | ||||
|   await git.config(authConfigKey, placeholder) | ||||
| 
 | ||||
|   // Determine the basic credential value
 | ||||
|   const basicCredential = Buffer.from( | ||||
|     `x-access-token:${authToken}`, | ||||
|     'utf8' | ||||
|   ).toString('base64') | ||||
|   core.setSecret(basicCredential) | ||||
| 
 | ||||
|   // Replace the value in the config file
 | ||||
|   const configPath = path.join(git.getWorkingDirectory(), '.git', 'config') | ||||
|   let content = (await fs.promises.readFile(configPath)).toString() | ||||
|   const placeholderIndex = content.indexOf(placeholder) | ||||
|   if ( | ||||
|     placeholderIndex < 0 || | ||||
|     placeholderIndex != content.lastIndexOf(placeholder) | ||||
|   ) { | ||||
|     throw new Error('Unable to replace auth placeholder in .git/config') | ||||
|   } | ||||
|   content = content.replace( | ||||
|     placeholder, | ||||
|     `AUTHORIZATION: basic ${basicCredential}` | ||||
|   ) | ||||
|   await fs.promises.writeFile(configPath, content) | ||||
| } | ||||
| 
 | ||||
| async function removeGitConfig( | ||||
|   git: IGitCommandManager, | ||||
|   configKey: string | ||||
| ): Promise<void> { | ||||
|   if ( | ||||
|     (await git.configExists(configKey)) && | ||||
|     !(await git.tryConfigUnset(configKey)) | ||||
|   ) { | ||||
|     // Load the config contents
 | ||||
|     core.warning(`Failed to remove '${configKey}' from the git config`) | ||||
|     return undefined | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										12
									
								
								src/git-source-settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/git-source-settings.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| export interface IGitSourceSettings { | ||||
|   repositoryPath: string | ||||
|   repositoryOwner: string | ||||
|   repositoryName: string | ||||
|   ref: string | ||||
|   commit: string | ||||
|   clean: boolean | ||||
|   fetchDepth: number | ||||
|   lfs: boolean | ||||
|   authToken: string | ||||
|   persistCredentials: boolean | ||||
| } | ||||
| @ -2,10 +2,10 @@ import * as core from '@actions/core' | ||||
| import * as fsHelper from './fs-helper' | ||||
| import * as github from '@actions/github' | ||||
| import * as path from 'path' | ||||
| import {ISourceSettings} from './git-source-provider' | ||||
| import {IGitSourceSettings} from './git-source-settings' | ||||
| 
 | ||||
| export function getInputs(): ISourceSettings { | ||||
|   const result = ({} as unknown) as ISourceSettings | ||||
| export function getInputs(): IGitSourceSettings { | ||||
|   const result = ({} as unknown) as IGitSourceSettings | ||||
| 
 | ||||
|   // GitHub workspace
 | ||||
|   let githubWorkspacePath = process.env['GITHUB_WORKSPACE'] | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| import * as core from '@actions/core' | ||||
| import * as coreCommand from '@actions/core/lib/command' | ||||
| 
 | ||||
| /** | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user