diff --git a/internal/pkg_npm/packager.js b/internal/pkg_npm/packager.js index 95a593057f..066006a607 100644 --- a/internal/pkg_npm/packager.js +++ b/internal/pkg_npm/packager.js @@ -60,11 +60,29 @@ function unquoteArgs(s) { return s.replace(/^'(.*)'$/, '$1'); } +/** + * The status files are expected to look like + * BUILD_SCM_HASH 83c699db39cfd74526cdf9bebb75aa6f122908bb + * BUILD_SCM_LOCAL_CHANGES true + * STABLE_BUILD_SCM_VERSION 6.0.0-beta.6+12.sha-83c699d.with-local-changes + * BUILD_TIMESTAMP 1520021990506 + * + * @param {string} p the path to the status file + * @returns a two-dimensional array of key/value pairs + */ +function parseStatusFile(p) { + if (!p) return []; + return fs.readFileSync(p, {encoding: 'utf-8'}) + .split('\n') + .filter(t => !!t) + .map(t => t.split(' ')); +} + function main(args) { args = fs.readFileSync(args[0], {encoding: 'utf-8'}).split('\n').map(unquoteArgs); const [outDir, baseDir, srcsArg, binDir, genDir, depsArg, packagesArg, substitutionsArg, - replaceWithVersion, stampFile, vendorExternalArg] = args; + volatileFile, infoFile, vendorExternalArg] = args; const substitutions = [ // Strip content between BEGIN-INTERNAL / END-INTERNAL comments @@ -74,27 +92,24 @@ function main(args) { for (const key of Object.keys(rawReplacements)) { substitutions.push([new RegExp(key, 'g'), rawReplacements[key]]) } - // Replace version last so that earlier substitutions can add - // the version placeholder - if (replaceWithVersion) { - let version = '0.0.0'; - if (stampFile) { - // The stamp file is expected to look like - // BUILD_SCM_HASH 83c699db39cfd74526cdf9bebb75aa6f122908bb - // BUILD_SCM_LOCAL_CHANGES true - // BUILD_SCM_VERSION 6.0.0-beta.6+12.sha-83c699d.with-local-changes - // BUILD_TIMESTAMP 1520021990506 - // - // We want version to be the 6.0.0-beta... part - const versionTag = fs.readFileSync(stampFile, {encoding: 'utf-8'}) - .split('\n') - .find(s => s.startsWith('BUILD_SCM_VERSION')); - // Don't assume BUILD_SCM_VERSION exists - if (versionTag) { - version = versionTag.split(' ')[1].replace(/^v/, '').trim(); + // Replace statuses last so that earlier substitutions can add + // status-related placeholders + if (volatileFile || infoFile) { + const statusEntries = parseStatusFile(volatileFile) + statusEntries.push(...parseStatusFile(infoFile)) + // Looks like {'BUILD_SCM_VERSION': 'v1.2.3'} + const statuses = new Map(statusEntries) + for (let idx = 0; idx < substitutions.length; idx++) { + const match = substitutions[idx][1].match(/\{(.*)\}/); + if (!match) continue; + const statusKey = match[1]; + if (statuses.has(statusKey)) { + // FIXME: need to strip leading "v" like we did in + // https://github.com/bazelbuild/rules_nodejs/pull/1591 + // (needs a test added) + substitutions[idx][1] = statuses.get(statusKey); } } - substitutions.push([new RegExp(replaceWithVersion, 'g'), version]); } // src like baseDir/my/path is just copied to outDir/my/path diff --git a/internal/pkg_npm/pkg_npm.bzl b/internal/pkg_npm/pkg_npm.bzl index 21e0cda340..dbc6ae03c6 100644 --- a/internal/pkg_npm/pkg_npm.bzl +++ b/internal/pkg_npm/pkg_npm.bzl @@ -91,8 +91,13 @@ PKG_NPM_ATTRS = { doc = """Optional package_name that this npm package may be imported as.""", ), "replace_with_version": attr.string( - doc = """If set this value is replaced with the version stamp data. - See the section on stamping in the README.""", + doc = """DEPRECATED: use substitutions instead. + + `replace_with_version = "my_version_placeholder"` is just syntax sugar for + `substitutions = {"my_version_placeholder": "{BUILD_SCM_VERSION}"}`. + + Follow this deprecation at https://github.com/bazelbuild/rules_nodejs/issues/1694 + """, default = "0.0.0-PLACEHOLDER", ), "srcs": attr.label_list( @@ -100,7 +105,12 @@ PKG_NPM_ATTRS = { allow_files = True, ), "substitutions": attr.string_dict( - doc = """Key-value pairs which are replaced in all the files while building the package.""", + doc = """Key-value pairs which are replaced in all the files while building the package. + + You can use values from the workspace status command using curly braces, for example + `{"0.0.0-PLACEHOLDER": "{STABLE_GIT_VERSION}"}`. + See the section on stamping in the README + """, ), "vendor_external": attr.string_list( doc = """External workspaces whose contents should be vendored into this workspace. @@ -184,7 +194,15 @@ def create_package(ctx, deps_files, nested_packages): # current package unless explicitely specified. filtered_deps_sources = _filter_out_external_files(ctx, deps_files, package_path) + # Back-compat for the replace_with_version stamping + # see https://github.com/bazelbuild/rules_nodejs/issues/1694 for removal + substitutions = dict(**ctx.attr.substitutions) + if ctx.attr.replace_with_version: + substitutions[ctx.attr.replace_with_version] = "{BUILD_SCM_VERSION}" + args = ctx.actions.args() + inputs = ctx.files.srcs + deps_files + nested_packages + args.use_param_file("%s", use_always = True) args.add(package_dir.path) args.add(package_path) @@ -193,19 +211,23 @@ def create_package(ctx, deps_files, nested_packages): args.add(ctx.genfiles_dir.path) args.add_joined(filtered_deps_sources, join_with = ",", omit_if_empty = False) args.add_joined([p.path for p in nested_packages], join_with = ",", omit_if_empty = False) - args.add(ctx.attr.substitutions) - args.add(ctx.attr.replace_with_version) - args.add(ctx.version_file.path if stamp else "") - args.add_joined(ctx.attr.vendor_external, join_with = ",", omit_if_empty = False) + args.add(substitutions) - inputs = ctx.files.srcs + deps_files + nested_packages - - # The version_file is an undocumented attribute of the ctx that lets us read the volatile-status.txt file - # produced by the --workspace_status_command. That command will be executed whenever - # this action runs, so we get the latest version info on each execution. - # See https://github.com/bazelbuild/bazel/issues/1054 if stamp: + # The version_file is an undocumented attribute of the ctx that lets us read the volatile-status.txt file + # produced by the --workspace_status_command. + # Similarly info_file reads the stable-status.txt file. + # That command will be executed whenever + # this action runs, so we get the latest version info on each execution. + # See https://github.com/bazelbuild/bazel/issues/1054 + args.add(ctx.version_file.path) inputs.append(ctx.version_file) + args.add(ctx.info_file.path) + inputs.append(ctx.info_file) + else: + args.add_all(["", ""]) + + args.add_joined(ctx.attr.vendor_external, join_with = ",", omit_if_empty = False) ctx.actions.run( progress_message = "Assembling npm package %s" % package_dir.short_path,