Итак, вы программист Dart, создающий приложения Dart, но какой язык вы используете для создания сред разработки/тестирования/производства?

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

В этой статье обсуждается использование Dart вместе с консольным SDK DCli в качестве предпочтительного языка для инструментов сборки, тестирования и развертывания.

Убийственный CLI-инструмент Dart

Чего не понимают многие разработчики Dart, так это того, что Dart на самом деле предоставляет несколько убийственных функций, которые делают его лучше для инструментов CLI, чем любой другой язык, который я использовал.

Итак, какие функции убийцы вы спросите?

  • Кроссплатформенность Dart разработан как кроссплатформенный, поэтому вы можете написать одно консольное приложение, которое будет работать в Linux, Windows, MacOS и Raspberry PI. DCli обеспечивает дополнительную кроссплатформенную поддержку.
  • Скомпилировать в отдельный исполняемый файл. Вы можете скомпилировать приложение Dart CLI в одну строку (`dart compile exe xxx.dart`), а затем развернуть полученный исполняемый файл на платформе, совместимой с двоичными файлами, без дополнительных требований к среде выполнения.
  • Вы можете запустить скрипт Dart непосредственно из CL без его предварительной компиляции.
  • отличный опыт отладки, включая горячую перезагрузку.
  • Простое развертывание через pub.dev или onepub.dev или просто скопировать исполняемый файл.

Сочетание этих 5 функций действительно делает Dart идеальным языком для разработки CLI.

DCli

SDK консоли DCli написан на Dart и состоит из инструментов CLI и библиотеки, специально предназначенной для создания приложений CLI.

DCli упрощает работу с вашими проектами Dart, предоставляя ряд высокоуровневых функций и классов со встроенными знаниями о Dart.

DCli поставляется с пакетами path и args, которые являются фундаментальными строительными блоками для приложений CLI.

Создайте свое первое приложение CLI

Итак, у нас есть инструментарий CLI, давайте создадим что-нибудь полезное.

Примечание: полную копию кода можно найти в конце этого блога.

В качестве упражнения мы создадим скрипт Dart, который позволит вам обновить номер версии pubspec.yaml. из командной строки.
Мы будем использовать пакет args для обработки аргументов командной строки, пакет path для создания путей к каталогам и DCli для навигации по структуре проекта.

Примечание: если у вас установлен флаттер, замените любую команду `dart` на `flutter`. например «Дарт-паб…» становится «Флаттер-паб…».

Начните с установки инструментов DCli:

dart pub global activate dcli
dcli install

Соглашения

Команда Dart рекомендует использовать каталог «tool» в вашем проекте для хранения любого кода/инструментов, используемых для поддержки вашего проекта.

Каталог инструментов может содержать скрипты Dart, которые имеют доступ ко всем зависимостям в pubspec.yaml вашего проекта, включая зависимости в разделе dev_dependencies.

Dart-скрипт `setversion.dart` (который мы собираемся создать) обычно будет находиться в каталоге инструментов вашего проекта, но чтобы все было чисто, мы собираемся создать совершенно новый проект.

В соответствующем каталоге запустите:

dcli create setversion
cd setversion
dart pub get

В итоге вы получите следующую структуру проекта:

 README.md
 CHANGELOG.md
 pubspec.lock
 analysis_options.yaml
 pubspec.yaml
 bin/serversion.dart

Используя ваш любимый редактор или IDE, давайте внесем некоторые изменения в только что созданный скрипт setversion.dart.

Я рекомендую VS Code, так как он легкий, хорошо работает и позволяет отлаживать удаленный сеанс.

Откройте `bin/setversion.dart` и замените его содержимое следующим:

#! /usr/bin/env dcli
import ‘dart:io’;
import ‘package:dcli/dcli.dart’;
void main(List<String> args) {
 // define the argument parser as taking a single option: –version or -v for short
 final parser = ArgParser()
 ..addOption(‘version’,
 mandatory: true, abbr: ‘v’, help: ‘Set the version no.’);
final ArgResults results;
 try {
 // parse the args passed on the command line
 results = parser.parse(args);
} on FormatException catch (e) {
 // if the args aren’t valid an exception is thrown
 // print the error to stderr and make it red.
 printerr(red(e.message));
// showUsage(parser);
// shutdown the script returning 1 as the exit code.
 exit(1);
 }
/// obtain the version no. the user passed on the command line
 final version = results[‘version’] as String;
// updatePubSpec(version);
 // createVersionFile(version);
}

