瀏覽代碼

Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in (#1196)

* Fix Self hosted runner issue wrt bad submodules - solution cleanup working space.

* Fix format with npm run format output

* Add mock implementation for new function submoduleStatus

* Add 2  test cases for submodule status.

* Codeql-Action Analyse revert v1 to v2

---------

Co-authored-by: Bassem Dghaidi <568794+Link-@users.noreply.github.com>
Co-authored-by: sminnie <minnie@sankhe.com>
SKi 2 年之前
父節點
當前提交
47fbe2df0a
共有 5 個文件被更改,包括 90 次插入0 次删除
  1. 3 0
      __test__/git-auth-helper.test.ts
  2. 62 0
      __test__/git-directory-helper.test.ts
  3. 12 0
      dist/index.js
  4. 7 0
      src/git-command-manager.ts
  5. 6 0
      src/git-directory-helper.ts

+ 3 - 0
__test__/git-auth-helper.test.ts

@@ -770,6 +770,9 @@ async function setup(testName: string): Promise<void> {
       return ''
     }),
     submoduleSync: jest.fn(),
+    submoduleStatus: jest.fn(async () => {
+      return true
+    }),
     submoduleUpdate: jest.fn(),
     tagExists: jest.fn(),
     tryClean: jest.fn(),

+ 62 - 0
__test__/git-directory-helper.test.ts

@@ -281,6 +281,65 @@ describe('git-directory-helper tests', () => {
     expect(git.branchDelete).toHaveBeenCalledWith(false, 'local-branch-2')
   })
 
+  const cleanWhenSubmoduleStatusIsFalse =
+    'cleans when submodule status is false'
+
+  it(cleanWhenSubmoduleStatusIsFalse, async () => {
+    // Arrange
+    await setup(cleanWhenSubmoduleStatusIsFalse)
+    await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
+
+    //mock bad submodule
+
+    const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
+    submoduleStatus.mockImplementation(async (remote: boolean) => {
+      return false
+    })
+
+    // Act
+    await gitDirectoryHelper.prepareExistingDirectory(
+      git,
+      repositoryPath,
+      repositoryUrl,
+      clean,
+      ref
+    )
+
+    // Assert
+    const files = await fs.promises.readdir(repositoryPath)
+    expect(files).toHaveLength(0)
+    expect(git.tryClean).toHaveBeenCalled()
+  })
+
+  const doesNotCleanWhenSubmoduleStatusIsTrue =
+    'does not clean when submodule status is true'
+
+  it(doesNotCleanWhenSubmoduleStatusIsTrue, async () => {
+    // Arrange
+    await setup(doesNotCleanWhenSubmoduleStatusIsTrue)
+    await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
+
+    const submoduleStatus = git.submoduleStatus as jest.Mock<any, any>
+    submoduleStatus.mockImplementation(async (remote: boolean) => {
+      return true
+    })
+
+    // Act
+    await gitDirectoryHelper.prepareExistingDirectory(
+      git,
+      repositoryPath,
+      repositoryUrl,
+      clean,
+      ref
+    )
+
+    // Assert
+
+    const files = await fs.promises.readdir(repositoryPath)
+    expect(files.sort()).toEqual(['.git', 'my-file'])
+    expect(git.tryClean).toHaveBeenCalled()
+  })
+
   const removesLockFiles = 'removes lock files'
   it(removesLockFiles, async () => {
     // Arrange
@@ -423,6 +482,9 @@ async function setup(testName: string): Promise<void> {
     submoduleForeach: jest.fn(),
     submoduleSync: jest.fn(),
     submoduleUpdate: jest.fn(),
+    submoduleStatus: jest.fn(async () => {
+      return true
+    }),
     tagExists: jest.fn(),
     tryClean: jest.fn(async () => {
       return true

+ 12 - 0
dist/index.js

@@ -765,6 +765,13 @@ class GitCommandManager {
             yield this.execGit(args);
         });
     }
+    submoduleStatus() {
+        return __awaiter(this, void 0, void 0, function* () {
+            const output = yield this.execGit(['submodule', 'status'], true);
+            core.debug(output.stdout);
+            return output.exitCode === 0;
+        });
+    }
     tagExists(pattern) {
         return __awaiter(this, void 0, void 0, function* () {
             const output = yield this.execGit(['tag', '--list', pattern]);
@@ -1023,6 +1030,11 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean, ref
                     }
                 }
                 core.endGroup();
+                // Check for submodules and delete any existing files if submodules are present
+                if (!(yield git.submoduleStatus())) {
+                    remove = true;
+                    core.info('Bad Submodules found, removing existing files');
+                }
                 // Clean
                 if (clean) {
                     core.startGroup('Cleaning the repository');

+ 7 - 0
src/git-command-manager.ts

@@ -41,6 +41,7 @@ export interface IGitCommandManager {
   submoduleForeach(command: string, recursive: boolean): Promise<string>
   submoduleSync(recursive: boolean): Promise<void>
   submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
+  submoduleStatus(): Promise<boolean>
   tagExists(pattern: string): Promise<boolean>
   tryClean(): Promise<boolean>
   tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
@@ -357,6 +358,12 @@ class GitCommandManager {
     await this.execGit(args)
   }
 
+  async submoduleStatus(): Promise<boolean> {
+    const output = await this.execGit(['submodule', 'status'], true)
+    core.debug(output.stdout)
+    return output.exitCode === 0
+  }
+
   async tagExists(pattern: string): Promise<boolean> {
     const output = await this.execGit(['tag', '--list', pattern])
     return !!output.stdout.trim()

+ 6 - 0
src/git-directory-helper.ts

@@ -81,6 +81,12 @@ export async function prepareExistingDirectory(
       }
       core.endGroup()
 
+      // Check for submodules and delete any existing files if submodules are present
+      if (!(await git.submoduleStatus())) {
+        remove = true
+        core.info('Bad Submodules found, removing existing files')
+      }
+
       // Clean
       if (clean) {
         core.startGroup('Cleaning the repository')