| "Repository rule to fetch the Yarn package manager" |
| |
| load("//nodejs/private:os_name.bzl", "is_windows_os") |
| load("//nodejs/private:node_labels.bzl", "get_node_label") |
| load("//nodejs/private:yarn_versions.bzl", "YARN_VERSIONS") |
| load("//nodejs:repositories.bzl", "GET_SCRIPT_DIR") |
| load("@bazel_skylib//lib:paths.bzl", "paths") |
| |
| _DOC = """Repository rule to fetch the yarnpkg.com package manager. |
| |
| Note, the recommended name is "yarn". If you choose a different name, you'll have to override the |
| `yarn` attribute in your `yarn_install` rule to point to your `yarn.js` file. |
| |
| ## Custom Yarn versions |
| |
| To specify custom Yarn versions, use the `yarn_releases` attribute |
| |
| ```python |
| yarn_repositories( |
| yarn_releases = { |
| "1.12.1": ("yarn-v1.12.1.tar.gz", "yarn-v1.12.1", "09bea8f4ec41e9079fa03093d3b2db7ac5c5331852236d63815f8df42b3ba88d"), |
| }, |
| ) |
| ``` |
| |
| Like `node_urls`, the `yarn_urls` attribute can be used to provide a list of custom URLs to use to download yarn |
| |
| ```python |
| yarn_repositories( |
| yarn_releases = { |
| "1.12.1": ("yarn-v1.12.1.tar.gz", "yarn-v1.12.1", "09bea8f4ec41e9079fa03093d3b2db7ac5c5331852236d63815f8df42b3ba88d"), |
| }, |
| yarn_version = "1.12.1", |
| yarn_urls = [ |
| "https://github.com/yarnpkg/yarn/releases/download/v{version}/{filename}", |
| ], |
| ) |
| ``` |
| |
| Will download yarn from https://github.com/yarnpkg/yarn/releases/download/v1.2.1/yarn-v1.12.1.tar.gz |
| and expect the file to have sha256sum `09bea8f4ec41e9079fa03093d3b2db7ac5c5331852236d63815f8df42b3ba88d`. |
| |
| If you don't use Yarn at all, you can skip downloading it by setting `yarn_urls = []`. |
| |
| ## Vendored yarn |
| |
| You can vendor the `yarn.js` file into your repo. In this case, don't call `yarn_repositories` at all. |
| Just pass the label of your vendored file to the `yarn` attribute of `yarn_install`. |
| """ |
| |
| YARN_EXTRACT_DIR = "bin/yarnpkg" |
| |
| def _download_yarn(repository_ctx): |
| """Used to download a yarn tool package. |
| |
| Args: |
| repository_ctx: The repository rule context |
| """ |
| yarn_urls = repository_ctx.attr.yarn_urls |
| |
| # If there are no URLs to download yarn, skip the download |
| if not len(yarn_urls): |
| repository_ctx.file("yarn_info", content = "# no yarn urls") |
| return |
| |
| yarn_version = repository_ctx.attr.yarn_version |
| yarn_releases = repository_ctx.attr.yarn_releases |
| |
| # We insert our default value here, not on the attribute's default, so it isn't documented. |
| # The size of YARN_VERSIONS constant is huge and not useful to document. |
| if not yarn_releases.items(): |
| yarn_releases = YARN_VERSIONS |
| |
| if yarn_version in yarn_releases: |
| filename, strip_prefix, sha256 = yarn_releases[yarn_version] |
| else: |
| fail("Unknown Yarn version %s" % yarn_version) |
| |
| urls = [url.format(version = yarn_version, filename = filename) for url in yarn_urls] |
| |
| auth = {} |
| for url in urls: |
| auth[url] = repository_ctx.attr.yarn_download_auth |
| |
| repository_ctx.download_and_extract( |
| auth = auth, |
| url = urls, |
| output = YARN_EXTRACT_DIR, |
| stripPrefix = strip_prefix, |
| sha256 = sha256, |
| ) |
| |
| repository_ctx.file("yarn_info", content = """# filename: {filename} |
| # strip_prefix: {strip_prefix} |
| # sha256: {sha256} |
| """.format( |
| filename = filename, |
| strip_prefix = strip_prefix, |
| sha256 = sha256, |
| )) |
| |
| def _yarn_repositories_impl(repository_ctx): |
| _download_yarn(repository_ctx) |
| is_windows = is_windows_os(repository_ctx) |
| entry_ext = ".cmd" if is_windows else "" |
| |
| yarn_path = YARN_EXTRACT_DIR |
| yarn_package = YARN_EXTRACT_DIR |
| |
| # Use the yarn.js script as the bin for osx & linux so there are no symlink issues with `%s/bin/npm` |
| yarn_bin = ("%s/bin/yarn.js" % yarn_path) if not is_windows else ("%s/bin/yarn.cmd" % yarn_path) |
| yarn_bin_label = ("%s/bin/yarn.js" % yarn_package) if not is_windows else ("%s/bin/yarn.cmd" % yarn_package) |
| yarn_script = "%s/bin/yarn.js" % yarn_path |
| yarn_entry = "bin/yarn%s" % entry_ext |
| yarn_script_relative = paths.relativize(yarn_script, "bin") |
| node_label = get_node_label(repository_ctx) |
| |
| # The entry points for yarn for osx/linux and windows. |
| # Runs yarn using appropriate node entry point. |
| # Unset YARN_IGNORE_PATH before calling yarn incase it is set so that |
| # .yarnrc yarn-path is followed if set. This is for the case when calling |
| # bazel from yarn with `yarn bazel ...` and yarn follows yarn-path in |
| # .yarnrc it will set YARN_IGNORE_PATH=1 which will prevent the bazel |
| # call into yarn from also following the yarn-path as desired. |
| if not is_windows: |
| # Yarn entry point |
| repository_ctx.file( |
| "bin/yarn", |
| content = """#!/usr/bin/env bash |
| # Generated by node_repositories.bzl |
| # Immediately exit if any command fails. |
| set -e |
| unset YARN_IGNORE_PATH |
| {get_script_dir} |
| "{node}" "$SCRIPT_DIR/{script}" "$@" |
| """.format( |
| get_script_dir = GET_SCRIPT_DIR, |
| node = repository_ctx.path(node_label), |
| script = yarn_script_relative, |
| ), |
| executable = True, |
| ) |
| else: |
| # Yarn entry point |
| repository_ctx.file( |
| "bin/yarn.cmd", |
| content = """@echo off |
| SET SCRIPT_DIR=%~dp0 |
| SET "YARN_IGNORE_PATH=" |
| "{node}" "%SCRIPT_DIR%\\{script}" %* |
| """.format( |
| node = repository_ctx.path(node_label), |
| script = yarn_script_relative, |
| ), |
| executable = True, |
| ) |
| |
| # Base BUILD file for this repository |
| build_content = """# Generated by node_repositories.bzl |
| package(default_visibility = ["//visibility:public"]) |
| exports_files(["{yarn_entry}"]) |
| alias(name = "yarn_bin", actual = "{yarn_bin_label}") |
| alias(name = "yarn", actual = "{yarn_entry}") |
| filegroup( |
| name = "yarn_files", |
| srcs = {yarn_files_glob}, |
| ) |
| """.format( |
| yarn_bin_export = ("\n \"%s\"," % yarn_bin), |
| yarn_files_glob = "glob([\"bin/yarnpkg/**\"])", |
| yarn_bin_label = yarn_bin_label, |
| yarn_entry = yarn_entry, |
| ) |
| |
| repository_ctx.file("BUILD.bazel", content = build_content) |
| |
| yarn_repositories = repository_rule( |
| implementation = _yarn_repositories_impl, |
| attrs = { |
| "node_repository": attr.string( |
| default = "nodejs", |
| doc = """The basename for a nodejs toolchain to use for running yarn. |
| Usually this is the value of the `name` attribute given to a nodejs_register_toolchains call in WORKSPACE""", |
| ), |
| "yarn_download_auth": attr.string_dict( |
| default = {}, |
| doc = """auth to use for all url requests |
| Example: {\"type\": \"basic\", \"login\": \"<UserName>\", \"password\": \"<Password>\" } |
| """, |
| ), |
| "yarn_releases": attr.string_list_dict( |
| doc = """Custom list of yarn releases to use. |
| |
| Dictionary mapping Yarn versions to their corresponding (filename, strip_prefix, sha256) tuples. |
| |
| By default, if this attribute has no items, we'll use a list of all public releases which |
| is periodically mirrored to rules_nodejs. |
| """, |
| ), |
| "yarn_urls": attr.string_list( |
| default = [ |
| "https://github.com/yarnpkg/yarn/releases/download/v{version}/{filename}", |
| ], |
| doc = """custom list of URLs to use to download Yarn |
| |
| Each entry is a template using `yarn_version` and `yarn_releases` in the substitutions. |
| |
| If this list is empty, we won't download yarn at all. |
| """, |
| ), |
| "yarn_version": attr.string( |
| doc = "the specific version of Yarn to install", |
| default = "1.22.11", |
| ), |
| }, |
| doc = _DOC, |
| ) |