Запускаем наш скрипт

Мы еще не закончили, но давайте возьмем наш скрипт на тест-драйв.

bin/setversion.dart

Вы должны увидеть следующий вывод:

Option version is mandatory.

Обновите номер версии.

Итак, давайте завершим реализацию нашего скрипта, обновив pubspec.yaml и добавив сообщение об использовании.

Во-первых, нам нужен еще один пакет, чтобы мы могли разобрать версию.
Добавьте пакет pub_semver в свой проект в качестве зависимости:

dart pub add pub_semver

Примечание: если вы добавляете инструменты сборки в каталог инструментов вашего собственного проекта, вам следует добавить pub_semver в качестве зависимости от разработчиков `dart pub add — dev pub_semver`, потому что это не часть кода, который вы отправляете.

Добавьте оператор импорта в начало файла `setversion.dart`:

import ‘package:pub_semver/pub_semver.dart’;

Добавьте следующие методы в конец скрипта setversion.dart:

// display how to use our script
void showUsage(ArgParser parser) {
 print(‘’);
 // use the DCli green function to colour code the output.
 print(green(‘Usage:’));
 print(green(‘setversion — version=<version>’));
 print(‘’);
 print(‘Updates pubspec.yaml to the passed version no.’);
 print(parser.usage);
}
/// Update the pubspec.yaml to reflect the selected version.
void updatePubSpec(String version) {
 final projectRoot = DartProject.self.pathToProjectRoot;
/// Use join from the paths package to build the path
 /// in a cross platform manner.
 final pathToPubSpec = join(projectRoot, ‘pubspec.yaml’);
/// load the pubspec.yaml, update the version
 /// and save the updated version.
 PubSpec.fromFile(pathToPubSpec)
 ..version = Version.parse(version)
 ..saveToFile(pathToPubSpec);
}

Обновите свою основную функцию, удалив комментарии из:

 showUsage(parser);

и

 updatePubSpec(version);

Один из моих любимых методов — это приведенный выше вызов DartProject.self.pathToProjectRoot. Вы можете находиться где угодно в корневом каталоге вашего проекта Dart, а pathToProjectRoot предоставляет ожидаемый путь. Это очень удобно при создании инструментов сборки.

Если вы сейчас запустите setversion, он обновит номер версии вашего pubspec.yaml!

bin/setversion.dart — version=1.1.1

Но подождите, мы можем сделать больше

Нет простого способа получить номер версии из вашего приложения, и это может быть очень полезно.

Итак, давайте создадим еще одну функцию, которая создает библиотеку Dart в вашем пакете, содержащую номер версии и имя вашего приложения.

Добавьте следующую функцию в конец setversion.dart и обновите основную функцию, чтобы она вызывала ее.

/// Create a dart library file with version number
/// and package name.
void createVersionFile(String version) {
 final projectRoot = DartProject.self.pathToProjectRoot;
/// create the path to the version.dart library
 final pathToSrc = join(projectRoot, ‘lib’, ‘src’);
 if (!exists(pathToSrc)) {
 createDir(pathToSrc, recursive: true);
 }
 final pathToVersionScript = join(pathToSrc, ‘version.dart’);
/// get the package name from the pubspec.
 final pathToPubSpec = join(projectRoot, ‘pubspec.yaml’);
 final pubspec = PubSpec.fromFile(pathToPubSpec);
 final name = pubspec.name;
/// write to the version script file using
 /// DCli’s String extension method `write`.
 pathToVersionScript.write(‘’’
 /// Generated by tool/setversion.dart
 const packageVersion = ‘$version’;
 const packageName = ‘$name’;
 ‘’’);
}

Обновите свою основную функцию, удалив комментарии из:

createVersionFile(version);

Запустите `bin/setversion.dart -v 0.0.2` еще раз, и теперь у вас должен быть файл version.dart в каталоге lib/src вашего проекта.

Вызов `write` является одним из набора расширений String, которые поддерживает DCli.

 /// write to the version script file using
 /// DCli’s String extension method `write`.
 pathToVersionScript.write(‘’’
 /// Generated by tool/setversion.dart
 const packageVersion = ‘$version’;
 const packageName = ‘$name’;
 ‘’’);

Вызов write обрабатывает содержимое pathToVersionScript как путь к файлу и записывает переданную строку в этот файл.

