@serve.zone/gitops 2.13.1

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.
Files changed (258) hide show
  1. package/.smartconfig.json +114 -0
  2. package/binary/gitops.ts +4 -0
  3. package/changelog.md +185 -0
  4. package/cli.child.js +4 -0
  5. package/cli.js +4 -0
  6. package/cli.ts.js +5 -0
  7. package/deno.json +10 -0
  8. package/dist_serve/bundle.js +36362 -0
  9. package/dist_serve/index.html +33 -0
  10. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  11. package/dist_ts/00_commitinfo_data.js +9 -0
  12. package/dist_ts/cache/classes.cache.cleaner.d.ts +23 -0
  13. package/dist_ts/cache/classes.cache.cleaner.js +61 -0
  14. package/dist_ts/cache/classes.cached.document.d.ts +30 -0
  15. package/dist_ts/cache/classes.cached.document.js +101 -0
  16. package/dist_ts/cache/classes.cachedb.d.ts +22 -0
  17. package/dist_ts/cache/classes.cachedb.js +58 -0
  18. package/dist_ts/cache/classes.secrets.scan.service.d.ts +51 -0
  19. package/dist_ts/cache/classes.secrets.scan.service.js +237 -0
  20. package/dist_ts/cache/documents/classes.cached.project.d.ts +13 -0
  21. package/dist_ts/cache/documents/classes.cached.project.js +101 -0
  22. package/dist_ts/cache/documents/classes.cached.secret.d.ts +24 -0
  23. package/dist_ts/cache/documents/classes.cached.secret.js +158 -0
  24. package/dist_ts/cache/documents/index.d.ts +2 -0
  25. package/dist_ts/cache/documents/index.js +3 -0
  26. package/dist_ts/cache/index.d.ts +7 -0
  27. package/dist_ts/cache/index.js +6 -0
  28. package/dist_ts/classes/actionlog.d.ts +19 -0
  29. package/dist_ts/classes/actionlog.js +44 -0
  30. package/dist_ts/classes/connectionmanager.d.ts +57 -0
  31. package/dist_ts/classes/connectionmanager.js +247 -0
  32. package/dist_ts/classes/gitopsapp.d.ts +30 -0
  33. package/dist_ts/classes/gitopsapp.js +101 -0
  34. package/dist_ts/classes/jobmanager.d.ts +47 -0
  35. package/dist_ts/classes/jobmanager.js +301 -0
  36. package/dist_ts/classes/jobrunners/autobookstackdocs.runner.d.ts +29 -0
  37. package/dist_ts/classes/jobrunners/autobookstackdocs.runner.js +361 -0
  38. package/dist_ts/classes/jobrunners/base.jobrunner.d.ts +14 -0
  39. package/dist_ts/classes/jobrunners/base.jobrunner.js +3 -0
  40. package/dist_ts/classes/jobrunners/index.d.ts +5 -0
  41. package/dist_ts/classes/jobrunners/index.js +14 -0
  42. package/dist_ts/classes/managedsecrets.manager.d.ts +47 -0
  43. package/dist_ts/classes/managedsecrets.manager.js +247 -0
  44. package/dist_ts/classes/syncmanager.d.ts +189 -0
  45. package/dist_ts/classes/syncmanager.js +1787 -0
  46. package/dist_ts/index.d.ts +6 -0
  47. package/dist_ts/index.js +32 -0
  48. package/dist_ts/logging.d.ts +49 -0
  49. package/dist_ts/logging.js +134 -0
  50. package/dist_ts/opsserver/classes.opsserver.d.ts +25 -0
  51. package/dist_ts/opsserver/classes.opsserver.js +70 -0
  52. package/dist_ts/opsserver/handlers/actionlog.handler.d.ts +9 -0
  53. package/dist_ts/opsserver/handlers/actionlog.handler.js +24 -0
  54. package/dist_ts/opsserver/handlers/actions.handler.d.ts +9 -0
  55. package/dist_ts/opsserver/handlers/actions.handler.js +38 -0
  56. package/dist_ts/opsserver/handlers/admin.handler.d.ts +19 -0
  57. package/dist_ts/opsserver/handlers/admin.handler.js +96 -0
  58. package/dist_ts/opsserver/handlers/connections.handler.d.ts +10 -0
  59. package/dist_ts/opsserver/handlers/connections.handler.js +109 -0
  60. package/dist_ts/opsserver/handlers/groups.handler.d.ts +9 -0
  61. package/dist_ts/opsserver/handlers/groups.handler.js +24 -0
  62. package/dist_ts/opsserver/handlers/index.d.ts +13 -0
  63. package/dist_ts/opsserver/handlers/index.js +14 -0
  64. package/dist_ts/opsserver/handlers/jobs.handler.d.ts +16 -0
  65. package/dist_ts/opsserver/handlers/jobs.handler.js +146 -0
  66. package/dist_ts/opsserver/handlers/logs.handler.d.ts +9 -0
  67. package/dist_ts/opsserver/handlers/logs.handler.js +21 -0
  68. package/dist_ts/opsserver/handlers/managedsecrets.handler.d.ts +11 -0
  69. package/dist_ts/opsserver/handlers/managedsecrets.handler.js +110 -0
  70. package/dist_ts/opsserver/handlers/pipelines.handler.d.ts +31 -0
  71. package/dist_ts/opsserver/handlers/pipelines.handler.js +204 -0
  72. package/dist_ts/opsserver/handlers/projects.handler.d.ts +9 -0
  73. package/dist_ts/opsserver/handlers/projects.handler.js +24 -0
  74. package/dist_ts/opsserver/handlers/secrets.handler.d.ts +10 -0
  75. package/dist_ts/opsserver/handlers/secrets.handler.js +171 -0
  76. package/dist_ts/opsserver/handlers/sync.handler.d.ts +16 -0
  77. package/dist_ts/opsserver/handlers/sync.handler.js +166 -0
  78. package/dist_ts/opsserver/handlers/webhook.handler.d.ts +7 -0
  79. package/dist_ts/opsserver/handlers/webhook.handler.js +55 -0
  80. package/dist_ts/opsserver/helpers/guards.d.ts +5 -0
  81. package/dist_ts/opsserver/helpers/guards.js +12 -0
  82. package/dist_ts/opsserver/index.d.ts +1 -0
  83. package/dist_ts/opsserver/index.js +2 -0
  84. package/dist_ts/paths.d.ts +9 -0
  85. package/dist_ts/paths.js +13 -0
  86. package/dist_ts/plugins.d.ts +25 -0
  87. package/dist_ts/plugins.js +32 -0
  88. package/dist_ts/providers/classes.baseprovider.d.ts +51 -0
  89. package/dist_ts/providers/classes.baseprovider.js +17 -0
  90. package/dist_ts/providers/classes.giteaprovider.d.ts +40 -0
  91. package/dist_ts/providers/classes.giteaprovider.js +224 -0
  92. package/dist_ts/providers/classes.gitlabprovider.d.ts +39 -0
  93. package/dist_ts/providers/classes.gitlabprovider.js +207 -0
  94. package/dist_ts/providers/index.d.ts +3 -0
  95. package/dist_ts/providers/index.js +4 -0
  96. package/dist_ts/storage/classes.storagemanager.d.ts +33 -0
  97. package/dist_ts/storage/classes.storagemanager.js +135 -0
  98. package/dist_ts/storage/index.d.ts +2 -0
  99. package/dist_ts/storage/index.js +2 -0
  100. package/dist_ts/timers.d.ts +4 -0
  101. package/dist_ts/timers.js +24 -0
  102. package/dist_ts_bundled/bundle.d.ts +4 -0
  103. package/dist_ts_bundled/bundle.js +12 -0
  104. package/dist_ts_interfaces/data/actionlog.d.ts +12 -0
  105. package/dist_ts_interfaces/data/actionlog.js +2 -0
  106. package/dist_ts_interfaces/data/branch.d.ts +8 -0
  107. package/dist_ts_interfaces/data/branch.js +2 -0
  108. package/dist_ts_interfaces/data/connection.d.ts +12 -0
  109. package/dist_ts_interfaces/data/connection.js +2 -0
  110. package/dist_ts_interfaces/data/group.d.ts +10 -0
  111. package/dist_ts_interfaces/data/group.js +2 -0
  112. package/dist_ts_interfaces/data/identity.d.ts +7 -0
  113. package/dist_ts_interfaces/data/identity.js +2 -0
  114. package/dist_ts_interfaces/data/index.d.ts +11 -0
  115. package/dist_ts_interfaces/data/index.js +12 -0
  116. package/dist_ts_interfaces/data/job.d.ts +37 -0
  117. package/dist_ts_interfaces/data/job.js +2 -0
  118. package/dist_ts_interfaces/data/managedsecret.d.ts +37 -0
  119. package/dist_ts_interfaces/data/managedsecret.js +2 -0
  120. package/dist_ts_interfaces/data/pipeline.d.ts +22 -0
  121. package/dist_ts_interfaces/data/pipeline.js +2 -0
  122. package/dist_ts_interfaces/data/project.d.ts +12 -0
  123. package/dist_ts_interfaces/data/project.js +2 -0
  124. package/dist_ts_interfaces/data/secret.d.ts +11 -0
  125. package/dist_ts_interfaces/data/secret.js +2 -0
  126. package/dist_ts_interfaces/data/sync.d.ts +34 -0
  127. package/dist_ts_interfaces/data/sync.js +2 -0
  128. package/dist_ts_interfaces/index.d.ts +5 -0
  129. package/dist_ts_interfaces/index.js +8 -0
  130. package/dist_ts_interfaces/plugins.d.ts +2 -0
  131. package/dist_ts_interfaces/plugins.js +4 -0
  132. package/dist_ts_interfaces/requests/actionlog.d.ts +15 -0
  133. package/dist_ts_interfaces/requests/actionlog.js +3 -0
  134. package/dist_ts_interfaces/requests/actions.d.ts +31 -0
  135. package/dist_ts_interfaces/requests/actions.js +3 -0
  136. package/dist_ts_interfaces/requests/admin.d.ts +31 -0
  137. package/dist_ts_interfaces/requests/admin.js +3 -0
  138. package/dist_ts_interfaces/requests/connections.d.ts +71 -0
  139. package/dist_ts_interfaces/requests/connections.js +3 -0
  140. package/dist_ts_interfaces/requests/groups.d.ts +14 -0
  141. package/dist_ts_interfaces/requests/groups.js +3 -0
  142. package/dist_ts_interfaces/requests/index.d.ts +13 -0
  143. package/dist_ts_interfaces/requests/index.js +14 -0
  144. package/dist_ts_interfaces/requests/jobs.d.ts +86 -0
  145. package/dist_ts_interfaces/requests/jobs.js +3 -0
  146. package/dist_ts_interfaces/requests/logs.d.ts +14 -0
  147. package/dist_ts_interfaces/requests/logs.js +3 -0
  148. package/dist_ts_interfaces/requests/managedsecrets.d.ts +84 -0
  149. package/dist_ts_interfaces/requests/managedsecrets.js +3 -0
  150. package/dist_ts_interfaces/requests/pipelines.d.ts +55 -0
  151. package/dist_ts_interfaces/requests/pipelines.js +3 -0
  152. package/dist_ts_interfaces/requests/projects.d.ts +14 -0
  153. package/dist_ts_interfaces/requests/projects.js +3 -0
  154. package/dist_ts_interfaces/requests/secrets.d.ts +72 -0
  155. package/dist_ts_interfaces/requests/secrets.js +3 -0
  156. package/dist_ts_interfaces/requests/sync.d.ts +120 -0
  157. package/dist_ts_interfaces/requests/sync.js +3 -0
  158. package/dist_ts_interfaces/requests/webhook.d.ts +13 -0
  159. package/dist_ts_interfaces/requests/webhook.js +3 -0
  160. package/license +21 -0
  161. package/package.json +81 -0
  162. package/readme.md +177 -0
  163. package/readme.todo.md +3 -0
  164. package/ts/00_commitinfo_data.ts +8 -0
  165. package/ts/cache/classes.cache.cleaner.ts +69 -0
  166. package/ts/cache/classes.cached.document.ts +57 -0
  167. package/ts/cache/classes.cachedb.ts +72 -0
  168. package/ts/cache/classes.secrets.scan.service.ts +267 -0
  169. package/ts/cache/documents/classes.cached.project.ts +32 -0
  170. package/ts/cache/documents/classes.cached.secret.ts +81 -0
  171. package/ts/cache/documents/index.ts +2 -0
  172. package/ts/cache/index.ts +7 -0
  173. package/ts/classes/actionlog.ts +57 -0
  174. package/ts/classes/connectionmanager.ts +263 -0
  175. package/ts/classes/gitopsapp.ts +128 -0
  176. package/ts/classes/jobmanager.ts +337 -0
  177. package/ts/classes/jobrunners/autobookstackdocs.runner.ts +435 -0
  178. package/ts/classes/jobrunners/base.jobrunner.ts +16 -0
  179. package/ts/classes/jobrunners/index.ts +17 -0
  180. package/ts/classes/managedsecrets.manager.ts +322 -0
  181. package/ts/classes/syncmanager.ts +2117 -0
  182. package/ts/index.ts +37 -0
  183. package/ts/logging.ts +162 -0
  184. package/ts/opsserver/classes.opsserver.ts +86 -0
  185. package/ts/opsserver/handlers/actionlog.handler.ts +30 -0
  186. package/ts/opsserver/handlers/actions.handler.ts +50 -0
  187. package/ts/opsserver/handlers/admin.handler.ts +122 -0
  188. package/ts/opsserver/handlers/connections.handler.ts +162 -0
  189. package/ts/opsserver/handlers/groups.handler.ts +32 -0
  190. package/ts/opsserver/handlers/index.ts +13 -0
  191. package/ts/opsserver/handlers/jobs.handler.ts +189 -0
  192. package/ts/opsserver/handlers/logs.handler.ts +29 -0
  193. package/ts/opsserver/handlers/managedsecrets.handler.ts +158 -0
  194. package/ts/opsserver/handlers/pipelines.handler.ts +281 -0
  195. package/ts/opsserver/handlers/projects.handler.ts +32 -0
  196. package/ts/opsserver/handlers/secrets.handler.ts +224 -0
  197. package/ts/opsserver/handlers/sync.handler.ts +224 -0
  198. package/ts/opsserver/handlers/webhook.handler.ts +62 -0
  199. package/ts/opsserver/helpers/guards.ts +16 -0
  200. package/ts/opsserver/index.ts +1 -0
  201. package/ts/paths.ts +19 -0
  202. package/ts/plugins.ts +38 -0
  203. package/ts/providers/classes.baseprovider.ts +99 -0
  204. package/ts/providers/classes.giteaprovider.ts +279 -0
  205. package/ts/providers/classes.gitlabprovider.ts +265 -0
  206. package/ts/providers/index.ts +3 -0
  207. package/ts/storage/classes.storagemanager.ts +144 -0
  208. package/ts/storage/index.ts +2 -0
  209. package/ts/timers.ts +34 -0
  210. package/ts_interfaces/data/actionlog.ts +13 -0
  211. package/ts_interfaces/data/branch.ts +9 -0
  212. package/ts_interfaces/data/connection.ts +13 -0
  213. package/ts_interfaces/data/group.ts +10 -0
  214. package/ts_interfaces/data/identity.ts +7 -0
  215. package/ts_interfaces/data/index.ts +11 -0
  216. package/ts_interfaces/data/job.ts +42 -0
  217. package/ts_interfaces/data/managedsecret.ts +41 -0
  218. package/ts_interfaces/data/pipeline.ts +32 -0
  219. package/ts_interfaces/data/project.ts +12 -0
  220. package/ts_interfaces/data/secret.ts +11 -0
  221. package/ts_interfaces/data/sync.ts +37 -0
  222. package/ts_interfaces/index.ts +9 -0
  223. package/ts_interfaces/plugins.ts +6 -0
  224. package/ts_interfaces/requests/actionlog.ts +19 -0
  225. package/ts_interfaces/requests/actions.ts +39 -0
  226. package/ts_interfaces/requests/admin.ts +43 -0
  227. package/ts_interfaces/requests/connections.ts +95 -0
  228. package/ts_interfaces/requests/groups.ts +18 -0
  229. package/ts_interfaces/requests/index.ts +13 -0
  230. package/ts_interfaces/requests/jobs.ts +118 -0
  231. package/ts_interfaces/requests/logs.ts +18 -0
  232. package/ts_interfaces/requests/managedsecrets.ts +112 -0
  233. package/ts_interfaces/requests/pipelines.ts +71 -0
  234. package/ts_interfaces/requests/projects.ts +18 -0
  235. package/ts_interfaces/requests/secrets.ts +92 -0
  236. package/ts_interfaces/requests/sync.ts +157 -0
  237. package/ts_interfaces/requests/webhook.ts +18 -0
  238. package/ts_web/00_commitinfo_data.ts +8 -0
  239. package/ts_web/appstate.ts +1251 -0
  240. package/ts_web/elements/gitops-dashboard.ts +350 -0
  241. package/ts_web/elements/index.ts +10 -0
  242. package/ts_web/elements/shared/css.ts +29 -0
  243. package/ts_web/elements/shared/index.ts +1 -0
  244. package/ts_web/elements/views/actionlog/index.ts +101 -0
  245. package/ts_web/elements/views/actions/index.ts +209 -0
  246. package/ts_web/elements/views/buildlog/index.ts +196 -0
  247. package/ts_web/elements/views/connections/index.ts +260 -0
  248. package/ts_web/elements/views/groups/index.ts +134 -0
  249. package/ts_web/elements/views/jobs/index.ts +424 -0
  250. package/ts_web/elements/views/managedsecrets/index.ts +502 -0
  251. package/ts_web/elements/views/overview/index.ts +86 -0
  252. package/ts_web/elements/views/pipelines/index.ts +561 -0
  253. package/ts_web/elements/views/projects/index.ts +149 -0
  254. package/ts_web/elements/views/secrets/index.ts +310 -0
  255. package/ts_web/elements/views/sync/index.ts +512 -0
  256. package/ts_web/index.ts +7 -0
  257. package/ts_web/plugins.ts +15 -0
  258. package/tsconfig.json +15 -0
