Skip to content

Commit

Permalink
feat(cypress): cypress executable toolchain (#2668)
Browse files Browse the repository at this point in the history
This is a major refactor the cypress_repository rule
Prior to this change, the cypress CLI from NPM was invoked
to download the corresponding Cypress binary.

This was bad for a number of reasons. Namely it was non cachable
and did not match bazel's semantics for grabbing the right binary
for a particular platform / architecture

The new cypress_repositories rule, downloads binaries for all
platforms from Cypress's CDN and uses bazel's select logic
to use the appropriate one given a particular configuration.
  • Loading branch information
mrmeku authored Jun 7, 2021
1 parent 5b7c36b commit f1f5ee6
Show file tree
Hide file tree
Showing 26 changed files with 1,390 additions and 1,118 deletions.
13 changes: 7 additions & 6 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ workspace(
name = "build_bazel_rules_nodejs",
managed_directories = {
"@angular_deps": ["packages/angular/node_modules"],
# cypress_deps must be a managed directory to ensure it is downloaded before cypress_repository is run.
# cypress_deps must be a managed directory to ensure it is downloaded before cypress_repositories is run.
"@cypress_deps": ["packages/cypress/test/node_modules"],
"@internal_test_multi_linker_sub_deps": ["internal/linker/test/multi_linker/sub/node_modules"],
"@npm": ["node_modules"],
Expand Down Expand Up @@ -100,13 +100,14 @@ ts_setup_dev_workspace()
#
# Install @bazel/cypress dependencies
#
load("//packages/cypress:index.bzl", "cypress_repository")
load("//packages/cypress:index.bzl", "cypress_repositories")

cypress_repository(
cypress_repositories(
name = "cypress",
cypress_bin = "@cypress_deps//:node_modules/cypress/bin/cypress",
# Currently cypress cannot be installed on our Linux/Windows CI machines
fail_on_error = False,
darwin_sha256 = "101a0ced77fb74b356800cb3a3919f5288d23cc63fdd39a0c500673159e954fc",
linux_sha256 = "d8ea8d16fed33fdae8f17178bcae076aaf532fa7ccb48f377df1f143e60abd59",
version = "7.3.0",
windows_sha256 = "8a8809e4fd22fe7bfc3103c39df3f4fce9db0964450ce927558e9a09558cb26c",
)

# Setup the rules_webtesting toolchain
Expand Down
411 changes: 411 additions & 0 deletions docs/Cypress.html

Large diffs are not rendered by default.

334 changes: 310 additions & 24 deletions docs/Cypress.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions examples/cypress/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
load("@cypress//:index.bzl", "cypress_web_test")
load("@yarn//@bazel/cypress:index.bzl", "cypress_web_test")
load("@yarn//@bazel/typescript:index.bzl", "ts_library")

# You must create a cypress plugin in order to boot a server to serve your application. It can be written as a javascript file or in typescript using ts_library or ts_project.
ts_library(
name = "plugins_file",
name = "plugin_file",
testonly = True,
srcs = ["plugin.ts"],
tsconfig = ":tsconfig.json",
Expand Down Expand Up @@ -38,5 +38,5 @@ cypress_web_test(
# Any runtime dependencies you need to boot your server or run your tests
data = ["@yarn//rxjs"],
# Your cypress plugin used to configure cypress and boot your server
plugins_file = ":plugins_file",
plugin_file = ":plugin_file",
)
9 changes: 6 additions & 3 deletions examples/cypress/WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ yarn_install(
yarn_lock = "//:yarn.lock",
)

load("@yarn//@bazel/cypress:index.bzl", "cypress_repository")
load("@yarn//@bazel/cypress:index.bzl", "cypress_repositories")

cypress_repository(
cypress_repositories(
name = "cypress",
cypress_bin = "@yarn//:node_modules/cypress/bin/cypress",
darwin_sha256 = "101a0ced77fb74b356800cb3a3919f5288d23cc63fdd39a0c500673159e954fc",
linux_sha256 = "d8ea8d16fed33fdae8f17178bcae076aaf532fa7ccb48f377df1f143e60abd59",
version = "7.3.0",
windows_sha256 = "8a8809e4fd22fe7bfc3103c39df3f4fce9db0964450ce927558e9a09558cb26c",
)
2 changes: 1 addition & 1 deletion examples/cypress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"@bazel/ibazel": "^0.15.10",
"@bazel/typescript": "^3.5.1",
"@types/node": "14.0.13",
"cypress": "^4.8.0",
"cypress": "7.3.0",
"rxjs": "^6.5.2",
"typescript": "3.9.5"
},
Expand Down
70 changes: 35 additions & 35 deletions internal/node/node.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ def _trim_package_node_modules(package_name):
segments.append(n)
return "/".join(segments)

def _compute_node_modules_roots(ctx):
def _compute_node_modules_roots(ctx, data):
"""Computes the node_modules root (if any) from data attribute."""
node_modules_roots = {}

# Add in roots from third-party deps
for d in ctx.attr.data:
for d in data:
if ExternalNpmPackageInfo in d:
path = d[ExternalNpmPackageInfo].path
workspace = d[ExternalNpmPackageInfo].workspace
Expand All @@ -56,7 +56,7 @@ def _compute_node_modules_roots(ctx):
node_modules_roots[path] = workspace

# Add in roots for multi-linked first party deps
for dep in ctx.attr.data:
for dep in data:
for k, v in getattr(dep, MODULE_MAPPINGS_ASPECT_RESULTS_NAME, {}).items():
map_key_split = k.split(":")
package_name = map_key_split[0]
Expand All @@ -65,12 +65,12 @@ def _compute_node_modules_roots(ctx):
node_modules_roots[package_path] = ""
return node_modules_roots

def _write_require_patch_script(ctx, node_modules_root):
def _write_require_patch_script(ctx, data, node_modules_root):
# Generates the JavaScript snippet of module roots mappings, with each entry
# in the form:
# {module_name: /^mod_name\b/, module_root: 'path/to/mod_name'}
module_mappings = []
for d in ctx.attr.data:
for d in data:
if hasattr(d, "runfiles_module_mappings"):
for [mn, mr] in d.runfiles_module_mappings.items():
escaped = mn.replace("/", "\\/").replace(".", "\\.")
Expand Down Expand Up @@ -145,13 +145,14 @@ def _to_execroot_path(ctx, file):
def _join(*elements):
return "/".join([f for f in elements if f])

def _nodejs_binary_impl(ctx):
def _nodejs_binary_impl(ctx, data = [], runfiles = [], expanded_args = []):
node_modules_manifest = write_node_modules_manifest(ctx, link_workspace_root = ctx.attr.link_workspace_root)
node_modules_depsets = []
data = ctx.attr.data + data

# Also include files from npm fine grained deps as inputs.
# These deps are identified by the ExternalNpmPackageInfo provider.
for d in ctx.attr.data:
for d in data:
if ExternalNpmPackageInfo in d:
node_modules_depsets.append(d[ExternalNpmPackageInfo].sources)

Expand All @@ -164,7 +165,7 @@ def _nodejs_binary_impl(ctx):
# transitive depset()s
sources_depsets = []

for d in ctx.attr.data:
for d in data:
if JSModuleInfo in d:
sources_depsets.append(d[JSModuleInfo].sources)

Expand All @@ -176,14 +177,14 @@ def _nodejs_binary_impl(ctx):
sources_depsets.append(d.files)
sources = depset(transitive = sources_depsets)

node_modules_roots = _compute_node_modules_roots(ctx)
node_modules_roots = _compute_node_modules_roots(ctx, data)

if "" in node_modules_roots:
node_modules_root = node_modules_roots[""] + "/node_modules"
else:
# there are no fine grained deps but we still need a node_modules_root even if it is a non-existant one
node_modules_root = "build_bazel_rules_nodejs/node_modules"
_write_require_patch_script(ctx, node_modules_root)
_write_require_patch_script(ctx, data, node_modules_root)

_write_loader_script(ctx)

Expand All @@ -193,7 +194,7 @@ def _nodejs_binary_impl(ctx):

# Add all env vars from the ctx attr
for [key, value] in ctx.attr.env.items():
env_vars += "export %s=%s\n" % (key, expand_location_into_runfiles(ctx, value, ctx.attr.data))
env_vars += "export %s=%s\n" % (key, expand_location_into_runfiles(ctx, value, data))

# While we can derive the workspace from the pwd when running locally
# because it is in the execroot path `execroot/my_wksp`, on RBE the
Expand Down Expand Up @@ -250,7 +251,7 @@ fi

is_builtin = ctx.attr._node.label.workspace_name in ["nodejs_%s" % p for p in BUILT_IN_NODE_PLATFORMS]

runfiles = []
runfiles = runfiles[:]
runfiles.extend(node_tool_files)
runfiles.extend(ctx.files._bash_runfile_helper)
runfiles.append(ctx.outputs.loader_script)
Expand All @@ -259,7 +260,7 @@ fi

# First replace any instances of "$(rlocation " with "$$(rlocation " to preserve
# legacy uses of "$(rlocation"
expanded_args = [preserve_legacy_templated_args(a) for a in ctx.attr.templated_args]
expanded_args = expanded_args + [preserve_legacy_templated_args(a) for a in ctx.attr.templated_args]

# chdir has to include rlocation lookup for windows
# that means we have to generate a script so there's an entry in the runfiles manifest
Expand All @@ -281,7 +282,7 @@ fi

# Next expand predefined source/output path variables:
# $(execpath), $(rootpath) & legacy $(location)
expanded_args = [expand_location_into_runfiles(ctx, a, ctx.attr.data) for a in expanded_args]
expanded_args = [expand_location_into_runfiles(ctx, a, data) for a in expanded_args]

# Finally expand predefined variables & custom variables
rule_dir = _join(ctx.bin_dir.path, ctx.label.workspace_root, ctx.label.package)
Expand Down Expand Up @@ -363,7 +364,7 @@ fi
# when downstream usage is ready to rely on linker
NodeRuntimeDepsInfo(
deps = depset(ctx.files.entry_point, transitive = [node_modules, sources]),
pkgs = ctx.attr.data,
pkgs = data,
),
# indicates that the this binary should be instrumented by coverage
# see https://docs.bazel.build/versions/master/skylark/lib/coverage_common.html
Expand Down Expand Up @@ -621,25 +622,21 @@ _NODEJS_EXECUTABLE_OUTPUTS = {
"require_patch_script": "%{name}_require_patch.js",
}

# The name of the declared rule appears in
# bazel query --output=label_kind
# So we make these match what the user types in their BUILD file
# and duplicate the definitions to give two distinct symbols.
nodejs_binary = rule(
implementation = _nodejs_binary_impl,
attrs = _NODEJS_EXECUTABLE_ATTRS,
doc = "Runs some JavaScript code in NodeJS.",
executable = True,
outputs = _NODEJS_EXECUTABLE_OUTPUTS,
toolchains = [
nodejs_binary_kwargs = {
"attrs": _NODEJS_EXECUTABLE_ATTRS,
"doc": "Runs some JavaScript code in NodeJS.",
"executable": True,
"implementation": _nodejs_binary_impl,
"outputs": _NODEJS_EXECUTABLE_OUTPUTS,
"toolchains": [
"@build_bazel_rules_nodejs//toolchains/node:toolchain_type",
"@bazel_tools//tools/sh:toolchain_type",
],
)
}

nodejs_test = rule(
implementation = _nodejs_binary_impl,
attrs = dict(_NODEJS_EXECUTABLE_ATTRS, **{
nodejs_test_kwargs = dict(
nodejs_binary_kwargs,
attrs = dict(nodejs_binary_kwargs["attrs"], **{
"expected_exit_code": attr.int(
doc = "The expected exit code for the test. Defaults to 0.",
default = 0,
Expand Down Expand Up @@ -679,9 +676,12 @@ The runtime will pause before executing the program, allowing you to connect a
remote debugger.
""",
test = True,
outputs = _NODEJS_EXECUTABLE_OUTPUTS,
toolchains = [
"@build_bazel_rules_nodejs//toolchains/node:toolchain_type",
"@bazel_tools//tools/sh:toolchain_type",
],
)

# The name of the declared rule appears in
# bazel query --output=label_kind
# So we make these match what the user types in their BUILD file
# and duplicate the definitions to give two distinct symbols.
nodejs_binary = rule(**nodejs_binary_kwargs)

nodejs_test = rule(**nodejs_test_kwargs)
50 changes: 20 additions & 30 deletions packages/cypress/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,23 @@ load("//third_party/github.com/bazelbuild/bazel-skylib:rules/copy_file.bzl", "co

package(default_visibility = ["//visibility:public"])

exports_files(
[
"internal/plugins/index.template.js",
"internal/run-cypress.js",
],
visibility = ["//packages/cypress/test:__subpackages__"],
)

codeowners(
teams = ["@mrmeku"],
)

bzl_library(
name = "bzl",
srcs = glob(["**/*.bzl"]),
srcs = [
":index.bzl",
"//packages/cypress/internal:cypress_repositories.bzl",
"//packages/cypress/internal:cypress_web_test.bzl",
"//packages/cypress/internal/toolchain:cypress_toolchain.bzl",
"@bazel_tools//tools:bzl_srcs",
],
deps = [
"@build_bazel_rules_nodejs//:bzl",
"@build_bazel_rules_nodejs//internal/common:bzl",
"@build_bazel_rules_nodejs//internal/node:bzl",
"//:bzl",
"//internal/common:bzl",
"//internal/node:bzl",
],
)

Expand All @@ -61,26 +59,18 @@ pkg_npm(
srcs = [
"index.bzl",
"package.json",
"//packages/cypress:internal/cypress_repository.bzl",
"//packages/cypress:internal/install-cypress.js",
"//packages/cypress:internal/plugins/base.js",
"//packages/cypress:internal/plugins/index.template.js",
"//packages/cypress:internal/run-cypress.js",
"//packages/cypress:internal/template.cypress_web_test.bzl",
"//packages/cypress/internal:BUILD.bazel",
"//packages/cypress/internal:cypress_repositories.bzl",
"//packages/cypress/internal:cypress_web_test.bzl",
"//packages/cypress/internal:plugins/base.js",
"//packages/cypress/internal:plugins/index.js.tpl",
"//packages/cypress/internal:run-cypress.js",
"//packages/cypress/internal/toolchain:BUILD.bazel",
"//packages/cypress/internal/toolchain:cypress_toolchain.bzl",
],
build_file_content = """exports_files([
"internal/plugins/base.js",
"internal/plugins/index.template.js",
"internal/run-cypress.js",
"plugins/base.js",
])""",
build_file_content = "",
substitutions = {
"@build_bazel_rules_nodejs//packages/cypress:internal/cypress_repository.bzl": "//packages/cypress:internal/cypress_repository.bzl",
"@build_bazel_rules_nodejs//packages/cypress:internal/install-cypress.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/install-cypress.js",
"@build_bazel_rules_nodejs//packages/cypress:internal/plugins/base.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/plugins/base.js",
"@build_bazel_rules_nodejs//packages/cypress:internal/plugins/index.template.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/plugins/index.template.js",
"@build_bazel_rules_nodejs//packages/cypress:internal/run-cypress.js": "TEMPLATED_node_modules_workspace_name//@bazel/cypress:internal/run-cypress.js",
"TEMPLATED_node_modules_workspace_name//tar": "TEMPLATED_node_modules_workspace_name//@bazel/cypress",
"@build_bazel_rules_nodejs//packages/cypress/internal": "//@bazel/cypress/internal",
},
deps = [
":README.md",
Expand Down
Loading

0 comments on commit f1f5ee6

Please sign in to comment.