getUpToDateRefsFromGitHub.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { spawnSync } from 'node:child_process'
  2. import prompts from 'prompts'
  3. import { TARGET_BRANCH, REPO_NAME, REPO_OWNER, STABLE_BRANCH } from './config.js'
  4. async function apiCall (endpoint, errorMessage) {
  5. const response = await fetch(
  6. `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}${endpoint}`,
  7. )
  8. if (response.ok) {
  9. return response.json()
  10. }
  11. console.warn(response)
  12. throw new Error(errorMessage)
  13. }
  14. export async function getRemoteHEAD () {
  15. return (
  16. await apiCall(
  17. `/git/ref/heads/${TARGET_BRANCH}`,
  18. 'Cannot get remote HEAD, check your internet connection.',
  19. )
  20. ).object.sha
  21. }
  22. async function getLatestReleaseSHA () {
  23. const response = await fetch(`https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${TARGET_BRANCH}/packages/uppy/package.json`)
  24. if (!response.ok) throw new Error(`Network call failed: ${response.status} ${response.statusText}`)
  25. const { version } = await response.json()
  26. const tag_name = `uppy@${version}`
  27. console.log(`Last release was ${tag_name}.`)
  28. return (
  29. await apiCall(
  30. `/git/ref/tags/${encodeURIComponent(tag_name)}`,
  31. `Failed to fetch information for release ${JSON.stringify(tag_name)}`,
  32. )
  33. ).object.sha
  34. }
  35. function getStableBranchMergeBase (REMOTE_HEAD) {
  36. spawnSync('git', ['fetch', `https://github.com/${REPO_OWNER}/${REPO_NAME}.git`, STABLE_BRANCH])
  37. const STABLE_HEAD = spawnSync('git', ['rev-parse', 'FETCH_HEAD']).stdout.toString().trim()
  38. return [[
  39. spawnSync('git', ['merge-base', REMOTE_HEAD, 'FETCH_HEAD']).stdout.toString().trim(),
  40. STABLE_HEAD,
  41. ].join('..'), STABLE_HEAD]
  42. }
  43. async function getLocalHEAD () {
  44. return spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trim()
  45. }
  46. export function rewindGitHistory (spawnOptions, sha) {
  47. return spawnSync('git', ['reset', sha, '--hard'], spawnOptions).status === 0
  48. }
  49. export async function validateGitStatus (spawnOptions) {
  50. const latestRelease = getLatestReleaseSHA() // run in parallel to speed things up
  51. const [REMOTE_HEAD, LOCAL_HEAD] = await Promise.all([getRemoteHEAD(), getLocalHEAD()])
  52. const { status, stderr } = spawnSync(
  53. 'git',
  54. ['diff', '--exit-code', '--quiet', REMOTE_HEAD, '--', '.'],
  55. spawnOptions,
  56. )
  57. if (status !== 0) {
  58. console.error(stderr.toString())
  59. console.log(
  60. `git repository is not clean and/or not in sync with ${REPO_OWNER}/${REPO_NAME}`,
  61. )
  62. if (spawnSync(
  63. 'git',
  64. ['diff', '--exit-code', '--quiet', LOCAL_HEAD, '--', '.'],
  65. spawnOptions,
  66. ).status !== 0) {
  67. const { value } = await prompts({
  68. type: 'confirm',
  69. name: 'value',
  70. message:
  71. 'Do you want to hard reset your local repository (all uncommitted changes will be lost)?',
  72. })
  73. if (!value) {
  74. throw new Error(
  75. 'Please ensure manually that your local repository is clean and up to date.',
  76. )
  77. }
  78. }
  79. if (stderr.indexOf('bad object') !== -1) {
  80. // eslint-disable-next-line no-shadow
  81. const { status, stdout, stderr } = spawnSync(
  82. 'git',
  83. [
  84. 'fetch',
  85. `https://github.com/${REPO_OWNER}/${REPO_NAME}.git`,
  86. TARGET_BRANCH,
  87. ],
  88. spawnOptions,
  89. )
  90. if (status) {
  91. console.log(stdout.toString())
  92. console.error(stderr.toString())
  93. throw new Error('Failed to fetch, please ensure manually that your local repository is up to date')
  94. }
  95. }
  96. if (!rewindGitHistory(spawnOptions, REMOTE_HEAD)) {
  97. throw new Error(
  98. 'Failed to reset, please ensure manually that your local repository is clean and up to date.',
  99. )
  100. }
  101. }
  102. return [await latestRelease, LOCAL_HEAD, ...getStableBranchMergeBase(REMOTE_HEAD)]
  103. }