diff --git a/docs/docker.md b/docs/docker.md index 2efb474..19c504f 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -7,26 +7,32 @@ -Use `nodejs_binary_archive` to create a tar and add that to the docker image. +Use `nodejs_binary_package` to create a tar and add that to the docker image. # Example ```bzl -load("@better_rules_javascript//nodejs:rules.bzl", "nodejs_binary_archive") -load("@io_bazel_rules_docker//container:container.bzl", "container_image") +load("@better_rules_javascript//nodejs:rules.bzl", "nodejs_binary_package") +load("@rules_oci//oci:defs.bzl", "oci_image") -container_image( +oci_image( name = "image", - base = "@base//image", - directory = "/opt/example", + base = "@base", entrypoint = ["/usr/local/bin/example"], + tars = ["image_layer"], +) + +pkg_tar( + name = "image_layer", + package_dir = "/opt/example", symlinks = { "/usr/local/bin/example": "/opt/example/bin", }, - tars = [":tar"], + # Merges these tars together into one layer + deps = [":tar"], ) -nodejs_binary_archive( +nodejs_binary_package( name = "tar", dep = ":lib", main = "src/main.js", diff --git a/npm/workspace.bzl b/npm/workspace.bzl index 15b56c6..6fbfc80 100644 --- a/npm/workspace.bzl +++ b/npm/workspace.bzl @@ -19,15 +19,45 @@ def _npm_import_external_impl(ctx, plugins): ctx.execute(["rm", "-r", "tmp"]) + patch_args = "--directory=npm --strip=1 --forward --reject-file=-" + for patch in ctx.attr.patches: + patch_result = ctx.execute([ + "sh", + "-c", + "patch %s < %s" % (patch_args, ctx.path(patch)), + ]) + + # Ignore return code 2, which signals the patch has already been applied + if patch_result.return_code != 0 and patch_result.return_code != 2: + fail("Could not apply patch %s: %s" % (patch, patch_result.stderr)) + files_result = ctx.execute(["find", "npm", "-type", "f"]) if files_result.return_code: fail("Could not list files") files = [file[len("npm/"):] for file in files_result.stdout.split("\n")] + final_package_path = ctx.attr.package + if ctx.attr.patches: + tar_result = ctx.execute([ + "tar", + "czf", + "patched-package.tgz", + "--strip-components=1", + "npm/", + ]) + if tar_result.return_code: + fail("Could not tar up patched-package.tgz") + final_package_path = "patched-package.tgz" + + # Don't leave the package contents sitting around now that we're done. Bazel + # builds will always extract from the .tgz file, so anyone wanting to tinker + # should go poke at the .tgz. + ctx.execute(["rm", "-r", "npm/"]) + build = "" package = struct( - archive = ctx.attr.package, + archive = final_package_path, deps = deps, extra_deps = extra_deps, name = package_name, @@ -63,6 +93,10 @@ def npm_import_external_rule(plugins): doc = "Package name.", mandatory = True, ), + "patches": attr.label_list( + allow_files = True, + mandatory = True, + ), }, ) @@ -155,6 +189,7 @@ def npm(name, packages, roots, plugins = DEFAULT_PLUGINS, auth_patterns = None, name = repo_name, package = file, package_name = package["name"], + patches = package.get("patches", []), deps = [json.encode({"id": package_repo_name(name, dep["id"]), "name": dep.get("name")}) for dep in package["deps"]], extra_deps = extra_deps, ) diff --git a/npm/yarn-resolve/src/bzl.ts b/npm/yarn-resolve/src/bzl.ts index ffc0451..485efc3 100644 --- a/npm/yarn-resolve/src/bzl.ts +++ b/npm/yarn-resolve/src/bzl.ts @@ -14,6 +14,7 @@ export interface BzlPackage { integrity: string; name: string; url: string; + patches: string[]; } export namespace BzlPackage { @@ -26,13 +27,22 @@ export namespace BzlPackage { extraDepsEntries.push([new StarlarkString(id), BzlDeps.toStarlark(deps)]); } - return new StarlarkDict([ + const elements: [StarlarkValue, StarlarkValue][] = [ [new StarlarkString("deps"), BzlDeps.toStarlark(value.deps)], [new StarlarkString("extra_deps"), new StarlarkDict(extraDepsEntries)], [new StarlarkString("integrity"), new StarlarkString(value.integrity)], [new StarlarkString("name"), new StarlarkString(value.name)], [new StarlarkString("url"), new StarlarkString(value.url)], - ]); + ]; + if (value.patches.length > 0) { + elements.push([ + new StarlarkString("patches"), + new StarlarkArray( + value.patches.map((patchPath) => new StarlarkString(patchPath)), + ), + ]); + } + return new StarlarkDict(elements); } } diff --git a/npm/yarn-resolve/src/resolve.ts b/npm/yarn-resolve/src/resolve.ts index 09b425f..8fdeae5 100644 --- a/npm/yarn-resolve/src/resolve.ts +++ b/npm/yarn-resolve/src/resolve.ts @@ -84,6 +84,7 @@ export async function resolvePackages( integrity: npmPackage.contentIntegrity, name: structUtils.stringifyIdent(yarnPackage.locator), url: npmPackage.contentUrl, + patches: extractPatchPaths(yarnPackage.locator), }); finished++; } else if (yarnPackage.locator.reference === "workspace:.") { @@ -132,6 +133,19 @@ function npmLocator(locator: Locator): Locator | undefined { } } +function extractPatchPaths(locator: Locator): string[] { + return locator.reference.startsWith("patch:") + ? patchUtils + .parseLocator(locator) + .patchPaths.filter((patchPath) => !patchPath.startsWith("~builtin")) + .map((patchPath) => { + // Replace final slash with a colon to make it a bazel label + const parts = patchPath.split("/"); + return `//${parts.slice(0, -1).join("/")}:${parts.slice(-1)}`; + }) + : []; +} + function bzlId(locator: Locator): string | undefined { if ( locator.reference.startsWith("http:") || diff --git a/util/starlark/src/index.ts b/util/starlark/src/index.ts index 4e562b3..3a1da72 100644 --- a/util/starlark/src/index.ts +++ b/util/starlark/src/index.ts @@ -98,7 +98,7 @@ function printValue(value: StarlarkValue, indent?: string | undefined): string { if (value instanceof StarlarkString) { return printString(value); } - throw new Error("Unreognized value"); + throw new Error("Unrecognized value"); } function printVariable(value: StarlarkVariable): string {