Skip to content

Commit d805f33

Browse files
author
Greg Magolan
authored
feat: add pre and post install patches to yarn_install and npm_install (#2607)
1 parent ab0b372 commit d805f33

File tree

14 files changed

+184
-0
lines changed

14 files changed

+184
-0
lines changed

WORKSPACE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,30 @@ filegroup(
432432
yarn_lock = "//:tools/fine_grained_goldens/yarn.lock",
433433
)
434434

435+
yarn_install(
436+
name = "internal_npm_install_test_patches_yarn",
437+
package_json = "//internal/npm_install/test/patches_yarn:package.json",
438+
package_path = "internal/npm_install/test/patches_yarn",
439+
patch_args = ["-p0"],
440+
patch_tool = "patch",
441+
post_install_patches = ["//internal/npm_install/test/patches_yarn:semver+1.0.0.patch"],
442+
pre_install_patches = ["//internal/npm_install/test/patches_yarn:package_json.patch"],
443+
symlink_node_modules = False,
444+
yarn_lock = "//internal/npm_install/test/patches_yarn:yarn.lock",
445+
)
446+
447+
npm_install(
448+
name = "internal_npm_install_test_patches_npm",
449+
package_json = "//internal/npm_install/test/patches_npm:package.json",
450+
package_lock_json = "//internal/npm_install/test/patches_npm:package-lock.json",
451+
package_path = "internal/npm_install/test/patches_npm",
452+
patch_args = ["-p0"],
453+
patch_tool = "patch",
454+
post_install_patches = ["//internal/npm_install/test/patches_npm:semver+1.0.0.patch"],
455+
pre_install_patches = ["//internal/npm_install/test/patches_npm:package_json.patch"],
456+
symlink_node_modules = False,
457+
)
458+
435459
yarn_install(
436460
name = "fine_grained_goldens_multi_linked",
437461
included_files = [

internal/npm_install/npm_install.bzl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,37 @@ fine grained npm dependencies.
168168
In most cases, this should be the directory of the package.json file so that the linker links the node_modules
169169
in the same location they are found in the source tree. In a future release, this will default to the package.json
170170
directory. This is planned for 4.0: https://github.com/bazelbuild/rules_nodejs/issues/2451""",
171+
),
172+
"patch_args": attr.string_list(
173+
default = ["-p0"],
174+
doc =
175+
"The arguments given to the patch tool. Defaults to -p0, " +
176+
"however -p1 will usually be needed for patches generated by " +
177+
"git. If multiple -p arguments are specified, the last one will take effect." +
178+
"If arguments other than -p are specified, Bazel will fall back to use patch " +
179+
"command line tool instead of the Bazel-native patch implementation. When falling " +
180+
"back to patch command line tool and patch_tool attribute is not specified, " +
181+
"`patch` will be used.",
182+
),
183+
"patch_tool": attr.string(
184+
default = "",
185+
doc = "The patch(1) utility to use. If this is specified, Bazel will use the specifed " +
186+
"patch tool instead of the Bazel-native patch implementation.",
187+
),
188+
"post_install_patches": attr.label_list(
189+
doc = """Patch files to apply after running package manager.
190+
191+
This can be used to make changes to installed packages after the package manager runs.
192+
193+
File paths in patches should be relative to workspace root.""",
194+
),
195+
"pre_install_patches": attr.label_list(
196+
doc = """Patch files to apply before running package manager.
197+
198+
This can be used to make changes to package.json or other data files passed in before running the
199+
package manager.
200+
201+
File paths in patches should be relative to workspace root.""",
171202
),
172203
"quiet": attr.bool(
173204
default = True,
@@ -205,6 +236,34 @@ data attribute.
205236
),
206237
})
207238