DCli имеет ряд расширений String, которые позволяют вам рассматривать String как имя файла и записывать или добавлять в файл.

Цель здесь состоит в том, чтобы предоставить простой метод выполнения операций, подобных Bash/CMD. Мы обсудим это подробнее в следующей статье.

Опубликовать

Мы создали универсальный полезный инструмент, который можно использовать в любом из ваших проектов Dart, поэтому давайте опубликуем его, чтобы у нас был легкий доступ к нему, когда он нам понадобится.

Установить OnePub

Мы могли бы опубликовать ваш код на pub.dev, что имеет то преимущество, что любой пользователь Dart может получить доступ к вашему произведению искусства, но поскольку это всего лишь экспериментальный фрагмент кода, я бы попросил вас не загрязнять pub.dev этим дерьмо :)

Pub.dev — критически важный ресурс в экосистеме Dart, поэтому важно, чтобы сообщество не заполняло его большим количеством экспериментальных и заброшенных пакетов.

Вместо этого мы собираемся публиковать в нашем собственном частном репозитории через OnePub.

Отказ от ответственности: я являюсь одним из основателей OnePub.

OnePub по сути такой же, как pub.dev, но для приватных пакетов. Для этого упражнения мы будем использовать бесплатную версию OnePub.

Вы можете думать об OnePub как о среде-песочнице, где вы можете экспериментировать и хранить свои собственные игрушки.

С OnePub вы можете экспериментировать в свое удовольствие, и когда у вас есть что-то, что может быть полезно для более широкого сообщества, публикуйте его на pub.dev.

Когда мы войдем в OnePub, он создаст ваш личный репозиторий Dart, в который вы сможете публиковать пакеты.

Установите OnePub и авторизуйтесь:

dart pub global activate onepub
onepub login

Примечание: если кеш dart pub не находится в вашем PATH, приведенный выше вызов `onepub` завершится ошибкой. Вернитесь к инструкциям по установке Dart и добавьте кэш паба Dart в свой PATH.

Отметьте наш проект `setversion` как частный пакет:

onepub pub private

Публиковать

Пометка нашего пакета как приватного через `onepub pub private` приведет к тому, что команда `dart pub publish` опубликует ваш пакет в OnePub, а не в pub.dev.

Теперь мы можем опубликовать наш пакет в OnePub.

touch LICENSE
dart pub publish

Примечание: для команды `dart pub publish` требуется наличие файла LICENSE. Команда touch создает пустой файл LICENSE, чего для наших целей достаточно.

Обмен

С самим собой

Теперь, когда вы опубликовали `setversion`, вы можете установить его на любой компьютер, глобально активировав его.

Если вы не знакомы с концепцией «глобальной активации» Dart, она по существу устанавливает пакет Dart, содержащий скрипт CLI. Сценарий CLI скомпилирован, и, если вы следовали инструкциям по установке Dart, глобально активированный сценарий будет находиться в вашем PATH.

onepub pub global activate setversion

Примечание: мы используем onepub pub, а не dart pub. Это потому, что мы хотим активировать `setversion` из вашего частного репозитория OnePub, а не pub.dev.

И ваша команда

Теперь вы можете поделиться setversion со своей командой, пригласив их в свой репозиторий OnePub.

Нажмите https://onepub.dev/invite, чтобы пригласить члена команды.

После того, как приглашение будет принято, ваши товарищи по команде также смогут «глобально активировать» setversion на своих системах.

dart pub global activate onepub
onepub login
onepub pub global activate setversion

Примечание: изменение на `onepub` для последней строки в приведенных выше командах. Поскольку setversion является приватным пакетом, вы не можете использовать стандартную команду `dart pub`, так как она не найдет ваш приватный репозиторий.

Члены вашей команды теперь могут запускать `setversion` в своем собственном проекте через:

cd myproject
setversion — version=1.0.0

Разверните автономный исполняемый файл

Если вам нужно запустить setversion в системе без установленного Dart (маловероятно для setversion, но он может понадобиться для других инструментов, которые вы пишете), вы можете скомпилировать его в автономный исполняемый файл:

cd setversion
dcli compile bin/setversion.dart

Теперь у вас есть исполняемый файл в каталоге bin, который называется либо `setversion`, либо в Windows `setversion.exe`.

Вы можете скопировать исполняемый файл на любую бинарно-совместимую платформу и запустить его без установки Dart.

