Skip to content

Commit eb63fbf

Browse files
author
khoden
committed
nots: дедупликация действий после сборки, пропуск pnpm install на актуальных node_modules
## Суть изменений: 1. Пересмотрены методы логирования при сборке (и после), чтобы не показывались логи действий, который не выполняются (пропускаются). Таким образом логирование секций кода (с последующим стиранием строки лога) теперь осуществляется в методе-обёртке. 2. Команда `nots install` теперь выполняет последовательную сборку пиров (раньше запускал параллельно, что приводило к состоянию гонки); 3. Сборка пиров в команде `nots install` выполняется без рекурсивного обхода пиров для пиров (все пиры и так переданы). Это позволило не "чинить дедупликацию", а в принципе не приводить к "дупликации" – каждый пир проходится один раз. Тут важно определить порядок обхода. 4. Пропуск повторных запусков `pnpm install` без необходимости. Необходимость вычисляется так: - `builder` в локальном режиме кладет в аутпут файлик `pre.pnpm-lockfile.yaml`, хеш которого используется в `nots/cli` - `builder` в локальном режиме рядом с папкой `node_modules` создает файлик `node_modules.json` с хешом `pre.pnpm-lockfile.yaml`, который использовался при сборке этого `node_modules` - `nots/cli` использует сравнивает файлик из аутпута и из `node_modules.json` и если отличаются, то `pnpm install` запускается. Пожалуй, пункт 4 стоит расписать. Кажется, что эти файлики всегда будут совпадать, но я перестраховываюсь: часть пиров может быть закеширована в сборке, но удалена из `~/.nots/nm_store`, например, при запуске с `nots --clean`. Чтобы избежать подобных локальных казусов я и перестраховываюсь. Хеш от `pre.pnpm-lockfile.yaml` предпочтительнее хеша от `pnpm-lockfile.yaml`, т.к. он включает в себя пиры (т.е. это результат смерживания лок-файлов). Также была версия с проверкой, что node_modules создалась в промежутке между проверкой и запуском nots/cli (т.е. в рамках `ya make`), но это не работает при кешировании узлов сборки пиров. Если у вас будут идеи, какие еще проверки можно сделать для принятия решения, запускать ли `pnpm install` – я открыт к предложениям. ## Побочные улучшения: ### nots/cli - Добавлен хелпер `utils.ts:processItems(items, action)` - отказоустойчивый `forEach`; - Для `log-formatters.ts:unlog` вместо прямой записи ESC-последовательностей в stdout используется модуль `readline`; - `log-formatters.ts:unlog` не срабатывает в тестах (пишет заглушку) и при включении отладочного вывода (`DEBUG`/`--verbose`); - Для `DoneHandler` добавлен метод-обёртка `runOnce(action, key, fn)` для более удобного использования, а также запись в лог отладки, если действие пропускается. ## Что не вошло в PR Осталось на будущее: 1. Дедупликация пиров нескольких таргетов. Т.е. сборка пиров при `nots install project1 project2` должна быть общей, а не своё поддерево для каждого. И `ya make` для них нужно запускать один раз. И пост-сборочные действия выполнять единожды в правильном порядке, деже не пытаясь в дупликацию. 2. Подобный пункту 4 механизм, но не для `nots build`, а `nots install` — не запускать `pnpm install`, если недавно ставили (тут нужно определиться с критерием) commit_hash:11f98acb44f759464876f61c5dbf69da7c0d0340
1 parent cec17a3 commit eb63fbf

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

build/plugins/lib/nots/package_manager/pnpm/package_manager.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import hashlib
2+
import json
13
import os
24
import shutil
35

6+
from .constants import PNPM_PRE_LOCKFILE_FILENAME
47
from .lockfile import PnpmLockfile
58
from .utils import build_lockfile_path, build_pre_lockfile_path, build_ws_config_path
69
from .workspace import PnpmWorkspace
710
from ..base import BasePackageManager, PackageManagerError
8-
from ..base.constants import NODE_MODULES_WORKSPACE_BUNDLE_FILENAME, PACKAGE_JSON_FILENAME, PNPM_LOCKFILE_FILENAME
11+
from ..base.constants import (
12+
NODE_MODULES_WORKSPACE_BUNDLE_FILENAME,
13+
PACKAGE_JSON_FILENAME,
14+
PNPM_LOCKFILE_FILENAME,
15+
)
916
from ..base.node_modules_bundler import bundle_node_modules
1017
from ..base.package_json import PackageJson
1118
from ..base.timeit import timeit
@@ -51,6 +58,17 @@ def get_local_pnpm_store():
5158
def get_local_old_pnpm_store():
5259
return os.path.join(home_dir(), ".cache", "pnpm-store")
5360

61+
@timeit
62+
def _get_file_hash(self, path: str):
63+
sha256 = hashlib.sha256()
64+
65+
with open(path, "rb") as f:
66+
# Read the file in chunks
67+
for chunk in iter(lambda: f.read(4096), b""):
68+
sha256.update(chunk)
69+
70+
return sha256.hexdigest()
71+
5472
@timeit
5573
def _create_local_node_modules(self, nm_store_path: str, store_dir: str, virtual_store_dir: str):
5674
"""
@@ -85,6 +103,11 @@ def _create_local_node_modules(self, nm_store_path: str, store_dir: str, virtual
85103

86104
self._run_pnpm_install(store_dir, virtual_store_dir, nm_store_path)
87105

106+
# Write node_modules.json to prevent extra `pnpm install` running 1
107+
with open(os.path.join(nm_store_path, "node_modules.json"), "w") as f:
108+
pre_pnpm_lockfile_hash = self._get_file_hash(build_pre_lockfile_path(self.build_path))
109+
json.dump({PNPM_PRE_LOCKFILE_FILENAME: {"hash": pre_pnpm_lockfile_hash}}, f)
110+
88111
@timeit
89112
def create_node_modules(self, yatool_prebuilder_path=None, local_cli=False, bundle=True):
90113
"""

0 commit comments

Comments
 (0)