В прошлый раз я не успел закончить сборщик, набросав только поверхностную версию для сборки всего модуля целиком. Однако залив его в личном кабинете маркета стало ясно, что работа по данной части еще не закончена. А кроме этого мне надо же еще и обновления для модулей собирать, а это уже чуть более сложная задача. Но обо всем по порядку …

Заливаем модуль, попытка номер раз.

В партнерском кабинете битрикса есть раздел «Маркетплейс 1С-Битрикс», в котором производится работа со всеми вашими модулями.

Интерфейс этот выглядит примерно так:

Добавление модуля сводится к тому, что вам нужно заполнить форму с кучей данных, включая архивчик с полной сборкой модуля. Там надо указать название будущего модуля, описание, цены (если модуль платный), связь с редакциями, категорию, контакты техподдержки, различные флаги для фильтра в маркетплейсе и т.д. Можно даже прицепить свой Google Analytics или метрику, чтобы трэкать просмотры и клики на странице модуля. Заполнив все обязательные поля и отправив форму, сразу стало понятно, что Битрикс - он такой Битрикс везде, даже в партнерском интерфейсе.

После отправки формы из URL пропадает ID модуля, что приводит к невозможности повторной отправки формы без возврата на предыдущую страницу. Точнее, форму то вы отправите, но битрикс сочтет это как попытку создать новый модуль, даже если он уже заведен в системе. Если в таком состоянии отправить форму с файлом сборки модуля, то форма будет отвечать глупой ошибкой:

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

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

Вторая проблема, с которой я столкнулся - это «Ошибка при распаковке архива». Уж не знаю, что битриксу не понравилось в том архиве, который я собирал с помощью своего сборщика (обычный tar.gz, ничего необычного), но он никак не мог его распаковать. Но я заметил, что если перепаковать архив руками в zip, то он битрикс уже не выдавал ошибки при распаковке. В связи с этим пришлось в сборщике переделать архивацию с tar.gz на zip, что в принципе пофиг.

Ошибки в модуле

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

Что?? Версия модуля не указана? Я же её формирую, блин, автоматом, и она 100% есть в архиве, 10 раз проверил. Что не так? … уж было собрался писать в техподдержку, но тут же смекнул, что надо бы представить себя в роли разработчика битрикс. Знаете, таким, новичком, который только месяц познакомился с инфоблоками, а вчера услышал от коллеги про регулярки. После этого, стало ясно, что инструмент написан топорно (как и многое в битриксе), а значит ровно также нужно подходить к генерации файла с версией. Подумав об этом обо всем, а также изучив файлы версий других модулей, удалось выявить такой набор правил:

  • для отступов нельзя использовать пробелы, нужно использовать один таб
  • нельзя использовать одинарные кавычки для строк, только двойные
  • короткий открывающий php тег
  • наличие закрывающего php тега
  • номер версии должен состоять из трех частей

После того, как подогнал генератор под эти требования, и залил его в маркет, ошибка сразу исчезла, а мое лицо расплылось в улыбке … печальной такой улыбке …

С остальными ошибками все было проще, когда я начал мыслить с позиции битрикс-разработчика. Ошибка «В файле install/index.php не указан $MODULE_ID» говорит нам о том, что нужно указать дефолтное значение этого для поля класса. В моем случае оно формировалось раньше в runtime в конструкторе, но битрикс требует наличия значения сразу.

Ошибка «В файле install/index.php неверно указано имя класса» - вообще как вишенка на торте. Она вылечилась удалением обратного слеша перед указанием CModule в определении класса модуля. Видимо при разработке этого инструментария разработчики не полагались на то, что в php есть неймспейсы.

После устранения всех этих «косяков» и загрузки модуля в zip битрикс схавал архив и больше не ругался.

Теперь, когда модуль залит, становится доступной вкладка «Тестирование». С помощью этого инструмента можно выгрузить залитый модуль на какой-нибудь сайт и провести тестирование там.

Доработка сборщика

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

Совершенно очевидно, что у сборщика есть контекст - версия, которую он собирает. А значит и все команды должны работать в контексте. Я решил вынести в контекст номера последних двух версий, они нужны будут для того, чтобы понять, по какому алгоритму собирать версию. Алгоритма два - один собирает текущую версию полностью, а другой собирает разницу между текущей и предыдущей версией (алгоритм сборки обновления). Для каждого из них я создал входные команды gulp - build_last_version и build_update. Первая команда работает почти также, как я описывал в прошлой статье. Я лишь произвел небольшой рефакторинг, поработал над синтаксисом, исправил мелкие ошибки и изменил формат архива.

Сборка обновления

Для сборки обновления появляется новый таск - diff, который собой заменяет таск move. Задача этого таска - вычислить разницу с помощью git, и положить эту разницу в директорию сборки.

Код у таска простой

// Перенос последней версии модуля в директорию сборки
gulp.task('diff', (callback) => {
    git.exec({args: `diff ${previousVersion.version} --name-only`}, (error, output) => {
        if (error) {
            callback(error);
        }

        const globs = extendGlob(output.split(os.EOL));

        gulp.src(globs, {base: './'})
            .pipe(gulp.dest(path.join(buildFolder, getVersionFolderName())))
            .on('end', callback);
    });
});

Тащим diff из гита, получая имена всех файлов. В случае успеха копируем файлы текущего репозитория в директорию сборки, где кладем это все в директорию с номером этой версии. Переменная previousVersion в данном случае берется из контекста (а по сути является глобальной :D), а getVersionFolderName - это функция, которая на основании версий контекста формирует название директории для будущего архива.

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

Вот так выглядят задачи build_last_version и build_update после рефакторинга:

// Сборка текущей версии модуля
gulp.task('build_last_version', (callback) => {
    getTags().then(function(output) {
        const versions = parseVersions(output);
        lastVersion = previousVersion = versions[0];
        sequence('clean', 'move', 'version', 'encode', 'archive', 'dist', 'clean', callback);
    }).catch((error) => {
        console.log(error);
    });
});

// Сборка обновления модуля (разница между последней и предпоследней версией по тегам git)
gulp.task('build_update', (callback) => {
    getTags().then(function(output) {
        const versions = parseVersions(output);
        lastVersion = versions[0];
        previousVersion = versions[1];
        sequence('clean', 'diff', 'version', 'encode', 'archive', 'dist', 'clean', callback)
    }).catch((error) => {
        console.log(error);
    });
});

Ну и осталось создать задачу default, которая будет запускать сразу обе сборки. Нужно это для того, чтобы запустить только команду gulp (или в моем случае npm run gulp) и не вспоминать название команд.

В следующей статье

Наверно, пора подводить первые итоги. Текущую версию модуля я храню на гитхабе. Любой желающий может скачать и установить её себе, пользоваться сколько влезет. Заранее хочу предупредить, что эта версия модуля очень сильно ограничена в функционале. Во-первых, она содержит на данный момент лишь 10% от тех возможностей, которыми я планирую этот модуль снабдить. Во-вторых, единственная на данный момент функция генерации купонов сильно ограничена в скорости (не мной, конечно же, а самим битриксом). Дело в том, что при каждом добавлении купона битрикс выполняет много лишних операций, от которых можно безболезненно избавиться. Каждый, кому это нужно будет, легко сможет снять это ограничение самостоятельно, и тогда купоны будут добавляться в базу очень быстро. Публикацию дальнейших версий модуля в opensource пока не планирую, т.к. сами понимаете …

К следующей статье цикла.