download-previous-build.ts 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import path from 'node:path';
  2. import { pipeline } from 'node:stream/promises';
  3. import { task } from './trace';
  4. import { defaultRequestInit, fetchWithRetry } from './lib/fetch-retry';
  5. import { extract as tarExtract } from 'tar-fs';
  6. import type { Headers as TarEntryHeaders } from 'tar-fs';
  7. import zlib from 'node:zlib';
  8. import { Readable } from 'node:stream';
  9. const GITHUB_CODELOAD_URL = 'https://codeload.github.com/sukkalab/ruleset.skk.moe/tar.gz/master';
  10. const GITLAB_CODELOAD_URL = 'https://gitlab.com/SukkaW/ruleset.skk.moe/-/archive/master/ruleset.skk.moe-master.tar.gz';
  11. export const downloadPreviousBuild = task(require.main === module, __filename)(async (span) => {
  12. const tarGzUrl = await span.traceChildAsync('get tar.gz url', async () => {
  13. const resp = await fetchWithRetry(GITHUB_CODELOAD_URL, {
  14. ...defaultRequestInit,
  15. method: 'HEAD',
  16. retry: {
  17. retryOnNon2xx: false
  18. }
  19. });
  20. if (resp.status !== 200) {
  21. console.warn('Download previous build from GitHub failed! Status:', resp.status);
  22. console.warn('Switch to GitLab');
  23. return GITLAB_CODELOAD_URL;
  24. }
  25. return GITHUB_CODELOAD_URL;
  26. });
  27. const publicDir = path.resolve(__dirname, '..', 'public');
  28. return span.traceChildAsync('download & extract previoud build', async () => {
  29. const resp = await fetchWithRetry(tarGzUrl, {
  30. headers: {
  31. 'User-Agent': 'curl/8.9.1',
  32. // https://github.com/unjs/giget/issues/97
  33. // https://gitlab.com/gitlab-org/gitlab/-/commit/50c11f278d18fe1f3fb12eb595067216bb58ade2
  34. 'sec-fetch-mode': 'same-origin'
  35. },
  36. // https://github.com/unjs/giget/issues/97
  37. // https://gitlab.com/gitlab-org/gitlab/-/commit/50c11f278d18fe1f3fb12eb595067216bb58ade2
  38. mode: 'same-origin',
  39. retry: {
  40. retryOnNon2xx: false
  41. }
  42. });
  43. if (resp.status !== 200) {
  44. console.warn('Download previous build failed! Status:', resp.status);
  45. if (resp.status === 404) {
  46. return;
  47. }
  48. }
  49. if (!resp.body) {
  50. throw new Error('Download previous build failed! No body found');
  51. }
  52. const pathPrefix = 'ruleset.skk.moe-master/';
  53. const gunzip = zlib.createGunzip();
  54. const extract = tarExtract(
  55. publicDir,
  56. {
  57. ignore: tarOnIgnore,
  58. map(header) {
  59. header.name = header.name.replace(pathPrefix, '');
  60. return header;
  61. }
  62. }
  63. );
  64. return pipeline(
  65. Readable.fromWeb(resp.body),
  66. gunzip,
  67. extract
  68. );
  69. });
  70. });
  71. function tarOnIgnore(_: string, header?: TarEntryHeaders) {
  72. if (header) {
  73. if (header.type !== 'file' && header.type !== 'directory') {
  74. return true;
  75. }
  76. const extname = path.extname(header.name);
  77. if (extname === '.ts') {
  78. return true;
  79. }
  80. }
  81. return false;
  82. }