git-directory-helper.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import * as assert from 'assert'
  2. import * as core from '@actions/core'
  3. import * as fs from 'fs'
  4. import * as fsHelper from './fs-helper'
  5. import * as io from '@actions/io'
  6. import * as path from 'path'
  7. import {IGitCommandManager} from './git-command-manager'
  8. export async function prepareExistingDirectory(
  9. git: IGitCommandManager | undefined,
  10. repositoryPath: string,
  11. repositoryUrl: string,
  12. clean: boolean,
  13. ref: string
  14. ): Promise<void> {
  15. assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
  16. assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined')
  17. // Indicates whether to delete the directory contents
  18. let remove = false
  19. // Check whether using git or REST API
  20. if (!git) {
  21. remove = true
  22. }
  23. // Fetch URL does not match
  24. else if (
  25. !fsHelper.directoryExistsSync(path.join(repositoryPath, '.git')) ||
  26. repositoryUrl !== (await git.tryGetFetchUrl())
  27. ) {
  28. remove = true
  29. } else {
  30. // Delete any index.lock and shallow.lock left by a previously canceled run or crashed git process
  31. const lockPaths = [
  32. path.join(repositoryPath, '.git', 'index.lock'),
  33. path.join(repositoryPath, '.git', 'shallow.lock')
  34. ]
  35. for (const lockPath of lockPaths) {
  36. try {
  37. await io.rmRF(lockPath)
  38. } catch (error) {
  39. core.debug(
  40. `Unable to delete '${lockPath}'. ${(error as any)?.message ?? error}`
  41. )
  42. }
  43. }
  44. try {
  45. core.startGroup('Removing previously created refs, to avoid conflicts')
  46. // Checkout detached HEAD
  47. if (!(await git.isDetached())) {
  48. await git.checkoutDetach()
  49. }
  50. // Remove all refs/heads/*
  51. let branches = await git.branchList(false)
  52. for (const branch of branches) {
  53. await git.branchDelete(false, branch)
  54. }
  55. // Remove any conflicting refs/remotes/origin/*
  56. // Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
  57. // Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
  58. if (ref) {
  59. ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`
  60. if (ref.startsWith('refs/heads/')) {
  61. const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length)
  62. const upperName1Slash = `${upperName1}/`
  63. branches = await git.branchList(true)
  64. for (const branch of branches) {
  65. const upperName2 = branch.substr('origin/'.length).toUpperCase()
  66. const upperName2Slash = `${upperName2}/`
  67. if (
  68. upperName1.startsWith(upperName2Slash) ||
  69. upperName2.startsWith(upperName1Slash)
  70. ) {
  71. await git.branchDelete(true, branch)
  72. }
  73. }
  74. }
  75. }
  76. core.endGroup()
  77. // Clean
  78. if (clean) {
  79. core.startGroup('Cleaning the repository')
  80. if (!(await git.tryClean())) {
  81. core.debug(
  82. `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}'.`
  83. )
  84. remove = true
  85. } else if (!(await git.tryReset())) {
  86. remove = true
  87. }
  88. core.endGroup()
  89. if (remove) {
  90. core.warning(
  91. `Unable to clean or reset the repository. The repository will be recreated instead.`
  92. )
  93. }
  94. }
  95. } catch (error) {
  96. core.warning(
  97. `Unable to prepare the existing repository. The repository will be recreated instead.`
  98. )
  99. remove = true
  100. }
  101. }
  102. if (remove) {
  103. // Delete the contents of the directory. Don't delete the directory itself
  104. // since it might be the current working directory.
  105. core.info(`Deleting the contents of '${repositoryPath}'`)
  106. for (const file of await fs.promises.readdir(repositoryPath)) {
  107. await io.rmRF(path.join(repositoryPath, file))
  108. }
  109. }
  110. }