Перейти к основному содержимому

Процесс синхронизации встроенных ресурсов

Более 600 файлов в resources/design-studio-builtin/ (Skill / Design System / Craft) не хранятся в git. Они извлекаются из upstream-репозитория nexu-io/open-design по зафиксированному коммиту и размещаются в пользовательском каталоге материализатором при первом запуске.

Причина такого подхода: upstream-контент обширен и обновляется часто. Коммит всего этого создавал бы значительный шум в истории git Elftia и частые конфликты слияния. Фиксация коммита обеспечивает воспроизводимые сборки, превращая «обновление встроенных ресурсов» в однократное обновление lock-файла.

:::warning Исключение при курировании: prompt-templates/ никогда не синхронизируется (2026-06-04)

prompt-templates/ — это вручную отобранное подмножество, поддерживаемое на месте и не участвующее в синхронизации: полная upstream-коллекция содержит материалы с риском, например реальные лица людей (напр., шаблоны для генерации изображений Илона Маска), и эти JSON-файлы будут упакованы как анонимно видимые «шаблоны сообщества» во всех релизах каналов. Поэтому:

  • subdirs в lock-файле не включает prompt-templates; в скрипте синхронизации есть защита NEVER_SYNC_SUBDIRS, которая жёстко завершится с ошибкой, если этот каталог будет добавлен обратно (повторная синхронизация сначала удалит отобранный набор через clearSubdirs, затем внедрит неотфильтрованный полный upstream-набор).
  • Чтобы добавить новые шаблоны из upstream: копируйте JSON по одному вручную и проверяйте (реальные лица / бренды / NSFW), обновляйте журнал курирования — см. resources/design-studio-builtin/prompt-templates/README.md.
  • Этот каталог входит в git в виде исключения (.gitignore инвертирован для resources/design-studio-builtin/* с !…/prompt-templates/); свежий клон немедленно воспроизводит отобранный набор без доступа к сети.

:::

Задействованные файлы

ПутьНазначениеВ git?
resources/design-studio-builtin.lock.jsonФайл фиксации — { repo, commit, subdirs }✅ Единственный файл в git
resources/design-studio-builtin/Синхронизированный контент на диске (600+ файлов)❌ gitignored
resources/design-studio-builtin/prompt-templates/Исключение при курировании — вручную отобранное подмножество, никогда не синхронизируется (см. предупреждение выше), включает журнал курирования README.md✅ Исключение в git (gitignore инвертирован)
resources/design-studio-builtin/.synced.jsonМаркер — содержит коммит последней успешной синхронизации, при совпадении — пропуск❌ (внутри игнорируемого каталога)
resources/design-studio-builtin/NOTICEАвтосгенерированные данные атрибуции третьих сторон
scripts/sync-design-studio.mjsСкрипт синхронизации (загрузка по lock)
scripts/update-design-studio.mjsСкрипт автообновления — загрузка HEAD upstream + обновление lock + синхронизация + обновление FLAG_FILENAME одной командой
packages/desktop/app/main/services/content/workspace/design/builtin/index.tsМатериализатор при первом запуске + FLAG_FILENAME

Моменты запуска

ТриггерКоманда / ХукРежим
npm installХук postinstall--soft (сетевой сбой не блокирует установку)
npm run setup / setup:nativeРучная инициализация--soft
npm run build:electron / build:official / build:steamМежду electron-vite build и electron-builderСтрогий режим (сбой блокирует сборку)
npm run sync:designРучное выполнениеСтрогий режим
npm run sync:design -- --forceРучное выполнение (принудительная повторная загрузка)Строгий режим + пропуск маркера
npm run sync:design:updateОбновление до HEAD upstream одним кликом (рекомендуется)См. «Обновление до последнего upstream-коммита» ниже

Когда маркер совпадает (все три поля {repo, commit, subdirs} в точности соответствуют lock), синхронизация пропускается и выводится [sync:design] already synced @ <repo>@<sha7> — skipping (use --force). Это нормальное состояние, не путайте с ошибкой.

Что делает скрипт синхронизации

Основной поток scripts/sync-design-studio.mjs:

  1. Читает resources/design-studio-builtin.lock.json, проверяет схему (должны присутствовать repo / commit / subdirs[])
  2. Сравнивает с локальным маркером .synced.json — если все три поля совпадают и нет --force, пропускает и завершает работу
  3. Загружает tarball:
    • Публичный репозиторий → https://codeload.github.com/<repo>/tar.gz/<commit>
    • После установки переменных окружения GITHUB_TOKEN / GH_TOKENhttps://api.github.com/repos/<repo>/tarball/<commit> (для приватных репозиториев)
  4. Распаковывает с помощью decompress, strip: 1 удаляет внешнюю директорию с именем пакета, filter оставляет только subdirs из lock
  5. Перед распаковкой fs.rm(subdir, { recursive: true, force: true }) очищает каждый целевой подкаталог во избежание устаревших файлов после удалений в upstream
  6. Записывает маркер .synced.json
  7. Записывает NOTICE — автогенерирует данные атрибуции третьих сторон (Apache-2.0)

Кросс-платформенная особенность decompress 4.x (не ломать)

strip применяется до filter, но на Windows пути, получаемые filter, уже нормализованы в обратные слэши. Фильтр в скрипте использует file.path.split(/[\\/]/)[0] для разделения по обоим разделителям — использование только / пропускает все 600+ файлов на Linux, но 0 на Windows, причём режим сбоя тихий (выводит только пустой каталог), что делает отладку болезненной. При изменении этого раздела проверяйте на обеих платформах.

Обновление до последнего upstream-коммита

Рекомендуемый путь: автоматизация одной командой

npm run sync:design:update

scripts/update-design-studio.mjs объединяет три шага процесса «обновления» в одно действие:

  1. GET https://api.github.com/repos/<repo>/commits/HEAD — парсит SHA HEAD ветки по умолчанию upstream
  2. Если совпадает с полем commit в lock → выводит already up to date — nothing to do и завершает работу с кодом 0
  3. Иначе заменяет по регулярному выражению поле commit в lock-файле (сохраняя исходный формат), вызывает sync-design-studio.mjs для загрузки и распаковки; при сбое синхронизации автоматически откатывает lock, не оставляя грязного указателя на несинхронизированный коммит
  4. Использует короткий SHA нового коммита upstream (первые 7 символов) для обновления константы FLAG_FILENAME в services/content/workspace/design/builtin/index.ts на .elftia-builtin-materialized-<short-sha>.json — старое имя флага у существующих пользователей в <userData> больше не совпадает, следующий запуск инициирует повторную материализацию нового контента
  5. Выводит подсказки git diff / git commit

При коммите изменяются только эти два файла: lock.json и builtin/index.ts.

Соглашение об именовании флагов: FLAG_FILENAME содержит короткий SHA коммита upstream (напр., .elftia-builtin-materialized-83ddf76.json). Раньше использовались целочисленные номера версий v1/v2/..., теперь мигрировано на формат короткого SHA — смена коммита автоматически меняет имя, не нужно поддерживать ручные номера версий.

Пробный запуск:

npm run sync:design:update -- --dry-run

Только выводит разницу SHA, не трогает файлы. Можно использовать в CI для обнаружения «доступна ли новая upstream-версия».

Авторизация: Когда GITHUB_TOKEN не установлен, используется публичный API GitHub с ограничением 60 запросов/час без аутентификации. В CI установите GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} для 5000 запросов/час.

Альтернативный путь: три шага вручную

Используйте только если автоскрипт сломан или нужно зафиксироваться на ветке не по умолчанию / историческом коммите:

  1. Измените lock: Введите полный 40-символьный SHA в поле commit файла resources/design-studio-builtin.lock.json (не короткий хеш — скрипт выполняет точное строковое сравнение)

  2. Запустите синхронизацию:

    npm run sync:design

    Когда commit не совпадает с маркером, автоматически выполняется повторная загрузка, --force не нужен. --force нужен только когда маркер и lock совпадают, но локальный каталог повреждён и нужна принудительная повторная загрузка.

  3. Обновите FLAG_FILENAME: Отредактируйте packages/desktop/app/main/services/content/workspace/design/builtin/index.ts, измените константу на короткий SHA нового коммита:

    const FLAG_FILENAME = '.elftia-builtin-materialized-<new short-sha>.json';

    Этот шаг критически важен — старый флаг существующих пользователей Elftia в <userData>/elftia/.elftia-builtin-materialized-<old>.json больше не совпадает, следующий запуск инициирует повторную материализацию. Без обновления только свежие установки получат новый контент, существующие пользователи продолжат видеть старый.

Проверка

# Удалить локальный маркер для принудительной повторной проверки
rm resources/design-studio-builtin/.synced.json
npm run sync:design

# Просмотр нового количества файлов / даты в NOTICE
cat resources/design-studio-builtin/.synced.json
cat resources/design-studio-builtin/NOTICE | head -10

Типовые операции

СценарийКоманда
Обновление до последнего upstream-коммита (включая обновление флага)npm run sync:design:update
Проверить наличие новой upstream-версии (без изменения файлов)npm run sync:design:update -- --dry-run
Узнать статус синхронизации / к какому коммиту синхронизированоcat resources/design-studio-builtin/.synced.json
Принудительная повторная загрузка (каталог повреждён / подозрение на неполную распаковку)npm run sync:design -- --force
Получить последний SHA ветки main upstreamgh api repos/nexu-io/open-design/commits/main --jq .sha
Временно пропустить синхронизацию (отладка CI)Удалить lock-файл → скрипт выведет «lock file missing, skipping» и завершит работу
Заменить upstream приватным форкомИзменить поле repo в lock, экспортировать GITHUB_TOKEN и запустить синхронизацию

Устранение неполадок

СимптомПричина / Решение
[sync:design] already synced @ ... — skipping (use --force)Нормально, маркер совпал. Добавьте -- --force для повторной загрузки
HTTP 404 ... (private repo? set GITHUB_TOKEN)Репозиторий в lock приватный или коммит не существует; экспортируйте GITHUB_TOKEN или проверьте SHA
no files matched subdirs [...]Каталог в subdirs не существует в этом коммите (upstream переименовал/рефакторил); проверьте структуру каталогов в этом коммите
На Windows синхронизация проходит успешно, но каталог продукта пустПроблема с разделителем путей в фильтре decompress. Не меняйте split(/[\\/]/) в скрипте на split('/')
Пользователь обновился, но не видит новые встроенные SkillЗабыли обновить FLAG_FILENAME, старый флаг пользователя всё ещё там → пропуск материализации. sync:design:update обновляет автоматически; при ручном изменении lock не забудьте шаг 3
sync:design:update сообщает HTTP 403 ... rate-limited?Неаутентифицированный API GitHub ограничен 60 запросами/час. Экспортируйте GITHUB_TOKEN и повторите
sync:design:update синхронизируется успешно, но флаг не изменилсяПроверьте, не был ли переименован FLAG_FILENAME в services/content/workspace/design/builtin/index.ts — скрипт использует регулярное выражение const FLAG_FILENAME = '\.elftia-builtin-materialized-...\.json'; для поиска, после переименования завершится с ошибкой
CI загружает tarball слишком медленно / превышает пропускную способностьИспользуйте actions/cache для кеширования resources/design-studio-builtin/ с хешем lock-файла в качестве ключа; при совпадении пропускайте загрузку

Связанные файлы / Модули