Skip to content
This repository has been archived by the owner on Jan 25, 2023. It is now read-only.

feat: better cache support for JS workspaces #526

Merged
merged 10 commits into from
Mar 8, 2021
105 changes: 99 additions & 6 deletions run-build-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ mkdir -p $NETLIFY_CACHE_DIR/ruby_version
mkdir -p $NETLIFY_CACHE_DIR/swift_version

# pwd caches
NETLIFY_JS_WORKSPACES_CACHE_DIR="$NETLIFY_CACHE_DIR/js-workspaces"

mkdir -p $NETLIFY_JS_WORKSPACES_CACHE_DIR
mkdir -p $NETLIFY_CACHE_DIR/node_modules
mkdir -p $NETLIFY_CACHE_DIR/.bundle
mkdir -p $NETLIFY_CACHE_DIR/bower_components
Expand Down Expand Up @@ -102,6 +105,38 @@ run_yarn() {
fi


if [ "$NETLIFY_YARN_WORKSPACES" = "true" ]
then
echo "NETLIFY_YARN_WORKSPACES feature flag set"
local workspace_output
local workspace_exit_code
# YARN_IGNORE_PATH will ignore the presence of a local yarn executable (i.e. yarn 2) and default
# to using the global one (which, for now, is alwasy yarn 1.x). See https://yarnpkg.com/configuration/yarnrc#ignorePath
JGAntunes marked this conversation as resolved.
Show resolved Hide resolved
workspace_output="$(YARN_IGNORE_PATH=1 yarn workspaces --json info )"
workspace_exit_code=$?
Copy link
Contributor

@ehmicky ehmicky Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sand!]

Suggested change
workspace_exit_code=$?
local workspace_exit_code=$?

(And removing the declaration above)
(Same for workspace_output)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ehmicky happy to do so for the workspace_exit_code however, for the previous one, declaring a local variable will actually override the exit code from the command we need to use afterwards - https://github.com/koalaman/shellcheck/wiki/SC2155

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL.
Good catch 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I owe it all to shellcheck - https://github.com/koalaman/shellcheck/ - I would be doomed writing bash/sh without it 😂

if [ $workspace_exit_code -eq 0 ]
then
local package_locations
# Extract all the packages and respective locations. .data will be a JSON object like
# {
# "my-package-1": {
# "location": "packages/blog-1",
# "workspaceDependencies": [],
# "mismatchedWorkspaceDependencies": []
# },
# (...)
# }
# We need to cache all the node_module dirs, or we'll always be installing them on each run
mapfile -t package_locations <<< "$(echo "$workspace_output" | jq -r '.data | fromjson | to_entries | .[].value.location')"
restore_js_workspaces_cache "${package_locations[@]}"
else
echo "No workspace detected"
restore_cwd_cache node_modules "node modules"
fi
else
restore_cwd_cache node_modules "node modules"
fi

echo "Installing NPM modules using Yarn version $(yarn --version)"
run_npm_set_temp

Expand Down Expand Up @@ -130,6 +165,8 @@ run_npm_set_temp() {
}