scp bin/sertversion some_remote:setversion
ssh some_remote
./setversion — version=1.2.0

Заключение

Мы едва коснулись того, что могут сделать Dart и DCli, но рассмотрели некоторые основные принципы создания инструментов сборки с использованием SDK консоли DCli.

Вам не нужно останавливаться на инструментах сборки. В OnePub мы заменили все наши bash-скрипты на Dart-скрипты.

По мере того, как мы создавали наши инструменты, заменяя сценарии bash и Chef/ruby, мы также публиковали растущую коллекцию повторно используемых пакетов, которые используются в наших инструментах.

В результате мы имеем более высокий уровень автоматизации в нашей среде разработки, тестирования и производства, чем когда-либо прежде.

Так просто собрать несколько скриптов, используя уже имеющиеся у нас навыки работы с Dart.

Вы можете узнать больше о SDK консоли DCli на dcli.onepub.dev.

Ниже приведен небольшой пример классов и функций, доступных при написании приложений CLI с помощью DCli.

Исследуйте свое окружение

  • Дартсдк
  • ДартПроект
  • Дартскрипт
  • PubSpec
  • Окружение
  • пабкеш
  • Платформа
  • Оболочка

Взаимодействие с пользователем

  • просить
  • подтверждать
  • меню
  • Распечатать
  • принтер
  • Терминал

Управление файлами

  • копировать
  • двигаться
  • удалить
  • копироватьдерево
  • двигатьсяДерево
  • удалитьдерево
  • createDir
  • isFile
  • находить
  • с TempDir
  • с временным файлом

Запустите любое приложение CLI как дочерний процесс

  • бегать
  • начинать
  • для каждого
  • к списку

Окончательный вариант

Вот окончательная версия нашего кода:

#! /usr/bin/env dcli
import ‘dart:io’;
import ‘package:dcli/dcli.dart’;
import ‘package:pub_semver/pub_semver.dart’;
void main(List<String> args) {
 // define the argument parser as taking a single option: –version or -v for short
 final parser = ArgParser()
 ..addOption(‘version’,
 mandatory: true, abbr: ‘v’, help: ‘Set the version no.’);
final ArgResults results;
 try {
 // parse the args passed on the command line
 results = parser.parse(args);
 } on FormatException catch (e) {
 // if the args aren’t valid an exception is thrown
 // print the error to stderr and make it red.
 printerr(red(e.message));
// showUsage(parser);
// shutdown the script returning 1 as the exit code.
 exit(1);
 }
/// obtain the version no. the user passed on the command line
 final version = results[‘version’] as String;
updatePubSpec(version);
 createVersionFile(version);
}
// display how to use our script
void showUsage(ArgParser parser) {
 print(‘’);
 // use the DCli green function to colour code the output.
 print(green(‘Usage:’));
 print(green(‘setversion — version=<version>’));
 print(‘’);
 print(‘Updates pubspec.yaml to the passed version no.’);
 print(parser.usage);
}
/// Update the pubspec.yaml to reflect the selected version.
void updatePubSpec(String version) {
 final projectRoot = DartProject.self.pathToProjectRoot;
/// Use join from the paths package to build the path
 /// in a cross platform manner.
 final pathToPubSpec = join(projectRoot, ‘pubspec.yaml’);
/// load the pubspec.yaml, update the version
 /// and save the updated version.
 PubSpec.fromFile(pathToPubSpec)
 ..version = Version.parse(version)
 ..saveToFile(pathToPubSpec);
}
/// Create a dart library file with version number
/// and package name.
void createVersionFile(String version) {
 final projectRoot = DartProject.self.pathToProjectRoot;
/// create the path to the version.dart library
 final pathToSrc = join(projectRoot, ‘lib’, ‘src’);
 if (!exists(pathToSrc)) {
 createDir(pathToSrc, recursive: true);
 }
 final pathToVersionScript = join(pathToSrc, ‘version.dart’);
/// get the package name from the pubspec.
 final pathToPubSpec = join(projectRoot, ‘pubspec.yaml’);
 final pubspec = PubSpec.fromFile(pathToPubSpec);
 final name = pubspec.name;
/// write to the version script file using
 /// DCli’s String extension method `write`.
 pathToVersionScript.write(‘’’
 /// Generated by tool/setversion.dart
 const packageVersion = ‘$version’;
 const packageName = ‘$name’;
 ‘’’);
}