143 lines
4.8 KiB
Bash
Executable File
143 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
#
|
||
# Check if we need to copy node_modules.
|
||
# (e.g. in case it has been modified after last copy)
|
||
#
|
||
# The reasoning for a (mostly) read-only node_modules folder:
|
||
# ideally we’d symlink the folder directly to the Nix store
|
||
# so that we’re guaranteed to have a reproducible source.
|
||
# Unfortunately react-native wants to build some stuff after the fact
|
||
# and this is incompatible with the concept of a pure Nix package.
|
||
# Therefore we copy the whole source to the repo directory,
|
||
# allow writing only on the folders where it is absolutely required,
|
||
# and therefore we still keep some peace of mind that the rest
|
||
# of node_modules is unchanged the rest of the time.
|
||
#
|
||
|
||
set -Eeuo pipefail
|
||
|
||
GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
|
||
source "${GIT_ROOT}/scripts/colors.sh"
|
||
|
||
# More concise output from 'time'
|
||
export TIMEFORMAT="Done in: %Es"
|
||
|
||
removeDir() {
|
||
[[ ! -d "${tmp}" ]] && return
|
||
chmod -R u+w "${tmp}"
|
||
rm -rf "${tmp}"
|
||
}
|
||
|
||
copyNodeModules() {
|
||
local src="${1}"
|
||
local dst="${2}"
|
||
# WARNING: The ../ is there to avoid a Nix builtins.path bug:
|
||
# https://github.com/NixOS/nix/issues/3593
|
||
local tmp=$(mktemp -d -p "$(dirname "${dst}")/../")
|
||
|
||
# We use a temporary directory to use mv as "atomic" change
|
||
trap "removeDir ${tmp}" ERR INT HUP
|
||
|
||
# WARNING: The -L here is crucial to let Metro find modules.
|
||
cp -LRf ${src}/node_modules/. "${tmp}"
|
||
chmod -R +w "${tmp}"
|
||
|
||
# WARNING: We can't de-reference .bin symlinks
|
||
cp -Rf ${src}/node_modules/.bin/. "${tmp}/.bin/"
|
||
# Cmake called by react-native-reanimated 3.3.0 during build creates read-only temporary files.
|
||
chmod -R u+w node_modules/react-native-reanimated/android/ -f || true
|
||
rm -r "${dst}"
|
||
mv -f "${tmp}" "${dst}"
|
||
}
|
||
|
||
# Find files that were modified and should cause a re-copying of node modules.
|
||
# Some files are generated/modified by build processes and should be ignored.
|
||
findFilesNewerThan() {
|
||
local sentinel="${1}"
|
||
local dir="${2}"
|
||
find "${dir}" -type f -writable \
|
||
-newer "${sentinel}" \
|
||
-not -ipath "*/*android/build/*" -prune \
|
||
-not -ipath "*/xcuserdata/*" -prune \
|
||
-not -ipath "*/scripts/.packager.env" \
|
||
\ # generated at runtime by react-native \
|
||
\ # related code https://github.com/facebook/react-native/blob/v0.72.5/packages/react-native/ReactCommon/React-rncore.podspec#L19 \
|
||
-not -path "*/node_modules/react-native/ReactCommon/react/renderer/components/rncore/*" \
|
||
\ # generated at runtime by react-native-config \
|
||
\ # related code https://github.com/lugg/react-native-config/blob/v1.5.0/react-native-config.podspec#L52 \
|
||
-not -path "*/node_modules/react-native-config/ios/ReactNativeConfig/GeneratedDotEnv.m" \
|
||
\ # generated at runtime by react-native from v0.73.x onwards \
|
||
-not -path "*/node_modules/react-native/React/Fabric/RCTThirdPartyFabricComponentsProvider.mm" \
|
||
-print
|
||
}
|
||
|
||
nodeModulesUnchanged() {
|
||
local src="$1"
|
||
local dst="$2"
|
||
local sentinelFile="${dst}/.copied~"
|
||
|
||
# Check if node_modules exists and is valid
|
||
if [[ ! -f "${sentinelFile}" ]]; then
|
||
# node_modules have not been created by this script
|
||
echo -e "${YLW}Node modules not created by Nix${RST}" >&2
|
||
return 1
|
||
fi
|
||
|
||
# Sentinel file holds location of the node_modules source in Nix store
|
||
currentNixSrc="$(cat "${sentinelFile}")"
|
||
|
||
if [ "${currentNixSrc}" != "${src}" ]; then
|
||
echo -e "${YLW}Yarn modules changed, copying new version over${RST}" >&2
|
||
return 1
|
||
fi
|
||
|
||
# Some build processes modify files in node_modules
|
||
modifiedFiles=($(findFilesNewerThan "${sentinelFile}" "${dst}"))
|
||
if [ ${#modifiedFiles[@]} -ne 0 ]; then
|
||
echo -e "${YLW}Changes detected in node_modules:${RST} ${#modifiedFiles[@]}" >&2
|
||
# Print files that have changes
|
||
for file in ${modifiedFiles[@]}; do
|
||
echo "- $(realpath --relative-to="${dst}" "${file}")" >&2
|
||
done
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${GRN}No changes detected.${RST}" >&2
|
||
return 0
|
||
}
|
||
|
||
replaceNodeModules() {
|
||
local src="$1"
|
||
local dst="$2"
|
||
local sentinelFile="${dst}/.copied~"
|
||
|
||
if nodeModulesUnchanged "${src}" "${dst}"; then
|
||
return
|
||
fi
|
||
|
||
# Replace node_modules if necessary
|
||
echo "Copying node_modules from Nix store:" >&2
|
||
echo " - ${src}" >&2
|
||
copyNodeModules "${src}" "${dst}"
|
||
echo -n "${src}" > "${sentinelFile}"
|
||
}
|
||
|
||
# Destination folder, Nix sets STATUS_MOBILE_HOME
|
||
dst="$STATUS_MOBILE_HOME/node_modules"
|
||
# Source of Node modules from /nix/store
|
||
src="$1"
|
||
|
||
if [[ ! -d ${src} ]]; then
|
||
echo -e "${RED}No such folder:${RST} ${src}" >&2
|
||
exit 1
|
||
fi
|
||
|
||
# Make those available in shell spawned by flock
|
||
export -f replaceNodeModules nodeModulesUnchanged copyNodeModules findFilesNewerThan removeDir
|
||
|
||
mkdir -p "${dst}"
|
||
# Leverage file lock to create an exclusive lock.
|
||
# Otherwise multiple calls to this script would clash.
|
||
flock "${dst}/" sh -c "time replaceNodeModules '${src}' '${dst}'"
|