long-git-cli 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Pipeline 监听测试页面逻辑
3
+ */
4
+
5
+ const PipelinePage = {
6
+ /**
7
+ * 初始化 Pipeline 监听页面
8
+ */
9
+ async init() {
10
+ /** 加载保存的配置 */
11
+ await this.loadConfig();
12
+
13
+ /** 绑定事件 */
14
+ this.bindEvents();
15
+ },
16
+
17
+ /**
18
+ * 加载保存的配置
19
+ */
20
+ async loadConfig() {
21
+ try {
22
+ const config = localStorage.getItem('pipeline-test-config');
23
+ if (config) {
24
+ const data = JSON.parse(config);
25
+ if (data.projectPath) {
26
+ document.getElementById('pipeline-project-path').value = data.projectPath;
27
+ document.getElementById('pipeline-workspace').value = data.workspace || '';
28
+ document.getElementById('pipeline-repo-slug').value = data.repoSlug || '';
29
+ document.getElementById('pipeline-display-path').textContent = data.projectPath;
30
+ document.getElementById('pipeline-path-info').style.display = 'block';
31
+
32
+ if (data.workspace && data.repoSlug) {
33
+ document.getElementById('pipeline-display-workspace').textContent = data.workspace;
34
+ document.getElementById('pipeline-display-repo-slug').textContent = data.repoSlug;
35
+ document.getElementById('pipeline-git-info').style.display = 'block';
36
+ }
37
+ }
38
+ document.getElementById('pipeline-tag-format').value = data.tagFormat || '';
39
+ document.getElementById('pipeline-fixed-tag').value = data.fixedTag || '';
40
+ }
41
+ } catch (error) {
42
+ console.error('加载配置失败:', error);
43
+ }
44
+ },
45
+
46
+ /**
47
+ * 保存配置
48
+ */
49
+ saveConfig() {
50
+ try {
51
+ const config = {
52
+ projectPath: document.getElementById('pipeline-project-path').value.trim(),
53
+ workspace: document.getElementById('pipeline-workspace').value.trim(),
54
+ repoSlug: document.getElementById('pipeline-repo-slug').value.trim(),
55
+ tagFormat: document.getElementById('pipeline-tag-format').value.trim(),
56
+ fixedTag: document.getElementById('pipeline-fixed-tag').value.trim(),
57
+ };
58
+
59
+ localStorage.setItem('pipeline-test-config', JSON.stringify(config));
60
+ Components.Toast.success('配置已保存');
61
+ } catch (error) {
62
+ console.error('保存配置失败:', error);
63
+ Components.Toast.error('保存配置失败: ' + error.message);
64
+ }
65
+ },
66
+
67
+ /**
68
+ * 绑定事件
69
+ */
70
+ bindEvents() {
71
+ /** 选择文件夹按钮 */
72
+ document.getElementById('pipeline-select-folder-btn')?.addEventListener('click', () => {
73
+ this.selectProjectFolder();
74
+ });
75
+
76
+ /** 保存配置按钮 */
77
+ document.getElementById('save-pipeline-config-btn')?.addEventListener('click', () => {
78
+ this.saveConfig();
79
+ });
80
+
81
+ /** 创建 Tag 并监听按钮 */
82
+ document.getElementById('pipeline-test-btn')?.addEventListener('click', () => {
83
+ this.testPipeline();
84
+ });
85
+
86
+ /** 查询固定 Tag 按钮 */
87
+ document.getElementById('pipeline-query-btn')?.addEventListener('click', () => {
88
+ this.queryFixedTag();
89
+ });
90
+ },
91
+
92
+ /**
93
+ * 选择项目文件夹
94
+ */
95
+ async selectProjectFolder() {
96
+ try {
97
+ Components.Loading.show('正在打开文件夹选择器...');
98
+
99
+ const response = await API.post('/select-folder', {});
100
+
101
+ Components.Loading.hide();
102
+
103
+ if (!response.path) {
104
+ Components.Toast.error('未选择文件夹');
105
+ return;
106
+ }
107
+
108
+ document.getElementById('pipeline-project-path').value = response.path;
109
+ await this.parseGitInfo(response.path);
110
+ } catch (error) {
111
+ Components.Loading.hide();
112
+
113
+ if (error.message.includes('用户取消')) {
114
+ return;
115
+ }
116
+
117
+ Components.Toast.error('选择文件夹失败: ' + error.message);
118
+ }
119
+ },
120
+
121
+ /**
122
+ * 解析 Git 仓库信息
123
+ */
124
+ async parseGitInfo(projectPath) {
125
+ try {
126
+ if (!projectPath) {
127
+ projectPath = document.getElementById('pipeline-project-path').value.trim();
128
+ }
129
+
130
+ if (!projectPath) {
131
+ Components.Toast.error('请先选择项目路径');
132
+ return;
133
+ }
134
+
135
+ Components.Loading.show('正在解析 Git 信息...');
136
+
137
+ const response = await API.post('/parse-git-repo', { projectPath });
138
+
139
+ Components.Loading.hide();
140
+
141
+ document.getElementById('pipeline-display-path').textContent = projectPath;
142
+ document.getElementById('pipeline-path-info').style.display = 'block';
143
+
144
+ if (response.warning) {
145
+ Components.Toast.error(response.warning);
146
+ Components.Modal.confirm(
147
+ `检测到 Git 仓库,但不是 Bitbucket 仓库。\n\nRemote URL: ${response.remoteUrl}\n\n是否手动填写 Bitbucket 信息?`,
148
+ () => {
149
+ const workspace = prompt('请输入 Bitbucket Workspace:');
150
+ const repoSlug = prompt('请输入 Bitbucket Repo Slug:');
151
+
152
+ if (workspace && repoSlug) {
153
+ document.getElementById('pipeline-workspace').value = workspace.trim();
154
+ document.getElementById('pipeline-repo-slug').value = repoSlug.trim();
155
+ document.getElementById('pipeline-display-workspace').textContent = workspace.trim();
156
+ document.getElementById('pipeline-display-repo-slug').textContent = repoSlug.trim();
157
+ document.getElementById('pipeline-git-info').style.display = 'block';
158
+ Components.Toast.success('已手动设置 Bitbucket 信息');
159
+ }
160
+ }
161
+ );
162
+ } else {
163
+ document.getElementById('pipeline-workspace').value = response.workspace;
164
+ document.getElementById('pipeline-repo-slug').value = response.repoSlug;
165
+ document.getElementById('pipeline-display-workspace').textContent = response.workspace;
166
+ document.getElementById('pipeline-display-repo-slug').textContent = response.repoSlug;
167
+ document.getElementById('pipeline-git-info').style.display = 'block';
168
+ Components.Toast.success('Git 信息解析成功');
169
+ }
170
+ } catch (error) {
171
+ Components.Loading.hide();
172
+
173
+ const path = document.getElementById('pipeline-project-path').value;
174
+ if (path) {
175
+ document.getElementById('pipeline-display-path').textContent = path;
176
+ document.getElementById('pipeline-path-info').style.display = 'block';
177
+ }
178
+
179
+ Components.Toast.error('解析失败: ' + error.message);
180
+ }
181
+ },
182
+
183
+ /**
184
+ * 添加日志
185
+ */
186
+ addLog(message, type = 'info') {
187
+ const logContainer = document.getElementById('pipeline-logs');
188
+ if (!logContainer) return;
189
+
190
+ const timestamp = new Date().toLocaleTimeString();
191
+ const logEntry = document.createElement('div');
192
+ logEntry.style.cssText = 'padding: 0.25rem 0; font-family: monospace; font-size: 0.875rem;';
193
+
194
+ const colors = {
195
+ info: 'var(--text-secondary)',
196
+ success: 'var(--success-color)',
197
+ error: 'var(--error-color)',
198
+ warning: '#f59e0b',
199
+ };
200
+
201
+ logEntry.innerHTML = `<span style="color: var(--text-secondary);">[${timestamp}]</span> <span style="color: ${colors[type]};">${message}</span>`;
202
+ logContainer.appendChild(logEntry);
203
+ logContainer.scrollTop = logContainer.scrollHeight;
204
+ },
205
+
206
+ /**
207
+ * 清空日志
208
+ */
209
+ clearLogs() {
210
+ const logContainer = document.getElementById('pipeline-logs');
211
+ if (logContainer) {
212
+ logContainer.innerHTML = '';
213
+ }
214
+ },
215
+
216
+ /**
217
+ * 查询固定 Tag 的构建状态
218
+ */
219
+ async queryFixedTag() {
220
+ try {
221
+ const workspace = document.getElementById('pipeline-workspace').value.trim();
222
+ const repoSlug = document.getElementById('pipeline-repo-slug').value.trim();
223
+ const fixedTag = document.getElementById('pipeline-fixed-tag').value.trim();
224
+
225
+ if (!workspace || !repoSlug) {
226
+ Components.Toast.error('请先选择项目文件夹');
227
+ return;
228
+ }
229
+
230
+ if (!fixedTag) {
231
+ Components.Toast.error('请输入要查询的 Tag 名称');
232
+ return;
233
+ }
234
+
235
+ const statusDiv = document.getElementById('pipeline-status');
236
+ statusDiv.style.display = 'block';
237
+ statusDiv.innerHTML = `
238
+ <div style="padding: 1rem; background-color: #1e1e1e; border-radius: var(--radius-md); border: 1px solid var(--border-color);">
239
+ <div style="color: #4ade80; font-size: 0.875rem; margin-bottom: 0.5rem; font-weight: 600;">
240
+ 📋 查询 Tag: ${fixedTag}
241
+ </div>
242
+ <div id="pipeline-logs" style="max-height: 400px; overflow-y: auto; color: var(--text-secondary); font-family: monospace; font-size: 0.875rem;">
243
+ </div>
244
+ </div>
245
+ `;
246
+
247
+ this.clearLogs();
248
+
249
+ // 使用 EventSource 接收 SSE
250
+ const params = new URLSearchParams({
251
+ workspace,
252
+ repoSlug,
253
+ fixedTag,
254
+ queryOnly: 'true',
255
+ });
256
+
257
+ const eventSource = new EventSource(`/api/query-tag-status-stream?${params}`);
258
+
259
+ eventSource.addEventListener('log', (e) => {
260
+ const data = JSON.parse(e.data);
261
+ this.addLog(data.message, data.type || 'info');
262
+ });
263
+
264
+ eventSource.addEventListener('error', (e) => {
265
+ try {
266
+ const data = JSON.parse(e.data);
267
+ this.addLog(`错误: ${data.message}`, 'error');
268
+ Components.Toast.error('查询失败');
269
+ } catch (err) {
270
+ console.error('解析错误消息失败:', err);
271
+ }
272
+ eventSource.close();
273
+ });
274
+
275
+ eventSource.addEventListener('complete', (e) => {
276
+ const data = JSON.parse(e.data);
277
+ this.addLog(`${data.message}`, 'success');
278
+ Components.Toast.success('查询完成');
279
+ eventSource.close();
280
+ });
281
+
282
+ eventSource.onerror = (error) => {
283
+ console.error('SSE 连接错误:', error);
284
+ this.addLog('连接中断或服务器错误', 'error');
285
+ Components.Toast.error('连接失败');
286
+ eventSource.close();
287
+ };
288
+
289
+ } catch (error) {
290
+ this.addLog(`错误: ${error.message}`, 'error');
291
+ Components.Toast.error('查询失败: ' + error.message);
292
+ }
293
+ },
294
+
295
+ /**
296
+ * 测试 Pipeline (使用 SSE)
297
+ */
298
+ async testPipeline() {
299
+ try {
300
+ const projectPath = document.getElementById('pipeline-project-path').value.trim();
301
+ const workspace = document.getElementById('pipeline-workspace').value.trim();
302
+ const repoSlug = document.getElementById('pipeline-repo-slug').value.trim();
303
+ const tagFormat = document.getElementById('pipeline-tag-format').value.trim();
304
+
305
+ if (!projectPath || !workspace || !repoSlug || !tagFormat) {
306
+ Components.Toast.error('请填写所有必填字段');
307
+ return;
308
+ }
309
+
310
+ const statusDiv = document.getElementById('pipeline-status');
311
+ statusDiv.style.display = 'block';
312
+ statusDiv.innerHTML = `
313
+ <div style="padding: 1rem; background-color: #1e1e1e; border-radius: var(--radius-md); border: 1px solid var(--border-color);">
314
+ <div style="color: #4ade80; font-size: 0.875rem; margin-bottom: 0.5rem; font-weight: 600;">
315
+ 📋 Pipeline 执行日志
316
+ </div>
317
+ <div id="pipeline-logs" style="max-height: 400px; overflow-y: auto; color: var(--text-secondary); font-family: monospace; font-size: 0.875rem;">
318
+ </div>
319
+ </div>
320
+ `;
321
+
322
+ this.clearLogs();
323
+
324
+ // 使用 EventSource 接收 SSE
325
+ const params = new URLSearchParams({
326
+ projectPath,
327
+ workspace,
328
+ repoSlug,
329
+ tagFormat,
330
+ });
331
+
332
+ const eventSource = new EventSource(`/api/test-pipeline-stream?${params}`);
333
+
334
+ eventSource.addEventListener('log', (e) => {
335
+ const data = JSON.parse(e.data);
336
+ this.addLog(data.message, data.type || 'info');
337
+ });
338
+
339
+ eventSource.addEventListener('error', (e) => {
340
+ const data = JSON.parse(e.data);
341
+ this.addLog(`错误: ${data.message}`, 'error');
342
+ Components.Toast.error('Pipeline 测试失败');
343
+ eventSource.close();
344
+ });
345
+
346
+ eventSource.addEventListener('complete', (e) => {
347
+ const data = JSON.parse(e.data);
348
+ this.addLog(`${data.message}`, 'success');
349
+ Components.Toast.success('Pipeline 执行成功');
350
+ eventSource.close();
351
+ });
352
+
353
+ eventSource.onerror = (error) => {
354
+ console.error('SSE 连接错误:', error);
355
+ this.addLog('连接中断', 'error');
356
+ eventSource.close();
357
+ };
358
+
359
+ } catch (error) {
360
+ this.addLog(`错误: ${error.message}`, 'error');
361
+ Components.Toast.error('Pipeline 测试失败: ' + error.message);
362
+ }
363
+ },
364
+ };