getUpToDateRefsFromGitHub.js 3.2 KB

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