@hominis/fireforge 0.9.0

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 (316) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +294 -0
  3. package/README.md +435 -0
  4. package/dist/bin/fireforge.d.ts +10 -0
  5. package/dist/bin/fireforge.js +29 -0
  6. package/dist/src/cli.d.ts +33 -0
  7. package/dist/src/cli.js +180 -0
  8. package/dist/src/commands/bootstrap.d.ts +9 -0
  9. package/dist/src/commands/bootstrap.js +73 -0
  10. package/dist/src/commands/build.d.ts +11 -0
  11. package/dist/src/commands/build.js +102 -0
  12. package/dist/src/commands/config.d.ts +13 -0
  13. package/dist/src/commands/config.js +135 -0
  14. package/dist/src/commands/discard.d.ts +12 -0
  15. package/dist/src/commands/discard.js +84 -0
  16. package/dist/src/commands/doctor.d.ts +18 -0
  17. package/dist/src/commands/doctor.js +356 -0
  18. package/dist/src/commands/download.d.ts +11 -0
  19. package/dist/src/commands/download.js +127 -0
  20. package/dist/src/commands/export-all.d.ts +11 -0
  21. package/dist/src/commands/export-all.js +122 -0
  22. package/dist/src/commands/export-shared.d.ts +48 -0
  23. package/dist/src/commands/export-shared.js +208 -0
  24. package/dist/src/commands/export.d.ts +13 -0
  25. package/dist/src/commands/export.js +178 -0
  26. package/dist/src/commands/furnace/apply.d.ts +7 -0
  27. package/dist/src/commands/furnace/apply.js +80 -0
  28. package/dist/src/commands/furnace/create.d.ts +8 -0
  29. package/dist/src/commands/furnace/create.js +377 -0
  30. package/dist/src/commands/furnace/deploy.d.ts +8 -0
  31. package/dist/src/commands/furnace/deploy.js +338 -0
  32. package/dist/src/commands/furnace/diff.d.ts +7 -0
  33. package/dist/src/commands/furnace/diff.js +119 -0
  34. package/dist/src/commands/furnace/index.d.ts +16 -0
  35. package/dist/src/commands/furnace/index.js +121 -0
  36. package/dist/src/commands/furnace/list.d.ts +5 -0
  37. package/dist/src/commands/furnace/list.js +65 -0
  38. package/dist/src/commands/furnace/override.d.ts +8 -0
  39. package/dist/src/commands/furnace/override.js +188 -0
  40. package/dist/src/commands/furnace/preview.d.ts +7 -0
  41. package/dist/src/commands/furnace/preview.js +96 -0
  42. package/dist/src/commands/furnace/remove.d.ts +8 -0
  43. package/dist/src/commands/furnace/remove.js +159 -0
  44. package/dist/src/commands/furnace/scan.d.ts +5 -0
  45. package/dist/src/commands/furnace/scan.js +112 -0
  46. package/dist/src/commands/furnace/status.d.ts +7 -0
  47. package/dist/src/commands/furnace/status.js +137 -0
  48. package/dist/src/commands/furnace/validate.d.ts +6 -0
  49. package/dist/src/commands/furnace/validate.js +91 -0
  50. package/dist/src/commands/furnace/validation-output.d.ts +7 -0
  51. package/dist/src/commands/furnace/validation-output.js +22 -0
  52. package/dist/src/commands/import.d.ts +11 -0
  53. package/dist/src/commands/import.js +241 -0
  54. package/dist/src/commands/lint.d.ts +10 -0
  55. package/dist/src/commands/lint.js +118 -0
  56. package/dist/src/commands/package.d.ts +11 -0
  57. package/dist/src/commands/package.js +80 -0
  58. package/dist/src/commands/re-export.d.ts +12 -0
  59. package/dist/src/commands/re-export.js +242 -0
  60. package/dist/src/commands/rebase/abort.d.ts +7 -0
  61. package/dist/src/commands/rebase/abort.js +49 -0
  62. package/dist/src/commands/rebase/confirm.d.ts +18 -0
  63. package/dist/src/commands/rebase/confirm.js +33 -0
  64. package/dist/src/commands/rebase/continue.d.ts +7 -0
  65. package/dist/src/commands/rebase/continue.js +81 -0
  66. package/dist/src/commands/rebase/index.d.ts +22 -0
  67. package/dist/src/commands/rebase/index.js +127 -0
  68. package/dist/src/commands/rebase/patch-loop.d.ts +9 -0
  69. package/dist/src/commands/rebase/patch-loop.js +135 -0
  70. package/dist/src/commands/rebase/summary.d.ts +12 -0
  71. package/dist/src/commands/rebase/summary.js +43 -0
  72. package/dist/src/commands/rebase.d.ts +4 -0
  73. package/dist/src/commands/rebase.js +6 -0
  74. package/dist/src/commands/register.d.ts +13 -0
  75. package/dist/src/commands/register.js +67 -0
  76. package/dist/src/commands/reset.d.ts +11 -0
  77. package/dist/src/commands/reset.js +83 -0
  78. package/dist/src/commands/resolve.d.ts +9 -0
  79. package/dist/src/commands/resolve.js +124 -0
  80. package/dist/src/commands/run.d.ts +9 -0
  81. package/dist/src/commands/run.js +91 -0
  82. package/dist/src/commands/setup-support.d.ts +23 -0
  83. package/dist/src/commands/setup-support.js +310 -0
  84. package/dist/src/commands/setup.d.ts +11 -0
  85. package/dist/src/commands/setup.js +94 -0
  86. package/dist/src/commands/status.d.ts +11 -0
  87. package/dist/src/commands/status.js +268 -0
  88. package/dist/src/commands/test.d.ts +12 -0
  89. package/dist/src/commands/test.js +182 -0
  90. package/dist/src/commands/token-coverage.d.ts +5 -0
  91. package/dist/src/commands/token-coverage.js +57 -0
  92. package/dist/src/commands/token.d.ts +14 -0
  93. package/dist/src/commands/token.js +121 -0
  94. package/dist/src/commands/watch.d.ts +9 -0
  95. package/dist/src/commands/watch.js +112 -0
  96. package/dist/src/commands/wire.d.ts +13 -0
  97. package/dist/src/commands/wire.js +149 -0
  98. package/dist/src/core/ast-utils.d.ts +47 -0
  99. package/dist/src/core/ast-utils.js +57 -0
  100. package/dist/src/core/brand-validation.d.ts +7 -0
  101. package/dist/src/core/brand-validation.js +15 -0
  102. package/dist/src/core/branding.d.ts +49 -0
  103. package/dist/src/core/branding.js +229 -0
  104. package/dist/src/core/browser-wire.d.ts +40 -0
  105. package/dist/src/core/browser-wire.js +66 -0
  106. package/dist/src/core/build-prepare.d.ts +25 -0
  107. package/dist/src/core/build-prepare.js +93 -0
  108. package/dist/src/core/config-mutate.d.ts +15 -0
  109. package/dist/src/core/config-mutate.js +51 -0
  110. package/dist/src/core/config-paths.d.ts +28 -0
  111. package/dist/src/core/config-paths.js +65 -0
  112. package/dist/src/core/config-state.d.ts +28 -0
  113. package/dist/src/core/config-state.js +152 -0
  114. package/dist/src/core/config-validate.d.ts +11 -0
  115. package/dist/src/core/config-validate.js +141 -0
  116. package/dist/src/core/config.d.ts +39 -0
  117. package/dist/src/core/config.js +70 -0
  118. package/dist/src/core/file-lock.d.ts +11 -0
  119. package/dist/src/core/file-lock.js +80 -0
  120. package/dist/src/core/firefox-archive.d.ts +40 -0
  121. package/dist/src/core/firefox-archive.js +63 -0
  122. package/dist/src/core/firefox-cache.d.ts +23 -0
  123. package/dist/src/core/firefox-cache.js +134 -0
  124. package/dist/src/core/firefox-download.d.ts +21 -0
  125. package/dist/src/core/firefox-download.js +129 -0
  126. package/dist/src/core/firefox-extract.d.ts +21 -0
  127. package/dist/src/core/firefox-extract.js +53 -0
  128. package/dist/src/core/firefox.d.ts +34 -0
  129. package/dist/src/core/firefox.js +78 -0
  130. package/dist/src/core/furnace-apply-helpers.d.ts +21 -0
  131. package/dist/src/core/furnace-apply-helpers.js +244 -0
  132. package/dist/src/core/furnace-apply.d.ts +16 -0
  133. package/dist/src/core/furnace-apply.js +147 -0
  134. package/dist/src/core/furnace-config.d.ts +94 -0
  135. package/dist/src/core/furnace-config.js +372 -0
  136. package/dist/src/core/furnace-constants.d.ts +4 -0
  137. package/dist/src/core/furnace-constants.js +6 -0
  138. package/dist/src/core/furnace-registration-ast.d.ts +24 -0
  139. package/dist/src/core/furnace-registration-ast.js +218 -0
  140. package/dist/src/core/furnace-registration-remove.d.ts +14 -0
  141. package/dist/src/core/furnace-registration-remove.js +89 -0
  142. package/dist/src/core/furnace-registration-validate.d.ts +20 -0
  143. package/dist/src/core/furnace-registration-validate.js +40 -0
  144. package/dist/src/core/furnace-registration.d.ts +29 -0
  145. package/dist/src/core/furnace-registration.js +96 -0
  146. package/dist/src/core/furnace-rollback.d.ts +20 -0
  147. package/dist/src/core/furnace-rollback.js +66 -0
  148. package/dist/src/core/furnace-scanner.d.ts +40 -0
  149. package/dist/src/core/furnace-scanner.js +143 -0
  150. package/dist/src/core/furnace-stories.d.ts +37 -0
  151. package/dist/src/core/furnace-stories.js +185 -0
  152. package/dist/src/core/furnace-validate-accessibility.d.ts +6 -0
  153. package/dist/src/core/furnace-validate-accessibility.js +32 -0
  154. package/dist/src/core/furnace-validate-checks.d.ts +4 -0
  155. package/dist/src/core/furnace-validate-checks.js +7 -0
  156. package/dist/src/core/furnace-validate-compatibility.d.ts +6 -0
  157. package/dist/src/core/furnace-validate-compatibility.js +57 -0
  158. package/dist/src/core/furnace-validate-helpers.d.ts +28 -0
  159. package/dist/src/core/furnace-validate-helpers.js +129 -0
  160. package/dist/src/core/furnace-validate-registration.d.ts +37 -0
  161. package/dist/src/core/furnace-validate-registration.js +220 -0
  162. package/dist/src/core/furnace-validate-structure.d.ts +6 -0
  163. package/dist/src/core/furnace-validate-structure.js +66 -0
  164. package/dist/src/core/furnace-validate.d.ts +16 -0
  165. package/dist/src/core/furnace-validate.js +103 -0
  166. package/dist/src/core/git-base.d.ts +47 -0
  167. package/dist/src/core/git-base.js +50 -0
  168. package/dist/src/core/git-diff.d.ts +63 -0
  169. package/dist/src/core/git-diff.js +246 -0
  170. package/dist/src/core/git-file-ops.d.ts +65 -0
  171. package/dist/src/core/git-file-ops.js +141 -0
  172. package/dist/src/core/git-status.d.ts +65 -0
  173. package/dist/src/core/git-status.js +163 -0
  174. package/dist/src/core/git.d.ts +113 -0
  175. package/dist/src/core/git.js +363 -0
  176. package/dist/src/core/license-headers.d.ts +36 -0
  177. package/dist/src/core/license-headers.js +83 -0
  178. package/dist/src/core/mach-build-artifacts.d.ts +29 -0
  179. package/dist/src/core/mach-build-artifacts.js +117 -0
  180. package/dist/src/core/mach-mozconfig.d.ts +17 -0
  181. package/dist/src/core/mach-mozconfig.js +50 -0
  182. package/dist/src/core/mach-python.d.ts +16 -0
  183. package/dist/src/core/mach-python.js +126 -0
  184. package/dist/src/core/mach.d.ts +106 -0
  185. package/dist/src/core/mach.js +166 -0
  186. package/dist/src/core/manifest-helpers.d.ts +25 -0
  187. package/dist/src/core/manifest-helpers.js +96 -0
  188. package/dist/src/core/manifest-register.d.ts +30 -0
  189. package/dist/src/core/manifest-register.js +65 -0
  190. package/dist/src/core/manifest-rules.d.ts +39 -0
  191. package/dist/src/core/manifest-rules.js +151 -0
  192. package/dist/src/core/manifest-tokenizers.d.ts +34 -0
  193. package/dist/src/core/manifest-tokenizers.js +84 -0
  194. package/dist/src/core/parser-fallback.d.ts +36 -0
  195. package/dist/src/core/parser-fallback.js +43 -0
  196. package/dist/src/core/patch-apply-fuzz.d.ts +29 -0
  197. package/dist/src/core/patch-apply-fuzz.js +70 -0
  198. package/dist/src/core/patch-apply.d.ts +46 -0
  199. package/dist/src/core/patch-apply.js +235 -0
  200. package/dist/src/core/patch-export.d.ts +99 -0
  201. package/dist/src/core/patch-export.js +314 -0
  202. package/dist/src/core/patch-files.d.ts +11 -0
  203. package/dist/src/core/patch-files.js +51 -0
  204. package/dist/src/core/patch-lint.d.ts +72 -0
  205. package/dist/src/core/patch-lint.js +403 -0
  206. package/dist/src/core/patch-lock.d.ts +8 -0
  207. package/dist/src/core/patch-lock.js +29 -0
  208. package/dist/src/core/patch-manifest-consistency.d.ts +24 -0
  209. package/dist/src/core/patch-manifest-consistency.js +135 -0
  210. package/dist/src/core/patch-manifest-io.d.ts +36 -0
  211. package/dist/src/core/patch-manifest-io.js +77 -0
  212. package/dist/src/core/patch-manifest-query.d.ts +48 -0
  213. package/dist/src/core/patch-manifest-query.js +124 -0
  214. package/dist/src/core/patch-manifest-validate.d.ts +22 -0
  215. package/dist/src/core/patch-manifest-validate.js +72 -0
  216. package/dist/src/core/patch-manifest.d.ts +11 -0
  217. package/dist/src/core/patch-manifest.js +12 -0
  218. package/dist/src/core/patch-parse.d.ts +43 -0
  219. package/dist/src/core/patch-parse.js +143 -0
  220. package/dist/src/core/patch-transform.d.ts +21 -0
  221. package/dist/src/core/patch-transform.js +138 -0
  222. package/dist/src/core/rebase-session.d.ts +47 -0
  223. package/dist/src/core/rebase-session.js +65 -0
  224. package/dist/src/core/register-browser-content.d.ts +11 -0
  225. package/dist/src/core/register-browser-content.js +116 -0
  226. package/dist/src/core/register-module.d.ts +11 -0
  227. package/dist/src/core/register-module.js +76 -0
  228. package/dist/src/core/register-shared-css.d.ts +11 -0
  229. package/dist/src/core/register-shared-css.js +117 -0
  230. package/dist/src/core/register-test-manifest.d.ts +18 -0
  231. package/dist/src/core/register-test-manifest.js +99 -0
  232. package/dist/src/core/state-file.d.ts +4 -0
  233. package/dist/src/core/state-file.js +25 -0
  234. package/dist/src/core/token-coverage.d.ts +12 -0
  235. package/dist/src/core/token-coverage.js +74 -0
  236. package/dist/src/core/token-manager.d.ts +55 -0
  237. package/dist/src/core/token-manager.js +387 -0
  238. package/dist/src/core/wire-destroy.d.ts +21 -0
  239. package/dist/src/core/wire-destroy.js +103 -0
  240. package/dist/src/core/wire-dom-fragment.d.ts +23 -0
  241. package/dist/src/core/wire-dom-fragment.js +129 -0
  242. package/dist/src/core/wire-init.d.ts +23 -0
  243. package/dist/src/core/wire-init.js +201 -0
  244. package/dist/src/core/wire-subscript.d.ts +20 -0
  245. package/dist/src/core/wire-subscript.js +134 -0
  246. package/dist/src/core/wire-targets.d.ts +7 -0
  247. package/dist/src/core/wire-targets.js +9 -0
  248. package/dist/src/core/wire-utils.d.ts +88 -0
  249. package/dist/src/core/wire-utils.js +279 -0
  250. package/dist/src/errors/base.d.ts +60 -0
  251. package/dist/src/errors/base.js +87 -0
  252. package/dist/src/errors/build.d.ts +52 -0
  253. package/dist/src/errors/build.js +114 -0
  254. package/dist/src/errors/codes.d.ts +29 -0
  255. package/dist/src/errors/codes.js +30 -0
  256. package/dist/src/errors/config.d.ts +31 -0
  257. package/dist/src/errors/config.js +61 -0
  258. package/dist/src/errors/download.d.ts +42 -0
  259. package/dist/src/errors/download.js +95 -0
  260. package/dist/src/errors/furnace.d.ts +10 -0
  261. package/dist/src/errors/furnace.js +22 -0
  262. package/dist/src/errors/git.d.ts +41 -0
  263. package/dist/src/errors/git.js +99 -0
  264. package/dist/src/errors/patch.d.ts +10 -0
  265. package/dist/src/errors/patch.js +26 -0
  266. package/dist/src/errors/rebase.d.ts +20 -0
  267. package/dist/src/errors/rebase.js +30 -0
  268. package/dist/src/index.d.ts +21 -0
  269. package/dist/src/index.js +21 -0
  270. package/dist/src/types/cli.d.ts +14 -0
  271. package/dist/src/types/cli.js +2 -0
  272. package/dist/src/types/commands/index.d.ts +6 -0
  273. package/dist/src/types/commands/index.js +6 -0
  274. package/dist/src/types/commands/options.d.ts +239 -0
  275. package/dist/src/types/commands/options.js +6 -0
  276. package/dist/src/types/commands/patches.d.ts +89 -0
  277. package/dist/src/types/commands/patches.js +6 -0
  278. package/dist/src/types/commands/project.d.ts +71 -0
  279. package/dist/src/types/commands/project.js +6 -0
  280. package/dist/src/types/config.d.ts +101 -0
  281. package/dist/src/types/config.js +2 -0
  282. package/dist/src/types/furnace.d.ts +158 -0
  283. package/dist/src/types/furnace.js +2 -0
  284. package/dist/src/types/index.d.ts +6 -0
  285. package/dist/src/types/index.js +6 -0
  286. package/dist/src/utils/errors.d.ts +2 -0
  287. package/dist/src/utils/errors.js +15 -0
  288. package/dist/src/utils/fs.d.ts +72 -0
  289. package/dist/src/utils/fs.js +179 -0
  290. package/dist/src/utils/logger.d.ts +58 -0
  291. package/dist/src/utils/logger.js +120 -0
  292. package/dist/src/utils/options.d.ts +8 -0
  293. package/dist/src/utils/options.js +16 -0
  294. package/dist/src/utils/package-root.d.ts +10 -0
  295. package/dist/src/utils/package-root.js +53 -0
  296. package/dist/src/utils/parse.d.ts +110 -0
  297. package/dist/src/utils/parse.js +200 -0
  298. package/dist/src/utils/paths.d.ts +10 -0
  299. package/dist/src/utils/paths.js +43 -0
  300. package/dist/src/utils/platform.d.ts +38 -0
  301. package/dist/src/utils/platform.js +56 -0
  302. package/dist/src/utils/process.d.ts +80 -0
  303. package/dist/src/utils/process.js +188 -0
  304. package/dist/src/utils/regex.d.ts +24 -0
  305. package/dist/src/utils/regex.js +40 -0
  306. package/dist/src/utils/validation.d.ts +133 -0
  307. package/dist/src/utils/validation.js +250 -0
  308. package/package.json +106 -0
  309. package/templates/configs/common.mozconfig +24 -0
  310. package/templates/configs/darwin.mozconfig +10 -0
  311. package/templates/configs/linux.mozconfig +12 -0
  312. package/templates/configs/win32.mozconfig +14 -0
  313. package/templates/licenses/0BSD.md +14 -0
  314. package/templates/licenses/EUPL-1.2.md +294 -0
  315. package/templates/licenses/GPL-2.0-or-later.md +339 -0
  316. package/templates/licenses/MPL-2.0.md +383 -0