@@ -0,0 +1,1787 @@
1
+ import * as plugins from '../plugins.js';
2
+ import { logger } from '../logging.js';
3
+ import { intervalMinutesToMs, unrefTimer, validateIntervalMinutes } from '../timers.js';
4
+ const SYNC_PREFIX = '/sync/';
5
+ const SYNC_STATUS_PREFIX = '/sync-status/';
6
+ /**
7
+ * Manages sync configurations and executes periodic git mirror operations.
8
+ * Each sync config defines a source → target connection mapping.
9
+ * Repos are mirrored using bare git repos stored on disk.
10
+ */
11
+ export class SyncManager {
12
+ storageManager;
13
+ connectionManager;
14
+ actionLog;
15
+ configs = [];
16
+ timers = new Map();
17
+ runningSync = new Set();
18
+ syncedGroupMeta = new Set();
19
+ currentSyncConfig = null;
20
+ avatarUploadCache = new Map();
21
+ activeGitChildren = new Set();
22
+ stopping = false;
23
+ mirrorsPath = '';
24
+ constructor(storageManager, connectionManager, actionLog) {
25
+ this.storageManager = storageManager;
26
+ this.connectionManager = connectionManager;
27
+ this.actionLog = actionLog;
28
+ }
29
+ async init() {
30
+ this.stopping = false;
31
+ // Create temp directory for mirrors (RAM-backed on most Linux systems via tmpfs)
32
+ this.mirrorsPath = await plugins.fs.mkdtemp(plugins.path.join(plugins.os.tmpdir(), 'gitops-mirrors-'));
33
+ await this.loadConfigs();
34
+ for (const config of this.configs) {
35
+ if (config.status === 'active') {
36
+ this.startTimer(config);
37
+ }
38
+ }
39
+ if (this.configs.length > 0) {
40
+ logger.info(`SyncManager loaded ${this.configs.length} sync config(s)`);
41
+ }
42
+ }
43
+ async stop() {
44
+ this.stopping = true;
45
+ for (const [_id, timer] of this.timers) {
46
+ clearInterval(timer);
47
+ }
48
+ this.timers.clear();
49
+ for (const child of this.activeGitChildren) {
50
+ if (child.exitCode === null && child.signalCode === null) {
51
+ child.kill('SIGTERM');
52
+ }
53
+ }
54
+ if (this.activeGitChildren.size > 0) {
55
+ const forceKillTimer = setTimeout(() => {
56
+ for (const child of this.activeGitChildren) {
57
+ if (child.exitCode === null && child.signalCode === null) {
58
+ child.kill('SIGKILL');
59
+ }
60
+ }
61
+ }, 5000);
62
+ unrefTimer(forceKillTimer);
63
+ await this.waitForRunningSyncs();
64
+ clearTimeout(forceKillTimer);
65
+ }
66
+ else {
67
+ await this.waitForRunningSyncs();
68
+ }
69
+ // Clean up temp mirrors directory
70
+ if (this.mirrorsPath) {
71
+ try {
72
+ await plugins.fs.rm(this.mirrorsPath, { recursive: true, force: true });
73
+ }
74
+ catch { /* may already be gone */ }
75
+ }
76
+ }
77
+ // ============================================================================
78
+ // CRUD
79
+ // ============================================================================
80
+ getConfigs() {
81
+ return [...this.configs];
82
+ }
83
+ getConfig(id) {
84
+ return this.configs.find((c) => c.id === id);
85
+ }
86
+ async createConfig(data) {
87
+ const config = {
88
+ id: crypto.randomUUID(),
89
+ name: data.name,
90
+ sourceConnectionId: data.sourceConnectionId,
91
+ targetConnectionId: data.targetConnectionId,
92
+ targetGroupOffset: data.targetGroupOffset,
93
+ intervalMinutes: validateIntervalMinutes(data.intervalMinutes, 'sync intervalMinutes', 5),
94
+ status: 'paused',
95
+ lastSyncAt: 0,
96
+ reposSynced: 0,
97
+ enforceDelete: data.enforceDelete ?? false,
98
+ enforceGroupDelete: data.enforceGroupDelete ?? false,
99
+ addMirrorHint: data.addMirrorHint ?? false,
100
+ useGroupAvatarsForProjects: data.useGroupAvatarsForProjects ?? false,
101
+ createdAt: Date.now(),
102
+ };
103
+ this.validateSyncConfig(config);
104
+ this.configs.push(config);
105
+ await this.persistConfig(config);
106
+ logger.success(`Sync config created (paused): ${config.name}`);
107
+ return config;
108
+ }
109
+ async updateConfig(id, updates) {
110
+ const config = this.configs.find((c) => c.id === id);
111
+ if (!config)
112
+ throw new Error(`Sync config not found: ${id}`);
113
+ if (updates.name)
114
+ config.name = updates.name;
115
+ if (updates.intervalMinutes !== undefined) {
116
+ config.intervalMinutes = validateIntervalMinutes(updates.intervalMinutes, 'sync intervalMinutes');
117
+ }
118
+ if (updates.enforceDelete !== undefined)
119
+ config.enforceDelete = updates.enforceDelete;
120
+ if (updates.enforceGroupDelete !== undefined)
121
+ config.enforceGroupDelete = updates.enforceGroupDelete;
122
+ if (updates.addMirrorHint !== undefined)
123
+ config.addMirrorHint = updates.addMirrorHint;
124
+ if (updates.useGroupAvatarsForProjects !== undefined)
125
+ config.useGroupAvatarsForProjects = updates.useGroupAvatarsForProjects;
126
+ if (updates.targetGroupOffset !== undefined)
127
+ config.targetGroupOffset = updates.targetGroupOffset;
128
+ this.validateSyncConfig(config);
129
+ await this.persistConfig(config);
130
+ // Restart timer with new interval
131
+ if (config.status === 'active') {
132
+ this.startTimer(config);
133
+ }
134
+ return config;
135
+ }
136
+ async deleteConfig(id) {
137
+ const idx = this.configs.findIndex((c) => c.id === id);
138
+ if (idx === -1)
139
+ throw new Error(`Sync config not found: ${id}`);
140
+ this.stopTimer(id);
141
+ this.configs.splice(idx, 1);
142
+ await this.storageManager.delete(`${SYNC_PREFIX}${id}.json`);
143
+ // Clean up repo statuses
144
+ const statusKeys = await this.storageManager.list(`${SYNC_STATUS_PREFIX}${id}/`);
145
+ for (const key of statusKeys) {
146
+ await this.storageManager.delete(key);
147
+ }
148
+ // Clean up mirror directory
149
+ const mirrorDir = plugins.path.join(this.mirrorsPath, id);
150
+ try {
151
+ await plugins.fs.rm(mirrorDir, { recursive: true, force: true });
152
+ }
153
+ catch {
154
+ // Directory may not exist
155
+ }
156
+ logger.info(`Sync config deleted: ${id}`);
157
+ }
158
+ async pauseConfig(id, paused) {
159
+ const config = this.configs.find((c) => c.id === id);
160
+ if (!config)
161
+ throw new Error(`Sync config not found: ${id}`);
162
+ config.status = paused ? 'paused' : 'active';
163
+ await this.persistConfig(config);
164
+ if (paused) {
165
+ this.stopTimer(id);
166
+ }
167
+ else {
168
+ this.startTimer(config);
169
+ }
170
+ logger.info(`Sync config ${paused ? 'paused' : 'resumed'}: ${config.name}`);
171
+ return config;
172
+ }
173
+ async getRepoStatuses(syncConfigId) {
174
+ const keys = await this.storageManager.list(`${SYNC_STATUS_PREFIX}${syncConfigId}/`);
175
+ const statuses = [];
176
+ for (const key of keys) {
177
+ const status = await this.storageManager.getJSON(key);
178
+ if (status)
179
+ statuses.push(status);
180
+ }
181
+ return statuses;
182
+ }
183
+ // ============================================================================
184
+ // Preview
185
+ // ============================================================================
186
+ async previewSync(configId) {
187
+ const config = this.configs.find((c) => c.id === configId);
188
+ if (!config)
189
+ throw new Error(`Sync config not found: ${configId}`);
190
+ logger.syncLog('info', `Preview started for "${config.name}"`, 'preview');
191
+ const sourceConn = this.connectionManager.getConnection(config.sourceConnectionId);
192
+ const targetConn = this.connectionManager.getConnection(config.targetConnectionId);
193
+ if (!sourceConn)
194
+ throw new Error(`Source connection not found: ${config.sourceConnectionId}`);
195
+ if (!targetConn)
196
+ throw new Error(`Target connection not found: ${config.targetConnectionId}`);
197
+ const sourceProvider = this.connectionManager.getProvider(config.sourceConnectionId);
198
+ const targetProvider = this.connectionManager.getProvider(config.targetConnectionId);
199
+ logger.syncLog('info', `Fetching source projects from "${sourceConn.name}"...`, 'preview');
200
+ const allProjects = await sourceProvider.getProjects();
201
+ const projects = allProjects.filter(p => !this.isObsoletePath(p.fullPath));
202
+ logger.syncLog('info', `Found ${projects.length} source projects (${allProjects.length - projects.length} obsolete excluded)`, 'preview');
203
+ const mappings = projects.map((project) => {
204
+ const targetFullPath = this.computeTargetFullPath(project.fullPath, sourceConn.groupFilter, config.targetGroupOffset);
205
+ return { sourceFullPath: project.fullPath, targetFullPath };
206
+ });
207
+ // Compute repo deletions when enforce-delete is enabled
208
+ let deletions = [];
209
+ if (config.enforceDelete) {
210
+ logger.syncLog('info', 'Computing repo deletions (enforce-delete enabled)...', 'preview');
211
+ const expectedTargetPaths = new Set(mappings.map((m) => m.targetFullPath.toLowerCase()));
212
+ const scopePrefix = config.targetGroupOffset;
213
+ const targetProjects = await targetProvider.getProjects();
214
+ for (const tp of targetProjects) {
215
+ if (this.isObsoletePath(tp.fullPath))
216
+ continue;
217
+ if (scopePrefix && !tp.fullPath.toLowerCase().startsWith(scopePrefix.toLowerCase() + '/')) {
218
+ continue;
219
+ }
220
+ if (!expectedTargetPaths.has(tp.fullPath.toLowerCase())) {
221
+ deletions.push(tp.fullPath);
222
+ }
223
+ }
224
+ }
225
+ // Compute group deletions when enforce-group-delete is enabled
226
+ let groupDeletions = [];
227
+ if (config.enforceGroupDelete) {
228
+ logger.syncLog('info', 'Computing group deletions (enforce-group-delete enabled)...', 'preview');
229
+ const sourceGroups = await sourceProvider.getGroups();
230
+ const expectedTargetGroups = new Set();
231
+ for (const sg of sourceGroups) {
232
+ const targetPath = this.computeTargetFullPath(sg.fullPath, sourceConn.groupFilter, config.targetGroupOffset);
233
+ expectedTargetGroups.add(targetPath.toLowerCase());
234
+ }
235
+ // Also include the offset itself and the obsolete group
236
+ if (config.targetGroupOffset) {
237
+ expectedTargetGroups.add(config.targetGroupOffset.toLowerCase());
238
+ expectedTargetGroups.add(`${config.targetGroupOffset}/obsolete`.toLowerCase());
239
+ }
240
+ const targetGroups = await targetProvider.getGroups();
241
+ const scopePrefix = config.targetGroupOffset;
242
+ for (const tg of targetGroups) {
243
+ if (this.isObsoletePath(tg.fullPath))
244
+ continue;
245
+ if (scopePrefix && !tg.fullPath.toLowerCase().startsWith(scopePrefix.toLowerCase() + '/')) {
246
+ continue;
247
+ }
248
+ if (!expectedTargetGroups.has(tg.fullPath.toLowerCase())) {
249
+ groupDeletions.push(tg.fullPath);
250
+ }
251
+ }
252
+ }
253
+ logger.syncLog('success', `Preview complete: ${mappings.length} mappings, ${deletions.length} deletions, ${groupDeletions.length} group deletions`, 'preview');
254
+ return { mappings, deletions, groupDeletions };
255
+ }
256
+ // ============================================================================
257
+ // Sync Engine
258
+ // ============================================================================
259
+ async executeSync(configId, force = false) {
260
+ if (this.stopping) {
261
+ logger.warn(`SyncManager is stopping, skipping sync ${configId}`);
262
+ return;
263
+ }
264
+ if (this.runningSync.has(configId)) {
265
+ logger.warn(`Sync ${configId} already running, skipping`);
266
+ return;
267
+ }
268
+ const config = this.configs.find((c) => c.id === configId);
269
+ if (!config)
270
+ return;
271
+ if (config.status === 'paused' && !force)
272
+ return;
273
+ this.runningSync.add(configId);
274
+ this.syncedGroupMeta.clear();
275
+ this.currentSyncConfig = config;
276
+ const startTime = Date.now();
277
+ logger.syncLog('info', `Sync started for "${config.name}"`, 'sync');
278
+ try {
279
+ const sourceConn = this.connectionManager.getConnection(config.sourceConnectionId);
280
+ const targetConn = this.connectionManager.getConnection(config.targetConnectionId);
281
+ if (!sourceConn)
282
+ throw new Error(`Source connection not found: ${config.sourceConnectionId}`);
283
+ if (!targetConn)
284
+ throw new Error(`Target connection not found: ${config.targetConnectionId}`);
285
+ this.validateSyncConfig(config);
286
+ // Get all projects from source
287
+ const sourceProvider = this.connectionManager.getProvider(config.sourceConnectionId);
288
+ logger.syncLog('info', `Fetching source projects from "${sourceConn.name}"...`, 'api');
289
+ const allProjects = await sourceProvider.getProjects();
290
+ const projects = allProjects.filter(p => !this.isObsoletePath(p.fullPath));
291
+ logger.syncLog('info', `Found ${projects.length} source projects (${allProjects.length - projects.length} obsolete excluded)`, 'api');
292
+ let synced = 0;
293
+ const CONCURRENCY = 10;
294
+ for (let i = 0; i < projects.length; i += CONCURRENCY) {
295
+ const batch = projects.slice(i, i + CONCURRENCY);
296
+ await Promise.all(batch.map(async (project) => {
297
+ try {
298
+ logger.syncLog('info', `Syncing ${project.fullPath}...`, 'sync');
299
+ await this.syncRepo(config, project, sourceConn, targetConn);
300
+ synced++;
301
+ await this.updateRepoStatus(config.id, project.fullPath, {
302
+ status: 'synced',
303
+ lastSyncAt: Date.now(),
304
+ lastSyncError: undefined,
305
+ });
306
+ logger.syncLog('success', `Synced ${project.fullPath}`, 'sync');
307
+ }
308
+ catch (err) {
309
+ const errMsg = err instanceof Error ? err.message : String(err);
310
+ await this.updateRepoStatus(config.id, project.fullPath, {
311
+ status: 'error',
312
+ lastSyncError: errMsg,
313
+ lastSyncAt: Date.now(),
314
+ });
315
+ logger.syncLog('error', `Sync failed for ${project.fullPath}: ${errMsg}`, 'sync');
316
+ }
317
+ }));
318
+ }
319
+ // Enforce deletion: move stale target repos to obsolete
320
+ if (config.enforceDelete) {
321
+ logger.syncLog('info', 'Checking for stale target repos...', 'sync');
322
+ await this.enforceDeleteStaleRepos(config, projects, sourceConn, targetConn);
323
+ }
324
+ // Enforce group deletion: move stale target groups to obsolete
325
+ if (config.enforceGroupDelete) {
326
+ logger.syncLog('info', 'Checking for stale target groups...', 'sync');
327
+ await this.enforceDeleteStaleGroups(config, sourceConn, targetConn);
328
+ }
329
+ config.lastSyncAt = Date.now();
330
+ config.reposSynced = synced;
331
+ config.lastSyncDurationMs = Date.now() - startTime;
332
+ config.lastSyncError = undefined;
333
+ if (config.status === 'error')
334
+ config.status = 'active';
335
+ await this.persistConfig(config);
336
+ logger.syncLog('success', `Sync complete for "${config.name}": ${synced}/${projects.length} repos in ${config.lastSyncDurationMs}ms`, 'sync');
337
+ }
338
+ catch (err) {
339
+ const errMsg = err instanceof Error ? err.message : String(err);
340
+ config.lastSyncError = errMsg;
341
+ config.lastSyncAt = Date.now();
342
+ config.lastSyncDurationMs = Date.now() - startTime;
343
+ config.status = 'error';
344
+ await this.persistConfig(config);
345
+ logger.syncLog('error', `Sync config "${config.name}" failed: ${errMsg}`, 'sync');
346
+ }
347
+ finally {
348
+ this.runningSync.delete(configId);
349
+ }
350
+ }
351
+ // ============================================================================
352
+ // Single Repo Sync
353
+ // ============================================================================
354
+ async syncRepo(config, project, sourceConn, targetConn) {
355
+ const targetFullPath = this.computeTargetFullPath(project.fullPath, sourceConn.groupFilter, config.targetGroupOffset);
356
+ // Build authenticated git URLs
357
+ const sourceUrl = this.buildAuthUrl(sourceConn, project.fullPath);
358
+ const targetUrl = this.buildAuthUrl(targetConn, targetFullPath);
359
+ // Mirror directory for this repo
360
+ const mirrorDir = plugins.path.join(this.mirrorsPath, config.id, this.sanitizePath(project.fullPath));
361
+ // Ensure target group/project hierarchy exists
362
+ await this.ensureTargetExists(targetConn, targetFullPath, project, sourceConn, sourceConn.groupFilter, config.targetGroupOffset);
363
+ // API-based ref comparison (fast path — avoids git clone when refs already match)
364
+ const sourceProvider = this.connectionManager.getProvider(sourceConn.id);
365
+ const targetProvider = this.connectionManager.getProvider(targetConn.id);
366
+ const apiRefsMatch = await this.refsMatchViaApi(sourceProvider, targetProvider, project.fullPath, targetFullPath);
367
+ if (apiRefsMatch === true) {
368
+ logger.syncLog('info', `Refs match via API for ${project.fullPath}, skipping git`, 'api');
369
+ await this.syncProjectMetadata(config, sourceConn, targetConn, project.fullPath, targetFullPath);
370
+ return;
371
+ }
372
+ // Clone or fetch from source
373
+ try {
374
+ const exists = await this.dirExists(mirrorDir);
375
+ if (!exists) {
376
+ await plugins.fs.mkdir(mirrorDir, { recursive: true });
377
+ await this.runGit(['clone', '--bare', sourceUrl, '.'], mirrorDir);
378
+ }
379
+ // Ensure fetch refspec is configured (bare clones don't set one by default,
380
+ // which prevents tracking branch renames like master -> main)
381
+ await this.runGit(['config', 'remote.origin.fetch', '+refs/heads/*:refs/heads/*'], mirrorDir);
382
+ // Update source remote URL in case connection changed
383
+ try {
384
+ await this.runGit(['remote', 'set-url', 'origin', sourceUrl], mirrorDir);
385
+ }
386
+ catch {
387
+ // Ignore errors
388
+ }
389
+ // Fetch latest refs from source (--prune removes branches deleted on remote)
390
+ await this.runGit(['fetch', '--prune', 'origin'], mirrorDir);
391
+ }
392
+ catch (err) {
393
+ const msg = err instanceof Error ? err.message : String(err);
394
+ if (msg.includes("couldn't find remote ref HEAD")) {
395
+ logger.syncLog('warn', `Skipping empty repo ${project.fullPath} (no HEAD ref)`, 'git');
396
+ return;
397
+ }
398
+ throw err;
399
+ }
400
+ // Set up target remote and push
401
+ const remotes = await this.runGit(['remote'], mirrorDir);
402
+ if (!remotes.includes('target')) {
403
+ await this.runGit(['remote', 'add', 'target', targetUrl], mirrorDir);
404
+ }
405
+ else {
406
+ await this.runGit(['remote', 'set-url', 'target', targetUrl], mirrorDir);
407
+ }
408
+ // Check for unrelated history before mirror-pushing
409
+ const isUnrelated = await this.checkUnrelatedHistory(mirrorDir);
410
+ if (isUnrelated) {
411
+ logger.syncLog('warn', `Target "${targetFullPath}" has unrelated history — moving to obsolete`, 'git');
412
+ await this.moveToObsolete(targetConn, targetFullPath, config.targetGroupOffset);
413
+ // Re-create fresh target
414
+ await this.ensureTargetExists(targetConn, targetFullPath, project, sourceConn, sourceConn.groupFilter, config.targetGroupOffset);
415
+ this.actionLog.append({
416
+ actionType: 'obsolete',
417
+ entityType: 'sync',
418
+ entityId: config.id,
419
+ entityName: config.name,
420
+ details: `Moved unrelated repo "${targetFullPath}" to obsolete`,
421
+ username: 'system',
422
+ });
423
+ }
424
+ // Compare refs to determine if push is needed
425
+ const refsAlreadyMatch = !isUnrelated && await this.refsMatch(mirrorDir);
426
+ if (refsAlreadyMatch) {
427
+ logger.syncLog('info', `Refs already match for ${project.fullPath}, skipping push`, 'api');
428
+ }
429
+ else {
430
+ // Phase 1: push all refs without pruning (ensures target has all source branches)
431
+ await this.runGit([
432
+ 'push', 'target',
433
+ '+refs/heads/*:refs/heads/*',
434
+ '+refs/tags/*:refs/tags/*',
435
+ ], mirrorDir);
436
+ // Phase 2: sync default_branch now that all branches exist on target
437
+ await this.syncDefaultBranchBeforePush(sourceConn, targetConn, project.fullPath, targetFullPath);
438
+ // Phase 2b: unprotect stale branches on target so --prune can delete them
439
+ await this.unprotectStaleBranches(targetConn, targetFullPath, mirrorDir);
440
+ // Phase 3: push with --prune to remove stale branches (safe now that default_branch is correct)
441
+ await this.runGit([
442
+ 'push', 'target',
443
+ '+refs/heads/*:refs/heads/*',
444
+ '+refs/tags/*:refs/tags/*',
445
+ '--prune',
446
+ ], mirrorDir);
447
+ }
448
+ // Sync project metadata (description, visibility, topics, default_branch, avatar)
449
+ await this.syncProjectMetadata(config, sourceConn, targetConn, project.fullPath, targetFullPath);
450
+ }
451
+ // ============================================================================
452
+ // Target Hierarchy Creation
453
+ // ============================================================================
454
+ async ensureTargetExists(targetConn, targetFullPath, sourceProject, sourceConn, sourceGroupFilter, targetGroupOffset) {
455
+ const segments = targetFullPath.split('/');
456
+ const projectName = segments.pop();
457
+ const groupSegments = segments;
458
+ if (targetConn.providerType === 'gitlab') {
459
+ await this.ensureGitLabTarget(targetConn, groupSegments, projectName, sourceProject, sourceConn, sourceGroupFilter, targetGroupOffset);
460
+ }
461
+ else {
462
+ await this.ensureGiteaTarget(targetConn, groupSegments, projectName, sourceProject, sourceConn, sourceGroupFilter, targetGroupOffset);
463
+ }
464
+ }
465
+ async ensureGitLabTarget(conn, groupSegments, projectName, sourceProject, sourceConn, sourceGroupFilter, targetGroupOffset) {
466
+ const client = new plugins.gitlabClient.GitLabClient(conn.baseUrl, conn.token);
467
+ // Walk group hierarchy top-down, creating each if needed
468
+ let parentId = undefined;
469
+ let currentPath = '';
470
+ for (const segment of groupSegments) {
471
+ currentPath = currentPath ? `${currentPath}/${segment}` : segment;
472
+ try {
473
+ const group = await client.getGroup(currentPath);
474
+ parentId = group.id;
475
+ }
476
+ catch {
477
+ // Group doesn't exist — create it
478
+ try {
479
+ const newGroup = await client.createGroup(segment, segment, parentId);
480
+ parentId = newGroup.id;
481
+ logger.info(`Created GitLab group: ${currentPath}`);
482
+ }
483
+ catch (createErr) {
484
+ // 409 = already exists (race condition), try fetching again
485
+ if (String(createErr).includes('409') || String(createErr).includes('already')) {
486
+ const group = await client.getGroup(currentPath);
487
+ parentId = group.id;
488
+ }
489
+ else {
490
+ throw createErr;
491
+ }
492
+ }
493
+ }
494
+ // Sync group metadata from source (once per group per sync cycle)
495
+ if (sourceConn && !this.syncedGroupMeta.has(currentPath)) {
496
+ const sourceGroupPath = this.reverseTargetGroupPath(currentPath, sourceGroupFilter, targetGroupOffset);
497
+ if (sourceGroupPath) {
498
+ this.syncedGroupMeta.add(currentPath);
499
+ await this.syncGroupMetadata(sourceConn, conn, sourceGroupPath, currentPath);
500
+ }
501
+ }
502
+ }
503
+ // Create the project if it doesn't exist
504
+ const projectPath = groupSegments.length > 0
505
+ ? `${groupSegments.join('/')}/${projectName}`
506
+ : projectName;
507
+ try {
508
+ // Check if project exists by path
509
+ await client.getGroup(projectPath);
510
+ // If this succeeds, it's actually a group, not a project... unlikely but handle
511
+ }
512
+ catch {
513
+ // Project doesn't exist as a group path; try creating it
514
+ try {
515
+ await client.createProject(projectName, {
516
+ path: projectName,
517
+ namespaceId: parentId,
518
+ description: sourceProject.description,
519
+ visibility: sourceProject.visibility || 'private',
520
+ });
521
+ logger.info(`Created GitLab project: ${projectPath}`);
522
+ }
523
+ catch (createErr) {
524
+ // Already exists is fine
525
+ if (!String(createErr).includes('409') && !String(createErr).includes('already been taken')) {
526
+ throw createErr;
527
+ }
528
+ }
529
+ }
530
+ }
531
+ async ensureGiteaTarget(conn, groupSegments, projectName, sourceProject, sourceConn, sourceGroupFilter, targetGroupOffset) {
532
+ const client = new plugins.giteaClient.GiteaClient(conn.baseUrl, conn.token);
533
+ // Gitea has flat orgs (no nesting). Use the first segment as org name.
534
+ // If there are nested segments, join them into the repo name.
535
+ const orgName = groupSegments[0] || conn.groupFilter || 'default';
536
+ const repoName = groupSegments.length > 1
537
+ ? [...groupSegments.slice(1), projectName].join('-')
538
+ : projectName;
539
+ // Ensure org exists
540
+ try {
541
+ await client.getOrg(orgName);
542
+ }
543
+ catch {
544
+ try {
545
+ await client.createOrg(orgName, { visibility: 'public' });
546
+ logger.info(`Created Gitea org: ${orgName}`);
547
+ }
548
+ catch (createErr) {
549
+ if (!String(createErr).includes('409') && !String(createErr).includes('already')) {
550
+ throw createErr;
551
+ }
552
+ }
553
+ }
554
+ // Sync org metadata from source if we have the source connection
555
+ if (sourceConn && groupSegments[0] && !this.syncedGroupMeta.has(groupSegments[0])) {
556
+ const sourceGroupPath = this.reverseTargetGroupPath(groupSegments[0], sourceGroupFilter, targetGroupOffset);
557
+ if (sourceGroupPath) {
558
+ this.syncedGroupMeta.add(groupSegments[0]);
559
+ await this.syncGroupMetadata(sourceConn, conn, sourceGroupPath, groupSegments[0]);
560
+ }
561
+ }
562
+ // Ensure repo exists in org
563
+ try {
564
+ await client.createOrgRepo(orgName, repoName, {
565
+ description: sourceProject.description,
566
+ private: sourceProject.visibility !== 'public',
567
+ });
568
+ logger.info(`Created Gitea repo: ${orgName}/${repoName}`);
569
+ }
570
+ catch (createErr) {
571
+ // Already exists is fine
572
+ if (!String(createErr).includes('409') && !String(createErr).includes('already')) {
573
+ throw createErr;
574
+ }
575
+ }
576
+ }
577
+ // ============================================================================
578
+ // Enforce Delete — remove stale target repos
579
+ // ============================================================================
580
+ async enforceDeleteStaleRepos(config, sourceProjects, sourceConn, targetConn) {
581
+ // Build set of expected target fullPaths from source
582
+ const expectedTargetPaths = new Set();
583
+ for (const project of sourceProjects) {
584
+ const targetFullPath = this.computeTargetFullPath(project.fullPath, sourceConn.groupFilter, config.targetGroupOffset);
585
+ expectedTargetPaths.add(targetFullPath.toLowerCase());
586
+ }
587
+ // Scope prefix — only delete repos under this prefix
588
+ const scopePrefix = config.targetGroupOffset;
589
+ // List all target projects (filtered by connection's groupFilter)
590
+ const targetProvider = this.connectionManager.getProvider(config.targetConnectionId);
591
+ const targetProjects = await targetProvider.getProjects();
592
+ // Delete target projects not in the expected set, scoped to the prefix
593
+ for (const targetProject of targetProjects) {
594
+ if (this.isObsoletePath(targetProject.fullPath))
595
+ continue;
596
+ // Skip repos outside our managed prefix
597
+ if (scopePrefix && !targetProject.fullPath.toLowerCase().startsWith(scopePrefix.toLowerCase() + '/')) {
598
+ continue;
599
+ }
600
+ if (!expectedTargetPaths.has(targetProject.fullPath.toLowerCase())) {
601
+ try {
602
+ await this.moveToObsolete(targetConn, targetProject.fullPath, config.targetGroupOffset);
603
+ logger.syncLog('warn', `Moved stale target repo "${targetProject.fullPath}" to obsolete`, 'sync');
604
+ this.actionLog.append({
605
+ actionType: 'obsolete',
606
+ entityType: 'sync',
607
+ entityId: config.id,
608
+ entityName: config.name,
609
+ details: `Moved stale target repo "${targetProject.fullPath}" to obsolete`,
610
+ username: 'system',
611
+ });
612
+ }
613
+ catch (err) {
614
+ const errMsg = err instanceof Error ? err.message : String(err);
615
+ logger.syncLog('error', `Enforce-delete failed for "${targetProject.fullPath}": ${errMsg}`, 'sync');
616
+ }
617
+ }
618
+ }
619
+ }
620
+ // ============================================================================
621
+ // Enforce Delete — remove stale target groups/orgs
622
+ // ============================================================================
623
+ async enforceDeleteStaleGroups(config, sourceConn, targetConn) {
624
+ const sourceProvider = this.connectionManager.getProvider(config.sourceConnectionId);
625
+ const targetProvider = this.connectionManager.getProvider(config.targetConnectionId);
626
+ // Build expected target group paths from source groups
627
+ const sourceGroups = await sourceProvider.getGroups();
628
+ const expectedTargetGroups = new Set();
629
+ for (const sg of sourceGroups) {
630
+ const targetPath = this.computeTargetFullPath(sg.fullPath, sourceConn.groupFilter, config.targetGroupOffset);
631
+ expectedTargetGroups.add(targetPath.toLowerCase());
632
+ }
633
+ // Always keep the offset itself and the obsolete group
634
+ if (config.targetGroupOffset) {
635
+ expectedTargetGroups.add(config.targetGroupOffset.toLowerCase());
636
+ expectedTargetGroups.add(`${config.targetGroupOffset}/obsolete`.toLowerCase());
637
+ }
638
+ // Find stale groups
639
+ const targetGroups = await targetProvider.getGroups();
640
+ const scopePrefix = config.targetGroupOffset;
641
+ const staleGroups = [];
642
+ for (const tg of targetGroups) {
643
+ if (this.isObsoletePath(tg.fullPath))
644
+ continue;
645
+ if (scopePrefix && !tg.fullPath.toLowerCase().startsWith(scopePrefix.toLowerCase() + '/')) {
646
+ continue;
647
+ }
648
+ if (!expectedTargetGroups.has(tg.fullPath.toLowerCase())) {
649
+ staleGroups.push(tg);
650
+ }
651
+ }
652
+ if (staleGroups.length === 0)
653
+ return;
654
+ // Sort by path depth (shallowest first) to move top-level groups first
655
+ staleGroups.sort((a, b) => {
656
+ const depthA = a.fullPath.split('/').length;
657
+ const depthB = b.fullPath.split('/').length;
658
+ return depthA - depthB;
659
+ });
660
+ // Track moved prefixes to skip children already moved with their parent
661
+ const movedPrefixes = [];
662
+ for (const group of staleGroups) {
663
+ // Skip if a parent was already moved (GitLab moves children along)
664
+ const isChildOfMoved = movedPrefixes.some((prefix) => group.fullPath.toLowerCase().startsWith(prefix.toLowerCase() + '/'));
665
+ if (isChildOfMoved && targetConn.providerType === 'gitlab')
666
+ continue;
667
+ try {
668
+ if (targetConn.providerType === 'gitlab') {
669
+ await this.moveGroupToObsolete(targetConn, group.fullPath, config.targetGroupOffset);
670
+ }
671
+ else {
672
+ await this.moveGiteaOrgToObsolete(targetConn, group.fullPath, config.targetGroupOffset);
673
+ }
674
+ movedPrefixes.push(group.fullPath);
675
+ logger.syncLog('warn', `Moved stale group "${group.fullPath}" to obsolete`, 'sync');
676
+ this.actionLog.append({
677
+ actionType: 'obsolete',
678
+ entityType: 'sync',
679
+ entityId: config.id,
680
+ entityName: config.name,
681
+ details: `Moved stale target group "${group.fullPath}" to obsolete`,
682
+ username: 'system',
683
+ });
684
+ }
685
+ catch (err) {
686
+ const errMsg = err instanceof Error ? err.message : String(err);
687
+ logger.syncLog('error', `Enforce-group-delete failed for "${group.fullPath}": ${errMsg}`, 'sync');
688
+ }
689
+ }
690
+ }
691
+ /**
692
+ * Move a GitLab group to the obsolete group with a unique suffix.
693
+ * Transfers the entire group (including subgroups and projects).
694
+ */
695
+ async moveGroupToObsolete(targetConn, groupFullPath, basePath) {
696
+ const suffix = this.generateSuffix();
697
+ const obsoleteTarget = await this.ensureObsoleteGroup(targetConn, basePath);
698
+ if (obsoleteTarget.type !== 'gitlab')
699
+ return;
700
+ // Get group by path
701
+ const group = await this.rawApiCall(targetConn, 'GET', `/api/v4/groups/${encodeURIComponent(groupFullPath)}`);
702
+ // Transfer group to be a child of the obsolete group
703
+ await this.rawApiCall(targetConn, 'POST', `/api/v4/groups/${group.id}/transfer`, { group_id: obsoleteTarget.groupId });
704
+ // Rename with suffix + set private
705
+ const originalPath = groupFullPath.split('/').pop();
706
+ await this.rawApiCall(targetConn, 'PUT', `/api/v4/groups/${group.id}`, { name: `${originalPath}-${suffix}`, path: `${originalPath}-${suffix}`, visibility: 'private' });
707
+ logger.info(`Moved GitLab group "${groupFullPath}" to obsolete as "${originalPath}-${suffix}"`);
708
+ }
709
+ /**
710
+ * Move all repos in a Gitea org to obsolete, then delete the empty org.
711
+ * Gitea orgs can't be transferred, so we move repos individually.
712
+ */
713
+ async moveGiteaOrgToObsolete(targetConn, orgName, basePath) {
714
+ // List all repos in the stale org (auto-paginate)
715
+ const allRepos = [];
716
+ let page = 1;
717
+ const perPage = 50;
718
+ while (true) {
719
+ const repos = await this.rawApiCall(targetConn, 'GET', `/api/v1/orgs/${encodeURIComponent(orgName)}/repos?page=${page}&limit=${perPage}`);
720
+ const repoList = Array.isArray(repos) ? repos : [];
721
+ allRepos.push(...repoList);
722
+ if (repoList.length < perPage)
723
+ break;
724
+ page++;
725
+ }
726
+ // Move each repo to obsolete
727
+ for (const repo of allRepos) {
728
+ try {
729
+ await this.moveToObsolete(targetConn, `${orgName}/${repo.name}`, basePath);
730
+ }
731
+ catch (err) {
732
+ const errMsg = err instanceof Error ? err.message : String(err);
733
+ logger.error(`Failed to move repo "${orgName}/${repo.name}" to obsolete: ${errMsg}`);
734
+ }
735
+ }
736
+ // Delete the now-empty org
737
+ try {
738
+ await this.rawApiCall(targetConn, 'DELETE', `/api/v1/orgs/${encodeURIComponent(orgName)}`);
739
+ logger.info(`Deleted empty Gitea org: ${orgName}`);
740
+ }
741
+ catch (err) {
742
+ const errMsg = err instanceof Error ? err.message : String(err);
743
+ logger.error(`Failed to delete empty Gitea org "${orgName}": ${errMsg}`);
744
+ }
745
+ }
746
+ // ============================================================================
747
+ // Helpers
748
+ // ============================================================================
749
+ /**
750
+ * Returns true if the given full path belongs to an obsolete namespace.
751
+ * Matches path segments named "obsolete" or ending with "-obsolete".
752
+ */
753
+ isObsoletePath(fullPath) {
754
+ const segments = fullPath.toLowerCase().split('/');
755
+ return segments.some(s => s === 'obsolete' || s.endsWith('-obsolete'));
756
+ }
757
+ buildAuthUrl(conn, repoPath) {
758
+ const url = new URL(conn.baseUrl);
759
+ if (conn.providerType === 'gitlab') {
760
+ return `${url.protocol}//oauth2:${conn.token}@${url.host}/${repoPath}.git`;
761
+ }
762
+ else {
763
+ return `${url.protocol}//gitea-token:${conn.token}@${url.host}/${repoPath}.git`;
764
+ }
765
+ }
766
+ computeRelativePath(fullPath, groupFilter) {
767
+ if (!groupFilter)
768
+ return fullPath;
769
+ if (fullPath.startsWith(groupFilter + '/')) {
770
+ return fullPath.substring(groupFilter.length + 1);
771
+ }
772
+ return fullPath;
773
+ }
774
+ /**
775
+ * Reverse-map a target group path to the corresponding source group path.
776
+ * Returns null if the target path is part of the offset itself (not a source-derived group).
777
+ */
778
+ reverseTargetGroupPath(targetGroupPath, sourceGroupFilter, targetGroupOffset) {
779
+ let relativePath = targetGroupPath;
780
+ // Strip the target offset prefix
781
+ if (targetGroupOffset) {
782
+ if (targetGroupPath === targetGroupOffset) {
783
+ // This IS the offset group itself, not a source-derived group
784
+ return null;
785
+ }
786
+ if (targetGroupPath.startsWith(targetGroupOffset + '/')) {
787
+ relativePath = targetGroupPath.substring(targetGroupOffset.length + 1);
788
+ }
789
+ else {
790
+ // Target path is not under the offset — can't reverse-map
791
+ return null;
792
+ }
793
+ }
794
+ // Re-add the source group filter prefix
795
+ if (sourceGroupFilter) {
796
+ return `${sourceGroupFilter}/${relativePath}`;
797
+ }
798
+ return relativePath;
799
+ }
800
+ computeTargetFullPath(sourceFullPath, sourceGroupFilter, targetGroupOffset) {
801
+ const relativePath = this.computeRelativePath(sourceFullPath, sourceGroupFilter);
802
+ return targetGroupOffset ? `${targetGroupOffset}/${relativePath}` : relativePath;
803
+ }
804
+ /**
805
+ * Validates that the sync config's targetGroupOffset is reachable
806
+ * by the target connection's groupFilter. Throws when enforceDelete
807
+ * is on and the offset is outside the filter scope.
808
+ */
809
+ validateSyncConfig(config) {
810
+ validateIntervalMinutes(config.intervalMinutes, 'sync intervalMinutes');
811
+ if (!config.targetGroupOffset)
812
+ return;
813
+ const targetConn = this.connectionManager.getConnection(config.targetConnectionId);
814
+ if (!targetConn?.groupFilter)
815
+ return;
816
+ const offset = config.targetGroupOffset.toLowerCase();
817
+ const filter = targetConn.groupFilter.toLowerCase();
818
+ const inScope = offset === filter || offset.startsWith(filter + '/');
819
+ if (!inScope && config.enforceDelete) {
820
+ throw new Error(`Target group offset "${config.targetGroupOffset}" is outside target connection's ` +
821
+ `group filter "${targetConn.groupFilter}". With enforce-delete enabled, the sync ` +
822
+ `engine cannot list repos under the offset. Either change the offset to be within ` +
823
+ `"${targetConn.groupFilter}/...", remove the target's group filter, or disable enforce-delete.`);
824
+ }
825
+ if (!inScope) {
826
+ logger.warn(`Target group offset "${config.targetGroupOffset}" is outside target connection's ` +
827
+ `group filter "${targetConn.groupFilter}". Git pushes will work but repos won't ` +
828
+ `appear in the target connection's listings.`);
829
+ }
830
+ }
831
+ // ============================================================================
832
+ // Metadata Sync
833
+ // ============================================================================
834
+ /**
835
+ * Download binary data (e.g. avatar image) with provider auth headers.
836
+ * Returns null on 404 or error.
837
+ */
838
+ async rawBinaryFetch(conn, url) {
839
+ try {
840
+ const headers = {};
841
+ if (conn.providerType === 'gitlab') {
842
+ headers['PRIVATE-TOKEN'] = conn.token;
843
+ }
844
+ else {
845
+ headers['Authorization'] = `token ${conn.token}`;
846
+ }
847
+ const resp = await fetch(url, { headers });
848
+ if (!resp.ok) {
849
+ await resp.body?.cancel();
850
+ return null;
851
+ }
852
+ return new Uint8Array(await resp.arrayBuffer());
853
+ }
854
+ catch {
855
+ return null;
856
+ }
857
+ }
858
+ /**
859
+ * Raw multipart call for avatar uploads (GitLab requires multipart FormData).
860
+ */
861
+ async rawMultipartCall(conn, method, apiPath, formData) {
862
+ const baseUrl = conn.baseUrl.replace(/\/+$/, '');
863
+ const url = `${baseUrl}${apiPath}`;
864
+ const headers = {};
865
+ if (conn.providerType === 'gitlab') {
866
+ headers['PRIVATE-TOKEN'] = conn.token;
867
+ }
868
+ else {
869
+ headers['Authorization'] = `token ${conn.token}`;
870
+ }
871
+ // Do NOT set Content-Type — let fetch set the multipart boundary
872
+ const resp = await fetch(url, { method, headers, body: formData });
873
+ if (!resp.ok) {
874
+ const text = await resp.text();
875
+ throw new Error(`${method} ${apiPath}: ${resp.status} - ${text}`);
876
+ }
877
+ try {
878
+ return await resp.json();
879
+ }
880
+ catch {
881
+ return undefined;
882
+ }
883
+ }
884
+ /**
885
+ * Normalize visibility values between GitLab and Gitea.
886
+ * GitLab: "public" | "internal" | "private"
887
+ * Gitea: "public" | "limited" | "private"
888
+ */
889
+ normalizeVisibility(visibility) {
890
+ const v = visibility?.toLowerCase() || 'private';
891
+ if (v === 'limited')
892
+ return 'internal';
893
+ return v;
894
+ }
895
+ /**
896
+ * Guess MIME type from binary content or URL for avatar uploads.
897
+ */
898
+ guessAvatarMimeType(data, url) {
899
+ // Check magic bytes
900
+ if (data[0] === 0x89 && data[1] === 0x50)
901
+ return 'image/png';
902
+ if (data[0] === 0xFF && data[1] === 0xD8)
903
+ return 'image/jpeg';
904
+ if (data[0] === 0x47 && data[1] === 0x49)
905
+ return 'image/gif';
906
+ // SVG: text-based XML, no magic bytes — check content
907
+ const textStart = new TextDecoder().decode(data.slice(0, 200));
908
+ if (textStart.includes('<svg') || textStart.includes('<?xml'))
909
+ return 'image/svg+xml';
910
+ // Fallback: check URL extension
911
+ if (url.includes('.png'))
912
+ return 'image/png';
913
+ if (url.includes('.jpg') || url.includes('.jpeg'))
914
+ return 'image/jpeg';
915
+ if (url.includes('.gif'))
916
+ return 'image/gif';
917
+ if (url.includes('.svg'))
918
+ return 'image/svg+xml';
919
+ return 'image/png'; // default
920
+ }
921
+ /**
922
+ * Pre-push: ensure target's default_branch matches source so --prune won't delete it.
923
+ */
924
+ async syncDefaultBranchBeforePush(sourceConn, targetConn, sourceFullPath, targetFullPath) {
925
+ try {
926
+ const sourceProject = await this.fetchProjectRaw(sourceConn, sourceFullPath);
927
+ if (!sourceProject)
928
+ return;
929
+ const targetProject = await this.fetchProjectRaw(targetConn, targetFullPath);
930
+ if (!targetProject)
931
+ return;
932
+ const sourceBranch = sourceProject.default_branch || 'main';
933
+ const targetBranch = targetProject.default_branch || 'main';
934
+ if (sourceBranch !== targetBranch) {
935
+ logger.syncLog('info', `Updating default branch for ${targetFullPath}: ${targetBranch} -> ${sourceBranch}`, 'api');
936
+ if (targetConn.providerType === 'gitlab') {
937
+ await this.rawApiCall(targetConn, 'PUT', `/api/v4/projects/${targetProject.id}`, {
938
+ default_branch: sourceBranch,
939
+ });
940
+ }
941
+ else {
942
+ const segments = targetFullPath.split('/');
943
+ const repo = segments.pop();
944
+ const owner = segments[0] || '';
945
+ await this.rawApiCall(targetConn, 'PATCH', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, {
946
+ default_branch: sourceBranch,
947
+ });
948
+ }
949
+ }
950
+ }
951
+ catch (err) {
952
+ const errMsg = err instanceof Error ? err.message : String(err);
953
+ logger.syncLog('warn', `Pre-push default_branch sync failed for ${targetFullPath}: ${errMsg}`, 'api');
954
+ }
955
+ }
956
+ /**
957
+ * Unprotect branches on the target that no longer exist in the source,
958
+ * so that git push --prune can delete them.
959
+ */
960
+ async unprotectStaleBranches(targetConn, targetFullPath, mirrorDir) {
961
+ if (targetConn.providerType !== 'gitlab')
962
+ return;
963
+ try {
964
+ const targetProject = await this.fetchProjectRaw(targetConn, targetFullPath);
965
+ if (!targetProject)
966
+ return;
967
+ const client = new plugins.gitlabClient.GitLabClient(targetConn.baseUrl, targetConn.token);
968
+ const protectedBranches = await client.requestGetProtectedBranches(targetProject.id);
969
+ if (protectedBranches.length === 0)
970
+ return;
971
+ // Get list of branches in the local mirror (= source branches)
972
+ const localBranchOutput = await this.runGit(['branch', '--list'], mirrorDir);
973
+ const localBranches = new Set(localBranchOutput.split('\n').map(b => b.trim().replace(/^\* /, '')).filter(Boolean));
974
+ for (const pb of protectedBranches) {
975
+ if (!localBranches.has(pb.name)) {
976
+ logger.syncLog('info', `Unprotecting stale branch "${pb.name}" on ${targetFullPath}`, 'api');
977
+ await client.requestUnprotectBranch(targetProject.id, pb.name);
978
+ }
979
+ }
980
+ }
981
+ catch (err) {
982
+ const errMsg = err instanceof Error ? err.message : String(err);
983
+ logger.syncLog('warn', `Failed to unprotect stale branches for ${targetFullPath}: ${errMsg}`, 'api');
984
+ }
985
+ }
986
+ /**
987
+ * Sync project metadata (description, visibility, topics, default_branch, avatar)
988
+ * from source to target after the git push.
989
+ */
990
+ async syncProjectMetadata(config, sourceConn, targetConn, sourceFullPath, targetFullPath) {
991
+ try {
992
+ // Fetch source project raw JSON
993
+ const sourceProject = await this.fetchProjectRaw(sourceConn, sourceFullPath);
994
+ if (!sourceProject)
995
+ return;
996
+ // Fetch target project raw JSON
997
+ const targetProject = await this.fetchProjectRaw(targetConn, targetFullPath);
998
+ if (!targetProject)
999
+ return;
1000
+ // Extract normalized metadata from both
1001
+ const sourceMeta = this.extractProjectMeta(sourceConn, sourceProject);
1002
+ const targetMeta = this.extractProjectMeta(targetConn, targetProject);
1003
+ // Append mirror hint to description if enabled
1004
+ if (config.addMirrorHint) {
1005
+ const mirrorUrl = `${sourceConn.baseUrl.replace(/\/+$/, '')}/${sourceFullPath}`;
1006
+ sourceMeta.description = `${sourceMeta.description}\n\n(This is a mirror of ${mirrorUrl})`.trim();
1007
+ }
1008
+ // Diff and update text metadata
1009
+ const changes = [];
1010
+ if (sourceMeta.description !== targetMeta.description)
1011
+ changes.push('description');
1012
+ if (this.normalizeVisibility(sourceMeta.visibility) !== this.normalizeVisibility(targetMeta.visibility))
1013
+ changes.push('visibility');
1014
+ if (JSON.stringify([...sourceMeta.topics].sort()) !== JSON.stringify([...targetMeta.topics].sort()))
1015
+ changes.push('topics');
1016
+ if (sourceMeta.defaultBranch !== targetMeta.defaultBranch)
1017
+ changes.push('default_branch');
1018
+ if (changes.length > 0) {
1019
+ logger.syncLog('info', `Syncing metadata for ${targetFullPath}: ${changes.join(', ')}`, 'api');
1020
+ await this.updateProjectMeta(targetConn, targetFullPath, targetProject, sourceMeta);
1021
+ logger.syncLog('success', `Updated metadata for ${targetFullPath}: ${changes.join(', ')}`, 'api');
1022
+ }
1023
+ // Sync avatar
1024
+ if (sourceMeta.avatarUrl) {
1025
+ await this.syncProjectAvatar(sourceConn, targetConn, sourceFullPath, targetFullPath, sourceMeta.avatarUrl, targetProject);
1026
+ }
1027
+ else if (config.useGroupAvatarsForProjects) {
1028
+ // Project has no avatar — inherit from parent group
1029
+ const groupPath = sourceFullPath.substring(0, sourceFullPath.lastIndexOf('/'));
1030
+ let groupAvatarApplied = false;
1031
+ if (groupPath) {
1032
+ try {
1033
+ const sourceGroup = await this.fetchGroupRaw(sourceConn, groupPath);
1034
+ if (sourceGroup) {
1035
+ const groupMeta = this.extractGroupMeta(sourceConn, sourceGroup);
1036
+ if (groupMeta.avatarUrl) {
1037
+ await this.syncProjectAvatar(sourceConn, targetConn, sourceFullPath, targetFullPath, groupMeta.avatarUrl, targetProject);
1038
+ groupAvatarApplied = true;
1039
+ }
1040
+ }
1041
+ }
1042
+ catch (err) {
1043
+ const errMsg = err instanceof Error ? err.message : String(err);
1044
+ logger.syncLog('warn', `Group avatar sync failed for ${targetFullPath}: ${errMsg}`, 'api');
1045
+ }
1046
+ }
1047
+ // If group also has no avatar, remove target avatar
1048
+ if (!groupAvatarApplied && targetMeta.avatarUrl) {
1049
+ await this.removeProjectAvatar(targetConn, targetFullPath, targetProject);
1050
+ }
1051
+ }
1052
+ else {
1053
+ // No source avatar, no group fallback — remove target avatar if present
1054
+ if (targetMeta.avatarUrl) {
1055
+ await this.removeProjectAvatar(targetConn, targetFullPath, targetProject);
1056
+ }
1057
+ }
1058
+ }
1059
+ catch (err) {
1060
+ const errMsg = err instanceof Error ? err.message : String(err);
1061
+ logger.syncLog('error', `Metadata sync failed for ${targetFullPath}: ${errMsg}`, 'api');
1062
+ }
1063
+ }
1064
+ /**
1065
+ * Sync group/org metadata (description, visibility, avatar) from source to target.
1066
+ */
1067
+ async syncGroupMetadata(sourceConn, targetConn, sourceGroupPath, targetGroupPath) {
1068
+ try {
1069
+ const sourceGroup = await this.fetchGroupRaw(sourceConn, sourceGroupPath);
1070
+ if (!sourceGroup)
1071
+ return;
1072
+ const targetGroup = await this.fetchGroupRaw(targetConn, targetGroupPath);
1073
+ if (!targetGroup)
1074
+ return;
1075
+ const sourceMeta = this.extractGroupMeta(sourceConn, sourceGroup);
1076
+ const targetMeta = this.extractGroupMeta(targetConn, targetGroup);
1077
+ // Append mirror hint to description if enabled
1078
+ if (this.currentSyncConfig?.addMirrorHint) {
1079
+ const mirrorUrl = `${sourceConn.baseUrl.replace(/\/+$/, '')}/${sourceGroupPath}`;
1080
+ sourceMeta.description = `${sourceMeta.description}\n\n(This is a mirror of ${mirrorUrl})`.trim();
1081
+ }
1082
+ const changes = [];
1083
+ if (sourceMeta.description !== targetMeta.description)
1084
+ changes.push('description');
1085
+ if (this.normalizeVisibility(sourceMeta.visibility) !== this.normalizeVisibility(targetMeta.visibility))
1086
+ changes.push('visibility');
1087
+ if (changes.length > 0) {
1088
+ logger.syncLog('info', `Syncing group metadata for ${targetGroupPath}: ${changes.join(', ')}`, 'api');
1089
+ await this.updateGroupMeta(targetConn, targetGroupPath, targetGroup, sourceMeta);
1090
+ logger.syncLog('success', `Updated group metadata for ${targetGroupPath}: ${changes.join(', ')}`, 'api');
1091
+ }
1092
+ // Sync avatar
1093
+ if (sourceMeta.avatarUrl) {
1094
+ await this.syncGroupAvatar(sourceConn, targetConn, sourceGroupPath, targetGroupPath, sourceMeta.avatarUrl, targetGroup);
1095
+ }
1096
+ else if (targetMeta.avatarUrl) {
1097
+ await this.removeGroupAvatar(targetConn, targetGroupPath, targetGroup);
1098
+ }
1099
+ }
1100
+ catch (err) {
1101
+ const errMsg = err instanceof Error ? err.message : String(err);
1102
+ logger.syncLog('error', `Group metadata sync failed for ${targetGroupPath}: ${errMsg}`, 'api');
1103
+ }
1104
+ }
1105
+ // ---- Raw metadata fetchers ----
1106
+ async fetchProjectRaw(conn, fullPath) {
1107
+ if (conn.providerType === 'gitlab') {
1108
+ return await this.rawApiCall(conn, 'GET', `/api/v4/projects/${encodeURIComponent(fullPath)}`);
1109
+ }
1110
+ else {
1111
+ const segments = fullPath.split('/');
1112
+ const repo = segments.pop();
1113
+ const owner = segments[0] || '';
1114
+ return await this.rawApiCall(conn, 'GET', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`);
1115
+ }
1116
+ }
1117
+ async fetchGroupRaw(conn, groupPath) {
1118
+ if (conn.providerType === 'gitlab') {
1119
+ return await this.rawApiCall(conn, 'GET', `/api/v4/groups/${encodeURIComponent(groupPath)}`);
1120
+ }
1121
+ else {
1122
+ // Gitea orgs are flat — the group path IS the org name
1123
+ const orgName = groupPath.split('/')[0] || groupPath;
1124
+ return await this.rawApiCall(conn, 'GET', `/api/v1/orgs/${encodeURIComponent(orgName)}`);
1125
+ }
1126
+ }
1127
+ // ---- Metadata extractors ----
1128
+ extractProjectMeta(conn, raw) {
1129
+ if (conn.providerType === 'gitlab') {
1130
+ return {
1131
+ description: raw.description || '',
1132
+ visibility: raw.visibility || 'private',
1133
+ topics: raw.topics || [],
1134
+ defaultBranch: raw.default_branch || 'main',
1135
+ avatarUrl: raw.avatar_url || '',
1136
+ };
1137
+ }
1138
+ else {
1139
+ return {
1140
+ description: raw.description || '',
1141
+ visibility: raw.private ? 'private' : 'public',
1142
+ topics: raw.topics || [],
1143
+ defaultBranch: raw.default_branch || 'main',
1144
+ avatarUrl: raw.avatar_url || '',
1145
+ };
1146
+ }
1147
+ }
1148
+ extractGroupMeta(conn, raw) {
1149
+ if (conn.providerType === 'gitlab') {
1150
+ return {
1151
+ description: raw.description || '',
1152
+ visibility: raw.visibility || 'private',
1153
+ avatarUrl: raw.avatar_url || '',
1154
+ };
1155
+ }
1156
+ else {
1157
+ return {
1158
+ description: raw.description || '',
1159
+ visibility: raw.visibility || 'public',
1160
+ avatarUrl: raw.avatar_url || '',
1161
+ };
1162
+ }
1163
+ }
1164
+ // ---- Metadata updaters ----
1165
+ async updateProjectMeta(conn, fullPath, rawProject, meta) {
1166
+ if (conn.providerType === 'gitlab') {
1167
+ // Update description, visibility, topics (always safe)
1168
+ await this.rawApiCall(conn, 'PUT', `/api/v4/projects/${rawProject.id}`, {
1169
+ description: meta.description,
1170
+ visibility: this.normalizeVisibility(meta.visibility),
1171
+ topics: meta.topics,
1172
+ });
1173
+ // Update default_branch separately — may fail if the branch doesn't exist in git
1174
+ try {
1175
+ await this.rawApiCall(conn, 'PUT', `/api/v4/projects/${rawProject.id}`, {
1176
+ default_branch: meta.defaultBranch,
1177
+ });
1178
+ }
1179
+ catch (err) {
1180
+ const errMsg = err instanceof Error ? err.message : String(err);
1181
+ logger.syncLog('warn', `Could not set default_branch to "${meta.defaultBranch}" for ${fullPath}: ${errMsg}`, 'api');
1182
+ }
1183
+ }
1184
+ else {
1185
+ const segments = fullPath.split('/');
1186
+ const repo = segments.pop();
1187
+ const owner = segments[0] || '';
1188
+ const encodedOwner = encodeURIComponent(owner);
1189
+ const encodedRepo = encodeURIComponent(repo);
1190
+ // Update description, visibility
1191
+ await this.rawApiCall(conn, 'PATCH', `/api/v1/repos/${encodedOwner}/${encodedRepo}`, {
1192
+ description: meta.description,
1193
+ private: this.normalizeVisibility(meta.visibility) === 'private',
1194
+ });
1195
+ // Update default_branch separately — may fail if the branch doesn't exist in git
1196
+ try {
1197
+ await this.rawApiCall(conn, 'PATCH', `/api/v1/repos/${encodedOwner}/${encodedRepo}`, {
1198
+ default_branch: meta.defaultBranch,
1199
+ });
1200
+ }
1201
+ catch (err) {
1202
+ const errMsg = err instanceof Error ? err.message : String(err);
1203
+ logger.syncLog('warn', `Could not set default_branch to "${meta.defaultBranch}" for ${fullPath}: ${errMsg}`, 'api');
1204
+ }
1205
+ // Topics are a separate endpoint in Gitea
1206
+ await this.rawApiCall(conn, 'PUT', `/api/v1/repos/${encodedOwner}/${encodedRepo}/topics`, {
1207
+ topics: meta.topics,
1208
+ });
1209
+ }
1210
+ }
1211
+ async updateGroupMeta(conn, groupPath, rawGroup, meta) {
1212
+ if (conn.providerType === 'gitlab') {
1213
+ try {
1214
+ await this.rawApiCall(conn, 'PUT', `/api/v4/groups/${rawGroup.id}`, {
1215
+ description: meta.description,
1216
+ visibility: this.normalizeVisibility(meta.visibility),
1217
+ });
1218
+ }
1219
+ catch (err) {
1220
+ const errMsg = err instanceof Error ? err.message : String(err);
1221
+ if (errMsg.includes('visibility_level') || errMsg.includes('visibility')) {
1222
+ logger.syncLog('warn', `Cannot sync visibility for group ${groupPath} (contains projects with higher visibility), syncing description only`, 'api');
1223
+ await this.rawApiCall(conn, 'PUT', `/api/v4/groups/${rawGroup.id}`, {
1224
+ description: meta.description,
1225
+ });
1226
+ }
1227
+ else {
1228
+ throw err;
1229
+ }
1230
+ }
1231
+ }
1232
+ else {
1233
+ const orgName = groupPath.split('/')[0] || groupPath;
1234
+ await this.rawApiCall(conn, 'PATCH', `/api/v1/orgs/${encodeURIComponent(orgName)}`, {
1235
+ description: meta.description,
1236
+ visibility: this.normalizeVisibility(meta.visibility) === 'private' ? 'private' : 'public',
1237
+ });
1238
+ }
1239
+ }
1240
+ // ---- Avatar sync ----
1241
+ async syncProjectAvatar(sourceConn, targetConn, _sourceFullPath, targetFullPath, sourceAvatarUrl, targetRawProject) {
1242
+ // Resolve relative avatar URLs
1243
+ const resolvedSourceUrl = sourceAvatarUrl.startsWith('http')
1244
+ ? sourceAvatarUrl
1245
+ : `${sourceConn.baseUrl.replace(/\/+$/, '')}${sourceAvatarUrl}`;
1246
+ const sourceAvatarData = await this.rawBinaryFetch(sourceConn, resolvedSourceUrl);
1247
+ if (!sourceAvatarData || sourceAvatarData.length === 0)
1248
+ return;
1249
+ // Skip SVG avatars — not supported by GitLab project endpoints
1250
+ const mimeType = this.guessAvatarMimeType(sourceAvatarData, resolvedSourceUrl);
1251
+ if (mimeType === 'image/svg+xml') {
1252
+ logger.syncLog('warn', `Skipping SVG avatar for ${targetFullPath} (not supported by target)`, 'api');
1253
+ return;
1254
+ }
1255
+ // Check in-memory cache: skip if source hasn't changed since last upload
1256
+ const sourceHash = await this.hashBytes(sourceAvatarData);
1257
+ const cacheKey = `project:${targetFullPath}`;
1258
+ if (this.avatarUploadCache.get(cacheKey) === sourceHash) {
1259
+ return; // Source avatar unchanged since last upload
1260
+ }
1261
+ // Compare with target's current avatar to avoid unnecessary uploads
1262
+ const targetMeta = this.extractProjectMeta(targetConn, targetRawProject);
1263
+ if (targetMeta.avatarUrl) {
1264
+ try {
1265
+ const resolvedTargetUrl = targetMeta.avatarUrl.startsWith('http')
1266
+ ? targetMeta.avatarUrl
1267
+ : `${targetConn.baseUrl.replace(/\/+$/, '')}${targetMeta.avatarUrl}`;
1268
+ const targetAvatarData = await this.rawBinaryFetch(targetConn, resolvedTargetUrl);
1269
+ if (targetAvatarData && this.binaryEqual(sourceAvatarData, targetAvatarData)) {
1270
+ this.avatarUploadCache.set(cacheKey, sourceHash);
1271
+ return; // Avatars are identical — skip upload
1272
+ }
1273
+ }
1274
+ catch {
1275
+ // Failed to fetch target avatar — proceed with upload as safe fallback
1276
+ }
1277
+ }
1278
+ logger.syncLog('info', `Syncing avatar for ${targetFullPath}...`, 'api');
1279
+ if (targetConn.providerType === 'gitlab') {
1280
+ // GitLab: multipart upload
1281
+ const blob = new Blob([sourceAvatarData.buffer], { type: mimeType });
1282
+ const ext = mimeType.split('/')[1] || 'png';
1283
+ const formData = new FormData();
1284
+ formData.append('avatar', blob, `avatar.${ext}`);
1285
+ await this.rawMultipartCall(targetConn, 'PUT', `/api/v4/projects/${targetRawProject.id}`, formData);
1286
+ }
1287
+ else {
1288
+ // Gitea: base64 JSON upload
1289
+ const segments = targetFullPath.split('/');
1290
+ const repo = segments.pop();
1291
+ const owner = segments[0] || '';
1292
+ const base64Image = this.uint8ArrayToBase64(sourceAvatarData);
1293
+ await this.rawApiCall(targetConn, 'POST', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/avatar`, { image: base64Image });
1294
+ }
1295
+ this.avatarUploadCache.set(cacheKey, sourceHash);
1296
+ }
1297
+ async removeProjectAvatar(targetConn, targetFullPath, targetRawProject) {
1298
+ logger.syncLog('info', `Removing avatar from ${targetFullPath}`, 'api');
1299
+ if (targetConn.providerType === 'gitlab') {
1300
+ await this.rawApiCall(targetConn, 'PUT', `/api/v4/projects/${targetRawProject.id}`, {
1301
+ avatar: '',
1302
+ });
1303
+ }
1304
+ else {
1305
+ const segments = targetFullPath.split('/');
1306
+ const repo = segments.pop();
1307
+ const owner = segments[0] || '';
1308
+ await this.rawApiCall(targetConn, 'DELETE', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/avatar`);
1309
+ }
1310
+ }
1311
+ async syncGroupAvatar(sourceConn, targetConn, _sourceGroupPath, targetGroupPath, sourceAvatarUrl, targetRawGroup) {
1312
+ const resolvedSourceUrl = sourceAvatarUrl.startsWith('http')
1313
+ ? sourceAvatarUrl
1314
+ : `${sourceConn.baseUrl.replace(/\/+$/, '')}${sourceAvatarUrl}`;
1315
+ const sourceAvatarData = await this.rawBinaryFetch(sourceConn, resolvedSourceUrl);
1316
+ if (!sourceAvatarData || sourceAvatarData.length === 0)
1317
+ return;
1318
+ // Skip SVG avatars — not supported by GitLab project endpoints
1319
+ const mimeType = this.guessAvatarMimeType(sourceAvatarData, resolvedSourceUrl);
1320
+ if (mimeType === 'image/svg+xml') {
1321
+ logger.syncLog('warn', `Skipping SVG avatar for group ${targetGroupPath} (not supported by target)`, 'api');
1322
+ return;
1323
+ }
1324
+ // Check in-memory cache: skip if source hasn't changed since last upload
1325
+ const sourceHash = await this.hashBytes(sourceAvatarData);
1326
+ const cacheKey = `group:${targetGroupPath}`;
1327
+ if (this.avatarUploadCache.get(cacheKey) === sourceHash) {
1328
+ return; // Source avatar unchanged since last upload
1329
+ }
1330
+ // Compare with target's current avatar to avoid unnecessary uploads
1331
+ const targetMeta = this.extractGroupMeta(targetConn, targetRawGroup);
1332
+ if (targetMeta.avatarUrl) {
1333
+ try {
1334
+ const resolvedTargetUrl = targetMeta.avatarUrl.startsWith('http')
1335
+ ? targetMeta.avatarUrl
1336
+ : `${targetConn.baseUrl.replace(/\/+$/, '')}${targetMeta.avatarUrl}`;
1337
+ const targetAvatarData = await this.rawBinaryFetch(targetConn, resolvedTargetUrl);
1338
+ if (targetAvatarData && this.binaryEqual(sourceAvatarData, targetAvatarData)) {
1339
+ this.avatarUploadCache.set(cacheKey, sourceHash);
1340
+ return; // Avatars are identical — skip upload
1341
+ }
1342
+ }
1343
+ catch {
1344
+ // Failed to fetch target avatar — proceed with upload as safe fallback
1345
+ }
1346
+ }
1347
+ logger.syncLog('info', `Syncing avatar for group ${targetGroupPath}...`, 'api');
1348
+ if (targetConn.providerType === 'gitlab') {
1349
+ const blob = new Blob([sourceAvatarData.buffer], { type: mimeType });
1350
+ const ext = mimeType.split('/')[1] || 'png';
1351
+ const formData = new FormData();
1352
+ formData.append('avatar', blob, `avatar.${ext}`);
1353
+ await this.rawMultipartCall(targetConn, 'PUT', `/api/v4/groups/${targetRawGroup.id}`, formData);
1354
+ }
1355
+ else {
1356
+ const orgName = targetGroupPath.split('/')[0] || targetGroupPath;
1357
+ const base64Image = this.uint8ArrayToBase64(sourceAvatarData);
1358
+ await this.rawApiCall(targetConn, 'POST', `/api/v1/orgs/${encodeURIComponent(orgName)}/avatar`, { image: base64Image });
1359
+ }
1360
+ this.avatarUploadCache.set(cacheKey, sourceHash);
1361
+ }
1362
+ async removeGroupAvatar(targetConn, targetGroupPath, targetRawGroup) {
1363
+ logger.syncLog('info', `Removing avatar from group ${targetGroupPath}`, 'api');
1364
+ if (targetConn.providerType === 'gitlab') {
1365
+ await this.rawApiCall(targetConn, 'PUT', `/api/v4/groups/${targetRawGroup.id}`, {
1366
+ avatar: '',
1367
+ });
1368
+ }
1369
+ else {
1370
+ const orgName = targetGroupPath.split('/')[0] || targetGroupPath;
1371
+ await this.rawApiCall(targetConn, 'DELETE', `/api/v1/orgs/${encodeURIComponent(orgName)}/avatar`);
1372
+ }
1373
+ }
1374
+ binaryEqual(a, b) {
1375
+ if (a.length !== b.length)
1376
+ return false;
1377
+ for (let i = 0; i < a.length; i++) {
1378
+ if (a[i] !== b[i])
1379
+ return false;
1380
+ }
1381
+ return true;
1382
+ }
1383
+ async hashBytes(data) {
1384
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data.buffer);
1385
+ return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
1386
+ }
1387
+ uint8ArrayToBase64(bytes) {
1388
+ let binary = '';
1389
+ for (let i = 0; i < bytes.length; i++) {
1390
+ binary += String.fromCharCode(bytes[i]);
1391
+ }
1392
+ return btoa(binary);
1393
+ }
1394
+ // ============================================================================
1395
+ // Obsolete — move repos instead of deleting/overwriting
1396
+ // ============================================================================
1397
+ /**
1398
+ * Raw HTTP call for API endpoints not supported by the client libraries.
1399
+ */
1400
+ async rawApiCall(conn, method, apiPath, body) {
1401
+ const baseUrl = conn.baseUrl.replace(/\/+$/, '');
1402
+ const url = `${baseUrl}${apiPath}`;
1403
+ const headers = { 'Content-Type': 'application/json' };
1404
+ if (conn.providerType === 'gitlab') {
1405
+ headers['PRIVATE-TOKEN'] = conn.token;
1406
+ }
1407
+ else {
1408
+ headers['Authorization'] = `token ${conn.token}`;
1409
+ }
1410
+ const resp = await fetch(url, {
1411
+ method,
1412
+ headers,
1413
+ body: body ? JSON.stringify(body) : undefined,
1414
+ });
1415
+ if (!resp.ok) {
1416
+ const text = await resp.text();
1417
+ throw new Error(`${method} ${apiPath}: ${resp.status} - ${text}`);
1418
+ }
1419
+ try {
1420
+ return await resp.json();
1421
+ }
1422
+ catch {
1423
+ return undefined;
1424
+ }
1425
+ }
1426
+ generateSuffix() {
1427
+ return crypto.randomUUID().substring(0, 6);
1428
+ }
1429
+ /**
1430
+ * Ensure an "obsolete" group/org exists under the target base path.
1431
+ * Returns the obsolete group/org identifier needed for transfers.
1432
+ */
1433
+ async ensureObsoleteGroup(targetConn, basePath) {
1434
+ if (targetConn.providerType === 'gitlab') {
1435
+ const client = new plugins.gitlabClient.GitLabClient(targetConn.baseUrl, targetConn.token);
1436
+ // Walk the basePath to find the parent group, then create "obsolete" subgroup
1437
+ let parentId;
1438
+ if (basePath) {
1439
+ const parentGroup = await client.getGroup(basePath);
1440
+ parentId = parentGroup.id;
1441
+ }
1442
+ // Try to get existing obsolete group
1443
+ const obsoletePath = basePath ? `${basePath}/obsolete` : 'obsolete';
1444
+ try {
1445
+ const group = await client.getGroup(obsoletePath);
1446
+ return { type: 'gitlab', groupId: group.id };
1447
+ }
1448
+ catch {
1449
+ // Doesn't exist — create it
1450
+ try {
1451
+ const newGroup = await client.createGroup('obsolete', 'obsolete', parentId);
1452
+ // Set to private via raw API (createGroup defaults to private already)
1453
+ logger.info(`Created GitLab obsolete group: ${obsoletePath}`);
1454
+ return { type: 'gitlab', groupId: newGroup.id };
1455
+ }
1456
+ catch (createErr) {
1457
+ if (String(createErr).includes('409') || String(createErr).includes('already')) {
1458
+ const group = await client.getGroup(obsoletePath);
1459
+ return { type: 'gitlab', groupId: group.id };
1460
+ }
1461
+ throw createErr;
1462
+ }
1463
+ }
1464
+ }
1465
+ else {
1466
+ // Gitea: flat orgs — create "{org}-obsolete"
1467
+ const client = new plugins.giteaClient.GiteaClient(targetConn.baseUrl, targetConn.token);
1468
+ const segments = basePath ? basePath.split('/') : [];
1469
+ const orgName = segments[0] || targetConn.groupFilter || 'default';
1470
+ const obsoleteOrg = `${orgName}-obsolete`;
1471
+ try {
1472
+ await client.getOrg(obsoleteOrg);
1473
+ }
1474
+ catch {
1475
+ try {
1476
+ await client.createOrg(obsoleteOrg, { visibility: 'private' });
1477
+ logger.info(`Created Gitea obsolete org: ${obsoleteOrg}`);
1478
+ }
1479
+ catch (createErr) {
1480
+ if (!String(createErr).includes('409') && !String(createErr).includes('already')) {
1481
+ throw createErr;
1482
+ }
1483
+ }
1484
+ }
1485
+ return { type: 'gitea', orgName: obsoleteOrg };
1486
+ }
1487
+ }
1488
+ /**
1489
+ * Move a target repo to the "obsolete" group with a unique suffix.
1490
+ * The project is also set to private.
1491
+ */
1492
+ async moveToObsolete(targetConn, targetFullPath, basePath) {
1493
+ const suffix = this.generateSuffix();
1494
+ const obsoleteTarget = await this.ensureObsoleteGroup(targetConn, basePath);
1495
+ if (obsoleteTarget.type === 'gitlab') {
1496
+ // 1. Get project by path
1497
+ const project = await this.rawApiCall(targetConn, 'GET', `/api/v4/projects/${encodeURIComponent(targetFullPath)}`);
1498
+ const projectId = project.id;
1499
+ // 2. Transfer to obsolete group
1500
+ await this.rawApiCall(targetConn, 'PUT', `/api/v4/projects/${projectId}/transfer`, { namespace: obsoleteTarget.groupId });
1501
+ // 3. Rename with suffix + set private
1502
+ const originalPath = targetFullPath.split('/').pop();
1503
+ await this.rawApiCall(targetConn, 'PUT', `/api/v4/projects/${projectId}`, { name: `${originalPath}-${suffix}`, path: `${originalPath}-${suffix}`, visibility: 'private' });
1504
+ logger.info(`Moved GitLab project "${targetFullPath}" to obsolete as "${originalPath}-${suffix}"`);
1505
+ }
1506
+ else {
1507
+ // Gitea: parse owner/repo
1508
+ const segments = targetFullPath.split('/');
1509
+ const repo = segments.pop();
1510
+ const owner = segments[0] || '';
1511
+ // 1. Transfer to obsolete org
1512
+ await this.rawApiCall(targetConn, 'POST', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/transfer`, { new_owner: obsoleteTarget.orgName });
1513
+ // 2. Rename with suffix + set private
1514
+ await this.rawApiCall(targetConn, 'PATCH', `/api/v1/repos/${encodeURIComponent(obsoleteTarget.orgName)}/${encodeURIComponent(repo)}`, { name: `${repo}-${suffix}`, private: true });
1515
+ logger.info(`Moved Gitea repo "${targetFullPath}" to obsolete as "${repo}-${suffix}"`);
1516
+ }
1517
+ }
1518
+ /**
1519
+ * Check whether the target remote in a bare mirror dir has unrelated history
1520
+ * compared to the source (origin). Returns true if histories are completely disjoint.
1521
+ */
1522
+ async checkUnrelatedHistory(mirrorDir) {
1523
+ // Fetch target refs into the bare mirror
1524
+ try {
1525
+ await this.runGit(['fetch', 'target'], mirrorDir);
1526
+ }
1527
+ catch {
1528
+ // Target is empty or unreachable — not unrelated
1529
+ return false;
1530
+ }
1531
+ // Get any source ref
1532
+ const sourceRefs = await this.runGit(['for-each-ref', '--format=%(objectname)', 'refs/heads/'], mirrorDir);
1533
+ const sourceRef = sourceRefs.trim().split('\n')[0];
1534
+ if (!sourceRef)
1535
+ return false; // Source is empty
1536
+ // Get any target ref
1537
+ const targetRefs = await this.runGit(['for-each-ref', '--format=%(objectname)', 'refs/remotes/target/'], mirrorDir);
1538
+ const targetRef = targetRefs.trim().split('\n')[0];
1539
+ if (!targetRef)
1540
+ return false; // Target is empty
1541
+ // Check for common ancestor
1542
+ try {
1543
+ await this.runGit(['merge-base', sourceRef, targetRef], mirrorDir);
1544
+ return false; // Common ancestor found — related
1545
+ }
1546
+ catch {
1547
+ return true; // No common ancestor — unrelated
1548
+ }
1549
+ }
1550
+ sanitizePath(fullPath) {
1551
+ return fullPath.replace(/[^a-zA-Z0-9._/-]/g, '_');
1552
+ }
1553
+ async dirExists(dirPath) {
1554
+ try {
1555
+ const stat = await plugins.fs.stat(dirPath);
1556
+ return stat.isDirectory();
1557
+ }
1558
+ catch {
1559
+ return false;
1560
+ }
1561
+ }
1562
+ /**
1563
+ * Fetch all branch and tag SHAs from a repo via provider API.
1564
+ * Returns null on any error (safe fallback to git-based comparison).
1565
+ */
1566
+ async listRefsViaProvider(provider, fullPath) {
1567
+ try {
1568
+ const [branches, tags] = await Promise.all([
1569
+ provider.getBranches(fullPath),
1570
+ provider.getTags(fullPath),
1571
+ ]);
1572
+ return {
1573
+ branches: new Map(branches.map((b) => [b.name, b.commitSha])),
1574
+ tags: new Map(tags.map((t) => [t.name, t.commitSha])),
1575
+ };
1576
+ }
1577
+ catch {
1578
+ return null;
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Compare refs between source and target via provider API (no git clone needed).
1583
+ * Returns true (match), false (differ), or null (can't determine — fall through to git).
1584
+ */
1585
+ async refsMatchViaApi(sourceProvider, targetProvider, sourceFullPath, targetFullPath) {
1586
+ const [sourceRefs, targetRefs] = await Promise.all([
1587
+ this.listRefsViaProvider(sourceProvider, sourceFullPath),
1588
+ this.listRefsViaProvider(targetProvider, targetFullPath),
1589
+ ]);
1590
+ if (!sourceRefs || !targetRefs)
1591
+ return null;
1592
+ // Compare branches
1593
+ if (sourceRefs.branches.size !== targetRefs.branches.size)
1594
+ return false;
1595
+ for (const [name, sha] of sourceRefs.branches) {
1596
+ if (targetRefs.branches.get(name) !== sha)
1597
+ return false;
1598
+ }
1599
+ // Compare tags
1600
+ if (sourceRefs.tags.size !== targetRefs.tags.size)
1601
+ return false;
1602
+ for (const [name, sha] of sourceRefs.tags) {
1603
+ if (targetRefs.tags.get(name) !== sha)
1604
+ return false;
1605
+ }
1606
+ return true;
1607
+ }
1608
+ /**
1609
+ * Compare local refs (source) with target remote refs.
1610
+ * Returns true when all branches and tags are identical — no push needed.
1611
+ */
1612
+ async refsMatch(mirrorDir) {
1613
+ try {
1614
+ // Local branches (source)
1615
+ const localHeadsRaw = await this.runGit(['for-each-ref', '--format=%(refname:strip=2) %(objectname)', 'refs/heads/'], mirrorDir);
1616
+ // Target branches (fetched by checkUnrelatedHistory)
1617
+ const targetHeadsRaw = await this.runGit(['for-each-ref', '--format=%(refname:strip=3) %(objectname)', 'refs/remotes/target/'], mirrorDir);
1618
+ // Local tags
1619
+ const localTagsRaw = await this.runGit(['for-each-ref', '--format=%(refname:strip=2) %(objectname)', 'refs/tags/'], mirrorDir);
1620
+ // Target tags via ls-remote (avoids shared refs/tags/ namespace ambiguity in bare repos)
1621
+ const targetTagsRaw = await this.runGit(['ls-remote', '--tags', 'target'], mirrorDir);
1622
+ const parseRefLines = (raw) => {
1623
+ const map = new Map();
1624
+ for (const line of raw.trim().split('\n')) {
1625
+ if (!line.trim())
1626
+ continue;
1627
+ const parts = line.trim().split(/\s+/);
1628
+ if (parts.length >= 2) {
1629
+ map.set(parts[0], parts[1]);
1630
+ }
1631
+ }
1632
+ return map;
1633
+ };
1634
+ const parseLsRemoteTags = (raw) => {
1635
+ const map = new Map();
1636
+ for (const line of raw.trim().split('\n')) {
1637
+ if (!line.trim())
1638
+ continue;
1639
+ // Skip ^{} dereference lines
1640
+ if (line.includes('^{}'))
1641
+ continue;
1642
+ const parts = line.trim().split(/\s+/);
1643
+ if (parts.length >= 2) {
1644
+ // parts[0] = sha, parts[1] = refs/tags/name
1645
+ const tagName = parts[1].replace('refs/tags/', '');
1646
+ map.set(tagName, parts[0]);
1647
+ }
1648
+ }
1649
+ return map;
1650
+ };
1651
+ const localHeads = parseRefLines(localHeadsRaw);
1652
+ const targetHeads = parseRefLines(targetHeadsRaw);
1653
+ const localTags = parseRefLines(localTagsRaw);
1654
+ const targetTags = parseLsRemoteTags(targetTagsRaw);
1655
+ // Compare branches
1656
+ if (localHeads.size !== targetHeads.size)
1657
+ return false;
1658
+ for (const [name, sha] of localHeads) {
1659
+ if (targetHeads.get(name) !== sha)
1660
+ return false;
1661
+ }
1662
+ // Compare tags
1663
+ if (localTags.size !== targetTags.size)
1664
+ return false;
1665
+ for (const [name, sha] of localTags) {
1666
+ if (targetTags.get(name) !== sha)
1667
+ return false;
1668
+ }
1669
+ return true;
1670
+ }
1671
+ catch {
1672
+ // On any error, fall back to pushing (safe default)
1673
+ return false;
1674
+ }
1675
+ }
1676
+ async runGit(args, cwd) {
1677
+ if (this.stopping) {
1678
+ throw new Error('SyncManager is stopping');
1679
+ }
1680
+ return await new Promise((resolve, reject) => {
1681
+ const child = plugins.childProcess.spawn('git', args, {
1682
+ cwd,
1683
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
1684
+ stdio: ['ignore', 'pipe', 'pipe'],
1685
+ });
1686
+ this.activeGitChildren.add(child);
1687
+ const stdoutChunks = [];
1688
+ const stderrChunks = [];
1689
+ let settled = false;
1690
+ const finish = (callback) => {
1691
+ if (settled)
1692
+ return;
1693
+ settled = true;
1694
+ this.activeGitChildren.delete(child);
1695
+ callback();
1696
+ };
1697
+ child.stdout.on('data', (chunk) => stdoutChunks.push(chunk));
1698
+ child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
1699
+ child.on('error', (err) => finish(() => reject(err)));
1700
+ child.on('close', (code, signal) => finish(() => {
1701
+ const stderr = plugins.Buffer.concat(stderrChunks).toString('utf8');
1702
+ if (code !== 0) {
1703
+ const exitInfo = signal ? `signal ${signal}` : `code ${code}`;
1704
+ reject(new Error(`git ${args[0]} failed with ${exitInfo}: ${stderr.trim()}`));
1705
+ return;
1706
+ }
1707
+ resolve(plugins.Buffer.concat(stdoutChunks).toString('utf8'));
1708
+ }));
1709
+ });
1710
+ }
1711
+ // ============================================================================
1712
+ // Persistence
1713
+ // ============================================================================
1714
+ async loadConfigs() {
1715
+ const keys = await this.storageManager.list(SYNC_PREFIX);
1716
+ this.configs = [];
1717
+ for (const key of keys) {
1718
+ const config = await this.storageManager.getJSON(key);
1719
+ if (config) {
1720
+ try {
1721
+ config.intervalMinutes = validateIntervalMinutes(config.intervalMinutes, `sync "${config.name}" intervalMinutes`, 5);
1722
+ }
1723
+ catch (err) {
1724
+ config.status = 'error';
1725
+ config.lastSyncError = err instanceof Error ? err.message : String(err);
1726
+ await this.persistConfig(config);
1727
+ }
1728
+ this.configs.push(config);
1729
+ }
1730
+ }
1731
+ }
1732
+ async persistConfig(config) {
1733
+ await this.storageManager.setJSON(`${SYNC_PREFIX}${config.id}.json`, config);
1734
+ }
1735
+ async updateRepoStatus(syncConfigId, sourceFullPath, updates) {
1736
+ const hash = this.sanitizePath(sourceFullPath).replace(/\//g, '__');
1737
+ const key = `${SYNC_STATUS_PREFIX}${syncConfigId}/${hash}.json`;
1738
+ let status = await this.storageManager.getJSON(key);
1739
+ if (!status) {
1740
+ status = {
1741
+ id: hash,
1742
+ syncConfigId,
1743
+ sourceFullPath,
1744
+ targetFullPath: '',
1745
+ lastSyncAt: 0,
1746
+ status: 'pending',
1747
+ };
1748
+ }
1749
+ Object.assign(status, updates);
1750
+ await this.storageManager.setJSON(key, status);
1751
+ }
1752
+ // ============================================================================
1753
+ // Timer Management
1754
+ // ============================================================================
1755
+ startTimer(config) {
1756
+ this.stopTimer(config.id);
1757
+ let intervalMs;
1758
+ try {
1759
+ intervalMs = intervalMinutesToMs(config.intervalMinutes, `sync "${config.name}" intervalMinutes`);
1760
+ }
1761
+ catch (err) {
1762
+ logger.error(`Timer not started for sync "${config.name}": ${err}`);
1763
+ return;
1764
+ }
1765
+ const timerId = setInterval(() => {
1766
+ this.executeSync(config.id).catch((err) => logger.error(`Scheduled sync for ${config.name} failed: ${err}`));
1767
+ }, intervalMs);
1768
+ unrefTimer(timerId);
1769
+ this.timers.set(config.id, timerId);
1770
+ }
1771
+ stopTimer(configId) {
1772
+ const timerId = this.timers.get(configId);
1773
+ if (timerId !== undefined) {
1774
+ clearInterval(timerId);
1775
+ this.timers.delete(configId);
1776
+ }
1777
+ }
1778
+ async waitForRunningSyncs() {
1779
+ while (this.runningSync.size > 0 || this.activeGitChildren.size > 0) {
1780
+ await new Promise((resolve) => {
1781
+ const timer = setTimeout(resolve, 100);
1782
+ unrefTimer(timer);
1783
+ });
1784
+ }
1785
+ }
1786
+ }
1787
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luY21hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jbGFzc2VzL3N5bmNtYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdkMsT0FBTyxFQUFFLG1CQUFtQixFQUFFLFVBQVUsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQVF4RixNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUM7QUFDN0IsTUFBTSxrQkFBa0IsR0FBRyxlQUFlLENBQUM7QUFFM0M7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxXQUFXO0lBYVo7SUFDQTtJQUNBO0lBZEYsT0FBTyxHQUFrQyxFQUFFLENBQUM7SUFDNUMsTUFBTSxHQUFnRCxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ2hFLFdBQVcsR0FBZ0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNyQyxlQUFlLEdBQWdCLElBQUksR0FBRyxFQUFFLENBQUM7SUFDekMsaUJBQWlCLEdBQXVDLElBQUksQ0FBQztJQUM3RCxpQkFBaUIsR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNuRCxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsRUFBZ0IsQ0FBQztJQUM1QyxRQUFRLEdBQUcsS0FBSyxDQUFDO0lBRWpCLFdBQVcsR0FBRyxFQUFFLENBQUM7SUFFekIsWUFDVSxjQUE4QixFQUM5QixpQkFBb0MsRUFDcEMsU0FBb0I7UUFGcEIsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBbUI7UUFDcEMsY0FBUyxHQUFULFNBQVMsQ0FBVztJQUMzQixDQUFDO0lBRUosS0FBSyxDQUFDLElBQUk7UUFDUixJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQztRQUN0QixpRkFBaUY7UUFDakYsSUFBSSxDQUFDLFdB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