getUpToDateRefsFromGitHub.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { spawnSync } from 'node:child_process'
  2. import prompts from 'prompts'
  3. import { TARGET_BRANCH, REPO_NAME, REPO_OWNER } 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. async function getLocalHEAD () {
  36. return spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trim()
  37. }
  38. export function rewindGitHistory (spawnOptions, sha) {
  39. return spawnSync('git', ['reset', sha, '--hard'], spawnOptions).status === 0
  40. }
  41. export async function validateGitStatus (spawnOptions) {
  42. const latestRelease = getLatestReleaseSHA() // run in parallel to speed things up
  43. const [REMOTE_HEAD, LOCAL_HEAD] = await Promise.all([getRemoteHEAD(), getLocalHEAD()])
  44. const { status, stderr } = spawnSync(
  45. 'git',
  46. ['diff', '--exit-code', '--quiet', REMOTE_HEAD, '--', '.'],
  47. spawnOptions,
  48. )
  49. if (status !== 0) {
  50. console.error(stderr.toString())
  51. console.log(
  52. `git repository is not clean and/or not in sync with ${REPO_OWNER}/${REPO_NAME}`,
  53. )
  54. if (spawnSync(
  55. 'git',
  56. ['diff', '--exit-code', '--quiet', LOCAL_HEAD, '--', '.'],
  57. spawnOptions,
  58. ).status !== 0) {
  59. const { value } = await prompts({
  60. type: 'confirm',
  61. name: 'value',
  62. message:
  63. 'Do you want to hard reset your local repository (all uncommitted changes will be lost)?',
  64. })
  65. if (!value) {
  66. throw new Error(
  67. 'Please ensure manually that your local repository is clean and up to date.',
  68. )
  69. }
  70. }
  71. if (stderr.indexOf('bad object') !== -1) {
  72. // eslint-disable-next-line no-shadow
  73. const { status, stdout, stderr } = spawnSync(
  74. 'git',
  75. [
  76. 'fetch',
  77. `https://github.com/${REPO_OWNER}/${REPO_NAME}.git`,
  78. TARGET_BRANCH,
  79. ],
  80. spawnOptions,
  81. )
  82. if (status) {
  83. console.log(stdout.toString())
  84. console.error(stderr.toString())
  85. throw new Error('Failed to fetch, please ensure manually that your local repository is up to date')
  86. }
  87. }
  88. if (!rewindGitHistory(spawnOptions, REMOTE_HEAD)) {
  89. throw new Error(
  90. 'Failed to reset, please ensure manually that your local repository is clean and up to date.',
  91. )
  92. }
  93. }
  94. return [await latestRelease, LOCAL_HEAD]
  95. }