input-helper.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import * as core from '@actions/core'
  2. import * as fsHelper from './fs-helper.js'
  3. import * as github from '@actions/github'
  4. import * as path from 'path'
  5. import * as unsafePrCheckoutHelper from './unsafe-pr-checkout-helper.js'
  6. import * as workflowContextHelper from './workflow-context-helper.js'
  7. import {IGitSourceSettings} from './git-source-settings.js'
  8. export async function getInputs(): Promise<IGitSourceSettings> {
  9. const result = {} as unknown as IGitSourceSettings
  10. // GitHub workspace
  11. let githubWorkspacePath = process.env['GITHUB_WORKSPACE']
  12. if (!githubWorkspacePath) {
  13. throw new Error('GITHUB_WORKSPACE not defined')
  14. }
  15. githubWorkspacePath = path.resolve(githubWorkspacePath)
  16. core.debug(`GITHUB_WORKSPACE = '${githubWorkspacePath}'`)
  17. fsHelper.directoryExistsSync(githubWorkspacePath, true)
  18. // Qualified repository
  19. const qualifiedRepository =
  20. core.getInput('repository') ||
  21. `${github.context.repo.owner}/${github.context.repo.repo}`
  22. core.debug(`qualified repository = '${qualifiedRepository}'`)
  23. const splitRepository = qualifiedRepository.split('/')
  24. if (
  25. splitRepository.length !== 2 ||
  26. !splitRepository[0] ||
  27. !splitRepository[1]
  28. ) {
  29. throw new Error(
  30. `Invalid repository '${qualifiedRepository}'. Expected format {owner}/{repo}.`
  31. )
  32. }
  33. result.repositoryOwner = splitRepository[0]
  34. result.repositoryName = splitRepository[1]
  35. // Repository path
  36. result.repositoryPath = core.getInput('path') || '.'
  37. result.repositoryPath = path.resolve(
  38. githubWorkspacePath,
  39. result.repositoryPath
  40. )
  41. if (
  42. !(result.repositoryPath + path.sep).startsWith(
  43. githubWorkspacePath + path.sep
  44. )
  45. ) {
  46. throw new Error(
  47. `Repository path '${result.repositoryPath}' is not under '${githubWorkspacePath}'`
  48. )
  49. }
  50. // Workflow repository?
  51. const isWorkflowRepository =
  52. qualifiedRepository.toUpperCase() ===
  53. `${github.context.repo.owner}/${github.context.repo.repo}`.toUpperCase()
  54. // Source branch, source version
  55. result.ref = core.getInput('ref')
  56. // core.getInput()'s default trim strips a range of Unicode characters such as a
  57. // leading BOM (U+FEFF) or NBSP (U+00A0). Those are valid in a git ref name, so
  58. // a fork branch named "<BOM>" + 40 hex chars would trim down to a bare SHA and
  59. // be silently reclassified as a commit, bypassing the unsafe fork PR checkout
  60. // guard.
  61. //
  62. // The trim below strips only the ASCII whitespace characters which are all forbidden
  63. // in a git branch name.
  64. // \t U+0009 horizontal tab - ASCII control, forbidden in ref names
  65. // \n U+000A line feed - ASCII control, forbidden in ref names
  66. // \v U+000B vertical tab - ASCII control, forbidden in ref names
  67. // \f U+000C form feed - ASCII control, forbidden in ref names
  68. // \r U+000D carriage return - ASCII control, forbidden in ref names
  69. // ' ' U+0020 space - forbidden in ref names
  70. const asciiTrimmedRef = core
  71. .getInput('ref', {trimWhitespace: false})
  72. .replace(/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g, '')
  73. if (!result.ref) {
  74. if (isWorkflowRepository) {
  75. result.ref = github.context.ref
  76. result.commit = github.context.sha
  77. // Some events have an unqualifed ref. For example when a PR is merged (pull_request closed event),
  78. // the ref is unqualifed like "main" instead of "refs/heads/main".
  79. if (result.commit && result.ref && !result.ref.startsWith('refs/')) {
  80. result.ref = `refs/heads/${result.ref}`
  81. }
  82. }
  83. }
  84. // SHA?
  85. else if (asciiTrimmedRef.match(/^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/)) {
  86. result.commit = asciiTrimmedRef
  87. result.ref = ''
  88. }
  89. core.debug(`ref = '${result.ref}'`)
  90. core.debug(`commit = '${result.commit}'`)
  91. // Clean
  92. result.clean = (core.getInput('clean') || 'true').toUpperCase() === 'TRUE'
  93. core.debug(`clean = ${result.clean}`)
  94. // Filter
  95. const filter = core.getInput('filter')
  96. if (filter) {
  97. result.filter = filter
  98. }
  99. core.debug(`filter = ${result.filter}`)
  100. // Sparse checkout
  101. const sparseCheckout = core.getMultilineInput('sparse-checkout')
  102. if (sparseCheckout.length) {
  103. result.sparseCheckout = sparseCheckout
  104. core.debug(`sparse checkout = ${result.sparseCheckout}`)
  105. }
  106. result.sparseCheckoutConeMode =
  107. (core.getInput('sparse-checkout-cone-mode') || 'true').toUpperCase() ===
  108. 'TRUE'
  109. // Fetch depth
  110. result.fetchDepth = Math.floor(Number(core.getInput('fetch-depth') || '1'))
  111. if (isNaN(result.fetchDepth) || result.fetchDepth < 0) {
  112. result.fetchDepth = 0
  113. }
  114. core.debug(`fetch depth = ${result.fetchDepth}`)
  115. // Fetch tags
  116. result.fetchTags =
  117. (core.getInput('fetch-tags') || 'false').toUpperCase() === 'TRUE'
  118. core.debug(`fetch tags = ${result.fetchTags}`)
  119. // Show fetch progress
  120. result.showProgress =
  121. (core.getInput('show-progress') || 'true').toUpperCase() === 'TRUE'
  122. core.debug(`show progress = ${result.showProgress}`)
  123. // LFS
  124. result.lfs = (core.getInput('lfs') || 'false').toUpperCase() === 'TRUE'
  125. core.debug(`lfs = ${result.lfs}`)
  126. // Submodules
  127. result.submodules = false
  128. result.nestedSubmodules = false
  129. const submodulesString = (core.getInput('submodules') || '').toUpperCase()
  130. if (submodulesString == 'RECURSIVE') {
  131. result.submodules = true
  132. result.nestedSubmodules = true
  133. } else if (submodulesString == 'TRUE') {
  134. result.submodules = true
  135. }
  136. core.debug(`submodules = ${result.submodules}`)
  137. core.debug(`recursive submodules = ${result.nestedSubmodules}`)
  138. // Auth token
  139. result.authToken = core.getInput('token', {required: true})
  140. // SSH
  141. result.sshKey = core.getInput('ssh-key')
  142. result.sshKnownHosts = core.getInput('ssh-known-hosts')
  143. result.sshStrict =
  144. (core.getInput('ssh-strict') || 'true').toUpperCase() === 'TRUE'
  145. result.sshUser = core.getInput('ssh-user')
  146. // Persist credentials
  147. result.persistCredentials =
  148. (core.getInput('persist-credentials') || 'false').toUpperCase() === 'TRUE'
  149. // Workflow organization ID
  150. result.workflowOrganizationId =
  151. await workflowContextHelper.getOrganizationId()
  152. // Set safe.directory in git global config.
  153. result.setSafeDirectory =
  154. (core.getInput('set-safe-directory') || 'true').toUpperCase() === 'TRUE'
  155. // Determine the GitHub URL that the repository is being hosted from
  156. result.githubServerUrl = core.getInput('github-server-url')
  157. core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
  158. // Allow unsafe PR checkout (opt-in for pull_request_target / workflow_run fork PRs)
  159. result.allowUnsafePrCheckout =
  160. (core.getInput('allow-unsafe-pr-checkout') || 'false').toUpperCase() ===
  161. 'TRUE'
  162. core.debug(`allow unsafe PR checkout = ${result.allowUnsafePrCheckout}`)
  163. // The default self-checkout (this repository with no explicit ref) always
  164. // resolves to the trusted ref/commit GitHub set for the triggering event, so
  165. // the fork-checkout guard only needs to run when the caller customized the
  166. // repository or ref.
  167. const isDefaultCheckout = isWorkflowRepository && !core.getInput('ref')
  168. if (!isDefaultCheckout) {
  169. unsafePrCheckoutHelper.assertSafePrCheckout({
  170. qualifiedRepository,
  171. ref: result.ref,
  172. commit: result.commit,
  173. allowUnsafePrCheckout: result.allowUnsafePrCheckout
  174. })
  175. }
  176. return result
  177. }