Skip to content

Commit cbaab60

Browse files
authored
fix(builtin): only stamp artifacts when --stamp is passed to bazel (#1441)
Fixes remote cache misses of rollup_bundle and npm_package targets observed by Angular team
1 parent 3a1b6d8 commit cbaab60

16 files changed

Lines changed: 97 additions & 18 deletions

File tree

docs/index.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,19 @@ Bazel is generally only a build tool, and is unaware of your version control sys
130130
However, when publishing releases, you typically want to embed version information in the resulting distribution.
131131
Bazel supports this natively, using the following approach:
132132

133-
First, pass the `workspace_status_command` argument to `bazel build`. We prefer to do this with an entry in `.bazelrc`:
133+
To stamp a build, you must pass the `--stamp` argument to Bazel.
134+
135+
> Previous releases of rules_nodejs stamped builds always.
136+
> However this caused stamp-aware actions to never be remotely cached, since the volatile
137+
> status file is passed as an input and its checksum always changes.
138+
139+
Also pass the `workspace_status_command` argument to `bazel build`.
140+
We prefer to do these with an entry in `.bazelrc`:
134141

135142
```sh
136143
# This tells Bazel how to interact with the version control system
137144
# Enable this with --config=release
138-
build:release --workspace_status_command=./tools/bazel_stamp_vars.sh
145+
build:release --stamp --workspace_status_command=./tools/bazel_stamp_vars.sh
139146
```
140147

141148
Then create `tools/bazel_stamp_vars.sh`.
@@ -151,8 +158,6 @@ echo BUILD_SCM_VERSION $(git describe --abbrev=7 --tags HEAD)
151158

152159
For a more full-featured script, take a look at the [bazel_stamp_vars in Angular]
153160

154-
Ideally, `rollup_bundle` and `npm_package` should honor the `--stamp` argument to `bazel build`. However this is not currently possible, see https://github.com/bazelbuild/bazel/issues/1054
155-
156161
Finally, we recommend a release script around Bazel. We typically have more than one npm package published from one Bazel workspace, so we do a `bazel query` to find them, and publish in a loop. Here is a template to get you started:
157162

158163
```sh

internal/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
load("@build_bazel_rules_nodejs//:index.bzl", "BAZEL_VERSION", "nodejs_test")
16+
load("//internal/node:context.bzl", "node_context_data")
1617

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

@@ -33,3 +34,19 @@ nodejs_test(
3334
# Not necessary here, just a regression test for #1043
3435
templated_args_file = "package_json_test.params",
3536
)
37+
38+
# Detect if the build is running under --stamp
39+
config_setting(
40+
name = "stamp",
41+
values = {"stamp": "true"},
42+
)
43+
44+
# Modelled after go_context_data from rules_go
45+
# passes config values to starlark via a provider
46+
node_context_data(
47+
name = "node_context_data",
48+
stamp = select({
49+
"@build_bazel_rules_nodejs//internal:stamp": True,
50+
"//conditions:default": False,
51+
}),
52+
)

internal/node/context.bzl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"node_context_data rule"
2+
3+
load("@build_bazel_rules_nodejs//:providers.bzl", "NodeContextInfo")
4+
5+
_DOC = """node_context_data gathers information about the build configuration.
6+
It is a common dependency of all targets that are sensitive to configuration.
7+
(currently npm_package and rollup_bundle)"""
8+
9+
def _impl(ctx):
10+
return [NodeContextInfo(stamp = ctx.attr.stamp)]
11+
12+
# Modelled after go_context_data in rules_go
13+
# Works around github.com/bazelbuild/bazel/issues/1054
14+
node_context_data = rule(
15+
implementation = _impl,
16+
attrs = {
17+
"stamp": attr.bool(mandatory = True),
18+
},
19+
doc = _DOC,
20+
)

internal/npm_package/npm_package.bzl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ If all users of your library code use Bazel, they should just add your library
66
to the `deps` of one of their targets.
77
"""
88

9-
load("//:providers.bzl", "DeclarationInfo", "JSNamedModuleInfo")
9+
load("//:providers.bzl", "DeclarationInfo", "JSNamedModuleInfo", "NodeContextInfo")
1010
load("//internal/common:path_utils.bzl", "strip_external")
1111

1212
# Takes a depset of files and returns a corresponding list of file paths without any files
@@ -40,6 +40,7 @@ def create_package(ctx, deps_sources, nested_packages):
4040
The tree artifact which is the publishable directory.
4141
"""
4242

43+
stamp = ctx.attr.node_context_data[NodeContextInfo].stamp
4344
package_dir = ctx.actions.declare_directory(ctx.label.name)
4445
package_path = ctx.label.package
4546

@@ -61,7 +62,7 @@ def create_package(ctx, deps_sources, nested_packages):
6162
args.add(ctx.attr.replacements)
6263
args.add_all([ctx.outputs.pack.path, ctx.outputs.publish.path])
6364
args.add(ctx.attr.replace_with_version)
64-
args.add(ctx.version_file.path if ctx.version_file else "")
65+
args.add(ctx.version_file.path if stamp else "")
6566
args.add_joined(ctx.attr.vendor_external, join_with = ",", omit_if_empty = False)
6667
args.add("1" if ctx.attr.rename_build_files else "0")
6768

@@ -75,7 +76,7 @@ def create_package(ctx, deps_sources, nested_packages):
7576
# produced by the --workspace_status_command. That command will be executed whenever
7677
# this action runs, so we get the latest version info on each execution.
7778
# See https://github.com/bazelbuild/bazel/issues/1054
78-
if ctx.version_file:
79+
if stamp:
7980
inputs.append(ctx.version_file)
8081

8182
ctx.actions.run(
@@ -129,6 +130,11 @@ NPM_PACKAGE_ATTRS = {
129130
doc = """Files inside this directory which are simply copied into the package.""",
130131
allow_files = True,
131132
),
133+
"node_context_data": attr.label(
134+
default = "@build_bazel_rules_nodejs//internal:node_context_data",
135+
providers = [NodeContextInfo],
136+
doc = "Internal use only",
137+
),
132138
"packages": attr.label_list(
133139
doc = """Other npm_package rules whose content is copied into this package.""",
134140
allow_files = True,

internal/npm_package/test/BUILD.bazel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package")
22
load("@npm_bazel_jasmine//:index.from_src.bzl", "jasmine_node_test")
33
load("@npm_bazel_typescript//:index.from_src.bzl", "ts_library")
4+
load("//internal/node:context.bzl", "node_context_data")
45
load("//third_party/github.com/bazelbuild/bazel-skylib:rules/write_file.bzl", "write_file")
56

67
write_file(
@@ -20,13 +21,21 @@ npm_package(
2021
srcs = ["dependent_file"],
2122
)
2223

24+
# Force stamping behavior even in builds without --stamp config
25+
# by mocking out the config data
26+
node_context_data(
27+
name = "force_stamp",
28+
stamp = True,
29+
)
30+
2331
npm_package(
2432
name = "test_pkg",
2533
srcs = [
2634
"package.json",
2735
"some_file",
2836
"@internal_npm_package_test_vendored_external//:vendored_external_file",
2937
],
38+
node_context_data = ":force_stamp",
3039
packages = [":dependent_pkg"],
3140
replacements = {"replace_me": "replaced"},
3241
vendor_external = [

internal/npm_package/test/npm_package.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe('npm_package srcs', () => {
3131
expect(read('data.json')).toEqual('[]');
3232
});
3333
it('replaced 0.0.0-PLACEHOLDER', () => {
34-
expect(read('package.json').version).not.toEqual('0.0.0-PLACEHOLDER');
34+
expect(JSON.parse(read('package.json')).version).toEqual('1.2.3');
3535
});
3636
it('copies files from deps', () => {
3737
expect(read('bundle.min.js')).toBe('bundle content');

packages/rollup/src/rollup_bundle.bzl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"Rules for running Rollup under Bazel"
22

3-
load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "NpmPackageInfo", "node_modules_aspect", "run_node")
3+
load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "NodeContextInfo", "NpmPackageInfo", "node_modules_aspect", "run_node")
44
load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "module_mappings_aspect")
55

66
_DOC = """Runs the Rollup.js CLI under Bazel.
@@ -126,6 +126,11 @@ Either this attribute or `entry_point` must be specified, but not both.
126126
values = ["amd", "cjs", "esm", "iife", "umd", "system"],
127127
default = "esm",
128128
),
129+
"node_context_data": attr.label(
130+
default = "@build_bazel_rules_nodejs//internal:node_context_data",
131+
providers = [NodeContextInfo],
132+
doc = "Internal use only",
133+
),
129134
"output_dir": attr.bool(
130135
doc = """Whether to produce a directory output.
131136
@@ -277,19 +282,21 @@ def _rollup_bundle(ctx):
277282

278283
args.add_all(["--format", ctx.attr.format])
279284

285+
stamp = ctx.attr.node_context_data[NodeContextInfo].stamp
286+
280287
config = ctx.actions.declare_file("_%s.rollup_config.js" % ctx.label.name)
281288
ctx.actions.expand_template(
282289
template = ctx.file.config_file,
283290
output = config,
284291
substitutions = {
285-
"bazel_stamp_file": "\"%s\"" % ctx.version_file.path if ctx.version_file else "undefined",
292+
"bazel_stamp_file": "\"%s\"" % ctx.version_file.path if stamp else "undefined",
286293
},
287294
)
288295

289296
args.add_all(["--config", config.path])
290297
inputs.append(config)
291298

292-
if ctx.version_file:
299+
if stamp:
293300
inputs.append(ctx.version_file)
294301

295302
# Prevent rollup's module resolver from hopping outside Bazel's sandbox

packages/rollup/test/integration/golden.amd.js_

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license A dummy license banner that goes at the top of the file.
3-
* This is version v1.2.3
3+
* This is version <unknown>
44
*/
55

66
define(['exports', 'some_global_var'], function (exports, some_global_var) { 'use strict';

packages/rollup/test/integration/golden.cjs.js_

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license A dummy license banner that goes at the top of the file.
3-
* This is version v1.2.3
3+
* This is version <unknown>
44
*/
55

66
'use strict';

packages/rollup/test/integration/golden.esm.js_

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license A dummy license banner that goes at the top of the file.
3-
* This is version v1.2.3
3+
* This is version <unknown>
44
*/
55

66
import { thing } from 'some_global_var';

0 commit comments

Comments
 (0)