deploy-if-changed.mjs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { spawnSync } from 'node:child_process';
  2. import { readFileSync } from 'node:fs';
  3. import { fileURLToPath } from 'node:url';
  4. import { dirname, resolve } from 'node:path';
  5. const watchPath = process.argv[2];
  6. const isWorkersBuild = Boolean(process.env.WORKERS_CI || process.env.WORKERS_CI_COMMIT_SHA);
  7. const forceDeploy = process.env.FORCE_DEPLOY === '1';
  8. const __dirname = dirname(fileURLToPath(import.meta.url));
  9. const noticeBindingName = 'NOTICE_STORE';
  10. if (!watchPath) {
  11. console.error('Usage: node deploy-if-changed.mjs <watch-path>');
  12. process.exit(1);
  13. }
  14. function run(command, args, options = {}) {
  15. return spawnSync(command, args, {
  16. encoding: 'utf8',
  17. shell: process.platform === 'win32',
  18. ...options,
  19. });
  20. }
  21. function deploy() {
  22. runPreDeploySetupIfNeeded();
  23. console.log(`Running wrangler deploy for ${watchPath}.`);
  24. const result = spawnSync('npx', ['wrangler', 'deploy'], {
  25. stdio: 'inherit',
  26. shell: process.platform === 'win32',
  27. });
  28. process.exit(result.status ?? 1);
  29. }
  30. function runPreDeploySetupIfNeeded() {
  31. if (String(watchPath || '').replace(/\\/g, '/') !== 'analytics/worker') {
  32. return;
  33. }
  34. const workerConfigPath = resolve(__dirname, '../worker/wrangler.jsonc');
  35. const source = readFileSync(workerConfigPath, 'utf8');
  36. if (hasNoticeStoreBinding(source)) {
  37. console.log('NOTICE_STORE KV namespace already configured; skipping setup.');
  38. return;
  39. }
  40. console.log('NOTICE_STORE KV namespace is not configured; running setup.');
  41. const setupScript = resolve(__dirname, 'setup-notice-kv.mjs');
  42. const result = spawnSync(process.execPath, [setupScript], {
  43. stdio: 'inherit',
  44. shell: process.platform === 'win32',
  45. });
  46. if (result.status !== 0) {
  47. process.exit(result.status ?? 1);
  48. }
  49. }
  50. function hasNoticeStoreBinding(source) {
  51. const escapedBinding = noticeBindingName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  52. const pattern = new RegExp(`\\{[\\s\\S]*?"binding"\\s*:\\s*"${escapedBinding}"[\\s\\S]*?"id"\\s*:\\s*"[^"<]+"[\\s\\S]*?\\}`);
  53. return pattern.test(source);
  54. }
  55. function getTrimmedStdout(result) {
  56. return String(result.stdout || '').trim();
  57. }
  58. if (!isWorkersBuild || forceDeploy) {
  59. deploy();
  60. }
  61. const repoRootResult = run('git', ['rev-parse', '--show-toplevel']);
  62. if (repoRootResult.status !== 0) {
  63. console.warn('Unable to find git root, deploying normally.');
  64. deploy();
  65. }
  66. const repoRoot = getTrimmedStdout(repoRootResult);
  67. const parentResult = run('git', ['rev-parse', 'HEAD^'], { cwd: repoRoot });
  68. if (parentResult.status !== 0) {
  69. console.warn('Unable to find parent commit, deploying normally.');
  70. deploy();
  71. }
  72. const parentCommit = getTrimmedStdout(parentResult);
  73. const diffResult = run('git', ['diff', '--name-only', parentCommit, 'HEAD', '--', watchPath], { cwd: repoRoot });
  74. if (diffResult.status !== 0) {
  75. console.warn('Unable to inspect changed files, deploying normally.');
  76. deploy();
  77. }
  78. const changedFiles = getTrimmedStdout(diffResult);
  79. if (!changedFiles) {
  80. console.log(`No changes under ${watchPath}; skipping wrangler deploy.`);
  81. process.exit(0);
  82. }
  83. console.log(`Changes detected under ${watchPath}:`);
  84. console.log(changedFiles);
  85. deploy();