@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.
- package/CHANGELOG.md +7 -0
- package/LICENSE.md +294 -0
- package/README.md +435 -0
- package/dist/bin/fireforge.d.ts +10 -0
- package/dist/bin/fireforge.js +29 -0
- package/dist/src/cli.d.ts +33 -0
- package/dist/src/cli.js +180 -0
- package/dist/src/commands/bootstrap.d.ts +9 -0
- package/dist/src/commands/bootstrap.js +73 -0
- package/dist/src/commands/build.d.ts +11 -0
- package/dist/src/commands/build.js +102 -0
- package/dist/src/commands/config.d.ts +13 -0
- package/dist/src/commands/config.js +135 -0
- package/dist/src/commands/discard.d.ts +12 -0
- package/dist/src/commands/discard.js +84 -0
- package/dist/src/commands/doctor.d.ts +18 -0
- package/dist/src/commands/doctor.js +356 -0
- package/dist/src/commands/download.d.ts +11 -0
- package/dist/src/commands/download.js +127 -0
- package/dist/src/commands/export-all.d.ts +11 -0
- package/dist/src/commands/export-all.js +122 -0
- package/dist/src/commands/export-shared.d.ts +48 -0
- package/dist/src/commands/export-shared.js +208 -0
- package/dist/src/commands/export.d.ts +13 -0
- package/dist/src/commands/export.js +178 -0
- package/dist/src/commands/furnace/apply.d.ts +7 -0
- package/dist/src/commands/furnace/apply.js +80 -0
- package/dist/src/commands/furnace/create.d.ts +8 -0
- package/dist/src/commands/furnace/create.js +377 -0
- package/dist/src/commands/furnace/deploy.d.ts +8 -0
- package/dist/src/commands/furnace/deploy.js +338 -0
- package/dist/src/commands/furnace/diff.d.ts +7 -0
- package/dist/src/commands/furnace/diff.js +119 -0
- package/dist/src/commands/furnace/index.d.ts +16 -0
- package/dist/src/commands/furnace/index.js +121 -0
- package/dist/src/commands/furnace/list.d.ts +5 -0
- package/dist/src/commands/furnace/list.js +65 -0
- package/dist/src/commands/furnace/override.d.ts +8 -0
- package/dist/src/commands/furnace/override.js +188 -0
- package/dist/src/commands/furnace/preview.d.ts +7 -0
- package/dist/src/commands/furnace/preview.js +96 -0
- package/dist/src/commands/furnace/remove.d.ts +8 -0
- package/dist/src/commands/furnace/remove.js +159 -0
- package/dist/src/commands/furnace/scan.d.ts +5 -0
- package/dist/src/commands/furnace/scan.js +112 -0
- package/dist/src/commands/furnace/status.d.ts +7 -0
- package/dist/src/commands/furnace/status.js +137 -0
- package/dist/src/commands/furnace/validate.d.ts +6 -0
- package/dist/src/commands/furnace/validate.js +91 -0
- package/dist/src/commands/furnace/validation-output.d.ts +7 -0
- package/dist/src/commands/furnace/validation-output.js +22 -0
- package/dist/src/commands/import.d.ts +11 -0
- package/dist/src/commands/import.js +241 -0
- package/dist/src/commands/lint.d.ts +10 -0
- package/dist/src/commands/lint.js +118 -0
- package/dist/src/commands/package.d.ts +11 -0
- package/dist/src/commands/package.js +80 -0
- package/dist/src/commands/re-export.d.ts +12 -0
- package/dist/src/commands/re-export.js +242 -0
- package/dist/src/commands/rebase/abort.d.ts +7 -0
- package/dist/src/commands/rebase/abort.js +49 -0
- package/dist/src/commands/rebase/confirm.d.ts +18 -0
- package/dist/src/commands/rebase/confirm.js +33 -0
- package/dist/src/commands/rebase/continue.d.ts +7 -0
- package/dist/src/commands/rebase/continue.js +81 -0
- package/dist/src/commands/rebase/index.d.ts +22 -0
- package/dist/src/commands/rebase/index.js +127 -0
- package/dist/src/commands/rebase/patch-loop.d.ts +9 -0
- package/dist/src/commands/rebase/patch-loop.js +135 -0
- package/dist/src/commands/rebase/summary.d.ts +12 -0
- package/dist/src/commands/rebase/summary.js +43 -0
- package/dist/src/commands/rebase.d.ts +4 -0
- package/dist/src/commands/rebase.js +6 -0
- package/dist/src/commands/register.d.ts +13 -0
- package/dist/src/commands/register.js +67 -0
- package/dist/src/commands/reset.d.ts +11 -0
- package/dist/src/commands/reset.js +83 -0
- package/dist/src/commands/resolve.d.ts +9 -0
- package/dist/src/commands/resolve.js +124 -0
- package/dist/src/commands/run.d.ts +9 -0
- package/dist/src/commands/run.js +91 -0
- package/dist/src/commands/setup-support.d.ts +23 -0
- package/dist/src/commands/setup-support.js +310 -0
- package/dist/src/commands/setup.d.ts +11 -0
- package/dist/src/commands/setup.js +94 -0
- package/dist/src/commands/status.d.ts +11 -0
- package/dist/src/commands/status.js +268 -0
- package/dist/src/commands/test.d.ts +12 -0
- package/dist/src/commands/test.js +182 -0
- package/dist/src/commands/token-coverage.d.ts +5 -0
- package/dist/src/commands/token-coverage.js +57 -0
- package/dist/src/commands/token.d.ts +14 -0
- package/dist/src/commands/token.js +121 -0
- package/dist/src/commands/watch.d.ts +9 -0
- package/dist/src/commands/watch.js +112 -0
- package/dist/src/commands/wire.d.ts +13 -0
- package/dist/src/commands/wire.js +149 -0
- package/dist/src/core/ast-utils.d.ts +47 -0
- package/dist/src/core/ast-utils.js +57 -0
- package/dist/src/core/brand-validation.d.ts +7 -0
- package/dist/src/core/brand-validation.js +15 -0
- package/dist/src/core/branding.d.ts +49 -0
- package/dist/src/core/branding.js +229 -0
- package/dist/src/core/browser-wire.d.ts +40 -0
- package/dist/src/core/browser-wire.js +66 -0
- package/dist/src/core/build-prepare.d.ts +25 -0
- package/dist/src/core/build-prepare.js +93 -0
- package/dist/src/core/config-mutate.d.ts +15 -0
- package/dist/src/core/config-mutate.js +51 -0
- package/dist/src/core/config-paths.d.ts +28 -0
- package/dist/src/core/config-paths.js +65 -0
- package/dist/src/core/config-state.d.ts +28 -0
- package/dist/src/core/config-state.js +152 -0
- package/dist/src/core/config-validate.d.ts +11 -0
- package/dist/src/core/config-validate.js +141 -0
- package/dist/src/core/config.d.ts +39 -0
- package/dist/src/core/config.js +70 -0
- package/dist/src/core/file-lock.d.ts +11 -0
- package/dist/src/core/file-lock.js +80 -0
- package/dist/src/core/firefox-archive.d.ts +40 -0
- package/dist/src/core/firefox-archive.js +63 -0
- package/dist/src/core/firefox-cache.d.ts +23 -0
- package/dist/src/core/firefox-cache.js +134 -0
- package/dist/src/core/firefox-download.d.ts +21 -0
- package/dist/src/core/firefox-download.js +129 -0
- package/dist/src/core/firefox-extract.d.ts +21 -0
- package/dist/src/core/firefox-extract.js +53 -0
- package/dist/src/core/firefox.d.ts +34 -0
- package/dist/src/core/firefox.js +78 -0
- package/dist/src/core/furnace-apply-helpers.d.ts +21 -0
- package/dist/src/core/furnace-apply-helpers.js +244 -0
- package/dist/src/core/furnace-apply.d.ts +16 -0
- package/dist/src/core/furnace-apply.js +147 -0
- package/dist/src/core/furnace-config.d.ts +94 -0
- package/dist/src/core/furnace-config.js +372 -0
- package/dist/src/core/furnace-constants.d.ts +4 -0
- package/dist/src/core/furnace-constants.js +6 -0
- package/dist/src/core/furnace-registration-ast.d.ts +24 -0
- package/dist/src/core/furnace-registration-ast.js +218 -0
- package/dist/src/core/furnace-registration-remove.d.ts +14 -0
- package/dist/src/core/furnace-registration-remove.js +89 -0
- package/dist/src/core/furnace-registration-validate.d.ts +20 -0
- package/dist/src/core/furnace-registration-validate.js +40 -0
- package/dist/src/core/furnace-registration.d.ts +29 -0
- package/dist/src/core/furnace-registration.js +96 -0
- package/dist/src/core/furnace-rollback.d.ts +20 -0
- package/dist/src/core/furnace-rollback.js +66 -0
- package/dist/src/core/furnace-scanner.d.ts +40 -0
- package/dist/src/core/furnace-scanner.js +143 -0
- package/dist/src/core/furnace-stories.d.ts +37 -0
- package/dist/src/core/furnace-stories.js +185 -0
- package/dist/src/core/furnace-validate-accessibility.d.ts +6 -0
- package/dist/src/core/furnace-validate-accessibility.js +32 -0
- package/dist/src/core/furnace-validate-checks.d.ts +4 -0
- package/dist/src/core/furnace-validate-checks.js +7 -0
- package/dist/src/core/furnace-validate-compatibility.d.ts +6 -0
- package/dist/src/core/furnace-validate-compatibility.js +57 -0
- package/dist/src/core/furnace-validate-helpers.d.ts +28 -0
- package/dist/src/core/furnace-validate-helpers.js +129 -0
- package/dist/src/core/furnace-validate-registration.d.ts +37 -0
- package/dist/src/core/furnace-validate-registration.js +220 -0
- package/dist/src/core/furnace-validate-structure.d.ts +6 -0
- package/dist/src/core/furnace-validate-structure.js +66 -0
- package/dist/src/core/furnace-validate.d.ts +16 -0
- package/dist/src/core/furnace-validate.js +103 -0
- package/dist/src/core/git-base.d.ts +47 -0
- package/dist/src/core/git-base.js +50 -0
- package/dist/src/core/git-diff.d.ts +63 -0
- package/dist/src/core/git-diff.js +246 -0
- package/dist/src/core/git-file-ops.d.ts +65 -0
- package/dist/src/core/git-file-ops.js +141 -0
- package/dist/src/core/git-status.d.ts +65 -0
- package/dist/src/core/git-status.js +163 -0
- package/dist/src/core/git.d.ts +113 -0
- package/dist/src/core/git.js +363 -0
- package/dist/src/core/license-headers.d.ts +36 -0
- package/dist/src/core/license-headers.js +83 -0
- package/dist/src/core/mach-build-artifacts.d.ts +29 -0
- package/dist/src/core/mach-build-artifacts.js +117 -0
- package/dist/src/core/mach-mozconfig.d.ts +17 -0
- package/dist/src/core/mach-mozconfig.js +50 -0
- package/dist/src/core/mach-python.d.ts +16 -0
- package/dist/src/core/mach-python.js +126 -0
- package/dist/src/core/mach.d.ts +106 -0
- package/dist/src/core/mach.js +166 -0
- package/dist/src/core/manifest-helpers.d.ts +25 -0
- package/dist/src/core/manifest-helpers.js +96 -0
- package/dist/src/core/manifest-register.d.ts +30 -0
- package/dist/src/core/manifest-register.js +65 -0
- package/dist/src/core/manifest-rules.d.ts +39 -0
- package/dist/src/core/manifest-rules.js +151 -0
- package/dist/src/core/manifest-tokenizers.d.ts +34 -0
- package/dist/src/core/manifest-tokenizers.js +84 -0
- package/dist/src/core/parser-fallback.d.ts +36 -0
- package/dist/src/core/parser-fallback.js +43 -0
- package/dist/src/core/patch-apply-fuzz.d.ts +29 -0
- package/dist/src/core/patch-apply-fuzz.js +70 -0
- package/dist/src/core/patch-apply.d.ts +46 -0
- package/dist/src/core/patch-apply.js +235 -0
- package/dist/src/core/patch-export.d.ts +99 -0
- package/dist/src/core/patch-export.js +314 -0
- package/dist/src/core/patch-files.d.ts +11 -0
- package/dist/src/core/patch-files.js +51 -0
- package/dist/src/core/patch-lint.d.ts +72 -0
- package/dist/src/core/patch-lint.js +403 -0
- package/dist/src/core/patch-lock.d.ts +8 -0
- package/dist/src/core/patch-lock.js +29 -0
- package/dist/src/core/patch-manifest-consistency.d.ts +24 -0
- package/dist/src/core/patch-manifest-consistency.js +135 -0
- package/dist/src/core/patch-manifest-io.d.ts +36 -0
- package/dist/src/core/patch-manifest-io.js +77 -0
- package/dist/src/core/patch-manifest-query.d.ts +48 -0
- package/dist/src/core/patch-manifest-query.js +124 -0
- package/dist/src/core/patch-manifest-validate.d.ts +22 -0
- package/dist/src/core/patch-manifest-validate.js +72 -0
- package/dist/src/core/patch-manifest.d.ts +11 -0
- package/dist/src/core/patch-manifest.js +12 -0
- package/dist/src/core/patch-parse.d.ts +43 -0
- package/dist/src/core/patch-parse.js +143 -0
- package/dist/src/core/patch-transform.d.ts +21 -0
- package/dist/src/core/patch-transform.js +138 -0
- package/dist/src/core/rebase-session.d.ts +47 -0
- package/dist/src/core/rebase-session.js +65 -0
- package/dist/src/core/register-browser-content.d.ts +11 -0
- package/dist/src/core/register-browser-content.js +116 -0
- package/dist/src/core/register-module.d.ts +11 -0
- package/dist/src/core/register-module.js +76 -0
- package/dist/src/core/register-shared-css.d.ts +11 -0
- package/dist/src/core/register-shared-css.js +117 -0
- package/dist/src/core/register-test-manifest.d.ts +18 -0
- package/dist/src/core/register-test-manifest.js +99 -0
- package/dist/src/core/state-file.d.ts +4 -0
- package/dist/src/core/state-file.js +25 -0
- package/dist/src/core/token-coverage.d.ts +12 -0
- package/dist/src/core/token-coverage.js +74 -0
- package/dist/src/core/token-manager.d.ts +55 -0
- package/dist/src/core/token-manager.js +387 -0
- package/dist/src/core/wire-destroy.d.ts +21 -0
- package/dist/src/core/wire-destroy.js +103 -0
- package/dist/src/core/wire-dom-fragment.d.ts +23 -0
- package/dist/src/core/wire-dom-fragment.js +129 -0
- package/dist/src/core/wire-init.d.ts +23 -0
- package/dist/src/core/wire-init.js +201 -0
- package/dist/src/core/wire-subscript.d.ts +20 -0
- package/dist/src/core/wire-subscript.js +134 -0
- package/dist/src/core/wire-targets.d.ts +7 -0
- package/dist/src/core/wire-targets.js +9 -0
- package/dist/src/core/wire-utils.d.ts +88 -0
- package/dist/src/core/wire-utils.js +279 -0
- package/dist/src/errors/base.d.ts +60 -0
- package/dist/src/errors/base.js +87 -0
- package/dist/src/errors/build.d.ts +52 -0
- package/dist/src/errors/build.js +114 -0
- package/dist/src/errors/codes.d.ts +29 -0
- package/dist/src/errors/codes.js +30 -0
- package/dist/src/errors/config.d.ts +31 -0
- package/dist/src/errors/config.js +61 -0
- package/dist/src/errors/download.d.ts +42 -0
- package/dist/src/errors/download.js +95 -0
- package/dist/src/errors/furnace.d.ts +10 -0
- package/dist/src/errors/furnace.js +22 -0
- package/dist/src/errors/git.d.ts +41 -0
- package/dist/src/errors/git.js +99 -0
- package/dist/src/errors/patch.d.ts +10 -0
- package/dist/src/errors/patch.js +26 -0
- package/dist/src/errors/rebase.d.ts +20 -0
- package/dist/src/errors/rebase.js +30 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.js +21 -0
- package/dist/src/types/cli.d.ts +14 -0
- package/dist/src/types/cli.js +2 -0
- package/dist/src/types/commands/index.d.ts +6 -0
- package/dist/src/types/commands/index.js +6 -0
- package/dist/src/types/commands/options.d.ts +239 -0
- package/dist/src/types/commands/options.js +6 -0
- package/dist/src/types/commands/patches.d.ts +89 -0
- package/dist/src/types/commands/patches.js +6 -0
- package/dist/src/types/commands/project.d.ts +71 -0
- package/dist/src/types/commands/project.js +6 -0
- package/dist/src/types/config.d.ts +101 -0
- package/dist/src/types/config.js +2 -0
- package/dist/src/types/furnace.d.ts +158 -0
- package/dist/src/types/furnace.js +2 -0
- package/dist/src/types/index.d.ts +6 -0
- package/dist/src/types/index.js +6 -0
- package/dist/src/utils/errors.d.ts +2 -0
- package/dist/src/utils/errors.js +15 -0
- package/dist/src/utils/fs.d.ts +72 -0
- package/dist/src/utils/fs.js +179 -0
- package/dist/src/utils/logger.d.ts +58 -0
- package/dist/src/utils/logger.js +120 -0
- package/dist/src/utils/options.d.ts +8 -0
- package/dist/src/utils/options.js +16 -0
- package/dist/src/utils/package-root.d.ts +10 -0
- package/dist/src/utils/package-root.js +53 -0
- package/dist/src/utils/parse.d.ts +110 -0
- package/dist/src/utils/parse.js +200 -0
- package/dist/src/utils/paths.d.ts +10 -0
- package/dist/src/utils/paths.js +43 -0
- package/dist/src/utils/platform.d.ts +38 -0
- package/dist/src/utils/platform.js +56 -0
- package/dist/src/utils/process.d.ts +80 -0
- package/dist/src/utils/process.js +188 -0
- package/dist/src/utils/regex.d.ts +24 -0
- package/dist/src/utils/regex.js +40 -0
- package/dist/src/utils/validation.d.ts +133 -0
- package/dist/src/utils/validation.js +250 -0
- package/package.json +106 -0
- package/templates/configs/common.mozconfig +24 -0
- package/templates/configs/darwin.mozconfig +10 -0
- package/templates/configs/linux.mozconfig +12 -0
- package/templates/configs/win32.mozconfig +14 -0
- package/templates/licenses/0BSD.md +14 -0
- package/templates/licenses/EUPL-1.2.md +294 -0
- package/templates/licenses/GPL-2.0-or-later.md +339 -0
- package/templates/licenses/MPL-2.0.md +383 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Fuzzy patch application with escalating fuzz factors.
|
|
4
|
+
*
|
|
5
|
+
* Tries an exact `git apply` first, then retries with increasing `--fuzz=N`
|
|
6
|
+
* values. If all fuzz levels fail, falls through to `git apply --reject`
|
|
7
|
+
* so the user gets `.rej` files for manual resolution.
|
|
8
|
+
*/
|
|
9
|
+
import { verbose } from '../utils/logger.js';
|
|
10
|
+
import { exec } from '../utils/process.js';
|
|
11
|
+
import { ensureGit } from './git-base.js';
|
|
12
|
+
// ── Implementation ──
|
|
13
|
+
/**
|
|
14
|
+
* Attempts to apply a patch with escalating fuzz factors.
|
|
15
|
+
*
|
|
16
|
+
* 1. `git apply --check` at fuzz 0 … maxFuzz
|
|
17
|
+
* 2. `git apply --fuzz=N` at the first passing level
|
|
18
|
+
* 3. Fall through to `git apply --reject` if nothing succeeds
|
|
19
|
+
*
|
|
20
|
+
* @param patchPath - Absolute path to the `.patch` file
|
|
21
|
+
* @param engineDir - Working directory (engine/)
|
|
22
|
+
* @param maxFuzz - Maximum fuzz factor to try (default 3)
|
|
23
|
+
*/
|
|
24
|
+
export async function applyPatchWithFuzz(patchPath, engineDir, maxFuzz = 3) {
|
|
25
|
+
await ensureGit();
|
|
26
|
+
// Try exact match first, then escalate
|
|
27
|
+
for (let fuzz = 0; fuzz <= maxFuzz; fuzz++) {
|
|
28
|
+
const fuzzArgs = fuzz > 0 ? [`--fuzz=${fuzz}`] : [];
|
|
29
|
+
const check = await exec('git', ['apply', '--check', ...fuzzArgs, '--', patchPath], {
|
|
30
|
+
cwd: engineDir,
|
|
31
|
+
});
|
|
32
|
+
if (check.exitCode === 0) {
|
|
33
|
+
// --check passed: apply for real
|
|
34
|
+
const apply = await exec('git', ['apply', ...fuzzArgs, '--', patchPath], {
|
|
35
|
+
cwd: engineDir,
|
|
36
|
+
});
|
|
37
|
+
if (apply.exitCode === 0) {
|
|
38
|
+
if (fuzz > 0) {
|
|
39
|
+
verbose(`Patch applied with fuzz=${fuzz}: ${patchPath}`);
|
|
40
|
+
}
|
|
41
|
+
return { success: true, fuzzFactor: fuzz };
|
|
42
|
+
}
|
|
43
|
+
// Unlikely: --check passed but apply failed; fall through to next fuzz
|
|
44
|
+
verbose(`git apply fuzz=${fuzz} --check passed but apply failed: ${apply.stderr.trim()}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// All fuzz levels failed → generate .rej files for manual resolution
|
|
48
|
+
const rejectResult = await exec('git', ['apply', '--reject', '--', patchPath], {
|
|
49
|
+
cwd: engineDir,
|
|
50
|
+
});
|
|
51
|
+
const errorMessage = rejectResult.stderr.trim() || 'All fuzz levels failed';
|
|
52
|
+
// Extract .rej file paths from stderr
|
|
53
|
+
const rejectFiles = [];
|
|
54
|
+
for (const line of rejectResult.stderr.split('\n')) {
|
|
55
|
+
const match = line.match(/Applying patch .* with (\d+) reject/);
|
|
56
|
+
if (match)
|
|
57
|
+
continue;
|
|
58
|
+
const rejMatch = line.match(/Rejected hunk.*to (.+\.rej)/);
|
|
59
|
+
if (rejMatch?.[1]) {
|
|
60
|
+
rejectFiles.push(rejMatch[1]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
fuzzFactor: maxFuzz,
|
|
66
|
+
error: errorMessage,
|
|
67
|
+
rejectFiles,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=patch-apply-fuzz.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch orchestration — coordinates patch discovery, application, and validation.
|
|
3
|
+
* Pure parsing, content transformation, and lock management are in separate modules.
|
|
4
|
+
*/
|
|
5
|
+
import type { ImportSummary, PatchResult } from '../types/commands/index.js';
|
|
6
|
+
export { PatchError } from '../errors/patch.js';
|
|
7
|
+
export { countPatches, discoverPatches, getAllTargetFilesFromPatch, getTargetFileFromPatch, isNewFilePatch, } from './patch-files.js';
|
|
8
|
+
export { withPatchDirectoryLock } from './patch-lock.js';
|
|
9
|
+
export { extractAffectedFiles, extractOrder, isNewFileInPatch, parseHunksForFile, } from './patch-parse.js';
|
|
10
|
+
export { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
|
|
11
|
+
/**
|
|
12
|
+
* Applies all patches in order. Rolls back all successfully applied
|
|
13
|
+
* patches when one fails so the engine directory stays clean.
|
|
14
|
+
* @param patchesDir - Path to the patches directory
|
|
15
|
+
* @param engineDir - Path to the engine directory
|
|
16
|
+
* @returns Results for each patch
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyPatches(patchesDir: string, engineDir: string): Promise<PatchResult[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Validates that all patches can be applied.
|
|
21
|
+
* @param patchesDir - Path to the patches directory
|
|
22
|
+
* @param engineDir - Path to the engine directory
|
|
23
|
+
* @returns Validation results
|
|
24
|
+
*/
|
|
25
|
+
export declare function validatePatches(patchesDir: string, engineDir: string): Promise<{
|
|
26
|
+
valid: boolean;
|
|
27
|
+
errors: string[];
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Enhanced patch application with continue mode.
|
|
31
|
+
* When continueOnFailure is false, rolls back all previously applied patches
|
|
32
|
+
* on the first failure to keep the engine directory in a clean state.
|
|
33
|
+
* @param patchesDir - Path to the patches directory
|
|
34
|
+
* @param engineDir - Path to the engine directory
|
|
35
|
+
* @param continueOnFailure - Whether to continue after failures
|
|
36
|
+
* @returns Import summary with all results
|
|
37
|
+
*/
|
|
38
|
+
export declare function applyPatchesWithContinue(patchesDir: string, engineDir: string, continueOnFailure?: boolean): Promise<ImportSummary>;
|
|
39
|
+
/**
|
|
40
|
+
* Computes the cumulative patched content for a file.
|
|
41
|
+
* @param patchesDir - Path to the patches directory
|
|
42
|
+
* @param engineDir - Path to the engine directory
|
|
43
|
+
* @param filePath - File path to compute content for
|
|
44
|
+
* @returns Content after all patches applied, or null if file doesn't exist
|
|
45
|
+
*/
|
|
46
|
+
export declare function computePatchedContent(patchesDir: string, engineDir: string, filePath: string): Promise<string | null>;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
+
/**
|
|
3
|
+
* Patch orchestration — coordinates patch discovery, application, and validation.
|
|
4
|
+
* Pure parsing, content transformation, and lock management are in separate modules.
|
|
5
|
+
*/
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { PatchError } from '../errors/patch.js';
|
|
8
|
+
import { toError } from '../utils/errors.js';
|
|
9
|
+
import { pathExists, readText, writeText } from '../utils/fs.js';
|
|
10
|
+
import { verbose } from '../utils/logger.js';
|
|
11
|
+
import { isContainedRelativePath } from '../utils/paths.js';
|
|
12
|
+
import { exec } from '../utils/process.js';
|
|
13
|
+
import { applyPatchIdempotent, reversePatch } from './git.js';
|
|
14
|
+
import { getFileContentFromHead } from './git-file-ops.js';
|
|
15
|
+
import { discoverPatches } from './patch-files.js';
|
|
16
|
+
import { findPatchesAffectingFile } from './patch-manifest.js';
|
|
17
|
+
import { extractAffectedFiles, extractConflictingFiles, isNewFileInPatch } from './patch-parse.js';
|
|
18
|
+
import { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
|
|
19
|
+
// Re-export from split modules so existing import sites continue working
|
|
20
|
+
export { PatchError } from '../errors/patch.js';
|
|
21
|
+
export { countPatches, discoverPatches, getAllTargetFilesFromPatch, getTargetFileFromPatch, isNewFilePatch, } from './patch-files.js';
|
|
22
|
+
export { withPatchDirectoryLock } from './patch-lock.js';
|
|
23
|
+
export { extractAffectedFiles, extractOrder, isNewFileInPatch, parseHunksForFile, } from './patch-parse.js';
|
|
24
|
+
export { applyPatchToContent, extractNewFileContent } from './patch-transform.js';
|
|
25
|
+
/**
|
|
26
|
+
* Applies a single patch.
|
|
27
|
+
* @param patch - Patch info
|
|
28
|
+
* @param engineDir - Path to the engine directory
|
|
29
|
+
* @returns Patch result
|
|
30
|
+
*/
|
|
31
|
+
async function applySinglePatch(patch, engineDir) {
|
|
32
|
+
let patchContent = '';
|
|
33
|
+
let affectedFiles = [];
|
|
34
|
+
try {
|
|
35
|
+
patchContent = await readText(patch.path);
|
|
36
|
+
affectedFiles = extractAffectedFiles(patchContent);
|
|
37
|
+
validatePatchTargets(patch, affectedFiles);
|
|
38
|
+
await applyPatchIdempotent(patch.path, engineDir);
|
|
39
|
+
return { patch, success: true };
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error instanceof PatchError) {
|
|
43
|
+
return { patch, success: false, error: error.message };
|
|
44
|
+
}
|
|
45
|
+
const applyError = toError(error);
|
|
46
|
+
// Check if this is a resolvable "new file" conflict
|
|
47
|
+
let resolvedNewFiles = false;
|
|
48
|
+
// Save original content for files we might overwrite, so we can restore on failure
|
|
49
|
+
const savedContents = new Map();
|
|
50
|
+
for (const file of affectedFiles) {
|
|
51
|
+
if (isNewFileInPatch(patchContent, file)) {
|
|
52
|
+
const targetPath = join(engineDir, file);
|
|
53
|
+
if (await pathExists(targetPath)) {
|
|
54
|
+
savedContents.set(file, await readText(targetPath));
|
|
55
|
+
const content = await extractNewFileContent(patch.path, file);
|
|
56
|
+
await writeText(targetPath, content);
|
|
57
|
+
resolvedNewFiles = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (resolvedNewFiles) {
|
|
62
|
+
try {
|
|
63
|
+
await applyPatchIdempotent(patch.path, engineDir);
|
|
64
|
+
return { patch, success: true, autoResolved: true };
|
|
65
|
+
}
|
|
66
|
+
catch (retryError) {
|
|
67
|
+
verbose(`Auto-resolved new-file retry failed for ${patch.filename}: ${toError(retryError).message}`);
|
|
68
|
+
// Restore original file content before falling through to --reject
|
|
69
|
+
for (const [file, originalContent] of savedContents) {
|
|
70
|
+
await writeText(join(engineDir, file), originalContent);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// If it's not a simple new-file conflict, try with --reject to help manual resolution
|
|
75
|
+
let errorMessage = applyError.message;
|
|
76
|
+
try {
|
|
77
|
+
// Use --reject to apply what we can and create .rej files for what we can't
|
|
78
|
+
await applyPatchIdempotent(patch.path, engineDir, { reject: true });
|
|
79
|
+
// If this somehow succeeds with --reject but failed without, it still shouldn't
|
|
80
|
+
// happen because applyPatch first runs --check which would fail.
|
|
81
|
+
// But if it did succeed, we should still return failure because manual fix is needed
|
|
82
|
+
// for the rejected hunks.
|
|
83
|
+
}
|
|
84
|
+
catch (rejectError) {
|
|
85
|
+
// This is expected to fail, but now we have .rej files
|
|
86
|
+
errorMessage = toError(rejectError).message;
|
|
87
|
+
}
|
|
88
|
+
return { patch, success: false, error: errorMessage };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Reverses previously applied patches in reverse order.
|
|
93
|
+
* Best-effort: logs warnings for individual failures but does not throw.
|
|
94
|
+
*/
|
|
95
|
+
async function rollbackPatches(results, engineDir) {
|
|
96
|
+
for (let i = results.length - 1; i >= 0; i--) {
|
|
97
|
+
const result = results[i];
|
|
98
|
+
if (!result?.success)
|
|
99
|
+
continue;
|
|
100
|
+
try {
|
|
101
|
+
await reversePatch(result.patch.path, engineDir);
|
|
102
|
+
verbose(`Rolled back ${result.patch.filename}`);
|
|
103
|
+
}
|
|
104
|
+
catch (rollbackError) {
|
|
105
|
+
verbose(`Failed to roll back ${result.patch.filename}: ${toError(rollbackError).message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Applies all patches in order. Rolls back all successfully applied
|
|
111
|
+
* patches when one fails so the engine directory stays clean.
|
|
112
|
+
* @param patchesDir - Path to the patches directory
|
|
113
|
+
* @param engineDir - Path to the engine directory
|
|
114
|
+
* @returns Results for each patch
|
|
115
|
+
*/
|
|
116
|
+
export async function applyPatches(patchesDir, engineDir) {
|
|
117
|
+
const patches = await discoverPatches(patchesDir);
|
|
118
|
+
const results = [];
|
|
119
|
+
for (const patch of patches) {
|
|
120
|
+
const result = await applySinglePatch(patch, engineDir);
|
|
121
|
+
results.push(result);
|
|
122
|
+
// Stop on first failure and roll back all previously applied patches
|
|
123
|
+
if (!result.success) {
|
|
124
|
+
const succeeded = results.filter((r) => r.success);
|
|
125
|
+
if (succeeded.length > 0) {
|
|
126
|
+
verbose(`Rolling back ${succeeded.length} previously applied patch(es)…`);
|
|
127
|
+
await rollbackPatches(succeeded, engineDir);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Validates that all patches can be applied.
|
|
136
|
+
* @param patchesDir - Path to the patches directory
|
|
137
|
+
* @param engineDir - Path to the engine directory
|
|
138
|
+
* @returns Validation results
|
|
139
|
+
*/
|
|
140
|
+
export async function validatePatches(patchesDir, engineDir) {
|
|
141
|
+
const patches = await discoverPatches(patchesDir);
|
|
142
|
+
const errors = [];
|
|
143
|
+
for (const patch of patches) {
|
|
144
|
+
try {
|
|
145
|
+
const patchContent = await readText(patch.path);
|
|
146
|
+
validatePatchTargets(patch, extractAffectedFiles(patchContent));
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
errors.push(`${patch.filename}: ${toError(error).message}`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const result = await exec('git', ['apply', '--check', '--', patch.path], { cwd: engineDir });
|
|
153
|
+
if (result.exitCode !== 0) {
|
|
154
|
+
const message = result.stderr.trim() || 'git apply --check failed';
|
|
155
|
+
errors.push(`${patch.filename}: ${message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { valid: errors.length === 0, errors };
|
|
159
|
+
}
|
|
160
|
+
function validatePatchTargets(patch, affectedFiles) {
|
|
161
|
+
for (const file of affectedFiles) {
|
|
162
|
+
if (!isContainedRelativePath(file)) {
|
|
163
|
+
throw new PatchError(`Patch targets a path outside engine/: ${file}`, patch.filename);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Enhanced patch application with continue mode.
|
|
169
|
+
* When continueOnFailure is false, rolls back all previously applied patches
|
|
170
|
+
* on the first failure to keep the engine directory in a clean state.
|
|
171
|
+
* @param patchesDir - Path to the patches directory
|
|
172
|
+
* @param engineDir - Path to the engine directory
|
|
173
|
+
* @param continueOnFailure - Whether to continue after failures
|
|
174
|
+
* @returns Import summary with all results
|
|
175
|
+
*/
|
|
176
|
+
export async function applyPatchesWithContinue(patchesDir, engineDir, continueOnFailure = false) {
|
|
177
|
+
const patches = await discoverPatches(patchesDir);
|
|
178
|
+
const succeeded = [];
|
|
179
|
+
const failed = [];
|
|
180
|
+
const skipped = [];
|
|
181
|
+
for (const patch of patches) {
|
|
182
|
+
const result = await applySinglePatch(patch, engineDir);
|
|
183
|
+
if (result.success) {
|
|
184
|
+
succeeded.push(result);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// Try to extract conflicting files from error message
|
|
188
|
+
result.conflictingFiles = extractConflictingFiles(result.error);
|
|
189
|
+
failed.push(result);
|
|
190
|
+
if (!continueOnFailure) {
|
|
191
|
+
// Roll back successfully applied patches to keep engine clean
|
|
192
|
+
if (succeeded.length > 0) {
|
|
193
|
+
verbose(`Rolling back ${succeeded.length} previously applied patch(es)…`);
|
|
194
|
+
await rollbackPatches(succeeded, engineDir);
|
|
195
|
+
}
|
|
196
|
+
// Mark remaining patches as skipped
|
|
197
|
+
const currentIndex = patches.indexOf(patch);
|
|
198
|
+
for (let i = currentIndex + 1; i < patches.length; i++) {
|
|
199
|
+
const remainingPatch = patches[i];
|
|
200
|
+
if (remainingPatch) {
|
|
201
|
+
skipped.push(remainingPatch);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
total: patches.length,
|
|
210
|
+
succeeded,
|
|
211
|
+
failed,
|
|
212
|
+
skipped,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Computes the cumulative patched content for a file.
|
|
217
|
+
* @param patchesDir - Path to the patches directory
|
|
218
|
+
* @param engineDir - Path to the engine directory
|
|
219
|
+
* @param filePath - File path to compute content for
|
|
220
|
+
* @returns Content after all patches applied, or null if file doesn't exist
|
|
221
|
+
*/
|
|
222
|
+
export async function computePatchedContent(patchesDir, engineDir, filePath) {
|
|
223
|
+
let content = await getFileContentFromHead(engineDir, filePath);
|
|
224
|
+
// Find all patches affecting this file
|
|
225
|
+
const affectingPatches = await findPatchesAffectingFile(patchesDir, filePath);
|
|
226
|
+
if (affectingPatches.length === 0) {
|
|
227
|
+
return content;
|
|
228
|
+
}
|
|
229
|
+
// Apply each patch in order
|
|
230
|
+
for (const { patch } of affectingPatches) {
|
|
231
|
+
content = await applyPatchToContent(content, patch.path, filePath);
|
|
232
|
+
}
|
|
233
|
+
return content;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=patch-apply.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { PatchCategory, PatchInfo, PatchMetadata } from '../types/commands/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Gets the next patch number for a new patch.
|
|
4
|
+
* @param patchesDir - Path to the patches directory
|
|
5
|
+
* @returns Next patch number (e.g., "005" for 4 existing patches)
|
|
6
|
+
*/
|
|
7
|
+
export declare function getNextPatchNumber(patchesDir: string): Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Generates the next patch filename with category.
|
|
10
|
+
* @param patchesDir - Path to the patches directory
|
|
11
|
+
* @param category - Patch category
|
|
12
|
+
* @param name - Human-readable name
|
|
13
|
+
* @returns Filename like "001-ui-sidebar.patch"
|
|
14
|
+
*/
|
|
15
|
+
export declare function getNextPatchFilename(patchesDir: string, category: PatchCategory, name: string): Promise<string>;
|
|
16
|
+
export interface CommitExportedPatchInput {
|
|
17
|
+
patchesDir: string;
|
|
18
|
+
category: PatchCategory;
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
diff: string;
|
|
22
|
+
filesAffected: string[];
|
|
23
|
+
sourceEsrVersion: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CommitExportedPatchResult {
|
|
26
|
+
patchFilename: string;
|
|
27
|
+
metadata: PatchMetadata;
|
|
28
|
+
superseded: PatchInfo[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Commits a freshly generated patch file and manifest update under an exclusive
|
|
32
|
+
* patch directory lock so concurrent exports cannot allocate the same number.
|
|
33
|
+
*/
|
|
34
|
+
export declare function commitExportedPatch(input: CommitExportedPatchInput): Promise<CommitExportedPatchResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Parses a patch filename to extract order, category, and name.
|
|
37
|
+
* Supports both new format (001-category-name.patch) and legacy (001-name.patch).
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseFilename(filename: string): {
|
|
40
|
+
order: number;
|
|
41
|
+
category: PatchCategory | null;
|
|
42
|
+
name: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Finds an existing patch that contains the specified file.
|
|
46
|
+
* Returns the most recent (highest order) patch if multiple exist.
|
|
47
|
+
* @param patchesDir - Path to the patches directory
|
|
48
|
+
* @param filePath - File path to search for
|
|
49
|
+
* @returns The patch info and metadata, or null if not found
|
|
50
|
+
*/
|
|
51
|
+
export declare function findExistingPatchForFile(patchesDir: string, filePath: string): Promise<{
|
|
52
|
+
patch: PatchInfo;
|
|
53
|
+
metadata: PatchMetadata;
|
|
54
|
+
} | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Updates the content of a patch file.
|
|
57
|
+
* @param patchPath - Path to the patch file
|
|
58
|
+
* @param newContent - New patch content
|
|
59
|
+
*/
|
|
60
|
+
export declare function updatePatch(patchPath: string, newContent: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Updates metadata for a patch in the manifest.
|
|
63
|
+
* @param patchesDir - Path to the patches directory
|
|
64
|
+
* @param filename - Patch filename
|
|
65
|
+
* @param updates - Partial metadata updates
|
|
66
|
+
*/
|
|
67
|
+
export declare function updatePatchMetadata(patchesDir: string, filename: string, updates: Partial<PatchMetadata>): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Finds patches that are completely superseded by newer patches.
|
|
70
|
+
* A patch is superseded if all its affected files are covered by newer patches.
|
|
71
|
+
* @param patchesDir - Path to the patches directory
|
|
72
|
+
* @param newPatchFiles - Files affected by the new patch
|
|
73
|
+
* @param excludeFilename - Filename to exclude from results (the new patch itself)
|
|
74
|
+
* @returns Superseded patches
|
|
75
|
+
*/
|
|
76
|
+
export declare function findSupersededPatches(patchesDir: string, newPatchFiles: string[], excludeFilename?: string): Promise<PatchInfo[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Deletes a patch file and removes it from the manifest.
|
|
79
|
+
* @param patchesDir - Path to the patches directory
|
|
80
|
+
* @param filename - Patch filename to delete
|
|
81
|
+
*/
|
|
82
|
+
export declare function deletePatch(patchesDir: string, filename: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Checks whether a patch is fully covered by a new export.
|
|
85
|
+
* A patch is fully covered when every file it affects is present in the new export.
|
|
86
|
+
* @param patchFiles - Files affected by the existing patch
|
|
87
|
+
* @param targetFiles - Files affected by the new export
|
|
88
|
+
* @returns True when the existing patch is fully covered
|
|
89
|
+
*/
|
|
90
|
+
export declare function isPatchFullyCovered(patchFiles: string[], targetFiles: string[]): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Finds patches whose filesAffected entries are fully covered by the specified files.
|
|
93
|
+
* Used for complete supersession when exporting full-file patches.
|
|
94
|
+
* @param patchesDir - Path to the patches directory
|
|
95
|
+
* @param targetFiles - Files affected by the new export
|
|
96
|
+
* @param excludeFilename - Filename to exclude from results (the new patch itself)
|
|
97
|
+
* @returns Patches that are fully covered by the new export
|
|
98
|
+
*/
|
|
99
|
+
export declare function findAllPatchesForFiles(patchesDir: string, targetFiles: string[], excludeFilename?: string): Promise<PatchInfo[]>;
|