git-directory-helper.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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(`Unable to delete '${lockPath}'. ${error.message}`)
  40. }
  41. }
  42. try {
  43. core.startGroup('Removing previously created refs, to avoid conflicts')
  44. // Checkout detached HEAD
  45. if (!(await git.isDetached())) {
  46. await git.checkoutDetach()
  47. }
  48. // Remove all refs/heads/*
  49. let branches = await git.branchList(false)
  50. for (const branch of branches) {
  51. await git.branchDelete(false, branch)
  52. }
  53. // Remove any conflicting refs/remotes/origin/*
  54. // Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
  55. // Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
  56. if (ref) {
  57. ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`
  58. if (ref.startsWith('refs/heads/')) {
  59. const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length)
  60. const upperName1Slash = `${upperName1}/`
  61. branches = await git.branchList(true)
  62. for (const branch of branches) {
  63. const upperName2 = branch.substr('origin/'.length).toUpperCase()
  64. const upperName2Slash = `${upperName2}/`
  65. if (
  66. upperName1.startsWith(upperName2Slash) ||
  67. upperName2.startsWith(upperName1Slash)
  68. ) {
  69. await git.branchDelete(true, branch)
  70. }
  71. }
  72. }
  73. }
  74. core.endGroup()
  75. // Clean
  76. if (clean) {
  77. core.startGroup('Cleaning the repository')
  78. if (!(await git.tryClean())) {
  79. core.debug(
  80. `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}'.`
  81. )
  82. remove = true
  83. } else if (!(await git.tryReset())) {
  84. remove = true
  85. }
  86. core.endGroup()
  87. if (remove) {
  88. core.warning(
  89. `Unable to clean or reset the repository. The repository will be recreated instead.`
  90. )
  91. }
  92. }
  93. } catch (error) {
  94. core.warning(
  95. `Unable to prepare the existing repository. The repository will be recreated instead.`
  96. )
  97. remove = true
  98. }
  99. }
  100. if (remove) {
  101. // Delete the contents of the directory. Don't delete the directory itself
  102. // since it might be the current working directory.
  103. core.info(`Deleting the contents of '${repositoryPath}'`)
  104. for (const file of await fs.promises.readdir(repositoryPath)) {
  105. await io.rmRF(path.join(repositoryPath, file))
  106. }
  107. }
  108. }