Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-ports
Path: blob/main/Tools/scripts/npmjs-fetch-with-dependencies.sh
20806 views
1
#!/bin/sh
2
#
3
# MAINTAINER: [email protected]
4
5
# This script is intended to be used in fetch targets of Node.js ports.
6
# It fetches a given Node.js package from npmjs.org along with all its dependencies,
7
# and creates a tarball with the package and all its dependencies.
8
# It doesn't build or install the package, just fetches it and its dependencies,
9
# such that the subsequent build step wouldn't require network access.
10
# This script generates a package-lock.json file for reproducible builds
11
# if it doesn't already exist.
12
13
14
set -eu -o pipefail
15
set -o pipefail
16
17
export LC_ALL=C
18
19
##
20
## npmjs-get-latest-version.sh: retrieves the latest version of a given Node.js package as registered on https://registry.npmjs.org
21
##
22
23
# args and env
24
25
PACKAGE_NAME="$1"
26
PACKAGE_VERSION="$2"
27
PACKAGE_LOCK_JSON="$3"
28
PACKAGE_TARBALL_OUTPUT="$4"
29
30
if [ -z "$PACKAGE_NAME" ] || [ -z "$PACKAGE_VERSION" ] || [ -z "$PACKAGE_LOCK_JSON" ] || [ -z "$PACKAGE_TARBALL_OUTPUT" ]; then
31
echo "Usage: $0 <package name> <package version> <package-lock.json> <package tarball output>"
32
echo "Example: $0 sharp 0.34.4 outdir/sharp-package-lock.json outdir/sharp-0.34.4.tar.gz"
33
exit 1
34
fi
35
36
PACKAGE_NAME_PURE="$(echo $PACKAGE_NAME | sed 's|.*/||')"
37
38
if [ -z "$TMPDIR" ]; then
39
TMPDIR="/tmp"
40
fi
41
42
43
# check that packaged dependencies are installed
44
45
for dep in npm jq; do
46
if ! which $dep >/dev/null 2>&1; then
47
echo "error: the '$dep' dependency is missing"
48
if [ $dep = "npm" ]; then
49
echo "... please install the 'npm' package"
50
elif [ $dep = "jq" ]; then
51
echo "... please install the 'jq' package"
52
fi
53
exit 1
54
fi
55
done
56
57
58
# MAIN
59
60
# to full paths
61
if ! echo "${PACKAGE_LOCK_JSON}" | grep -q "^/"; then
62
PACKAGE_LOCK_JSON="`pwd`/${PACKAGE_LOCK_JSON}"
63
fi
64
if ! echo "${PACKAGE_TARBALL_OUTPUT}" | grep -q "^/"; then
65
PACKAGE_TARBALL_OUTPUT="`pwd`/${PACKAGE_TARBALL_OUTPUT}"
66
fi
67
if ! echo "${TMPDIR}" | grep -q "^/"; then
68
TMPDIR="`pwd`/${TMPDIR}"
69
fi
70
71
# create dirs for output files
72
mkdir -p "$(dirname "${PACKAGE_LOCK_JSON}")"
73
mkdir -p "$(dirname "${PACKAGE_TARBALL_OUTPUT}")"
74
mkdir -p "${TMPDIR}"
75
76
# create build dir and change to there
77
BUILD_DIR="${TMPDIR}/${PACKAGE_NAME_PURE}-${PACKAGE_VERSION}"
78
rm -rf ${BUILD_DIR}
79
mkdir ${BUILD_DIR}
80
cd ${BUILD_DIR}
81
82
# either just fetch, or regenarate package-lock.json and fetch
83
if [ -f $PACKAGE_LOCK_JSON ]; then
84
# fail if package-lock.json does not contain the requested package and version
85
JSON_NAME=$(jq -r ".packages | .\"node_modules/${PACKAGE_NAME}\" .version" $PACKAGE_LOCK_JSON)
86
if [ "$JSON_NAME" != "$PACKAGE_VERSION" ]; then
87
echo "error: the existing package-lock.json ($PACKAGE_LOCK_JSON) does not contain the requested package ${PACKAGE_NAME}@${PACKAGE_VERSION}"
88
echo " please delete the existing package-lock.json ($PACKAGE_LOCK_JSON) and re-run this script to regenerate it"
89
exit 1
90
fi
91
92
# fetch dependencies
93
echo "{\"name\":\"${PACKAGE_NAME}-installer\",\"version\":\"1.0.0\",\"dependencies\":{\"${PACKAGE_NAME}\":\"${PACKAGE_VERSION}\"}}" > package.json
94
cp $PACKAGE_LOCK_JSON package-lock.json
95
HOME=${TMPDIR} npm ci --ignore-scripts --global-style --legacy-peer-deps --omit=dev
96
else
97
# info
98
echo "INFO: the file $PACKAGE_LOCK_JSON does not exist, we will attempt to generate it"
99
100
# generate package-lock.json
101
echo "{\"name\":\"${PACKAGE_NAME}-installer\",\"version\":\"1.0.0\"}" > package.json
102
npm install --package-lock-only --global-style --legacy-peer-deps ${PACKAGE_NAME}@${PACKAGE_VERSION}
103
104
# copy generated package-lock.json to the expected location
105
cp package-lock.json ${PACKAGE_LOCK_JSON}
106
107
# info
108
echo "INFO: ${PACKAGE_LOCK_JSON} did not exist and was generated"
109
110
# fetch dependencies
111
HOME=${TMPDIR} npm ci --ignore-scripts --global-style --legacy-peer-deps --omit=dev
112
fi
113
114
# generate tarball with all dependencies
115
116
cd ${TMPDIR}
117
118
# Remove files that npm generates inconsistently across environments, breaking reproducibility:
119
#
120
# 1. .package-lock.json: npm creates this cache file in node_modules/ recording what was installed.
121
# Different npm versions write different content - e.g., npm 11.6.1 includes entries for ALL
122
# optional dependencies (with "ideallyInert": true) even for other platforms, while npm 11.6.2
123
# only includes actually-installed packages. This causes checksum mismatches between systems.
124
#
125
# 2. Empty @-scoped directories: When npm skips platform-specific optional dependencies (e.g.,
126
# @img/sharp-wasm32 on x64), some npm versions create empty placeholder directories like
127
# @emnapi/ while others don't. These empty directories also cause tarball differences.
128
#
129
# Neither file is needed for the build - npm regenerates .package-lock.json during install,
130
# and empty directories serve no purpose.
131
find ${PACKAGE_NAME_PURE}-${PACKAGE_VERSION} -name '.package-lock.json' -delete
132
find ${PACKAGE_NAME_PURE}-${PACKAGE_VERSION} -type d -name '@*' -empty -delete
133
134
find ${PACKAGE_NAME_PURE}-${PACKAGE_VERSION} -and -exec touch -h -d 1970-01-01T00:00:00Z {} \;
135
find ${PACKAGE_NAME_PURE}-${PACKAGE_VERSION} -print0 | sort -z | \
136
tar czf ${PACKAGE_TARBALL_OUTPUT} --format=bsdtar --no-read-sparse --gid 0 --uid 0 --options gzip:!timestamp --no-recursion --null -T -
137
rm -rf ${PACKAGE_NAME_PURE}-${PACKAGE_VERSION}
138
echo "INFO: created package tarball with dependencies at: ${PACKAGE_TARBALL_OUTPUT}"
139
140