import nodejs-packaging-2023.10-5.el10

c10-beta imports/c10-beta/nodejs-packaging-2023.10-5.el10
MSVSphere Packaging Team 1 month ago
commit 8445d05bfa
Signed by: sys_gitsync
GPG Key ID: B2B0B9F29E528FE8

1
.gitignore vendored

@ -0,0 +1 @@
SOURCES/test.tar.gz

@ -0,0 +1 @@
dfaa3308397a75e566baec53cc32e6a635eca1e9 SOURCES/test.tar.gz

@ -0,0 +1,19 @@
Copyright 2012, 2013 T.C. Hollingsworth <tchollingsworth@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

@ -0,0 +1,79 @@
# How to bundle nodejs libraries in Fedora
The upstream Node.js stance on
[global library packages](https://nodejs.org/en/blog/npm/npm-1-0-global-vs-local-installation/)
is that they are ".. best avoided if not needed." In Fedora, we take the same
stance with our nodejs packages. You can provide a package that uses nodejs,
but you should bundle all the nodejs libraries that are needed.
We are providing a sample spec file and bundling script here.
For more detailed packaging information go to the
[Fedora Node.js Packaging Guildelines](https://docs.fedoraproject.org/en-US/packaging-guidelines/Node.js/)
## Bundling Script
```
nodejs-packaging-bundler <npm_name> [version] [tarball]
```
nodejs-packaging-bundler is it's own package, nodejs-packaging-bundler and must be installed before use.
nodejs-packaging-bundler gets the latest npm version available, if no version is given, or uses the existing tarball if one is given. Using npm is preferred for to ease reproducibility. If a local tarball is required (e.g. because the package is missing from npm or its version is too old), please ensure to document how the tarball was created.
It produces four files and puts them in ${HOME}/rpmbuild/SOURCES
* <npm_name>-<version>.tgz - This is the tarball from npm.org
* <npm_name>-<version>-nm-prod.tgz - This is the tarball that contains all the bundled nodejs modules <npm_name> needs to run
* <npm_name>-<version>-nm-dev.tgz - This is the tarball that contains all the bundled nodejs modules <npm_name> needs to test
* <npm_name>-<version>-bundled-licenses.txt - This lists the bundled licenses in <npm_name>-<version>-nm-prod.tgz
## Sample Spec File
```
%global npm_name my_nodejs_application
...
License: <license1> and <license2> and <license3>
...
Source0: http://registry.npmjs.org/%{npm_name}/-/%{npm_name}-%{version}.tgz
Source1: %{npm_name}-%{version}-nm-prod.tgz
Source2: %{npm_name}-%{version}-nm-dev.tgz
Source3: %{npm_name}-%{version}-bundled-licenses.txt
...
BuildRequires: nodejs-devel
...
%prep
%setup -q -n package
cp %{SOURCE3} .
...
%build
# Setup bundled node modules
tar xfz %{SOURCE1}
mkdir -p node_modules
pushd node_modules
ln -s ../node_modules_prod/* .
ln -s ../node_modules_prod/.bin .
popd
...
%install
mkdir -p %{buildroot}%{nodejs_sitelib}/%{npm_name}
cp -pr index.js lib package.json %{buildroot}%{nodejs_sitelib}/%{npm_name}
# Copy over bundled nodejs modules
cp -pr node_modules node_modules_prod %{buildroot}%{nodejs_sitelib}/%{npm_name}
...
%check
%nodejs_symlink_deps --check
# Setup bundled dev node_modules for testing
tar xfz %{SOURCE2}
pushd node_modules
ln -s ../node_modules_dev/* .
popd
pushd node_modules/.bin
ln -s ../../node_modules_dev/.bin/* .
popd
# Example test run using the binary in ./node_modules/.bin/
./node_modules/.bin/vows --spec --isolate
...
%files
%doc HISTORY.md
%license LICENSE.md %{npm_name}-%{version}-bundled-licenses.txt
%{nodejs_sitelib}/%{npm_name}
```

@ -0,0 +1,38 @@
# nodejs binary
%__nodejs %{_bindir}/node
# currently installed nodejs version
%nodejs_version %(%{__nodejs} -v | sed s/v//)
%_nodejs_major_version %(echo %{nodejs_version} | sed 's#^\\([0-9]\\+\\).*#\\1#')
# nodejs library directory
%nodejs_sitelib %{_prefix}/lib/node_modules_%{_nodejs_major_version}
#arch specific library directory
#for future-proofing only; we don't do multilib
%nodejs_sitearch %{nodejs_sitelib}
# symlink dependencies so `npm link` works
# this should be run in every module's %%install section
# pass --check to work in the current directory instead of the buildroot
# pass --no-devdeps to ignore devDependencies when --check is used
%nodejs_symlink_deps %{_rpmconfigdir}/nodejs-symlink-deps %{nodejs_sitelib}
# patch package.json to fix a dependency
# see `man npm-json` for details on writing dependencies for package.json files
# e.g. `%%nodejs_fixdep frobber` makes any version of frobber do
# `%%nodejs_fixdep frobber '>1.0'` requires frobber > 1.0
# `%%nodejs_fixdep -r frobber removes the frobber dep
%nodejs_fixdep %{_rpmconfigdir}/nodejs-fixdep
# patch package.json to set the package version
# e.g. `%%nodejs_setversion 1.2.3`
%nodejs_setversion %{_rpmconfigdir}/nodejs-setversion
# macro to filter unwanted provides from Node.js binary native modules
%nodejs_default_filter %{expand: \
%global __provides_exclude_from ^%{nodejs_sitearch}/.*\\.node$
}
# no-op macro to allow spec compatibility with EPEL
%nodejs_find_provides_and_requires %{nil}

@ -0,0 +1,3 @@
uglify-js
inherits
nan

@ -0,0 +1,117 @@
#!/usr/bin/python3
"""Modify a dependency listed in a package.json file"""
# Copyright 2013 T.C. Hollingsworth <tchollingsworth@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import json
import optparse
import os
import re
import shutil
import sys
RE_VERSION = re.compile(r'\s*v?([<>=~^]{0,2})\s*([0-9][0-9\.\-]*)\s*')
p = optparse.OptionParser(
description='Modifies dependency entries in package.json files')
p.add_option('-r', '--remove', action='store_true')
p.add_option('-m', '--move', action='store_true')
p.add_option('--dev', action='store_const', const='devDependencies',
dest='deptype', help='affect devDependencies')
p.add_option('--optional', action='store_const', const='optionalDependencies',
dest='deptype', help='affect optionalDependencies')
p.add_option('--caret', action='store_true',
help='convert all or specified dependencies to use the caret operator')
options, args = p.parse_args()
if not os.path.exists('package.json~'):
shutil.copy2('package.json', 'package.json~')
md = json.load(open('package.json'))
deptype = options.deptype if options.deptype is not None else 'dependencies'
if deptype not in md:
md[deptype] = {}
# convert alternate JSON dependency representations to a dictionary
if not options.caret and not isinstance(md[deptype], dict):
if isinstance(md[deptype], list):
deps = md[deptype]
md[deptype] = {}
for dep in deps:
md[deptype][dep] = '*'
elif isinstance(md[deptype], str):
md[deptype] = { md[deptype] : '*' }
if options.remove:
dep = args[0]
del md[deptype][dep]
elif options.move:
dep = args[0]
ver = None
for fromtype in ['dependencies', 'optionalDependencies', 'devDependencies']:
if fromtype in md:
if isinstance(md[fromtype], dict) and dep in md[fromtype]:
ver = md[fromtype][dep]
del md[fromtype][dep]
elif isinstance(md[fromtype], list) and md[fromtype].count(dep) > 0:
ver = '*'
md[fromtype].remove(dep)
elif isinstance(md[fromtype], str) and md[fromtype] == dep:
ver = '*'
del md[fromtype]
if ver != None:
md[deptype][dep] = ver
elif options.caret:
if not isinstance(md[deptype], dict):
sys.stderr.write('All dependencies are unversioned. Unable to apply ' +
'caret operator.\n')
sys.exit(2)
deps = args if len(args) > 0 else md[deptype].keys()
for dep in deps:
if md[deptype][dep][0] == '^':
continue
elif md[deptype][dep][0] in ('~','0','1','2','3','4','5','6','7','8','9'):
ver = re.match(RE_VERSION, md[deptype][dep]).group(2)
md[deptype][dep] = '^' + ver
else:
sys.stderr.write('Attempted to convert non-numeric or tilde ' +
'dependency to caret. This is not permitted.\n')
sys.exit(1)
else:
dep = args[0]
if len(args) > 1:
ver = args[1]
else:
ver = '*'
md[deptype][dep] = ver
fh = open('package.json', 'w')
data = json.JSONEncoder(indent=4).encode(md)
fh.write(data)
fh.close()

@ -0,0 +1,118 @@
#!/bin/bash
OUTPUT_DIR="$(rpm -E '%{_sourcedir}')"
usage() {
echo "Usage `basename $0` <npm_name> [version] [tarball]" >&2
echo >&2
echo " Given a npm module name, and optionally a version," >&2
echo " download the npm, the prod and dev dependencies," >&2
echo " each in their own tarball." >&2
echo " Also finds licenses prod dependencies." >&2
echo " All three tarballs and the license list are copied to ${OUTPUT_DIR}" >&2
echo " If a tarball is passed, use that instead of downloading from npm" >&2
echo >&2
exit 1
}
if ! [ -f /usr/bin/npm ]; then
echo >&2
echo "`basename $0` requires npm to run" >&2
echo >&2
echo "Run the following to fix this" >&2
echo " sudo dnf install npm" >&2
echo >&2
exit 2
fi
if [ $# -lt 1 ]; then
usage
else
case $1 in
-h | --help )
usage
;;
* )
PACKAGE="$1"
;;
esac
fi
if [ $# -ge 2 ]; then
VERSION="$2"
if [ $# -ge 3 ]; then
TARBALL="$(realpath "$3")"
fi
else
VERSION="$(npm view ${PACKAGE} version)"
fi
# the package name might contain invalid characters, sanitize first
PACKAGE_SAFE=$(echo $PACKAGE | sed -e 's|/|-|g')
TMP_DIR=$(mktemp -d -t ci-XXXXXXXXXX)
mkdir -p ${OUTPUT_DIR}
mkdir -p ${TMP_DIR}
pushd ${TMP_DIR}
if [ -f "$TARBALL" ]; then
TARBALL_DIR=$(mktemp -d -t ci-XXXXXXXXXX)
pushd ${TARBALL_DIR}
tar xfz ${TARBALL} --strip-components 1
npm pack .
popd > /dev/null
mv ${TARBALL_DIR}/*.tgz .
rm -rf ${TARBALL_DIR}
else
npm pack ${PACKAGE}
fi
tar xfz *.tgz
cd package
for packagejson in $(find . -type d -name node_modules\* -prune -o -type f -name package.json -print); do
pushd $(dirname $packagejson)
echo " Downloading prod dependencies"
npm install --omit=dev --omit=optional
if [ $? -ge 1 ] ; then
echo " ERROR WILL ROBINSON"
rm -rf node_modules
else
echo " Successful prod dependencies download"
mv node_modules/ node_modules_prod
fi
popd
done
echo "LICENSES IN BUNDLE:"
find . -name "package.json" -exec jq '.license | strings' {} \; >> ${TMP_DIR}/${PACKAGE_SAFE}-${VERSION}-bundled-licenses.txt
find . -name "package.json" -exec jq '.license | objects | .type' {} \; >> ${TMP_DIR}/${PACKAGE_SAFE}-${VERSION}-bundled-licenses.txt 2>/dev/null
find . -name "package.json" -exec jq '.licenses[] .type' {} \; >> ${TMP_DIR}/${PACKAGE_SAFE}-${VERSION}-bundled-licenses.txt 2>/dev/null
sort -u -o ${TMP_DIR}/${PACKAGE_SAFE}-${VERSION}-bundled-licenses.txt ${TMP_DIR}/${PACKAGE_SAFE}-${VERSION}-bundled-licenses.txt
# Locate any dependencies without a provided license
find . -type f -name package.json -execdir jq 'if .license==null and .licenses==null then .name else null end' '{}' '+' | grep -vE '^null$' | sort -u > ${TMP_DIR}/nolicense.txt
if [ -s ${TMP_DIR}/nolicense.txt ]; then
echo -e "\e[5m\e[41mSome dependencies do not list a license. Manual verification required!\e[0m"
cat ${TMP_DIR}/nolicense.txt
echo -e "\e[5m\e[41m======================================================================\e[0m"
fi
for packagejson in $(find . -type d -name node_modules\* -prune -o -type f -name package.json -print); do
pushd $(dirname $packagejson)
echo " Downloading dev dependencies"
npm install --omit=optional
if [ $? -ge 1 ] ; then
echo " ERROR WILL ROBINSON"
else
echo " Successful dev dependencies download"
mv node_modules/ node_modules_dev
fi
popd
done
if [ -d node_modules_prod ] ; then
tar cfz ../${PACKAGE_SAFE}-${VERSION}-nm-prod.tgz $(find . -type d -name node_modules_prod)
fi
if [ -d node_modules_dev ] ; then
tar cfz ../${PACKAGE_SAFE}-${VERSION}-nm-dev.tgz $(find . -type d -name node_modules_dev)
fi
cd ..
cp -v ${PACKAGE_SAFE}-${VERSION}* "${OUTPUT_DIR}"
popd > /dev/null
rm -rf ${TMP_DIR}

@ -0,0 +1,43 @@
#!/usr/bin/python3
"""Set a package version in a package.json file"""
# Copyright 2018 Tom Hughes <tom@compton.nu>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import json
import os
import shutil
import sys
if not os.path.exists('package.json~'):
shutil.copy2('package.json', 'package.json~')
md = json.load(open('package.json'))
if 'version' in md and sys.argv[1] != md['version']:
raise RuntimeError('Version is already set to {0}'.format(md['version']))
else:
md['version'] = sys.argv[1]
fh = open('package.json', 'w')
data = json.JSONEncoder(indent=4).encode(md)
fh.write(data)
fh.close()

@ -0,0 +1,141 @@
#!/usr/bin/python3
"""Symlink a node module's dependencies into the node_modules directory so users
can `npm link` RPM-installed modules into their personal projects."""
# Copyright 2012, 2013 T.C. Hollingsworth <tchollingsworth@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import json
import os
import re
import shutil
import sys
def symlink(source, dest):
try:
os.symlink(source, dest)
except OSError:
if os.path.islink(dest) and os.path.realpath(dest) == os.path.normpath(source):
sys.stderr.write("""
WARNING: the symlink for dependency "{0}" already exists
This could mean that the dependency exists in both devDependencies and
dependencies, which may cause trouble for people using this module with npm.
Please report this to upstream. For more information, see:
<https://github.com/tchollingsworth/nodejs-packaging/pull/1>
""".format(dest))
elif '--force' in sys.argv:
if os.path.isdir(dest):
shutil.rmtree(dest)
else:
os.unlink(dest)
os.symlink(source, dest)
else:
sys.stderr.write("""
ERROR: the path for dependency "{0}" already exists
This could mean that bundled modules are being installed. Bundled libraries are
forbidden in Fedora. For more information, see:
<https://fedoraproject.org/wiki/Packaging:No_Bundled_Libraries>
It is generally reccomended to remove the entire "node_modules" directory in
%prep when it exists. For more information, see:
<https://fedoraproject.org/wiki/Packaging:Node.js#Removing_bundled_modules>
If you have obtained permission from the Fedora Packaging Committee to bundle
libraries, please use `%nodejs_fixdep -r` in %prep to remove the dependency on
the bundled module. This will prevent an unnecessary dependency on the system
version of the module and eliminate this error.
""".format(dest))
sys.exit(1)
def symlink_deps(deps, check):
if isinstance(deps, dict):
#read in the list of mutiple-versioned packages
mvpkgs = open('/usr/share/node/multiver_modules').read().split('\n')
for dep, ver in deps.items():
if dep in mvpkgs and ver != '' and ver != '*' and ver != 'latest':
depver = re.sub('^ *(~|\^|=|>=|<=) *', '', ver).split('.')[0]
target = os.path.join(sitelib, '{0}@{1}'.format(dep, depver))
else:
target = os.path.join(sitelib, dep)
if not check or os.path.exists(target):
symlink(target, dep)
elif isinstance(deps, list):
for dep in deps:
target = os.path.join(sitelib, dep)
if not check or os.path.exists(target):
symlink(target, dep)
elif isinstance(deps, str):
target = os.path.join(sitelib, deps)
if not check or os.path.exists(target):
symlink(target, deps)
else:
raise TypeError("Invalid package.json: dependencies weren't a recognized type")
#the %nodejs_symlink_deps macro passes %nodejs_sitelib as the first argument
sitelib = sys.argv[1]
if '--check' in sys.argv or '--build' in sys.argv:
check = True
modules = [os.getcwd()]
else:
check = False
br_sitelib = os.path.join(os.environ['RPM_BUILD_ROOT'], sitelib.lstrip('/'))
modules = [os.path.join(br_sitelib, module) for module in os.listdir(br_sitelib)]
if '--optional' in sys.argv:
optional = True
else:
optional = False
for path in modules:
os.chdir(path)
md = json.load(open('package.json'))
if 'dependencies' in md or (check and 'devDependencies' in md) or (optional and 'optionalDependencies' in md):
try:
os.mkdir('node_modules')
except OSError:
sys.stderr.write('WARNING: node_modules already exists. Make sure you have ' +
'no bundled dependencies.\n')
os.chdir('node_modules')
if 'dependencies' in md:
symlink_deps(md['dependencies'], check)
if check and '--no-devdeps' not in sys.argv and 'devDependencies' in md:
symlink_deps(md['devDependencies'], check)
if optional and 'optionalDependencies' in md:
symlink_deps(md['optionalDependencies'], check)

@ -0,0 +1,4 @@
%__nodejs_provides %{_rpmconfigdir}/nodejs.prov
%__nodejs_requires %{_rpmconfigdir}/nodejs.req
%__nodejs_suggests %{_rpmconfigdir}/nodejs.req --optional
%__nodejs_path ^/usr/lib/node_modules\(_[[:digit:]]\+\)\{0,1\}/[^/]\+/package\.json$

@ -0,0 +1,121 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright 2012 T.C. Hollingsworth <tchollingsworth@gmail.com>
# Copyright 2017 Tomas Tomecek <ttomecek@redhat.com>
# Copyright 2019 Jan Staněk <jstanek@redhat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
"""Automatic provides generator for Node.js libraries.
Metadata taken from package.json. See `man npm-json` for details.
"""
from __future__ import print_function, with_statement
import json
import os
import sys
from itertools import chain, groupby
DEPENDENCY_TEMPLATE = "npm(%(name)s) = %(version)s"
BUNDLED_TEMPLATE = "bundled(nodejs-%(name)s) = %(version)s"
NODE_MODULES = {"node_modules", "node_modules_prod"}
class PrivatePackage(RuntimeError):
"""Private package metadata that should not be listed."""
#: Something is wrong with the ``package.json`` file
_INVALID_METADATA_FILE = (IOError, PrivatePackage, KeyError)
def format_metadata(metadata, bundled=False):
"""Format ``package.json``-like metadata into RPM dependency.
Arguments:
metadata (dict): Package metadata, presumably read from ``package.json``.
bundled (bool): Should the bundled dependency format be used?
Returns:
str: RPM dependency (i.e. ``npm(example) = 1.0.0``)
Raises:
KeyError: Expected key (i.e. ``name``, ``version``) missing in metadata.
PrivatePackage: The metadata indicate private (unlisted) package.
"""
# Skip private packages
if metadata.get("private", False):
raise PrivatePackage(metadata)
template = BUNDLED_TEMPLATE if bundled else DEPENDENCY_TEMPLATE
return template % metadata
def generate_dependencies(module_path, module_dir_set=NODE_MODULES):
"""Generate RPM dependency for a module and all it's dependencies.
Arguments:
module_path (str): Path to a module directory or it's ``package.json``
module_dir_set (set): Base names of directories to look into
for bundled dependencies.
Yields:
str: RPM dependency for the module and each of it's (public) bundled dependencies.
Raises:
ValueError: module_path is not valid module or ``package.json`` file
"""
# Determine paths to root module directory and package.json
if os.path.isdir(module_path):
root_dir = module_path
elif os.path.basename(module_path) == "package.json":
root_dir = os.path.dirname(module_path)
else: # Invalid metadata path
raise ValueError("Invalid module path '%s'" % module_path)
for dir_path, subdir_list, file_list in os.walk(root_dir):
# We are only interested in directories that contain package.json
if "package.json" not in file_list:
continue
# Read and format metadata
metadata_path = os.path.join(dir_path, "package.json")
bundled = dir_path != root_dir
try:
with open(metadata_path, mode="r") as metadata_file:
metadata = json.load(metadata_file)
yield format_metadata(metadata, bundled=bundled)
except _INVALID_METADATA_FILE:
pass # Ignore
# Only visit subdirectories in module_dir_set
subdir_list[:] = list(module_dir_set & set(subdir_list))
if __name__ == "__main__":
module_paths = (path.strip() for path in sys.stdin)
provides = chain.from_iterable(generate_dependencies(m) for m in module_paths)
# sort|uniq
for provide, __ in groupby(sorted(provides)):
print(provide)

@ -0,0 +1,707 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright 2012, 2013 T.C. Hollingsworth <tchollingsworth@gmail.com>
# Copyright 2019 Jan Staněk <jstanek@redat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
""" Automatic dependency generator for Node.js libraries.
Metadata parsed from package.json. See `man npm-json` for details.
"""
from __future__ import print_function, with_statement
import json
import operator
import os
import re
import sys
from collections import namedtuple
from itertools import chain
from itertools import takewhile
# Python version detection
_PY2 = sys.version_info[0] <= 2
_PY3 = sys.version_info[0] >= 3
if _PY2:
from future_builtins import map, filter
#: Name format of the requirements
REQUIREMENT_NAME_TEMPLATE = "npm({name})"
#: ``simple`` product of the NPM semver grammar.
RANGE_SPECIFIER_SIMPLE = re.compile(
r"""
(?P<operator>
<= | >= | < | > | = # primitive
| ~ | \^ # tilde/caret operators
)?
\s*(?P<version>\S+)\s* # version specifier
""",
flags=re.VERBOSE,
)
class UnsupportedVersionToken(ValueError):
"""Version specifier contains token unsupported by the parser."""
class Version(tuple):
"""Normalized RPM/NPM version.
The version has up to 3 components major, minor, patch.
Any part set to None is treated as unspecified.
::
1.2.3 == Version(1, 2, 3)
1.2 == Version(1, 2)
1 == Version(1)
* == Version()
"""
__slots__ = ()
#: Version part meaning 'Any'
#: ``xr`` in https://docs.npmjs.com/misc/semver#range-grammar
_PART_ANY = re.compile(r"^[xX*]$")
#: Numeric version part
#: ``nr`` in https://docs.npmjs.com/misc/semver#range-grammar
_PART_NUMERIC = re.compile(r"0|[1-9]\d*")
def __new__(cls, *args):
"""Create new version.
Arguments:
Version components in the order of "major", "minor", "patch".
All parts are optional::
>>> Version(1, 2, 3)
Version(1, 2, 3)
>>> Version(1)
Version(1)
>>> Version()
Version()
Returns:
New Version.
"""
if len(args) > 3:
raise ValueError("Version has maximum of 3 components")
return super(Version, cls).__new__(cls, map(int, args))
def __repr__(self):
"""Pretty debugging format."""
return "{0}({1})".format(self.__class__.__name__, ", ".join(map(str, self)))
def __str__(self):
"""RPM version format."""
return ".".join(format(part, "d") for part in self)
@property
def major(self):
"""Major version number, if any."""
return self[0] if len(self) > 0 else None
@property
def minor(self):
"""Major version number, if any."""
return self[1] if len(self) > 1 else None
@property
def patch(self):
"""Major version number, if any."""
return self[2] if len(self) > 2 else None
@property
def empty(self):
"""True if the version contains nothing but zeroes."""
return not any(self)
@classmethod
def parse(cls, version_string):
"""Parse individual version string (like ``1.2.3``) into Version.
This is the ``partial`` production in the grammar:
https://docs.npmjs.com/misc/semver#range-grammar
Examples::
>>> Version.parse("1.2.3")
Version(1, 2, 3)
>>> Version.parse("v2.x")
Version(2)
>>> Version.parse("")
Version()
Arguments:
version_string (str): The version_string to parse.
Returns:
Version: Parsed result.
"""
# Ignore leading ``v``, if any
version_string = version_string.lstrip("v")
part_list = version_string.split(".", 2)
# Use only parts up to first "Any" indicator
part_list = list(takewhile(lambda p: not cls._PART_ANY.match(p), part_list))
if not part_list:
return cls()
# Strip off and discard any pre-release or build qualifiers at the end.
# We can get away with this, because there is no sane way to represent
# these kinds of version requirements in RPM, and we generally expect
# the distro will only carry proper releases anyway.
try:
part_list[-1] = cls._PART_NUMERIC.match(part_list[-1]).group()
except AttributeError: # no match
part_list.pop()
# Extend with ``None``s at the end, if necessary
return cls(*part_list)
def incremented(self):
"""Increment the least significant part of the version::
>>> Version(1, 2, 3).incremented()
Version(1, 2, 4)
>>> Version(1, 2).incremented()
Version(1, 3)
>>> Version(1).incremented()
Version(2)
>>> Version().incremented()
Version()
Returns:
Version: New incremented Version.
"""
if len(self) == 0:
return self.__class__()
else:
args = self[:-1] + (self[-1] + 1,)
return self.__class__(*args)
class VersionBoundary(namedtuple("VersionBoundary", ("version", "operator"))):
"""Normalized version range boundary."""
__slots__ = ()
#: Ordering of primitive operators.
#: Operators not listed here are handled specially; see __compare below.
#: Convention: Lower boundary < 0, Upper boundary > 0
_OPERATOR_ORDER = {"<": 2, "<=": 1, ">=": -1, ">": -2}
def __str__(self):
"""Pretty-print the boundary"""
return "{0.operator}{0.version}".format(self)
def __compare(self, other, operator):
"""Compare two boundaries with provided operator.
Boundaries compare same as (version, operator_order) tuple.
In case the boundary operator is not listed in _OPERATOR_ORDER,
it's order is treated as 0.
Arguments:
other (VersionBoundary): The other boundary to compare with.
operator (Callable[[VersionBoundary, VersionBoundary], bool]):
Comparison operator to delegate to.
Returns:
bool: The result of the operator's comparison.
"""
ORDER = self._OPERATOR_ORDER
lhs = self.version, ORDER.get(self.operator, 0)
rhs = other.version, ORDER.get(other.operator, 0)
return operator(lhs, rhs)
def __eq__(self, other):
return self.__compare(other, operator.eq)
def __lt__(self, other):
return self.__compare(other, operator.lt)
def __le__(self, other):
return self.__compare(other, operator.le)
def __gt__(self, other):
return self.__compare(other, operator.gt)
def __ge__(self, other):
return self.__compare(other, operator.ge)
@property
def upper(self):
"""True if self is upper boundary."""
return self._OPERATOR_ORDER.get(self.operator, 0) > 0
@property
def lower(self):
"""True if self is lower boundary."""
return self._OPERATOR_ORDER.get(self.operator, 0) < 0
@classmethod
def equal(cls, version):
"""Normalize single samp:`={version}` into equivalent x-range::
>>> empty = VersionBoundary.equal(Version()); tuple(map(str, empty))
()
>>> patch = VersionBoundary.equal(Version(1, 2, 3)); tuple(map(str, patch))
('>=1.2.3', '<1.2.4')
>>> minor = VersionBoundary.equal(Version(1, 2)); tuple(map(str, minor))
('>=1.2', '<1.3')
>>> major = VersionBoundary.equal(Version(1)); tuple(map(str, major))
('>=1', '<2')
See `X-Ranges <https://docs.npmjs.com/misc/semver#x-ranges-12x-1x-12->`_
for details.
Arguments:
version (Version): The version the x-range should be equal to.
Returns:
(VersionBoundary, VersionBoundary):
Lower and upper bound of the x-range.
(): Empty tuple in case version is empty (any version matches).
"""
if version:
return (
cls(version=version, operator=">="),
cls(version=version.incremented(), operator="<"),
)
else:
return ()
@classmethod
def tilde(cls, version):
"""Normalize :samp:`~{version}` into equivalent range.
Tilde allows patch-level changes if a minor version is specified.
Allows minor-level changes if not::
>>> with_minor = VersionBoundary.tilde(Version(1, 2, 3)); tuple(map(str, with_minor))
('>=1.2.3', '<1.3')
>>> no_minor = VersionBoundary.tilde(Version(1)); tuple(map(str, no_minor))
('>=1', '<2')
Arguments:
version (Version): The version to tilde-expand.
Returns:
(VersionBoundary, VersionBoundary):
The lower and upper boundary of the tilde range.
"""
# Fail on ``~*`` or similar nonsense specifier
assert version.major is not None, "Nonsense '~*' specifier"
lower_boundary = cls(version=version, operator=">=")
if version.minor is None:
upper_boundary = cls(version=Version(version.major + 1), operator="<")
else:
upper_boundary = cls(
version=Version(version.major, version.minor + 1), operator="<"
)
return lower_boundary, upper_boundary
@classmethod
def caret(cls, version):
"""Normalize :samp:`^{version}` into equivalent range.
Caret allows changes that do not modify the left-most non-zero digit
in the ``(major, minor, patch)`` tuple.
In other words, this allows
patch and minor updates for versions 1.0.0 and above,
patch updates for versions 0.X >=0.1.0,
and no updates for versions 0.0.X::
>>> major = VersionBoundary.caret(Version(1, 2, 3)); tuple(map(str, major))
('>=1.2.3', '<2')
>>> minor = VersionBoundary.caret(Version(0, 2, 3)); tuple(map(str, minor))
('>=0.2.3', '<0.3')
>>> patch = VersionBoundary.caret(Version(0, 0, 3)); tuple(map(str, patch))
('>=0.0.3', '<0.0.4')
When parsing caret ranges, a missing patch value desugars to the number 0,
but will allow flexibility within that value,
even if the major and minor versions are both 0::
>>> rel = VersionBoundary.caret(Version(1, 2)); tuple(map(str, rel))
('>=1.2', '<2')
>>> pre = VersionBoundary.caret(Version(0, 0)); tuple(map(str, pre))
('>=0.0', '<0.1')
A missing minor and patch values will desugar to zero,
but also allow flexibility within those values,
even if the major version is zero::
>>> rel = VersionBoundary.caret(Version(1)); tuple(map(str, rel))
('>=1', '<2')
>>> pre = VersionBoundary.caret(Version(0)); tuple(map(str, pre))
('>=0', '<1')
Arguments:
version (Version): The version to range-expand.
Returns:
(VersionBoundary, VersionBoundary):
The lower and upper boundary of caret-range.
"""
# Fail on ^* or similar nonsense specifier
assert len(version) != 0, "Nonsense '^*' specifier"
lower_boundary = cls(version=version, operator=">=")
# Increment left-most non-zero part
for idx, part in enumerate(version):
if part != 0:
upper_version = Version(*(version[:idx] + (part + 1,)))
break
else: # No non-zero found; increment last specified part
upper_version = version.incremented()
upper_boundary = cls(version=upper_version, operator="<")
return lower_boundary, upper_boundary
@classmethod
def hyphen(cls, lower_version, upper_version):
"""Construct hyphen range (inclusive set)::
>>> full = VersionBoundary.hyphen(Version(1, 2, 3), Version(2, 3, 4)); tuple(map(str, full))
('>=1.2.3', '<=2.3.4')
If a partial version is provided as the first version in the inclusive range,
then the missing pieces are treated as zeroes::
>>> part = VersionBoundary.hyphen(Version(1, 2), Version(2, 3, 4)); tuple(map(str, part))
('>=1.2', '<=2.3.4')
If a partial version is provided as the second version in the inclusive range,
then all versions that start with the supplied parts of the tuple are accepted,
but nothing that would be greater than the provided tuple parts::
>>> part = VersionBoundary.hyphen(Version(1, 2, 3), Version(2, 3)); tuple(map(str, part))
('>=1.2.3', '<2.4')
>>> part = VersionBoundary.hyphen(Version(1, 2, 3), Version(2)); tuple(map(str, part))
('>=1.2.3', '<3')
Arguments:
lower_version (Version): Version on the lower range boundary.
upper_version (Version): Version on the upper range boundary.
Returns:
(VersionBoundary, VersionBoundary):
Lower and upper boundaries of the hyphen range.
"""
lower_boundary = cls(version=lower_version, operator=">=")
if len(upper_version) < 3:
upper_boundary = cls(version=upper_version.incremented(), operator="<")
else:
upper_boundary = cls(version=upper_version, operator="<=")
return lower_boundary, upper_boundary
def parse_simple_seq(specifier_string):
"""Parse all specifiers from a space-separated string::
>>> single = parse_simple_seq(">=1.2.3"); list(map(str, single))
['>=1.2.3']
>>> multi = parse_simple_seq("~1.2.0 <1.2.5"); list(map(str, multi))
['>=1.2.0', '<1.3', '<1.2.5']
This method implements the ``simple (' ' simple)*`` part of the grammar:
https://docs.npmjs.com/misc/semver#range-grammar.
Arguments:
specifier_string (str): Space-separated string of simple version specifiers.
Yields:
VersionBoundary: Parsed boundaries.
"""
# Per-operator dispatch table
# API: Callable[[Version], Iterable[VersionBoundary]]
handler = {
">": lambda v: [VersionBoundary(version=v, operator=">")],
">=": lambda v: [VersionBoundary(version=v, operator=">=")],
"<=": lambda v: [VersionBoundary(version=v, operator="<=")],
"<": lambda v: [VersionBoundary(version=v, operator="<")],
"=": VersionBoundary.equal,
"~": VersionBoundary.tilde,
"^": VersionBoundary.caret,
None: VersionBoundary.equal,
}
for match in RANGE_SPECIFIER_SIMPLE.finditer(specifier_string):
operator, version_string = match.group("operator", "version")
for boundary in handler[operator](Version.parse(version_string)):
yield boundary
def parse_range(range_string):
"""Parse full NPM version range specification::
>>> empty = parse_range(""); list(map(str, empty))
[]
>>> simple = parse_range("^1.0"); list(map(str, simple))
['>=1.0', '<2']
>>> hyphen = parse_range("1.0 - 2.0"); list(map(str, hyphen))
['>=1.0', '<2.1']
This method implements the ``range`` part of the grammar:
https://docs.npmjs.com/misc/semver#range-grammar.
Arguments:
range_string (str): The range specification to parse.
Returns:
Iterable[VersionBoundary]: Parsed boundaries.
Raises:
UnsupportedVersionToken: ``||`` is present in range_string.
"""
HYPHEN = " - "
# FIXME: rpm should be able to process OR in dependencies
# This error reporting kept for backward compatibility
if "||" in range_string:
raise UnsupportedVersionToken(range_string)
if HYPHEN in range_string:
version_pair = map(Version.parse, range_string.split(HYPHEN, 2))
return VersionBoundary.hyphen(*version_pair)
elif range_string != "":
return parse_simple_seq(range_string)
else:
return []
def unify_range(boundary_iter):
"""Calculate largest allowed continuous version range from a set of boundaries::
>>> unify_range([])
()
>>> _ = unify_range(parse_range("=1.2.3 <2")); tuple(map(str, _))
('>=1.2.3', '<1.2.4')
>>> _ = unify_range(parse_range("~1.2 <1.2.5")); tuple(map(str, _))
('>=1.2', '<1.2.5')
Arguments:
boundary_iter (Iterable[VersionBoundary]): The version boundaries to unify.
Returns:
(VersionBoundary, VersionBoundary):
Lower and upper boundary of the unified range.
"""
# Drop boundaries with empty version
boundary_iter = (
boundary for boundary in boundary_iter if not boundary.version.empty
)
# Split input sequence into upper/lower boundaries
lower_list, upper_list = [], []
for boundary in boundary_iter:
if boundary.lower:
lower_list.append(boundary)
elif boundary.upper:
upper_list.append(boundary)
else:
msg = "Unsupported boundary for unify_range: {0}".format(boundary)
raise ValueError(msg)
# Select maximum from lower boundaries and minimum from upper boundaries
intermediate = (
max(lower_list) if lower_list else None,
min(upper_list) if upper_list else None,
)
return tuple(filter(None, intermediate))
def rpm_format(requirement, version_spec="*"):
"""Format requirement as RPM boolean dependency::
>>> rpm_format("nodejs(engine)")
'nodejs(engine)'
>>> rpm_format("npm(foo)", ">=1.0.0")
'npm(foo) >= 1.0.0'
>>> rpm_format("npm(bar)", "~1.2")
'(npm(bar) >= 1.2 with npm(bar) < 1.3)'
Arguments:
requirement (str): The name of the requirement.
version_spec (str): The NPM version specification for the requirement.
Returns:
str: Formatted requirement.
"""
TEMPLATE = "{name} {boundary.operator} {boundary.version!s}"
try:
boundary_tuple = unify_range(parse_range(version_spec))
except UnsupportedVersionToken:
# FIXME: Typos and print behavior kept for backward compatibility
warning_lines = [
"WARNING: The {requirement} dependency contains an OR (||) dependency: '{version_spec}.",
"Please manually include a versioned dependency in your spec file if necessary",
]
warning = "\n".join(warning_lines).format(
requirement=requirement, version_spec=version_spec
)
print(warning, end="", file=sys.stderr)
return requirement
formatted = [
TEMPLATE.format(name=requirement, boundary=boundary)
for boundary in boundary_tuple
]
if len(formatted) > 1:
return "({0})".format(" with ".join(formatted))
elif len(formatted) == 1:
return formatted[0]
else:
return requirement
def has_only_bundled_dependencies(module_dir_path):
"""Determines if the module contains only bundled dependencies.
Dependencies are considered un-bundled when they are symlinks
pointing outside the root module's tree.
Arguments:
module_dir_path (str):
Path to the module directory (directory with ``package.json``).
Returns:
bool: True if all dependencies are bundled, False otherwise.
"""
module_root_path = os.path.abspath(module_dir_path)
dependency_root_path = os.path.join(module_root_path, "node_modules")
try:
dependency_path_iter = (
os.path.join(dependency_root_path, basename)
for basename in os.listdir(dependency_root_path)
)
bundled_dependency_iter = (
os.path.realpath(path)
for path in dependency_path_iter
if not os.path.islink(path) or path.startswith(module_root_path)
)
return any(bundled_dependency_iter)
except OSError: # node_modules does not exist
return False
def extract_dependencies(metadata_path, optional=False):
"""Extract all dependencies in RPM format from package metadata.
Arguments:
metadata_path (str): Path to package metadata (``package.json``).
optional (bool):
If True, extract ``optionalDependencies``
instead of ``dependencies``.
Yields:
RPM-formatted dependencies.
Raises:
TypeError: Invalid dependency data type.
"""
if has_only_bundled_dependencies(os.path.dirname(metadata_path)):
return # skip
# Read metadata
try:
with open(metadata_path, mode="r") as metadata_file:
metadata = json.load(metadata_file)
except OSError: # Invalid metadata file
return # skip
# Report required NodeJS version with required dependencies
if not optional:
try:
yield rpm_format("nodejs(engine)", metadata["engines"]["node"])
except KeyError: # NodeJS engine version unspecified
yield rpm_format("nodejs(engine)")
# Report listed dependencies
kind = "optionalDependencies" if optional else "dependencies"
container = metadata.get(kind, {})
if isinstance(container, dict):
for name, version_spec in container.items():
yield rpm_format(REQUIREMENT_NAME_TEMPLATE.format(name=name), version_spec)
elif isinstance(container, list):
for name in container:
yield rpm_format(REQUIREMENT_NAME_TEMPLATE.format(name=name))
elif isinstance(container, str):
yield rpm_format(REQUIREMENT_NAME_TEMPLATE.format(name=name))
else:
raise TypeError("invalid package.json: dependencies not a valid type")
if __name__ == "__main__":
nested = (
extract_dependencies(path.strip(), optional="--optional" in sys.argv)
for path in sys.stdin
)
flat = chain.from_iterable(nested)
# Ignore parentheses around the requirements when sorting
ordered = sorted(flat, key=lambda s: s.strip("()"))
print(*ordered, sep="\n")

@ -0,0 +1,2 @@
%__nodejs_native_requires %{_rpmconfigdir}/nodejs_abi.req
%__nodejs_native_path ^/usr/lib.*/node_modules/.*\\.node$

@ -0,0 +1,8 @@
#!/bin/bash
# Get the ABI version
abi_version=$(/usr/bin/node -p process.versions.modules)
# Write out the Virtual Requires
echo "nodejs(abi) = ${abi_version}"

@ -0,0 +1,274 @@
## START: Set by rpmautospec
## (rpmautospec version 0.6.1)
## RPMAUTOSPEC: autorelease
%define autorelease(e:s:pb:n) %{?-p:0.}%{lua:
release_number = 5;
base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}"));
print(release_number + base_release_number - 1);
}%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}}
## END: Set by rpmautospec
%global macrosdir %(d=%{_rpmconfigdir}/macros.d; [ -d $d ] || d=%{_sysconfdir}/rpm; echo $d)
Name: nodejs-packaging
Version: 2023.10
Release: %autorelease
Summary: RPM Macros and Utilities for Node.js Packaging
BuildArch: noarch
License: MIT
URL: https://fedoraproject.org/wiki/Node.js/Packagers
ExclusiveArch: %{nodejs_arches} noarch
Source0001: LICENSE
Source0002: README.md
Source0003: macros.nodejs
Source0004: multiver_modules
Source0005: nodejs-fixdep
Source0006: nodejs-setversion
Source0007: nodejs-symlink-deps
Source0008: nodejs.attr
Source0009: nodejs.prov
Source0010: nodejs.req
Source0011: nodejs_abi.attr
Source0012: nodejs_abi.req
Source0111: nodejs-packaging-bundler
# Created with `tar cfz test.tar.gz test`
Source0101: test.tar.gz
BuildRequires: python3
# Several of the macros require the /usr/bin/node command, so we need to
# ensure that it is present when packaging.
Requires: /usr/bin/node
Requires: redhat-rpm-config
%description
This package contains RPM macros and other utilities useful for packaging
Node.js modules and applications in RPM-based distributions.
%package bundler
Summary: Bundle a node.js application dependencies
Requires: npm
Requires: coreutils, findutils, jq
%description bundler
nodejs-packaging-bundler bundles a node.js application node_module dependencies
It gathers the application tarball.
It generates a runtime (prod) tarball with runtime node_module dependencies
It generates a testing (dev) tarball with node_module dependencies for testing
It generates a bundled license file that gets the licenses in the runtime
dependency tarball
%prep
pushd %{_topdir}/BUILD
cp -da %{_sourcedir}/* .
tar xvf test.tar.gz
popd
%build
#nothing to do
%install
install -Dpm0644 macros.nodejs %{buildroot}%{macrosdir}/macros.nodejs
install -Dpm0644 nodejs.attr %{buildroot}%{_rpmconfigdir}/fileattrs/nodejs.attr
install -Dpm0644 nodejs_abi.attr %{buildroot}%{_rpmconfigdir}/fileattrs/nodejs_abi.attr
install -pm0755 nodejs.prov %{buildroot}%{_rpmconfigdir}/nodejs.prov
install -pm0755 nodejs.req %{buildroot}%{_rpmconfigdir}/nodejs.req
install -pm0755 nodejs_abi.req %{buildroot}%{_rpmconfigdir}/nodejs_abi.req
install -pm0755 nodejs-symlink-deps %{buildroot}%{_rpmconfigdir}/nodejs-symlink-deps
install -pm0755 nodejs-fixdep %{buildroot}%{_rpmconfigdir}/nodejs-fixdep
install -pm0755 nodejs-setversion %{buildroot}%{_rpmconfigdir}/nodejs-setversion
install -Dpm0644 multiver_modules %{buildroot}%{_datadir}/node/multiver_modules
install -Dpm0755 nodejs-packaging-bundler %{buildroot}%{_bindir}/nodejs-packaging-bundler
%check
./test/run
%files
%license LICENSE
%{macrosdir}/macros.nodejs
%{_rpmconfigdir}/fileattrs/nodejs*.attr
%{_rpmconfigdir}/nodejs*
%{_datadir}/node/multiver_modules
%files bundler
%{_bindir}/nodejs-packaging-bundler
%changelog
* Thu Jan 19 2023 Fedora Release Engineering <releng@fedoraproject.org> - 2022.10-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
* Thu Oct 20 2022 Stephen Gallagher <sgallagh@redhat.com> - 2022.10-1
- Move native module building tools here from Node.js
- Add `Requires: /usr/bin/node`
* Tue Oct 18 2022 Davide Cavalca <dcavalca@fedoraproject.org> - 2021.06-7
- NPM bundler: recursively bundle modules for all packages found
* Fri Jul 22 2022 Fedora Release Engineering <releng@fedoraproject.org> - 2021.06-6
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Sun May 01 2022 Davide Cavalca <dcavalca@fedoraproject.org> - 2021.06-5
- NPM bundler: optionally use a local tarball instead of npm
* Thu Jan 20 2022 Stephen Gallagher <sgallagh@redhat.com> - 2021.06-4
- NPM bundler: also find namespaced bundled dependencies
* Thu Jul 22 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2021.06-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Tue Jun 22 2021 Stephen Gallagher <sgallagh@redhat.com> - 2021.06-2
- Fix hard-coded output directory in the bundler
* Wed Jun 02 2021 Stephen Gallagher <sgallagh@redhat.com> - 2021.06-1
- Update to 2021.06-1
- bundler: Handle archaic license metadata
- bundler: Warn about bundled dependencies with no license metadata
* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2021.01-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Wed Jan 20 2021 Stephen Gallagher <sgallagh@redhat.com> - 2021.01-2
- nodejs-packaging-bundler improvements to handle uncommon characters
* Wed Jan 06 2021 Troy Dawson <tdawson@redhat.com> - 2021.01
- Add nodejs-packaging-bundler and update README.md
* Fri Sep 18 2020 Stephen Gallagher <sgallagh@redhat.com> - 2020.09-1
- Move to dist-git as the upstream
* Wed Sep 02 2020 Stephen Gallagher <sgallagh@redhat.com> - 25-1
- Fix incorrect bundled library detection for Requires
* Tue Sep 01 2020 Stephen Gallagher <sgallagh@redhat.com> - 24-1
- Check node_modules_prod for bundled dependencies
* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 23-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
* Wed Jun 03 2020 Stephen Gallagher <sgallagh@redhat.com> - 23-3
- Drop Requires: nodejs(engine)
* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 23-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Thu Oct 31 2019 Tom Hughes <tom@compton.nu> - 23-1
- Ensure nodejs(engine) is required for packages with no dependencies
* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 22-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Tue Jul 2 2019 Tom Hughes <tom@compton.nu> - 22-1
- Refactor nodejs.req in more idiomatic Python
- Treat only external dependency links as un-bundled
* Mon Jun 10 2019 Tom Hughes <tom@compton.nu> - 21-1
- Refactor nodejs.prov in more idiomatic Python
* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 20-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Sat Jan 5 2019 Tom Hughes <tom@compton.nu> - 20-1
- Fix handling of ^ dependencies for multiversion modules
* Thu Jan 3 2019 Tom Hughes <tom@compton.nu> - 18-1
- Handle =, >= and <= dependencies for multiversion modules
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 17-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Thu May 3 2018 Tom Hughes <tom@compton.nu> - 17-1
- Fix version comparators with a space after the operator
* Tue May 1 2018 Tom Hughes <tom@compton.nu> - 16-1
- Rewrite nodejs.req to better match npm versioning rules
- Add tests for nodejs.req and nodejs.prov
* Mon Apr 30 2018 Tom Hughes <tom@compton.nu> - 15-1
- Fix caret dependency ranges
* Thu Apr 12 2018 Tom Hughes <tom@compton.nu> - 14-1
- Only match top level modules for requires and provides generation
* Wed Feb 28 2018 Tom Hughes <tom@compton.nu> - 13-1
- Add %%nodejs_setversion macro
* Fri Feb 23 2018 Tom Hughes <tom@compton.nu> - 12-1
- Port to python 3
* Thu Feb 08 2018 Fedora Release Engineering <releng@fedoraproject.org> - 11-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
* Sat Jan 13 2018 Tom Hughes <tom@compton.nu> - 11-1
- nodesjs.req: use boolean with for range dependencies
* Tue Sep 12 2017 Stephen Gallagher <sgallagh@redhat.com> - 10-1
- Release v10
- Automatically generate Provides for bundled npm dependencies
* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 9-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Tue Feb 21 2017 Tom Hughes <tom@compton.nu> - 9-3
- switch source URL to pagure
* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 9-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
* Tue Feb 2 2016 Tom Hughes <tom@compton.nu> - 9-1
- nodejs-fixdep: stop --move erroring on missing dependency types
* Sun Jan 31 2016 Tom Hughes <tom@compton.nu> - 8-1
- nodejs-fixdep: add --move option
- nodejs-symlink-deps: add --optional option
- req: generate suggests for optional dependencies
* Mon Nov 16 2015 Tom Hughes <tom@compton.nu> - 7-5
- nodejs-symlink-deps: handle caret in versions
* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 7-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
* Wed Mar 4 2015 Ville Skyttä <ville.skytta@iki.fi> - 7-3
- Install macros in %%{_rpmconfidir}/macros.d where available (#1074279)
* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 7-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
* Sun May 25 2014 T.C. Hollingsworth <tchollingsworth@gmail.com> - 7-1
- nodejs-symlink-deps: fix regression preventing multiply versioned modules from
being symlinked correctly
* Sat May 24 2014 T.C. Hollingsworth <tchollingsworth@gmail.com> - 6-1
- nodejs-fixdep: use real option parsing
- nodejs-fixdep: support modifying optionalDependencies and devDependencies
- req: support the caret operator
- nodejs-symlink-deps: add --force option
- nodejs-symlink-deps: add --build alias for --check
- nodejs-fixdep: support converting to caret dependencies
- nodejs-fixdep: support non-dictionary dependency properties
- multiver_modules: add nan
* Mon Jul 29 2013 T.C. Hollingsworth <tchollingsworth@gmail.com> - 4-1
- handle cases where the symlink target exists gracefully
* Wed Jul 10 2013 T.C. Hollingsworth <tchollingsworth@gmail.com> - 3-1
- dependencies and engines can be lists or strings too
- handle unversioned dependencies on multiply versioned modules correctly
(RHBZ#982798)
- restrict to compatible arches
* Fri Jun 21 2013 T.C. Hollingsworth <tchollingsworth@gmail.com> - 2-1
- move multiple version list to /usr/share/node
- bump nodejs Requires to 0.10.12
- add Requires: redhat-rpm-config
* Thu Jun 13 2013 T.C. Hollingsworth <tchollingsworth@gmail.com> - 1-1
- initial package
Loading…
Cancel
Save