239+
def _apply_patches(repository_ctx, patches):
240+
bash_exe = repository_ctx.os.environ["BAZEL_SH"] if "BAZEL_SH" in repository_ctx.os.environ else "bash"
241+
242+
patch_tool = repository_ctx.attr.patch_tool
243+
if not patch_tool:
244+
patch_tool = "patch"
245+
patch_args = repository_ctx.attr.patch_args
246+
247+
for patchfile in patches:
248+
command = "{patchtool} {patch_args} < {patchfile}".format(
249+
patchtool = patch_tool,
250+
patchfile = repository_ctx.path(patchfile),
251+
patch_args = " ".join([
252+
"'%s'" % arg
253+
for arg in patch_args
254+
]),
255+
)
256+
st = repository_ctx.execute(
257+
[bash_exe, "-c", command],
258+
quiet = repository_ctx.attr.quiet,
259+
# Working directory is _ which is where all files are copied to and
260+
# where the install is run; patches should be relative to workspace root.
261+
working_directory = "_",
262+
)
263+
if st.return_code:
264+
fail("Error applying patch %s:\n%s%s" %
265+
(str(patchfile), st.stderr, st.stdout))
266+
208267
def _create_build_files(repository_ctx, rule_type, node, lock_file, generate_local_modules_build_files):
209268
repository_ctx.report_progress("Processing node_modules: installing Bazel packages and generating BUILD files")
210269
if repository_ctx.attr.manual_build_file_contents:
@@ -399,6 +458,7 @@ cd /D "{root}" && "{npm}" {npm_args}
399458
_copy_data_dependencies(repository_ctx)
400459
_add_scripts(repository_ctx)
401460
_add_node_repositories_info_deps(repository_ctx)
461+
_apply_patches(repository_ctx, repository_ctx.attr.pre_install_patches)
402462

403463
result = repository_ctx.execute(
404464
[node, "pre_process_package_json.js", repository_ctx.path(repository_ctx.attr.package_json), "npm"],
@@ -439,6 +499,7 @@ cd /D "{root}" && "{npm}" {npm_args}
439499
fail("remove_npm_absolute_paths failed: %s (%s)" % (result.stdout, result.stderr))
440500

441501
_symlink_node_modules(repository_ctx)
502+
_apply_patches(repository_ctx, repository_ctx.attr.post_install_patches)
442503

443504
_create_build_files(repository_ctx, "npm_install", node, repository_ctx.attr.package_lock_json, repository_ctx.attr.generate_local_modules_build_files)
444505

@@ -550,6 +611,7 @@ cd /D "{root}" && "{yarn}" {yarn_args}
550611
_copy_data_dependencies(repository_ctx)
551612
_add_scripts(repository_ctx)
552613
_add_node_repositories_info_deps(repository_ctx)
614+
_apply_patches(repository_ctx, repository_ctx.attr.pre_install_patches)
553615

554616
result = repository_ctx.execute(
555617
[node, "pre_process_package_json.js", repository_ctx.path(repository_ctx.attr.package_json), "yarn"],
@@ -575,6 +637,7 @@ cd /D "{root}" && "{yarn}" {yarn_args}
575637
fail("yarn_install failed: %s (%s)" % (result.stdout, result.stderr))
576638

577639
_symlink_node_modules(repository_ctx)
640+
_apply_patches(repository_ctx, repository_ctx.attr.post_install_patches)
578641

579642
_create_build_files(repository_ctx, "yarn_install", node, repository_ctx.attr.yarn_lock, repository_ctx.attr.generate_local_modules_build_files)
580643

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("//:index.bzl", "nodejs_test")
2+
3+
nodejs_test(
4+
name = "test",
5+
data = [
6+
"@internal_npm_install_test_patches_npm//semver",
7+
],
8+
entry_point = "test.js",
9+
)

internal/npm_install/test/patches_npm/package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"dependencies": {
3+
"__invalid_dependency__": "removed by pre_install_patches",
4+
"semver": "1.0.0"
5+
}
6+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--- internal/npm_install/test/patches_npm/package.json
2+
+++ internal/npm_install/test/patches_npm/package.json
3+
@@ -1,6 +1,5 @@
4+
{
5+
"dependencies": {
6+
- "__invalid_dependency__": "removed by pre_install_patches",
7+
"semver": "1.0.0"
8+
}
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--- internal/npm_install/test/patches_npm/node_modules/semver/semver.js
2+
+++ internal/npm_install/test/patches_npm/node_modules/semver/semver.js
3+
@@ -34,6 +34,7 @@ exports.valid = valid
4+
exports.validPackage = validPackage
5+
exports.validRange = validRange
6+
exports.maxSatisfying = maxSatisfying
7+
+exports.patched = true
8+
9+
function clean (ver) {
10+
v = exports.parse(ver)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const semver = require('semver')
2+
if (!semver.patched) {
3+
console.error('Expected semver to be patched');
4+
process.exitCode = 1;
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
load("//:index.bzl", "nodejs_test")
2+
3+
nodejs_test(
4+
name = "test",
5+
data = [
6+
"@internal_npm_install_test_patches_yarn//semver",
7+
],
8+
entry_point = "test.js",
9+
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"dependencies": {
3+
"__invalid_dependency__": "removed by pre_install_patches",
4+
"semver": "1.0.0"
5+
}
6+
}

0 commit comments

Comments
 (0)