From 76e7bc197e6248b660a633a80399fcb8ceab50c9 Mon Sep 17 00:00:00 2001 From: Joey Perrott Date: Mon, 19 Apr 2021 10:18:28 -0700 Subject: [PATCH] fix(builtin): properly parse status file value containing spaces Based on Bazel's documentation, values provided through the workspace_status_command can contain whitespace on the same line the key is defined on: > The key names can be anything but they may only use upper case letters and underscores. The > first space after the key name separates it from the value. The value is the rest of the line > (including additional whitespaces). The `parseStatusFile` utility function is updated to rely on a regex for performing this parsing to match the described behavior. --- internal/pkg_npm/packager.js | 46 +++++++++++------- internal/pkg_web/assembler.js | 38 +++++++++++---- packages/rollup/install.md | 42 ++++++++++++++--- .../rollup/test/integration/rollup.config.js | 44 +++++++++++++---- .../test/version_stamp/rollup.config.js | 47 ++++++++++++++----- 5 files changed, 164 insertions(+), 53 deletions(-) diff --git a/internal/pkg_npm/packager.js b/internal/pkg_npm/packager.js index 0cde7a45ae..f9041a3a92 100644 --- a/internal/pkg_npm/packager.js +++ b/internal/pkg_npm/packager.js @@ -61,21 +61,32 @@ function unquoteArgs(s) { } /** - * 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 - */ +* 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 +* +* Parsing regex is created based on Bazel's documentation describing the status file schema: +* The key names can be anything but they may only use upper case letters and underscores. The +* first space after the key name separates it from the value. The value is the rest of the line +* (including additional whitespaces). +* +* @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(' ')); + const results = {}; + const statusFile = fs.readFileSync(p, {encoding: 'utf-8'}); + for (const match of `\n${statusFile}`.matchAll(/^([A-Z_]+) (.*)/gm)) { + // Lines which go unmatched define an index value of `0` and should be skipped. + if (match.index === 0) { + continue; + } + results[match[1]] = match[2]; + } + return results; } function main(args) { @@ -95,15 +106,16 @@ function main(args) { // Replace statuses last so that earlier substitutions can add // status-related placeholders if (volatileFile || infoFile) { - const statusEntries = parseStatusFile(volatileFile) - statusEntries.push(...parseStatusFile(infoFile)) + const statuses = { + ...parseStatusFile(volatileFile), + ...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]; - let statusValue = statuses.get(statusKey); + let statusValue = statuses[statusKey]; if (statusValue) { // npm versions must be numeric, so if the VCS tag starts with leading 'v', strip it // See https://github.com/bazelbuild/rules_nodejs/pull/1591 diff --git a/internal/pkg_web/assembler.js b/internal/pkg_web/assembler.js index 5a0743915c..9f2d20cdbf 100644 --- a/internal/pkg_web/assembler.js +++ b/internal/pkg_web/assembler.js @@ -34,15 +34,33 @@ function unquoteArgs(s) { return s.replace(/^'(.*)'$/, '$1'); } -function getBazelStatusMappings(statusFilePath) { - if (!statusFilePath) return {}; - const stampFileLines = fs.readFileSync(statusFilePath, {encoding: 'utf-8'}).trim().split('\n'); - const stampMap = {}; - for (const line of stampFileLines) { - const [key, value] = line.split(' '); - stampMap[key] = value; +/** +* 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 +* +* Parsing regex is created based on Bazel's documentation describing the status file schema: +* The key names can be anything but they may only use upper case letters and underscores. The +* first space after the key name separates it from the value. The value is the rest of the line +* (including additional whitespaces). +* +* @param {string} p the path to the status file +* @returns a two-dimensional array of key/value pairs +*/ +function parseStatusFile(p) { + if (!p) return []; + const results = {}; + const statusFile = fs.readFileSync(p, {encoding: 'utf-8'}); + for (const match of `\n${statusFile}`.matchAll(/^([A-Z_]+) (.*)/gm)) { + // Lines which go unmatched define an index value of `0` and should be skipped. + if (match.index === 0) { + continue; + } + results[match[1]] = match[2]; } - return stampMap; + return results; } function normalizeSubstitutions(substitutionsArg, stampMap) { @@ -74,8 +92,8 @@ function main(params) { const rawSubstitutions = params.shift().replace(/^'(.*)'$/, '$1'); const stampMap = { - ...getBazelStatusMappings(volatileFilePath), - ...getBazelStatusMappings(stableFilePath), + ...parseStatusFile(volatileFilePath), + ...parseStatusFile(stableFilePath), }; const normalizedSubstitutions = normalizeSubstitutions(rawSubstitutions, stampMap) diff --git a/packages/rollup/install.md b/packages/rollup/install.md index 669fd67c69..05ec9aec05 100644 --- a/packages/rollup/install.md +++ b/packages/rollup/install.md @@ -99,15 +99,43 @@ To use these files, you write JS code in your `rollup.config.js` to read from th Each line is a space-separated key/value pair. ```javascript +/** +* 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 +* +* Parsing regex is created based on Bazel's documentation describing the status file schema: +* The key names can be anything but they may only use upper case letters and underscores. The +* first space after the key name separates it from the value. The value is the rest of the line +* (including additional whitespaces). +* +* @param {string} p the path to the status file +* @returns a two-dimensional array of key/value pairs +*/ +function parseStatusFile(p) { + if (!p) return []; + const results = {}; + const statusFile = require('fs').readFileSync(p, {encoding: 'utf-8'}); + for (const match of `\n${statusFile}`.matchAll(/^([A-Z_]+) (.*)/gm)) { + // Lines which go unmatched define an index value of `0` and should be skipped. + if (match.index === 0) { + continue; + } + results[match[1]] = match[2]; + } + return results; +} + +const statuses = parseStatusFile(bazel_version_file); // Parse the stamp file produced by Bazel from the version control system let version = ''; -if (bazel_info_file) { - const versionTag = require('fs') - .readFileSync(bazel_info_file, {encoding: 'utf-8'}) - .split('\n') - .find(s => s.startsWith('STABLE_GIT_COMMIT')); - if (versionTag) { - version = 'v' + versionTag.split(' ')[1].trim(); +// Don't assume BUILD_SCM_VERSION exists +if (statuses['BUILD_SCM_VERSION']) { + version = 'v' + statuses['BUILD_SCM_VERSION']; + if (DEBUG) { + version += '_debug'; } } ``` diff --git a/packages/rollup/test/integration/rollup.config.js b/packages/rollup/test/integration/rollup.config.js index 70e6c3755c..fa9041d597 100644 --- a/packages/rollup/test/integration/rollup.config.js +++ b/packages/rollup/test/integration/rollup.config.js @@ -2,16 +2,44 @@ import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import nodeResolve from '@rollup/plugin-node-resolve'; +/** +* 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 +* +* Parsing regex is created based on Bazel's documentation describing the status file schema: +* The key names can be anything but they may only use upper case letters and underscores. The +* first space after the key name separates it from the value. The value is the rest of the line +* (including additional whitespaces). +* +* @param {string} p the path to the status file +* @returns a two-dimensional array of key/value pairs +*/ +function parseStatusFile(p) { + if (!p) return []; + const results = {}; + const statusFile = require('fs').readFileSync(p, {encoding: 'utf-8'}); + for (const match of `\n${statusFile}`.matchAll(/^([A-Z_]+) (.*)/gm)) { + // Lines which go unmatched define an index value of `0` and should be skipped. + if (match.index === 0) { + continue; + } + results[match[1]] = match[2]; + } + return results; +} + // Parse the stamp file produced by Bazel from the version control system let version = ''; -if (bazel_version_file) { - const versionTag = require('fs') - .readFileSync(bazel_version_file, {encoding: 'utf-8'}) - .split('\n') - .find(s => s.startsWith('BUILD_SCM_VERSION')); - // Don't assume BUILD_SCM_VERSION exists - if (versionTag) { - version = 'v' + versionTag.split(' ')[1].trim(); + +const statuses = parseStatusFile(bazel_version_file); +// Don't assume BUILD_SCM_VERSION exists +if (statuses['BUILD_SCM_VERSION']) { + version = 'v' + statuses['BUILD_SCM_VERSION']; + if (DEBUG) { + version += '_debug'; } } diff --git a/packages/rollup/test/version_stamp/rollup.config.js b/packages/rollup/test/version_stamp/rollup.config.js index 1daf460875..6ed85d752a 100644 --- a/packages/rollup/test/version_stamp/rollup.config.js +++ b/packages/rollup/test/version_stamp/rollup.config.js @@ -1,18 +1,43 @@ +/** +* 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 +* +* Parsing regex is created based on Bazel's documentation describing the status file schema: +* The key names can be anything but they may only use upper case letters and underscores. The +* first space after the key name separates it from the value. The value is the rest of the line +* (including additional whitespaces). +* +* @param {string} p the path to the status file +* @returns a two-dimensional array of key/value pairs +*/ +function parseStatusFile(p) { + if (!p) return []; + const results = {}; + const statusFile = require('fs').readFileSync(p, {encoding: 'utf-8'}); + for (const match of `\n${statusFile}`.matchAll(/^([A-Z_]+) (.*)/gm)) { + // Lines which go unmatched define an index value of `0` and should be skipped. + if (match.index === 0) { + continue; + } + results[match[1]] = match[2]; + } + return results; +} + const DEBUG = process.env['COMPILATION_MODE'] === 'dbg'; // Parse the stamp file produced by Bazel from the version control system let version = ''; -if (bazel_version_file) { - const versionTag = require('fs') - .readFileSync(bazel_version_file, {encoding: 'utf-8'}) - .split('\n') - .find(s => s.startsWith('BUILD_SCM_VERSION')); - // Don't assume BUILD_SCM_VERSION exists - if (versionTag) { - version = 'v' + versionTag.split(' ')[1].trim(); - if (DEBUG) { - version += '_debug'; - } + +const statuses = parseStatusFile(bazel_version_file); +// Don't assume BUILD_SCM_VERSION exists +if (statuses['BUILD_SCM_VERSION']) { + version = 'v' + statuses['BUILD_SCM_VERSION']; + if (DEBUG) { + version += '_debug'; } }