Skip to content

Commit 8a84200

Browse files
committed
Ensure npm install runs once at a time
On starting up the Lektor server, the server_spawn and before_build_all events both fire, each one spawning `npm install`. This ensures that only one runs at a time.
1 parent 677c839 commit 8a84200

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

lektor_webpack_support.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import threading
23

34
from lektor.pluginsystem import Plugin
45
from lektor.reporter import reporter
@@ -11,6 +12,7 @@ class WebpackSupportPlugin(Plugin):
1112

1213
def __init__(self, *args, **kwargs):
1314
Plugin.__init__(self, *args, **kwargs)
15+
self.npm_lock = threading.Lock()
1416
self.webpack_process = None
1517

1618
def is_enabled(self, extra_flags):
@@ -24,9 +26,13 @@ def run_webpack(self, watch=False):
2426
return portable_popen(args, cwd=webpack_root)
2527

2628
def npm_install(self):
27-
reporter.report_generic('Running npm install')
28-
webpack_root = os.path.join(self.env.root_path, 'webpack')
29-
portable_popen(['npm', 'install'], cwd=webpack_root).wait()
29+
if self.npm_lock.acquire(False):
30+
reporter.report_generic('Running npm install')
31+
webpack_root = os.path.join(self.env.root_path, 'webpack')
32+
portable_popen(['npm', 'install'], cwd=webpack_root).wait()
33+
else:
34+
self.npm_lock.acquire()
35+
self.npm_lock.release()
3036

3137
def on_server_spawn(self, **extra):
3238
extra_flags = extra.get("extra_flags") or extra.get("build_flags") or {}

tests/test_plugin.py

+18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import py
2+
import threading
3+
import time
24

35

46
def test_disabled_by_default(plugin):
@@ -66,3 +68,19 @@ def test_server_stop(plugin, mocker):
6668
plugin.webpack_process = mocker.Mock()
6769
plugin.on_server_stop()
6870
plugin.webpack_process.kill.assert_called_with()
71+
72+
73+
def test_single_npm_process(plugin, builder, mocker):
74+
def pause(*args, **kwargs):
75+
time.sleep(.1)
76+
return mocker.DEFAULT
77+
78+
mock_popen = mocker.patch("lektor_webpack_support.portable_popen")
79+
mock_popen.side_effect = pause
80+
thread1 = threading.Thread(target=plugin.npm_install)
81+
thread2 = threading.Thread(target=plugin.npm_install)
82+
thread1.start()
83+
thread2.start()
84+
thread1.join()
85+
thread2.join()
86+
assert mock_popen.call_count == 1

0 commit comments

Comments
 (0)