@@ -0,0 +1,113 @@
1
+ export type { GitStatusEntry } from './git-base.js';
2
+ /**
3
+ * Checks if a directory is a git repository.
4
+ * @param dir - Directory to check
5
+ * @returns True if the directory is a git repository
6
+ */
7
+ export declare function isGitRepository(dir: string): Promise<boolean>;
8
+ /**
9
+ * Ensures the repository has an "origin" remote.
10
+ *
11
+ * Firefox's mach bootstrap and build scripts shell out to
12
+ * `git remote get-url origin` and emit noisy errors when the remote is
13
+ * absent. This adds a local-only dummy remote so those scripts stay quiet.
14
+ * Nothing is ever fetched from or pushed to this remote.
15
+ *
16
+ * @param dir - Git working directory
17
+ */
18
+ export declare function ensureOriginRemote(dir: string): Promise<void>;
19
+ /**
20
+ * Stages all files in the repository.
21
+ * Tries a monolithic `git add -A` first; if that times out, falls back to
22
+ * directory-by-directory staged adds.
23
+ */
24
+ export declare function stageAllFiles(dir: string, options?: {
25
+ onProgress?: (message: string) => void;
26
+ timeout?: number;
27
+ }): Promise<void>;
28
+ /**
29
+ * Initializes a new git repository with an orphan branch.
30
+ * @param dir - Directory to initialize
31
+ * @param branchName - Name for the initial branch
32
+ */
33
+ export declare function initRepository(dir: string, branchName?: string, options?: {
34
+ onProgress?: (message: string) => void;
35
+ }): Promise<void>;
36
+ /**
37
+ * Resumes a partially initialized git repository (e.g. after a killed
38
+ * `git add -A` left an unborn HEAD). Re-applies performance settings,
39
+ * cleans up stale locks, stages all files, and creates the initial commit.
40
+ */
41
+ export declare function resumeRepository(dir: string, options?: {
42
+ onProgress?: (message: string) => void;
43
+ }): Promise<void>;
44
+ /**
45
+ * Applies a patch file using git apply.
46
+ * @param patchPath - Path to the patch file
47
+ * @param repoDir - Repository directory
48
+ * @param options - Application options
49
+ */
50
+ export declare function applyPatch(patchPath: string, repoDir: string, options?: {
51
+ reject?: boolean;
52
+ }): Promise<void>;
53
+ /**
54
+ * Applies a patch idempotently using reverse-forward pattern.
55
+ * First tries to reverse the patch (in case it's already applied),
56
+ * then applies it forward.
57
+ * @param patchPath - Path to the patch file
58
+ * @param repoDir - Repository directory
59
+ * @param options - Application options
60
+ */
61
+ export declare function applyPatchIdempotent(patchPath: string, repoDir: string, options?: {
62
+ reject?: boolean;
63
+ }): Promise<void>;
64
+ /**
65
+ * Reverses a previously applied patch.
66
+ * @param patchPath - Path to the patch file
67
+ * @param repoDir - Repository directory
68
+ */
69
+ export declare function reversePatch(patchPath: string, repoDir: string): Promise<void>;
70
+ /**
71
+ * Checks if the repository has uncommitted changes.
72
+ * @param repoDir - Repository directory
73
+ * @returns True if there are uncommitted changes
74
+ */
75
+ export declare function hasChanges(repoDir: string): Promise<boolean>;
76
+ /**
77
+ * Checks whether an error indicates the repository has no HEAD (e.g. unborn branch).
78
+ * @param error - The error to check
79
+ * @returns True if the error is a missing-HEAD error
80
+ */
81
+ export declare function isMissingHeadError(error: unknown): boolean;
82
+ /**
83
+ * Gets the current HEAD commit hash.
84
+ * @param repoDir - Repository directory
85
+ * @returns Commit hash
86
+ */
87
+ export declare function getHead(repoDir: string): Promise<string>;
88
+ /**
89
+ * Gets the current branch name.
90
+ * @param repoDir - Repository directory
91
+ * @returns Branch name
92
+ */
93
+ export declare function getCurrentBranch(repoDir: string): Promise<string>;
94
+ /**
95
+ * Resets all changes in the repository.
96
+ * @param repoDir - Repository directory
97
+ */
98
+ export declare function resetChanges(repoDir: string): Promise<void>;
99
+ /**
100
+ * Creates a commit with all current changes.
101
+ * @param repoDir - Repository directory
102
+ * @param message - Commit message
103
+ */
104
+ export declare function commit(repoDir: string, message: string): Promise<void>;
105
+ /**
106
+ * Gets the status of files with their status codes.
107
+ * @param repoDir - Repository directory
108
+ * @returns Array of [status, filepath] tuples
109
+ */
110
+ export declare function getStatusWithCodes(repoDir: string): Promise<Array<{
111
+ status: string;
112
+ file: string;
113
+ }>>;
@@ -0,0 +1,363 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ import { readdir, stat } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { GitError, GitIndexLockError, PatchApplyError } from '../errors/git.js';
5
+ import { toError } from '../utils/errors.js';
6
+ import { pathExists, removeFile } from '../utils/fs.js';
7
+ import { verbose } from '../utils/logger.js';
8
+ import { exec } from '../utils/process.js';
9
+ import { configureGitPerformance, ensureGit, git, GIT_ADD_CHUNK_TIMEOUT_MS, GIT_ADD_TIMEOUT_MS, } from './git-base.js';
10
+ import { getWorkingTreeStatus } from './git-status.js';
11
+ // ── Functions that remain in this file ──
12
+ /**
13
+ * Checks if a directory is a git repository.
14
+ * @param dir - Directory to check
15
+ * @returns True if the directory is a git repository
16
+ */
17
+ export async function isGitRepository(dir) {
18
+ const gitDir = join(dir, '.git');
19
+ return pathExists(gitDir);
20
+ }
21
+ /**
22
+ * Ensures the repository has an "origin" remote.
23
+ *
24
+ * Firefox's mach bootstrap and build scripts shell out to
25
+ * `git remote get-url origin` and emit noisy errors when the remote is
26
+ * absent. This adds a local-only dummy remote so those scripts stay quiet.
27
+ * Nothing is ever fetched from or pushed to this remote.
28
+ *
29
+ * @param dir - Git working directory
30
+ */
31
+ export async function ensureOriginRemote(dir) {
32
+ const result = await exec('git', ['remote', 'get-url', 'origin'], { cwd: dir });
33
+ if (result.exitCode !== 0) {
34
+ await git(['remote', 'add', 'origin', 'https://github.com/mozilla-firefox/firefox'], dir);
35
+ }
36
+ }
37
+ // ── Large-tree staging helpers ──
38
+ const GIT_ADD_ENV = { GIT_INDEX_THREADS: '0' };
39
+ /**
40
+ * Returns true when the error looks like a process killed by the spawn timeout
41
+ * (SIGTERM → exit code 143).
42
+ */
43
+ function isTimeoutError(error) {
44
+ if (!(error instanceof GitError))
45
+ return false;
46
+ return /SIGTERM|timed out|exit code 143/i.test(error.message);
47
+ }
48
+ /**
49
+ * Removes `.git/index.lock` left behind by a killed git process.
50
+ */
51
+ async function cleanupIndexLock(dir) {
52
+ const lockPath = join(dir, '.git', 'index.lock');
53
+ if (await pathExists(lockPath)) {
54
+ await removeFile(lockPath);
55
+ verbose('Cleaned up stale .git/index.lock after timeout');
56
+ }
57
+ }
58
+ /**
59
+ * Stages every file by walking top-level directories one at a time.
60
+ * This avoids a single monolithic `git add -A` that may time out on
61
+ * very large (~300 K file) trees like Firefox.
62
+ */
63
+ async function stageAllFilesChunked(dir, options = {}) {
64
+ const entries = await readdir(dir, { withFileTypes: true });
65
+ const directories = entries
66
+ .filter((e) => e.isDirectory() && e.name !== '.git')
67
+ .map((e) => e.name)
68
+ .sort();
69
+ for (const dirName of directories) {
70
+ options.onProgress?.(`Staging directory: ${dirName}/...`);
71
+ await git(['add', '--', dirName], dir, {
72
+ timeout: GIT_ADD_CHUNK_TIMEOUT_MS,
73
+ env: GIT_ADD_ENV,
74
+ });
75
+ }
76
+ // Stage any top-level files
77
+ const topLevelFiles = entries.filter((e) => e.isFile()).map((e) => e.name);
78
+ if (topLevelFiles.length > 0) {
79
+ options.onProgress?.('Staging top-level files...');
80
+ await git(['add', '--', ...topLevelFiles], dir, {
81
+ timeout: GIT_ADD_CHUNK_TIMEOUT_MS,
82
+ env: GIT_ADD_ENV,
83
+ });
84
+ }
85
+ }
86
+ /**
87
+ * Stages all files in the repository.
88
+ * Tries a monolithic `git add -A` first; if that times out, falls back to
89
+ * directory-by-directory staged adds.
90
+ */
91
+ export async function stageAllFiles(dir, options = {}) {
92
+ const timeout = options.timeout ?? GIT_ADD_TIMEOUT_MS;
93
+ try {
94
+ await git(['add', '-A'], dir, { timeout, env: GIT_ADD_ENV });
95
+ return;
96
+ }
97
+ catch (error) {
98
+ if (!isTimeoutError(error)) {
99
+ throw await maybeWrapIndexLockError(dir, error);
100
+ }
101
+ options.onProgress?.('Monolithic git add timed out; falling back to chunked staging...');
102
+ }
103
+ // The killed process may have left an index lock
104
+ await cleanupIndexLock(dir);
105
+ await stageAllFilesChunked(dir, options);
106
+ }
107
+ /**
108
+ * Initializes a new git repository with an orphan branch.
109
+ * @param dir - Directory to initialize
110
+ * @param branchName - Name for the initial branch
111
+ */
112
+ export async function initRepository(dir, branchName = 'main', options = {}) {
113
+ await ensureGit();
114
+ const reportProgress = options.onProgress ?? (() => { });
115
+ // Initialize repository
116
+ reportProgress('Creating git repository...');
117
+ await git(['init'], dir);
118
+ // Create orphan branch
119
+ reportProgress(`Creating ${branchName} baseline branch...`);
120
+ await git(['checkout', '--orphan', branchName], dir);
121
+ // Configure git for the repository
122
+ reportProgress('Configuring git identity...');
123
+ await git(['config', 'user.email', 'fireforge@localhost'], dir);
124
+ await git(['config', 'user.name', 'FireForge'], dir);
125
+ // Enable performance settings for large trees
126
+ reportProgress('Configuring git performance settings...');
127
+ await configureGitPerformance(dir);
128
+ // Add a local-only origin remote so that Firefox's mach bootstrap and
129
+ // build scripts (which shell out to `git remote get-url origin`) don't
130
+ // fail. Nothing is ever fetched from or pushed to this remote.
131
+ reportProgress('Configuring origin remote for build compatibility...');
132
+ await git(['remote', 'add', 'origin', 'https://github.com/mozilla-firefox/firefox'], dir);
133
+ // Add all files
134
+ reportProgress('Indexing Firefox source with git add -A (this can take several minutes on large trees)...');
135
+ await assertNoGitIndexLock(dir);
136
+ try {
137
+ await stageAllFiles(dir, { onProgress: reportProgress });
138
+ }
139
+ catch (error) {
140
+ throw await maybeWrapIndexLockError(dir, error);
141
+ }
142
+ // Create initial commit
143
+ reportProgress('Creating initial Firefox source commit...');
144
+ try {
145
+ await git(['commit', '-m', 'Initial Firefox source'], dir);
146
+ }
147
+ catch (error) {
148
+ throw await maybeWrapIndexLockError(dir, error);
149
+ }
150
+ }
151
+ /**
152
+ * Resumes a partially initialized git repository (e.g. after a killed
153
+ * `git add -A` left an unborn HEAD). Re-applies performance settings,
154
+ * cleans up stale locks, stages all files, and creates the initial commit.
155
+ */
156
+ export async function resumeRepository(dir, options = {}) {
157
+ await ensureGit();
158
+ const reportProgress = options.onProgress ?? (() => { });
159
+ if (!(await isGitRepository(dir))) {
160
+ throw new GitError('Not a git repository', 'resume');
161
+ }
162
+ reportProgress('Resuming interrupted repository initialization...');
163
+ // Ensure performance settings are in place (may not have been set)
164
+ reportProgress('Configuring git performance settings...');
165
+ await configureGitPerformance(dir);
166
+ // Clean up any stale index lock left by the killed process
167
+ await cleanupIndexLock(dir);
168
+ // Ensure origin remote exists (may have been added before the interrupt)
169
+ await ensureOriginRemote(dir);
170
+ // Stage all files
171
+ reportProgress('Indexing Firefox source (resuming)...');
172
+ await assertNoGitIndexLock(dir);
173
+ try {
174
+ await stageAllFiles(dir, { onProgress: reportProgress });
175
+ }
176
+ catch (error) {
177
+ throw await maybeWrapIndexLockError(dir, error);
178
+ }
179
+ // Create initial commit
180
+ reportProgress('Creating initial Firefox source commit...');
181
+ try {
182
+ await git(['commit', '-m', 'Initial Firefox source'], dir);
183
+ }
184
+ catch (error) {
185
+ throw await maybeWrapIndexLockError(dir, error);
186
+ }
187
+ }
188
+ async function assertNoGitIndexLock(dir) {
189
+ const lockPath = join(dir, '.git', 'index.lock');
190
+ if (!(await pathExists(lockPath))) {
191
+ return;
192
+ }
193
+ throw new GitIndexLockError(lockPath, await getLockAgeMs(lockPath));
194
+ }
195
+ async function getLockAgeMs(lockPath) {
196
+ try {
197
+ const stats = await stat(lockPath);
198
+ return Math.max(0, Date.now() - stats.mtimeMs);
199
+ }
200
+ catch (error) {
201
+ void error;
202
+ return undefined;
203
+ }
204
+ }
205
+ async function maybeWrapIndexLockError(dir, error) {
206
+ const lockPath = join(dir, '.git', 'index.lock');
207
+ if (error instanceof GitError &&
208
+ /index\.lock/i.test(error.message) &&
209
+ /(unable to create|another git process seems to be running|file exists)/i.test(error.message)) {
210
+ return new GitIndexLockError(lockPath);
211
+ }
212
+ if (error instanceof GitError &&
213
+ /(unable to create|locked|lock file)/i.test(error.message) &&
214
+ (await pathExists(lockPath))) {
215
+ return new GitIndexLockError(lockPath, await getLockAgeMs(lockPath));
216
+ }
217
+ return toError(error);
218
+ }
219
+ /**
220
+ * Applies a patch file using git apply.
221
+ * @param patchPath - Path to the patch file
222
+ * @param repoDir - Repository directory
223
+ * @param options - Application options
224
+ */
225
+ export async function applyPatch(patchPath, repoDir, options = {}) {
226
+ await ensureGit();
227
+ if (!options.reject) {
228
+ const checkArgs = ['apply', '--check', '--', patchPath];
229
+ const result = await exec('git', checkArgs, { cwd: repoDir });
230
+ if (result.exitCode !== 0) {
231
+ throw new PatchApplyError(patchPath, new Error(result.stderr));
232
+ }
233
+ }
234
+ // Actually apply the patch
235
+ const applyArgs = ['apply'];
236
+ if (options.reject) {
237
+ applyArgs.push('--reject');
238
+ }
239
+ applyArgs.push('--', patchPath);
240
+ const applyResult = await exec('git', applyArgs, { cwd: repoDir });
241
+ if (applyResult.exitCode !== 0) {
242
+ throw new PatchApplyError(patchPath, new Error(applyResult.stderr));
243
+ }
244
+ }
245
+ /**
246
+ * Applies a patch idempotently using reverse-forward pattern.
247
+ * First tries to reverse the patch (in case it's already applied),
248
+ * then applies it forward.
249
+ * @param patchPath - Path to the patch file
250
+ * @param repoDir - Repository directory
251
+ * @param options - Application options
252
+ */
253
+ export async function applyPatchIdempotent(patchPath, repoDir, options = {}) {
254
+ await ensureGit();
255
+ // Try to reverse the patch (ignore errors if not applied)
256
+ const reverseResult = await exec('git', ['apply', '--reverse', '--', patchPath], {
257
+ cwd: repoDir,
258
+ });
259
+ // If reverse failed (patch wasn't applied), restore only the files the
260
+ // patch would have touched so that unrelated local edits are preserved.
261
+ if (reverseResult.exitCode !== 0) {
262
+ // Extract the set of files referenced in the patch
263
+ const listResult = await exec('git', ['apply', '--numstat', '--', patchPath], { cwd: repoDir });
264
+ const touchedFiles = listResult.stdout
265
+ .split('\n')
266
+ .map((line) => line.split('\t')[2])
267
+ .filter((f) => !!f);
268
+ if (touchedFiles.length > 0) {
269
+ // Restore only the files the patch touches
270
+ await exec('git', ['checkout', 'HEAD', '--', ...touchedFiles], { cwd: repoDir });
271
+ }
272
+ }
273
+ // Apply forward
274
+ await applyPatch(patchPath, repoDir, options);
275
+ }
276
+ /**
277
+ * Reverses a previously applied patch.
278
+ * @param patchPath - Path to the patch file
279
+ * @param repoDir - Repository directory
280
+ */
281
+ export async function reversePatch(patchPath, repoDir) {
282
+ await ensureGit();
283
+ const result = await exec('git', ['apply', '--reverse', '--', patchPath], { cwd: repoDir });
284
+ if (result.exitCode !== 0) {
285
+ throw new PatchApplyError(patchPath, new Error(result.stderr));
286
+ }
287
+ }
288
+ /**
289
+ * Checks if the repository has uncommitted changes.
290
+ * @param repoDir - Repository directory
291
+ * @returns True if there are uncommitted changes
292
+ */
293
+ export async function hasChanges(repoDir) {
294
+ await ensureGit();
295
+ const entries = await getWorkingTreeStatus(repoDir);
296
+ return entries.length > 0;
297
+ }
298
+ /**
299
+ * Checks whether an error indicates the repository has no HEAD (e.g. unborn branch).
300
+ * @param error - The error to check
301
+ * @returns True if the error is a missing-HEAD error
302
+ */
303
+ export function isMissingHeadError(error) {
304
+ return (error instanceof Error &&
305
+ /(ambiguous argument 'HEAD'|unknown revision or path not in the working tree)/i.test(error.message));
306
+ }
307
+ /**
308
+ * Gets the current HEAD commit hash.
309
+ * @param repoDir - Repository directory
310
+ * @returns Commit hash
311
+ */
312
+ export async function getHead(repoDir) {
313
+ await ensureGit();
314
+ const output = await git(['rev-parse', 'HEAD'], repoDir);
315
+ return output.trim();
316
+ }
317
+ /**
318
+ * Gets the current branch name.
319
+ * @param repoDir - Repository directory
320
+ * @returns Branch name
321
+ */
322
+ export async function getCurrentBranch(repoDir) {
323
+ await ensureGit();
324
+ const output = await git(['rev-parse', '--abbrev-ref', 'HEAD'], repoDir);
325
+ return output.trim();
326
+ }
327
+ /**
328
+ * Resets all changes in the repository.
329
+ * @param repoDir - Repository directory
330
+ */
331
+ export async function resetChanges(repoDir) {
332
+ await ensureGit();
333
+ try {
334
+ await git(['reset', '--hard', 'HEAD'], repoDir);
335
+ }
336
+ catch (error) {
337
+ throw await maybeWrapIndexLockError(repoDir, error);
338
+ }
339
+ await git(['clean', '-fd'], repoDir);
340
+ }
341
+ /**
342
+ * Creates a commit with all current changes.
343
+ * @param repoDir - Repository directory
344
+ * @param message - Commit message
345
+ */
346
+ export async function commit(repoDir, message) {
347
+ await ensureGit();
348
+ await stageAllFiles(repoDir);
349
+ await git(['commit', '-m', message], repoDir);
350
+ }
351
+ /**
352
+ * Gets the status of files with their status codes.
353
+ * @param repoDir - Repository directory
354
+ * @returns Array of [status, filepath] tuples
355
+ */
356
+ export async function getStatusWithCodes(repoDir) {
357
+ const entries = await getWorkingTreeStatus(repoDir);
358
+ return entries.map((entry) => ({
359
+ status: entry.status.trim(),
360
+ file: entry.file,
361
+ }));
362
+ }
363
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1,36 @@
1
+ import type { ProjectLicense } from '../types/config.js';
2
+ /**
3
+ * Comment style for license header formatting.
4
+ * - `js` — `// ...` line comments
5
+ * - `css` — block comments
6
+ * - `hash` — `# ...` line comments (FTL, shell, etc.)
7
+ */
8
+ export type CommentStyle = 'js' | 'css' | 'hash';
9
+ /** Default license when fireforge.json omits the license field. */
10
+ export declare const DEFAULT_LICENSE: ProjectLicense;
11
+ /**
12
+ * Returns a formatted license header comment for the given license and
13
+ * comment style.
14
+ *
15
+ * @param license - SPDX identifier of the project license
16
+ * @param style - Comment syntax to wrap the header in
17
+ * @returns Multi-line string ready to be placed at the top of a source file
18
+ */
19
+ export declare function getLicenseHeader(license: ProjectLicense, style: CommentStyle): string;
20
+ /**
21
+ * Returns true if `content` starts with any known license header for the
22
+ * given comment style.
23
+ *
24
+ * @param content - File content to check
25
+ * @param style - Comment syntax of the file
26
+ */
27
+ export declare function hasAnyLicenseHeader(content: string, style: CommentStyle): boolean;
28
+ /**
29
+ * Prepends the license header to a file on disk if it is not already present.
30
+ *
31
+ * @param filePath - Absolute path to the file
32
+ * @param license - SPDX identifier of the license to add
33
+ * @param style - Comment syntax matching the file type
34
+ * @returns true if the header was added, false if already present
35
+ */
36
+ export declare function addLicenseHeaderToFile(filePath: string, license: ProjectLicense, style: CommentStyle): Promise<boolean>;
@@ -0,0 +1,83 @@
1
+ import { readText, writeText } from '../utils/fs.js';
2
+ /** Default license when fireforge.json omits the license field. */
3
+ export const DEFAULT_LICENSE = 'MPL-2.0';
4
+ /**
5
+ * Raw (unwrapped) header lines per license.
6
+ *
7
+ * Each entry uses the community-recommended file notice for the license.
8
+ */
9
+ const HEADER_LINES = {
10
+ 'MPL-2.0': [
11
+ 'This Source Code Form is subject to the terms of the Mozilla Public',
12
+ 'License, v. 2.0. If a copy of the MPL was not distributed with this',
13
+ 'file, You can obtain one at http://mozilla.org/MPL/2.0/.',
14
+ ],
15
+ 'EUPL-1.2': ['SPDX-License-Identifier: EUPL-1.2'],
16
+ 'GPL-2.0-or-later': [
17
+ 'SPDX-License-Identifier: GPL-2.0-or-later',
18
+ 'This file is free software; you can redistribute it and/or modify it',
19
+ 'under the terms of the GNU General Public License as published by the',
20
+ 'Free Software Foundation; either version 2 of the License, or (at your',
21
+ 'option) any later version.',
22
+ ],
23
+ '0BSD': ['SPDX-License-Identifier: 0BSD'],
24
+ };
25
+ /**
26
+ * Returns a formatted license header comment for the given license and
27
+ * comment style.
28
+ *
29
+ * @param license - SPDX identifier of the project license
30
+ * @param style - Comment syntax to wrap the header in
31
+ * @returns Multi-line string ready to be placed at the top of a source file
32
+ */
33
+ export function getLicenseHeader(license, style) {
34
+ const lines = HEADER_LINES[license];
35
+ switch (style) {
36
+ case 'js':
37
+ if (lines.length === 1) {
38
+ return `/* ${lines[0]} */`;
39
+ }
40
+ return lines.map((l) => `// ${l}`).join('\n');
41
+ case 'css':
42
+ if (lines.length === 1) {
43
+ return `/* ${lines[0]} */`;
44
+ }
45
+ return (`/* ${lines[0]}\n` +
46
+ lines
47
+ .slice(1, -1)
48
+ .map((l) => ` * ${l}`)
49
+ .join('\n') +
50
+ (lines.length > 2 ? '\n' : '') +
51
+ ` * ${lines[lines.length - 1]} */`);
52
+ case 'hash':
53
+ return lines.map((l) => `# ${l}`).join('\n');
54
+ }
55
+ }
56
+ /**
57
+ * Returns true if `content` starts with any known license header for the
58
+ * given comment style.
59
+ *
60
+ * @param content - File content to check
61
+ * @param style - Comment syntax of the file
62
+ */
63
+ export function hasAnyLicenseHeader(content, style) {
64
+ const licenses = Object.keys(HEADER_LINES);
65
+ return licenses.some((license) => content.startsWith(getLicenseHeader(license, style)));
66
+ }
67
+ /**
68
+ * Prepends the license header to a file on disk if it is not already present.
69
+ *
70
+ * @param filePath - Absolute path to the file
71
+ * @param license - SPDX identifier of the license to add
72
+ * @param style - Comment syntax matching the file type
73
+ * @returns true if the header was added, false if already present
74
+ */
75
+ export async function addLicenseHeaderToFile(filePath, license, style) {
76
+ const content = await readText(filePath);
77
+ const header = getLicenseHeader(license, style);
78
+ if (content.startsWith(header))
79
+ return false;
80
+ await writeText(filePath, header + '\n' + content);
81
+ return true;
82
+ }
83
+ //# sourceMappingURL=license-headers.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Result of checking for build artifacts.
3
+ */
4
+ export interface BuildArtifactCheck {
5
+ /** Whether build artifacts exist */
6
+ exists: boolean;
7
+ /** Name of the obj-* directory if found */
8
+ objDir?: string;
9
+ /** Whether multiple valid obj-* directories were found */
10
+ ambiguous?: boolean;
11
+ /** All candidate obj-* directories with build artifacts */
12
+ objDirs?: string[];
13
+ /** Build metadata points at a different source or objdir */
14
+ metadataMismatch?: {
15
+ objDir: string;
16
+ topsrcdir?: string;
17
+ topobjdir?: string;
18
+ mozconfig?: string;
19
+ };
20
+ }
21
+ /**
22
+ * Checks if build artifacts exist in the engine directory.
23
+ * Looks for obj-* directories with a dist subdirectory.
24
+ * @param engineDir - Path to the engine directory
25
+ * @returns Build artifact check result
26
+ */
27
+ export declare function hasBuildArtifacts(engineDir: string): Promise<BuildArtifactCheck>;
28
+ /** Builds a user-facing explanation when detected build artifacts belong to another workspace. */
29
+ export declare function buildArtifactMismatchMessage(engineDir: string, buildCheck: BuildArtifactCheck, commandName: string): string | undefined;