diff --git a/.gitignore b/.gitignore index 11a0b06..b651a3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /d3-flame-graph-*.tar.gz -/d3-flame-graph-deps-*.tar.xz +/js-d3-flame-graph-vendor-*.tar.xz +/js-d3-flame-graph-vendor-*.tar.xz.manifest +/d3-flame-graph*/ +*.rpm diff --git a/001-remove-unused-frontend-crypto-and-update-hash-fn.patch b/001-remove-unused-frontend-crypto-and-update-hash-fn.patch new file mode 100644 index 0000000..ff0e49a --- /dev/null +++ b/001-remove-unused-frontend-crypto-and-update-hash-fn.patch @@ -0,0 +1,80 @@ +diff --git a/package.json b/package.json +index 2284a2f..26bb09b 100644 +--- a/package.json ++++ b/package.json +@@ -58,5 +58,8 @@ + "d3-selection": "^1.4.1", + "d3-transition": "^1.3.2", + "d3-dispatch": "^1.0.6" ++ }, ++ "resolutions": { ++ "crypto-browserify": "https://registry.yarnpkg.com/@favware/skip-dependency/-/skip-dependency-1.1.1.tgz" + } + } +diff --git a/webpack.config.js b/webpack.config.js +index 7153477..e663cf1 100644 +--- a/webpack.config.js ++++ b/webpack.config.js +@@ -19,7 +19,11 @@ module.exports = [{ + filename: '[name].js', + library: 'flamegraph', + libraryExport: 'default', +- libraryTarget: 'umd' ++ libraryTarget: 'umd', ++ hashFunction: 'sha256', ++ }, ++ node: { ++ crypto: false + }, + plugins: [ + new CopyWebpackPlugin([{ +@@ -54,7 +58,11 @@ module.exports = [{ + path: path.resolve(__dirname, 'dist'), + filename: '[name].js', + library: ['flamegraph', 'colorMapper'], +- libraryTarget: 'umd' ++ libraryTarget: 'umd', ++ hashFunction: 'sha256', ++ }, ++ node: { ++ crypto: false + }, + module: { + rules: [{ +@@ -80,7 +88,11 @@ module.exports = [{ + path: path.resolve(__dirname, 'dist'), + filename: '[name].js', + library: ['flamegraph', 'tooltip'], +- libraryTarget: 'umd' ++ libraryTarget: 'umd', ++ hashFunction: 'sha256', ++ }, ++ node: { ++ crypto: false + }, + module: { + rules: [{ +@@ -101,7 +113,11 @@ module.exports = [{ + entry: './template.js', + output: { + path: path.resolve(__dirname, 'dist', 'templates'), +- filename: 'bundle.js' ++ filename: 'bundle.js', ++ hashFunction: 'sha256', ++ }, ++ node: { ++ crypto: false + }, + plugins: [ + new CleanWebpackPlugin({ +@@ -131,5 +147,10 @@ module.exports = [{ + use: ['style-loader', 'css-loader'] + } + ] ++ }, ++ optimization: { ++ minimizer: [ ++ new TerserPlugin() ++ ] + } + }] diff --git a/002-webpack-update-hash-fn.vendor.patch b/002-webpack-update-hash-fn.vendor.patch new file mode 100644 index 0000000..b842d88 --- /dev/null +++ b/002-webpack-update-hash-fn.vendor.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/webpack/lib/optimize/ConcatenatedModule.js b/node_modules/webpack/lib/optimize/ConcatenatedModule.js +index 03711c4..df4d216 100644 +--- a/node_modules/webpack/lib/optimize/ConcatenatedModule.js ++++ b/node_modules/webpack/lib/optimize/ConcatenatedModule.js +@@ -560,7 +560,7 @@ class ConcatenatedModule extends Module { + orderedConcatenationListIdentifiers += " "; + } + } +- const hash = createHash("md4"); ++ const hash = createHash("sha256"); + hash.update(orderedConcatenationListIdentifiers); + return this.rootModule.identifier() + " " + hash.digest("hex"); + } diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cef2f9b --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +ifndef VER + $(error VER is undefined) +endif +ifndef REL + $(error REL is undefined) +endif + +NAME := d3-flame-graph +RPM_NAME := js-$(NAME) +SOURCE_DIR := $(NAME)-$(VER) +SOURCE_TAR := $(NAME)-$(VER).tar.gz +VENDOR_TAR := $(RPM_NAME)-vendor-$(VER)-$(REL).tar.xz + +ALL_PATCHES := $(sort $(wildcard *.patch)) +VENDOR_PATCHES := $(sort $(wildcard *.vendor.patch)) +SOURCE_PATCHES := $(filter-out $(VENDOR_PATCHES),$(ALL_PATCHES)) + +all: $(SOURCE_TAR) $(VENDOR_TAR) + +$(SOURCE_TAR): + spectool -g $(RPM_NAME).spec + +$(VENDOR_TAR): $(SOURCE_TAR) + rm -rf $(SOURCE_DIR) + tar xf $(SOURCE_TAR) + + # Patches to apply before vendoring + for patch in $(SOURCE_PATCHES); do echo $$patch; patch -d $(SOURCE_DIR) -p1 --fuzz=0 < $$patch; done + + # Node.js + cd $(SOURCE_DIR) && yarn import && yarn install --pure-lockfile + ./list_bundled_nodejs_packages.py $(SOURCE_DIR)/ >> $@.manifest + + # Patches to apply after vendoring + for patch in $(VENDOR_PATCHES); do echo $$patch; patch -d $(SOURCE_DIR) -p1 --fuzz=0 < $$patch; done + + # Create tarball + XZ_OPT=-9 tar cJf $@ \ + $(SOURCE_DIR)/node_modules + +clean: + rm -rf *.tar.gz *.tar.xz *.manifest *.rpm $(NAME)-*/ diff --git a/README.md b/README.md index 88eb3b8..b72f694 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,22 @@ The js-d3-flame-graph package -## Build instructions -``` -VER=3.0.2 -spectool -g js-d3-flame-graph.spec -./create_dependency_bundle.sh d3-flame-graph-$VER.tar.gz d3-flame-graph-deps-$VER.tar.xz -./check_npm_dependencies.py js-d3-flame-graph.spec d3-flame-graph-$VER.tar.gz d3-flame-graph-deps-$VER.tar.xz -fedpkg new-sources d3-flame-graph-$VER.tar.gz d3-flame-graph-deps-$VER.tar.xz +## Upgrade instructions +(replace X.Y.Z and R with the new version/release) -fedpkg local -fedpkg lint -fedpkg mockbuild -fedpkg scratch-build --srpm +* update `Version`, `Release` and `%changelog` in the specfile +* create bundles and manifest: `VER=X.Y.Z REL=R make clean all` +* update specfile with contents of the `.manifest` file +* run local build: `rpkg local` +* run rpm linter: `rpkg lint -r js-d3-flame-graph.rpmlintrc` +* run a scratch build: `fedpkg scratch-build --srpm` +* upload new source tarballs: `fedpkg new-sources *.tar.gz *.tar.xz` -fedpkg build -fedpkg update -``` +## Backporting +* create the patch +* declare and apply (`%prep`) the patch in the specfile +* if the patch affects Node.js dependencies + * create new tarballs + * update the specfile with new tarball path and contents of the `.manifest` file + +Note: the Makefile automatically applies patches before creating the tarballs diff --git a/check_npm_dependencies.py b/check_npm_dependencies.py deleted file mode 100755 index b10a762..0000000 --- a/check_npm_dependencies.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 -import sys -import tarfile -import json -import os.path - -def read_spec_provides(spec_path): - with open(spec_path) as f: - for line in f: - if line.startswith('Provides:'): - yield line.strip() - -def read_node_deps(source_archive_path): - root_dir = os.path.basename(source_archive_path)[:-len('.tar.gz')] - with tarfile.open(source_archive_path) as tar: - f = tar.extractfile(f'{root_dir}/package.json') - package_json = json.load(f) - return list(package_json['devDependencies'].keys()) + list(package_json['dependencies'].keys()) - -def read_node_deps_versions(dep_bundle_path, dependencies): - with tarfile.open(dep_bundle_path) as tar: - for dependency in dependencies: - f = tar.extractfile(f'node_modules/{dependency}/package.json') - package_json = json.load(f) - yield f'Provides: bundled(nodejs-{package_json["name"]}) = {package_json["version"]}' - -if __name__ == '__main__': - if len(sys.argv) != 4: - print('usage: {} specfile source-archive dependency-bundle'.format(sys.argv[0])) - sys.exit(1) - - provides = list(read_spec_provides(sys.argv[1])) - dependencies = sorted(read_node_deps(sys.argv[2])) - actual_provides = list(read_node_deps_versions(sys.argv[3], dependencies)) - - if provides == actual_provides: - print("Dependencies are up-to-date with the specfile.") - sys.exit(0) - else: - print("Dependencies don't match, please update the specfile:") - print ('\n'.join(actual_provides)) - sys.exit(1) diff --git a/create_dependency_bundle.sh b/create_dependency_bundle.sh deleted file mode 100755 index b0af465..0000000 --- a/create_dependency_bundle.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -eu - -SRC=$(readlink -f "${1:?Usage: $0 source destination}") -DEST=$(readlink -f "${2:?Usage: $0 source destination}") - -if [ -f "$DEST" ]; then - echo "File $DEST exists already." - exit 0 -fi -if [ "$#" -gt 2 ]; then - PATCHES=$(readlink -f "${@:3}") -else - PATCHES="" -fi - -pushd "$(mktemp -d)" - -echo Extracting sources... -tar xfz "$SRC" -cd d3-flame-graph-* - -echo Applying patches... -for patch in $PATCHES -do - patch -p1 < $patch -done - -echo Installing dependencies... -npm install - -echo Compressing... -XZ_OPT=-9 tar cJf "$DEST" node_modules - -popd diff --git a/js-d3-flame-graph.rpmlintrc b/js-d3-flame-graph.rpmlintrc new file mode 100644 index 0000000..fa44ec4 --- /dev/null +++ b/js-d3-flame-graph.rpmlintrc @@ -0,0 +1,2 @@ +addFilter("W: strange-permission list_bundled_nodejs_packages.py 755") +addFilter("W: patch-not-applied .+.vendor.patch") diff --git a/js-d3-flame-graph.spec b/js-d3-flame-graph.spec index 89aedd1..2b451c6 100644 --- a/js-d3-flame-graph.spec +++ b/js-d3-flame-graph.spec @@ -3,7 +3,7 @@ Name: js-d3-flame-graph Version: 3.0.2 -Release: 4%{?dist} +Release: 5%{?dist} Summary: A D3.js plugin that produces flame graphs BuildArch: noarch @@ -12,40 +12,44 @@ License: ASL 2.0 URL: %{github} Source0: %{github}/archive/%{version}/%{pkgname}-%{version}.tar.gz -Source1: d3-flame-graph-deps-%{version}.tar.xz -Source2: create_dependency_bundle.sh +Source1: js-d3-flame-graph-vendor-%{version}-6.tar.xz +Source2: Makefile +Source3: list_bundled_nodejs_packages.py + +Patch1: 001-remove-unused-frontend-crypto-and-update-hash-fn.patch +Patch2: 002-webpack-update-hash-fn.vendor.patch BuildRequires: web-assets-devel BuildRequires: nodejs Requires: web-assets-filesystem # Bundled npm packages -Provides: bundled(nodejs-clean-webpack-plugin) = 3.0.0 -Provides: bundled(nodejs-copy-webpack-plugin) = 5.1.1 -Provides: bundled(nodejs-css-loader) = 3.4.2 -Provides: bundled(nodejs-d3-array) = 2.4.0 -Provides: bundled(nodejs-d3-dispatch) = 1.0.6 -Provides: bundled(nodejs-d3-ease) = 1.0.6 -Provides: bundled(nodejs-d3-format) = 1.4.3 -Provides: bundled(nodejs-d3-hierarchy) = 1.1.9 -Provides: bundled(nodejs-d3-scale) = 3.2.1 -Provides: bundled(nodejs-d3-selection) = 1.4.1 -Provides: bundled(nodejs-d3-transition) = 1.3.2 -Provides: bundled(nodejs-eslint) = 6.8.0 -Provides: bundled(nodejs-eslint-config-standard) = 14.1.0 -Provides: bundled(nodejs-eslint-loader) = 3.0.3 -Provides: bundled(nodejs-eslint-plugin-import) = 2.20.1 -Provides: bundled(nodejs-eslint-plugin-node) = 11.0.0 -Provides: bundled(nodejs-eslint-plugin-promise) = 4.2.1 -Provides: bundled(nodejs-eslint-plugin-standard) = 4.0.1 -Provides: bundled(nodejs-html-webpack-plugin) = 3.2.0 -Provides: bundled(nodejs-script-ext-html-webpack-plugin) = 2.1.4 -Provides: bundled(nodejs-style-loader) = 1.1.3 -Provides: bundled(nodejs-tape) = 4.13.2 -Provides: bundled(nodejs-terser-webpack-plugin) = 2.3.5 -Provides: bundled(nodejs-webpack) = 4.42.0 -Provides: bundled(nodejs-webpack-cli) = 3.3.11 -Provides: bundled(nodejs-webpack-dev-server) = 3.10.3 +Provides: bundled(npm(clean-webpack-plugin)) = 3.0.0 +Provides: bundled(npm(copy-webpack-plugin)) = 5.1.1 +Provides: bundled(npm(css-loader)) = 3.4.2 +Provides: bundled(npm(d3-array)) = 2.4.0 +Provides: bundled(npm(d3-dispatch)) = 1.0.6 +Provides: bundled(npm(d3-ease)) = 1.0.6 +Provides: bundled(npm(d3-format)) = 1.4.3 +Provides: bundled(npm(d3-hierarchy)) = 1.1.9 +Provides: bundled(npm(d3-scale)) = 3.2.1 +Provides: bundled(npm(d3-selection)) = 1.4.1 +Provides: bundled(npm(d3-transition)) = 1.3.2 +Provides: bundled(npm(eslint)) = 6.8.0 +Provides: bundled(npm(eslint-config-standard)) = 14.1.0 +Provides: bundled(npm(eslint-loader)) = 3.0.3 +Provides: bundled(npm(eslint-plugin-import)) = 2.20.1 +Provides: bundled(npm(eslint-plugin-node)) = 11.0.0 +Provides: bundled(npm(eslint-plugin-promise)) = 4.2.1 +Provides: bundled(npm(eslint-plugin-standard)) = 4.0.1 +Provides: bundled(npm(html-webpack-plugin)) = 3.2.0 +Provides: bundled(npm(script-ext-html-webpack-plugin)) = 2.1.4 +Provides: bundled(npm(style-loader)) = 1.1.3 +Provides: bundled(npm(tape)) = 4.13.2 +Provides: bundled(npm(terser-webpack-plugin)) = 1.4.3 +Provides: bundled(npm(webpack)) = 4.42.0 +Provides: bundled(npm(webpack-cli)) = 3.3.11 +Provides: bundled(npm(webpack-dev-server)) = 3.10.3 %description A D3.js plugin that produces flame graphs from hierarchical data. @@ -59,12 +63,15 @@ Documentation and example files for js-d3-flame-graph. %prep -%setup -q -n %{pkgname}-%{version} -%setup -q -a 1 -n %{pkgname}-%{version} +%setup -q -T -D -b 0 -n %{pkgname}-%{version} +%setup -q -T -D -b 1 -n %{pkgname}-%{version} + +%patch1 -p1 + %build -rm -rf dist -./node_modules/webpack/bin/webpack.js --mode production +./node_modules/.bin/webpack --mode production + %install install -d -m 755 %{buildroot}/%{_datadir}/%{pkgname} @@ -74,6 +81,7 @@ rmdir dist/templates install -d -m 755 %{buildroot}/%{_jsdir}/%{pkgname} cp -a dist/* %{buildroot}/%{_jsdir}/%{pkgname} + %files %{_jsdir}/%{pkgname} %{_datadir}/%{pkgname} @@ -81,10 +89,16 @@ cp -a dist/* %{buildroot}/%{_jsdir}/%{pkgname} %license LICENSE %doc README.md + %files doc %doc README.md examples + %changelog +* Wed Sep 29 2021 Andreas Gerstmayr - 3.0.2-5 +- change webpack hash function to sha256 +- remove unused cryptographic implementations + * Thu Jul 22 2021 Fedora Release Engineering - 3.0.2-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild diff --git a/list_bundled_nodejs_packages.py b/list_bundled_nodejs_packages.py new file mode 100755 index 0000000..3158c2c --- /dev/null +++ b/list_bundled_nodejs_packages.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# +# generates Provides: bundled(npm(...)) = ... lines for each declared dependency and devDependency of package.json +# +import sys +import json +import re +from packaging import version + + +def read_declared_pkgs(package_json_path): + with open(package_json_path) as f: + package_json = json.load(f) + return list(package_json['dependencies'].keys()) + list(package_json['devDependencies'].keys()) + + +def read_installed_pkgs(yarn_lock_path): + with open(yarn_lock_path) as f: + lockfile = f.read() + return re.findall(r'^"?' # can start with a " + r'(.+?)@.+(?:,.*)?:\n' # characters up to @ + r' version "(.+)"', # and the version + lockfile, re.MULTILINE) + + +def list_provides(declared_pkgs, installed_pkgs): + for declared_pkg in declared_pkgs: + # there can be multiple versions installed of one package (transitive dependencies) + # but rpm doesn't support Provides: with a single package and multiple versions + # so let's declare the oldest version here + versions = [version.parse(pkg_version) + for pkg_name, pkg_version in installed_pkgs if pkg_name == declared_pkg] + oldest_version = sorted(versions)[0] + yield f"Provides: bundled(npm({declared_pkg})) = {oldest_version}" + + +if __name__ == "__main__": + if len(sys.argv) != 2: + print(f"usage: {sys.argv[0]} package-X.Y.Z/", file=sys.stdout) + sys.exit(1) + + package_dir = sys.argv[1] + declared_pkgs = read_declared_pkgs(f"{package_dir}/package.json") + installed_pkgs = read_installed_pkgs(f"{package_dir}/yarn.lock") + provides = list_provides(declared_pkgs, installed_pkgs) + for provide in sorted(provides): + print(provide) diff --git a/sources b/sources index d8fa059..1dc559a 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ SHA512 (d3-flame-graph-3.0.2.tar.gz) = 992c21ed8e88428cfa2475c6b4cffbc14fbdb163137cdab79a7f93ec9df8acacb890037a763d2e0d81e6e3755c8eb2d502cfaeb9027a077e80c5691c40516e47 -SHA512 (d3-flame-graph-deps-3.0.2.tar.xz) = b062cf151816a5d1ef4d60667097ed6e395dc14d5dcd877423e62ebed7e0d8c8513ac63f1d82c55ba03fa965a069afcaacba438e2ad1c6967f78f26428432900 +SHA512 (js-d3-flame-graph-vendor-3.0.2-6.tar.xz) = 8afa777c80d0b8e8fcc8b10e440fbefae9b35f3ee184efa1e8782c88042bca8d1d5b719f97a580b9360b7506786a78e9f9ae0710810c8fa9b1ee5d851362b0d4