Мы часто сталкиваемся с дилеммой вокруг примеров проектов. Это полезные учебные ресурсы, которые демонстрируют отдельные функции работающего приложения, упрощая настройку кода и экспериментирование с ним. Но с другой стороны, они могут быстро стать бременем для обслуживания. По мере развития фреймворка требуются значительные постоянные усилия, чтобы постоянно обновлять эти примеры с использованием новейших API, соглашений и передовых методов.

Поскольку наши инструменты интерфейса командной строки позволяют легко и быстро создать новый проект, мы не смогли устоять перед соблазном и в итоге получили около 18 примеров проектов. Чтобы обновить их, нужно открыть 18 пул реквестов - это утомительная работа! Когда началась работа над LoopBack 4 (a.k.a Next), обслуживание примеров проектов было вне поля зрения. Было слишком много более важной работы в других областях, от выяснения монорепозитория Lerna с помощью сборок TypeScript до поиска правильного дизайна и API, которые позволят нам устранить болевые точки кодовой базы LoopBack 3.x.

Недавно мы начали заниматься некоторыми задачами по очистке. Например, когда мы рассмотрели отказ от поддержки Node.js 6.x (см. Issue # 611), мы поняли, что с 8 примерами проектов, живущими в собственных репозиториях GitHub, многие изменения, которые легко применить в основном монорепозитории, являются превращаясь в долгие и утомительные задачи.

Несмотря на то, что вначале мы приняли решение хранить все в одном репозитории, теперь мы спрашивали себя, почему проекты-примеры живут за пределами нашего монорепозитория. Основной причиной был пользовательский опыт, поскольку мы не хотели, чтобы наши пользователи git клонировали весь монорепозиторий, когда их интересует только один пример проекта! После некоторого размышления мы, к счастью, нашли простое решение: инструмент командной строки, который загружает только файлы желаемого примера проекта.

Так родился lb4 example.

$ lb4 example
? What example would you like to clone? (Use arrow keys) 
❯ getting-started: An application and tutorial on how to build with LoopBack 4. 
  hello-world: A simple hello-world Application using LoopBack 4
  log-extension: An example extension project for LoopBack 4
  rpc-server: A basic RPC server using a made-up protocol.

После выбора примера для загрузки инструмент делает HTTPS-запрос на загрузку архива, содержащего последний код из нашего монорепозитория, и извлекает только файлы из выбранного примера проекта.

$ lb4 example
? What example would you like to clone? getting-started
The example was cloned to loopback4-example-getting-started.

Под капотом

Мы используем GitHub API ссылки на архив для загрузки архива, содержащего текущую ветку master. Затем он извлекает файлы из соответствующего каталога пакета примеров и помещает их во вновь созданный локальный каталог, попутно переименовывая записи файлов с packages/example-{name}/{path} на loopback4-example-{name}/{path}. Хорошим побочным эффектом является то, что пользователям не нужно устанавливать git, чтобы получить наши примеры проектов.

GitHub предлагает два формата архивов: ZIP и tar + gzip, где формат ZIP широко представлен на веб-сайте. Поскольку я не знал об Archive API, моей первой реализацией была загрузка ZIP-архива. Однако мы не были очень довольны этим решением.

Поскольку популярная реализация zip / unzip yauzl не поддерживает потоковый режим, нам пришлось сохранить загруженный архив во временный файл. Изначально мы думали, что это ограничение yauzl, но дальнейшее изучение выявило ограничение формата ZIP. Цитата из yauzl No Streaming Unzip API:

Из-за конструкции формата файла .zip невозможно интерпретировать файл .zip от начала до конца (например, из читаемого потока) без ущерба для правильности. Центральный каталог, который является органом управления содержимым файла .zip, находится в конце файла .zip, а не в начале. Потоковому API потребуется либо буферизовать весь файл .zip, чтобы попасть в центральный каталог, прежде чем что-либо интерпретировать (нарушая цель интерфейса потоковой передачи), либо полагаться на заголовки локальных файлов, которые перемежаются через файл .zip. Однако заголовки локальных файлов явно осуждаются в спецификации как ненадежные копии центрального каталога, поэтому доверять им было бы нарушением спецификации.

API yauzl также был слишком низкоуровневым. Посмотрите, насколько задействована ZIP-версия extractExample():

Packages/cli/example/clone-example.js@0359a262.

Модуль extract-zip предоставляет API более высокого уровня, который проще в использовании, но, к сожалению, он спроектирован таким образом, что нашу задачу фильтрации и переименования файлов невозможно реализовать.

После того, как Raymond указал, что GitHub также предлагает архивы tar + gzip, я переписал свой код, чтобы обрабатывать tarball вместо ZIP-архивов, и результат стал намного лучше! Нет временных файлов, с которыми нужно иметь дело, всего несколько строк Stream piping:

return new Promise((resolve, reject) => {
    request(GITHUB_ARCHIVE_URL)
      .pipe(gunzip())
      .pipe(untar(outDir, exampleName))
      .on('error', reject)
      .on('finish', () => resolve(outDir));
  });

Реализация untar() все еще более сложна, чем хотелось бы, из-за того, как tar-fs применяет операторы map и filter, но, по крайней мере, эта сложность не ускользает от нашего метода untar().

Вы можете найти полный исходный код для загрузки и извлечения файлов примеров здесь:

Пакеты / cli / example / clone-example.js.

Заключительные мысли

Работая над примерами, я внес еще несколько связанных изменений.

Страница документации Примеры и учебные пособия теперь содержит обновленный список примеров, которые мы поддерживаем, вместе с краткими инструкциями, показывающими пользователям, как получить отдельные примеры.

У нас есть устаревшие примеры проектов, которые больше не нужны:

Пример проекта «loopback-next-example» демонстрирует монорепозиторий с несколькими микросервисами. Мы чувствуем, что это не относится к нашему монорепозиторию. Это монорепозиторий сам по себе, и в конечном итоге он будет зависеть от внешних инструментов, таких как оркестратор микросервисов, которые мы не хотим использовать в нашем основном монорепозитории. Я переименовал проект, чтобы лучше понять его цель:

Loopback4-example-microservices.

Обратите внимание, что кодовая база довольно устарела и даже не компилируется с последними альфа-версиями LoopBack4. Это то, что нам нужно исправить в ближайшее время (см. Loopback4-example-microservices # 76).

Призыв к действию

Будущий успех LoopBack зависит от вас. Мы ценим вашу постоянную поддержку и участие, чтобы сделать LoopBack еще лучше и значимым для вашего опыта создания API. Присоединяйтесь к нам и помогите проекту:

Первоначально опубликовано на strongloop.com.