blob: ceeea2afb5a987188936728f126c00d87abcc000 [file] [log] [blame]
"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,
)