NepsenX commited on
Commit
a3dd95d
·
verified ·
1 Parent(s): 05a5cb2

Update src/index.ts

Browse files
Files changed (1) hide show
  1. src/index.ts +38 -25
src/index.ts CHANGED
@@ -36,7 +36,14 @@ const CONFIG: Config = {
36
  SMTP_PASS: process.env.SMTP_PASS || 'your-app-password'
37
  };
38
 
39
- // ================== স্টেট ম্যানজমেন্ট (TypeScript টাইপসহ) ==================
 
 
 
 
 
 
 
40
  let prodProcess: ChildProcess | null = null;
41
  let prodVersionPath: string | null = null;
42
  let isUpdating: boolean = false;
@@ -60,7 +67,7 @@ async function sendEmail(subject: string, text: string): Promise<void> {
60
  }
61
  }
62
 
63
- // ================== গিট ইউটিলিটি (Promise র্যাপার) ==================
64
  function runGitCommand(cmd: string, cwd: string): Promise<string> {
65
  return new Promise((resolve, reject) => {
66
  exec(cmd, { cwd }, (error, stdout, stderr) => {
@@ -70,35 +77,44 @@ function runGitCommand(cmd: string, cwd: string): Promise<string> {
70
  });
71
  }
72
 
 
 
 
 
 
 
 
 
73
  async function getCurrentCommitSha(repoUrl: string): Promise<string | null> {
74
  try {
75
- const output = await runGitCommand(`git ls-remote ${repoUrl} main`, process.cwd());
 
76
  return output.split(/\s/)[0];
77
- } catch {
 
78
  return null;
79
  }
80
  }
81
 
82
  async function cloneOrPull(repoUrl: string, targetDir: string): Promise<void> {
 
83
  if (!fs.existsSync(targetDir)) {
84
  console.log(`📥 Cloning ${repoUrl} into ${targetDir}...`);
85
- await runGitCommand(`git clone ${repoUrl} ${targetDir}`, process.cwd());
86
  } else {
87
  console.log(`🔄 Pulling latest in ${targetDir}...`);
88
  await runGitCommand(`git pull`, targetDir);
89
  }
90
  }
91
 
92
- // ================== এজেন্ট ডিপ্লয় (পোর্ট প্যারামিটার সহ) ==================
93
  function deployAgent(versionDir: string, port: number): Promise<ChildProcess> {
94
  return new Promise((resolve, reject) => {
95
  console.log(`🚀 Starting agent on port ${port} from ${versionDir}...`);
96
 
97
- // ১. ডিপেন্ডেন্সি ও বিল্ড
98
  exec(`npm install && npm run build`, { cwd: versionDir }, (err) => {
99
  if (err) return reject(`Build failed: ${err.message}`);
100
 
101
- // ২. প্রসেস স্পন (পোর্ট এনভ ভেরিয়েবল হিসেবে)
102
  const proc = spawn('npm', ['start'], {
103
  cwd: versionDir,
104
  env: { ...process.env, PORT: String(port) },
@@ -108,10 +124,10 @@ function deployAgent(versionDir: string, port: number): Promise<ChildProcess> {
108
  proc.stdout.on('data', (data) => console.log(`[AGENT:${port}] ${data}`));
109
  proc.stderr.on('data', (data) => console.error(`[AGENT ERR:${port}] ${data}`));
110
 
111
- // ৩. হেলথ চেক (৫ সেকেন্ড অপেক্ষা)
112
  setTimeout(async () => {
113
  try {
114
- const res = await axios.get(`http://localhost:${port}/health`, { timeout: 2000 });
115
  if (res.status === 200) {
116
  console.log(`✅ Health check passed on port ${port}!`);
117
  resolve(proc);
@@ -121,7 +137,7 @@ function deployAgent(versionDir: string, port: number): Promise<ChildProcess> {
121
  } catch (err: any) {
122
  reject(`Health check error on port ${port}: ${err.message}`);
123
  }
124
- }, 5000);
125
  });
126
  });
127
  }
@@ -132,7 +148,8 @@ async function syncMemory(versionPath: string): Promise<void> {
132
  try {
133
  const memoryBackupDir = '/app/memory-backup';
134
  if (!fs.existsSync(memoryBackupDir)) {
135
- await runGitCommand(`git clone ${CONFIG.RES3_REPO} ${memoryBackupDir}`, process.cwd());
 
136
  }
137
 
138
  const srcMemory = path.join(versionPath, 'memory');
@@ -143,15 +160,20 @@ async function syncMemory(versionPath: string): Promise<void> {
143
  fs.cpSync(srcMemory, dstMemory, { recursive: true });
144
  }
145
 
146
- await runGitCommand(`git add . && git commit -m "Memory sync from runner [skip ci]" || echo "No changes"`, memoryBackupDir);
 
 
 
 
147
  await runGitCommand(`git push`, memoryBackupDir);
 
148
  console.log('💾 Memory synced to Res3.');
149
  } catch (err: any) {
150
  console.error('⚠️ Memory sync failed:', err.message);
151
  }
152
  }
153
 
154
- // ================== মূল আপডেট লজিক (ব্লু-গ্রিন) ==================
155
  async function checkAndUpdate(): Promise<void> {
156
  if (isUpdating) {
157
  console.log('⏳ Update already in progress...');
@@ -162,11 +184,9 @@ async function checkAndUpdate(): Promise<void> {
162
  try {
163
  console.log('🔍 Checking for updates...');
164
 
165
- // ১. সর্বশেষ SHA বের করা
166
  const latestSha = await getCurrentCommitSha(CONFIG.RES1_REPO);
167
  if (!latestSha) throw new Error('Could not fetch latest SHA');
168
 
169
- // ২. বর্তমান প্রোডাকশন SHA বের করা
170
  let currentSha: string | null = null;
171
  if (prodVersionPath && fs.existsSync(path.join(prodVersionPath, '.git'))) {
172
  try {
@@ -182,32 +202,25 @@ async function checkAndUpdate(): Promise<void> {
182
 
183
  console.log(`🆕 New version detected: ${latestSha}. Deploying V2 on port ${CONFIG.TEST_PORT}...`);
184
 
185
- // ৩. V2 (টেস্ট) ডাউনলোড
186
  const testVersionDir = path.join(CONFIG.VERSIONS_DIR, `v2_${Date.now()}`);
187
  await cloneOrPull(CONFIG.RES1_REPO, testVersionDir);
188
 
189
- // ৪. V2-কে পোর্ট ২০০০-এ চালান
190
  const testProc = await deployAgent(testVersionDir, CONFIG.TEST_PORT);
191
-
192
  console.log(`✅ V2 passed health check! Promoting V2 to port ${CONFIG.PROD_PORT}...`);
193
 
194
- // ৫. প্রমোশন: পুরনো V1 (পোর্ট ৭০০০) বন্ধ
195
  if (prodProcess) {
196
  console.log('🛑 Stopping old V1 (Production)...');
197
  prodProcess.kill('SIGTERM');
198
  setTimeout(() => { if (!prodProcess?.killed) prodProcess?.kill('SIGKILL'); }, 3000);
199
  }
200
 
201
- // ৬. V2-কে পোর্ট ২০০০ থেকে তুলে ৭০০০-এ চালু
202
  testProc.kill('SIGTERM');
203
  await new Promise(resolve => setTimeout(resolve, 1000));
204
  const newProdProc = await deployAgent(testVersionDir, CONFIG.PROD_PORT);
205
 
206
- // ৭. স্টেট আপডেট
207
  prodProcess = newProdProc;
208
  prodVersionPath = testVersionDir;
209
 
210
- // ৮. মেমোরি সিঙ্ক
211
  await syncMemory(testVersionDir);
212
 
213
  console.log(`✅ Successfully deployed V2 (${latestSha}) on port ${CONFIG.PROD_PORT}`);
@@ -226,7 +239,7 @@ async function checkAndUpdate(): Promise<void> {
226
  }
227
  }
228
 
229
- // ================== HTTP সার্ভার (পোর্ট ৭৮৬০ - UptimeRobot) ==================
230
  const server = http.createServer(async (req, res) => {
231
  const url = new URL(req.url || '/', `http://${req.headers.host}`);
232
 
@@ -250,7 +263,7 @@ server.listen(CONFIG.HEALTH_PORT, '0.0.0.0', () => {
250
  checkAndUpdate();
251
  });
252
 
253
- // ================== প্রসেস হ্যান্ডলিং (ক্র্যাশ হলে রিস্টার্ট) ==================
254
  process.on('uncaughtException', (err) => {
255
  console.error('💥 Uncaught Exception:', err);
256
  sendEmail('CRASH - Uncaught Exception', err.stack || err.message);
 
36
  SMTP_PASS: process.env.SMTP_PASS || 'your-app-password'
37
  };
38
 
39
+ // ================== PAT ==================
40
+ if (!CONFIG.GITHUB_PAT) {
41
+ console.error('❌ GITHUB_PAT environment variable is not set!');
42
+ console.error('Please add GITHUB_PAT to Repository Secrets.');
43
+ process.exit(1);
44
+ }
45
+
46
+ // ================== স্টেট ম্যানেজমেন্ট ==================
47
  let prodProcess: ChildProcess | null = null;
48
  let prodVersionPath: string | null = null;
49
  let isUpdating: boolean = false;
 
67
  }
68
  }
69
 
70
+ // ================== গিট ইউটিলিটি (PAT সহ) ==================
71
  function runGitCommand(cmd: string, cwd: string): Promise<string> {
72
  return new Promise((resolve, reject) => {
73
  exec(cmd, { cwd }, (error, stdout, stderr) => {
 
77
  });
78
  }
79
 
80
+ // PAT সহ URL তৈরি করা
81
+ function getRepoUrlWithPat(repoUrl: string): string {
82
+ // যদি URL-এ ইতিমধ্যে PAT থাকে, তাহলে ফেরত দিন
83
+ if (repoUrl.includes('@')) return repoUrl;
84
+ // https://github.com/... -> https://PAT@github.com/...
85
+ return repoUrl.replace('https://', `https://${CONFIG.GITHUB_PAT}@`);
86
+ }
87
+
88
  async function getCurrentCommitSha(repoUrl: string): Promise<string | null> {
89
  try {
90
+ const urlWithPat = getRepoUrlWithPat(repoUrl);
91
+ const output = await runGitCommand(`git ls-remote ${urlWithPat} main`, process.cwd());
92
  return output.split(/\s/)[0];
93
+ } catch (err: any) {
94
+ console.error('⚠️ Failed to fetch SHA:', err.message);
95
  return null;
96
  }
97
  }
98
 
99
  async function cloneOrPull(repoUrl: string, targetDir: string): Promise<void> {
100
+ const urlWithPat = getRepoUrlWithPat(repoUrl);
101
  if (!fs.existsSync(targetDir)) {
102
  console.log(`📥 Cloning ${repoUrl} into ${targetDir}...`);
103
+ await runGitCommand(`git clone ${urlWithPat} ${targetDir}`, process.cwd());
104
  } else {
105
  console.log(`🔄 Pulling latest in ${targetDir}...`);
106
  await runGitCommand(`git pull`, targetDir);
107
  }
108
  }
109
 
110
+ // ================== এজেন্ট ডিপ্লয় ==================
111
  function deployAgent(versionDir: string, port: number): Promise<ChildProcess> {
112
  return new Promise((resolve, reject) => {
113
  console.log(`🚀 Starting agent on port ${port} from ${versionDir}...`);
114
 
 
115
  exec(`npm install && npm run build`, { cwd: versionDir }, (err) => {
116
  if (err) return reject(`Build failed: ${err.message}`);
117
 
 
118
  const proc = spawn('npm', ['start'], {
119
  cwd: versionDir,
120
  env: { ...process.env, PORT: String(port) },
 
124
  proc.stdout.on('data', (data) => console.log(`[AGENT:${port}] ${data}`));
125
  proc.stderr.on('data', (data) => console.error(`[AGENT ERR:${port}] ${data}`));
126
 
127
+ // ৫ সেকেন্ড লথ চে
128
  setTimeout(async () => {
129
  try {
130
+ const res = await axios.get(`http://localhost:${port}/health`, { timeout: 3000 });
131
  if (res.status === 200) {
132
  console.log(`✅ Health check passed on port ${port}!`);
133
  resolve(proc);
 
137
  } catch (err: any) {
138
  reject(`Health check error on port ${port}: ${err.message}`);
139
  }
140
+ }, 8000);
141
  });
142
  });
143
  }
 
148
  try {
149
  const memoryBackupDir = '/app/memory-backup';
150
  if (!fs.existsSync(memoryBackupDir)) {
151
+ const urlWithPat = getRepoUrlWithPat(CONFIG.RES3_REPO);
152
+ await runGitCommand(`git clone ${urlWithPat} ${memoryBackupDir}`, process.cwd());
153
  }
154
 
155
  const srcMemory = path.join(versionPath, 'memory');
 
160
  fs.cpSync(srcMemory, dstMemory, { recursive: true });
161
  }
162
 
163
+ // PAT দিয়ে পুশ করা
164
+ await runGitCommand(`git config --local user.email "oracus-runner@bot.com"`, memoryBackupDir);
165
+ await runGitCommand(`git config --local user.name "Oracus Runner"`, memoryBackupDir);
166
+ await runGitCommand(`git add .`, memoryBackupDir);
167
+ await runGitCommand(`git commit -m "Memory sync from runner [skip ci]" || echo "No changes"`, memoryBackupDir);
168
  await runGitCommand(`git push`, memoryBackupDir);
169
+
170
  console.log('💾 Memory synced to Res3.');
171
  } catch (err: any) {
172
  console.error('⚠️ Memory sync failed:', err.message);
173
  }
174
  }
175
 
176
+ // ================== মূল আপডেট লজিক ==================
177
  async function checkAndUpdate(): Promise<void> {
178
  if (isUpdating) {
179
  console.log('⏳ Update already in progress...');
 
184
  try {
185
  console.log('🔍 Checking for updates...');
186
 
 
187
  const latestSha = await getCurrentCommitSha(CONFIG.RES1_REPO);
188
  if (!latestSha) throw new Error('Could not fetch latest SHA');
189
 
 
190
  let currentSha: string | null = null;
191
  if (prodVersionPath && fs.existsSync(path.join(prodVersionPath, '.git'))) {
192
  try {
 
202
 
203
  console.log(`🆕 New version detected: ${latestSha}. Deploying V2 on port ${CONFIG.TEST_PORT}...`);
204
 
 
205
  const testVersionDir = path.join(CONFIG.VERSIONS_DIR, `v2_${Date.now()}`);
206
  await cloneOrPull(CONFIG.RES1_REPO, testVersionDir);
207
 
 
208
  const testProc = await deployAgent(testVersionDir, CONFIG.TEST_PORT);
 
209
  console.log(`✅ V2 passed health check! Promoting V2 to port ${CONFIG.PROD_PORT}...`);
210
 
 
211
  if (prodProcess) {
212
  console.log('🛑 Stopping old V1 (Production)...');
213
  prodProcess.kill('SIGTERM');
214
  setTimeout(() => { if (!prodProcess?.killed) prodProcess?.kill('SIGKILL'); }, 3000);
215
  }
216
 
 
217
  testProc.kill('SIGTERM');
218
  await new Promise(resolve => setTimeout(resolve, 1000));
219
  const newProdProc = await deployAgent(testVersionDir, CONFIG.PROD_PORT);
220
 
 
221
  prodProcess = newProdProc;
222
  prodVersionPath = testVersionDir;
223
 
 
224
  await syncMemory(testVersionDir);
225
 
226
  console.log(`✅ Successfully deployed V2 (${latestSha}) on port ${CONFIG.PROD_PORT}`);
 
239
  }
240
  }
241
 
242
+ // ================== HTTP সার্ভার ==================
243
  const server = http.createServer(async (req, res) => {
244
  const url = new URL(req.url || '/', `http://${req.headers.host}`);
245
 
 
263
  checkAndUpdate();
264
  });
265
 
266
+ // ================== প্রসেস হ্যান্ডলিং ==================
267
  process.on('uncaughtException', (err) => {
268
  console.error('💥 Uncaught Exception:', err);
269
  sendEmail('CRASH - Uncaught Exception', err.stack || err.message);