run_npm() {
restore_cwd_cache node_modules "node modules"

if [ -n "$NPM_VERSION" ]
then
if [ "$(npm --version)" != "$NPM_VERSION" ]
Expand Down Expand Up @@ -475,7 +512,6 @@ install_dependencies() {

if [ -f package.json ]
then
restore_cwd_cache node_modules "node modules"
if [ "$NETLIFY_USE_YARN" = "true" ] || ([ "$NETLIFY_USE_YARN" != "false" ] && [ -f yarn.lock ])
then
run_yarn $YARN_VERSION
Expand Down Expand Up @@ -669,9 +705,17 @@ install_dependencies() {
#
cache_artifacts() {
echo "Caching artifacts"

cache_cwd_directory ".bundle" "ruby gems"
cache_cwd_directory "bower_components" "bower components"
cache_cwd_directory "node_modules" "node modules"

if [ "$NETLIFY_YARN_WORKSPACES" == "true" ]
then
cache_node_modules
else
cache_cwd_directory "node_modules" "node modules"
fi

Comment on lines +712 to +718
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to, once we can drop the feature flag, we keep only the cache_node_modules function containing all the logic relative to node_modules caching.

cache_cwd_directory ".venv" "python virtualenv"
cache_cwd_directory ".build" "swift build"
cache_cwd_directory ".netlify/plugins" "build plugins"
Expand All @@ -690,7 +734,7 @@ cache_artifacts() {
cache_home_directory ".composer" "composer dependencies"
cache_home_directory ".homebrew-cache", "homebrew cache"
cache_home_directory ".rustup" "rust rustup cache"

if [ -f Cargo.toml ] || [ -f Cargo.lock ]
then
cache_home_directory ".cargo/registry" "rust cargo registry cache"
Expand Down Expand Up @@ -747,11 +791,11 @@ cache_artifacts() {
move_cache() {
local src=$1
local dst=$2
if [ -d $src ]
if [ -d "$src" ]
then
echo "Started $3"
rm -rf $dst
mv $src $dst
rm -rf "$dst"
mv "$src" "$dst"
echo "Finished $3"
fi
}
Expand Down Expand Up @@ -779,6 +823,55 @@ restore_cwd_cache() {
move_cache "$NETLIFY_CACHE_DIR/$1" "$PWD/$1" "restoring cached $2"
}

#
# Restores node_modules dirs cached for js workspaces
# See https://github.com/netlify/pod-workflow/issues/139/ for more context
#
# Expects:
# $@ each argument should be a package location relative to the repo's root
restore_js_workspaces_cache() {
# Keep a record of the workspaces in the project in order to cache them later
NETLIFY_JS_WORKSPACE_LOCATIONS=("$@")

# Retrieve each workspace node_modules
for location in "${NETLIFY_JS_WORKSPACE_LOCATIONS[@]}"; do
move_cache "$NETLIFY_JS_WORKSPACES_CACHE_DIR/$location/node_modules" \
"$NETLIFY_REPO_DIR/$location/node_modules" \
"restoring workspace $location node modules"
done
# Retrieve hoisted node_modules
move_cache "$NETLIFY_JS_WORKSPACES_CACHE_DIR/node_modules" "$NETLIFY_REPO_DIR/node_modules" "restoring workspace root node modules"
}

#
# Caches node_modules dirs for a js project. Either detects the presence of js workspaces
# via the `NETLIFY_JS_WORKSPACE_LOCATIONS` variable, or looks at the node_modules in the cwd.
#
cache_node_modules() {
# Check the number of workspace locations detected
if [ "${#NETLIFY_JS_WORKSPACE_LOCATIONS[@]}" -eq 0 ]
then
cache_cwd_directory "node_modules" "node modules"
else
cache_js_workspaces
fi
}

#
# Caches node_modules dirs from js workspaces. It acts based on the presence of a
# `NETLIFY_JS_WORKSPACE_LOCATIONS` variable previously set in `restore_js_workspaces_cache()`
#
cache_js_workspaces() {
for location in "${NETLIFY_JS_WORKSPACE_LOCATIONS[@]}"; do
mkdir -p "$NETLIFY_JS_WORKSPACES_CACHE_DIR/$location"
move_cache "$NETLIFY_REPO_DIR/$location/node_modules" \
"$NETLIFY_JS_WORKSPACES_CACHE_DIR/$location/node_modules" \
"saving workspace $location node modules"
done
# Retrieve hoisted node_modules
move_cache "$NETLIFY_REPO_DIR/node_modules" "$NETLIFY_JS_WORKSPACES_CACHE_DIR/node_modules" "saving workspace root node modules"
}

cache_cwd_directory() {
move_cache "$PWD/$1" "$NETLIFY_CACHE_DIR/$1" "saving $2"
}
Expand Down