Здесь, в Mavenlink, моя команда хранит множество небольших ответвлений от master для разработки наших функций. Наша цель - выпускать небольшие функции как можно чаще, поэтому мы часто ответвляем новые функциональные ветки от других функциональных веток, которые в настоящее время находятся в разработке. Это приводит к тому, что несколько цепочек веток выходят за пределы master, что может занять очень много времени, чтобы отслеживать и понимать. Например, ветвь cool-new-feature может быть ответвлением от master, а затем cool-new-feature-followup является ответвлением от cool-new-feature. Это означает, что у нас есть цепочка, которая выглядит так: master <- cool-new-feature <- cool-new-feature-followup, и запрос на вытягивание для cool-new-feature должен быть объединен с master, прежде чем cool-new-feature-followup будет объединен с master . Мы используем GitHub, чтобы сгруппировать пул-реквесты нашей команды, и нам нужен способ визуально видеть, какие пул-реквесты у нас открыты, над чем нужно работать и какие ветки имеют наивысший приоритет.

TL; DR: Если вы хотите легко визуализировать запросы на вытягивание в Google Sheet, перейдите в репо. В противном случае читайте дальше, чтобы узнать, как мы это сделали!

До этой саги мы использовали собственное представление GitHub для просмотра наших запросов на извлечение, но у него есть недостатки:

Основная проблема в том, что я не могу видеть, какие ветви находятся вне master, а какие - друг от друга. Это затрудняет определение ветвей с высоким приоритетом (ближайших к главной). Итак, моя команда начала использовать Google Drawing, чтобы вручную отслеживать наше PR-дерево.

Каждый раз, когда мы объединяли или создавали ветку, нам приходилось обновлять электронную таблицу, добавляя имя ветки, заголовок и ссылку. На обслуживание уходит уйма времени, поэтому я хотел автоматизировать этот процесс. Я знал, что GraphQL API и соответствующий проводник GitHub являются мощными и гибкими, и я знал, что мы можем написать сценарий для рендеринга данных в электронную таблицу Google. Я не хотел создавать еще один документ, который мне нужно было отслеживать (у меня уже слишком много закрепленных вкладок), поэтому я хотел настроить таргетинг на электронную таблицу, которую команда уже часто использовала. Поскольку мы занимаемся парным программированием и хотим часто менять пары, мы отслеживаем наши пары и ежедневные приоритеты в общей электронной таблице для нашего стендапа, который был идеальным кандидатом.

Другой инженер в команде тоже был заинтересован в автоматизации процесса, поэтому мы потратили утро пятницы на его создание. Было весело и на удивление легко заставить его работать. Если вы хотите сразу перейти к коду, репо здесь. Объяснение приведено ниже (примечание: код изменен по сравнению с окончательной версией, поэтому не все переменные определены):

Войдите в Скрипты Google Apps! Я никогда раньше с ними не работал. Скрипты Google Apps - это среда, в которой вы можете писать файлы Javascript для взаимодействия с другими продуктами Google (такими как таблицы, документы и т. Д.). Некоторые методы отличаются от тех, к которым вы привыкли в обычном браузере: выполнение сетевых вызовов выполняется синхронно, и вы должны использовать специальные методы, предоставляемые Google, для многих вещей, которые браузеры предоставляют вам по умолчанию. (Для получения дополнительной информации прочтите документацию.) Сценарий, привязанный к электронной таблице Google, может изменять ячейки на листе с помощью Javascript. Мы планировали отобразить наше дерево запросов на включение во вкладку в нашем расписании сопряжения.

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

Это захватывает последние 100 открытых PR на нашем этапе, а также их заголовки, URL-адреса, названия веток (headRefName) и название ветки родительского PR (baseRefName). Предполагая, что все PR в нашем дереве находятся на нашей вехе, это все, что нам нужно для построения нашего дерева. Поскольку API GitHub возвращает массив PR, нам нужно будет восстановить дерево из ссылок на родительские ветви, поэтому, если какой-либо из родителей PR нашей вехи также не находится в вехе, построение нашего дерева сломается.

Мы получаем PR и перебираем их, чтобы построить дерево с корневым узлом, являющимся ветвью master. Вот созданный нами класс узла:

Интересный факт: Google Scripts еще не поддерживает все функции ES6 (включая class), поэтому здесь мы вернулись к старому доброму прототипному наследованию ES5.

Чтобы отобразить наше дерево, мы начинаем с master и рекурсивно проходим через PR, отображая каждый из них в электронную таблицу с помощью Google Spreadsheets API. Мы выясняем, на какую ячейку нацелить отрисовку, сказав: Увеличить мой столбец на единицу от моего родителя и увеличить мою строку на (строка моего старшего брата + количество листовых узлов, которые есть у моего старшего брата). Затем, если родительский PR имеет несколько дочерних элементов, мы объединяем его с правильным количеством ячеек ниже, чтобы он охватил всех его потомков:

Мы визуализируем ветвь, вызывая displayNode(sheet, 0, 0, masterNode), где sheet - это электронная таблица Google (доступная ранее, не показана), а masterNode - это PullRequest со ссылками на все его дочерние PR.

Ниже показан результат. Это дает нам большой путь к тому, чего мы хотим: столбец A - это master. В следующем столбце справа перечислены все ветви, которые указывают на master, и так далее, так что каждая ветвь будет объединена с ячейкой слева от нее. Ветви, расположенные левее, имеют более высокий приоритет. Такие ветви, как Rpm per page feature master, у которых есть два дочерних элемента, занимают несколько строк, что означает, что легко увидеть, что Rpm — Per Page — focus styles является ответвлением от Rpm per page feature master.

Мы приближаемся к этому, но это представление трудно читать, и я хочу, чтобы в моих таблицах были «ПРИВЕТСТВЕННЫЕ ЦВЕТА», поэтому давайте закодируем каждую ячейку PR в зависимости от ее статуса. Каков его статус? Отличный вопрос! Это будет зависеть от процессов любой команды, но мы хотели, чтобы наши статусы указывали, когда действие возможно для любого данного PR. Таким образом:

  • Зеленый (готов к отправке), если он прошел наш CI, был рассмотрен и прошел проверку качества.
  • Красный (возникла проблема, требует внимания), если у нас есть некачественные спецификации, запрос на внесение изменений из обзора, отклоненная история из отдела контроля качества или неправильные ярлыки.
  • Желтый (устарел, доведите его до конца!), если он был открыт более 5 дней - это был выбор, который мы сделали, чтобы побудить нашу команду быстрее выполнять открытые PR и сосредоточиться на сжатых циклах итераций.
  • Розовый, если на нем есть ярлык Blocked
  • Пурпурный, если он имеет ярлык WIP
  • Серый (немедленные действия не требуются), если ничего из вышеперечисленного не применимо.

Вычисление статуса означало запрос дополнительных данных из GitHub для каждого PR - нам нужно было знать его тестовый статус и его метки. Поскольку мы интегрируем GitHub с нашим CI, каждый раз, когда мы нажимаем на GitHub, мы запускаем наши тесты, и результат теста связывается с последней нажатой фиксацией. Таким образом, тестовый статус PR в целом - это статус последней фиксации PR. Окончательный запрос выглядел так:

Наконец, мы устанавливаем цвет фона ячейки в зависимости от ее статуса, добавляем легенду и готово!

Мы можем обновить представление в любое время, щелкнув пункт меню Mavenlink и выбрав Branch Strategy:

В этот момент все работало, но находилось в одном гигантском файле, в котором было сложно ориентироваться и понимать, к тому же ничего нельзя было проверить! Хотя в Google Scripts есть возможность тестирования, этот инструмент непрост в использовании и сложен для скриптов, привязанных к одной электронной таблице, как эта. Кроме того, в Google Scripts нет системы импорта или модулей, поэтому разбить код на тестируемые / понятные фрагменты сложно. О, CommonJS и Webpack, как я по тебе скучаю!

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

Войдите на GitHub и eval на помощь! (Да, действительно). Я вытащил весь код в репозиторий GitHub и извлек информацию, относящуюся к этапам, в переменные, которые можно было передать в универсальные функции. Затем все, что нужно сделать, чтобы использовать его, - это скопировать следующий фрагмент в свой собственный скрипт, прикрепленный к таблице Google, и заполнить соответствующие переменные:

Теперь мы рассматриваем GitHub как сеть CDN, на которой размещается код выборки и рендеринга данных. Мы используем eval для получения необработанного файла JavaScript. Каждый файл упакован в IIFE (который, не случайно, также как Webpack компилирует ваши пакеты), который мы eval. Затем вы добавляете правильные триггеры в таблицу, и все готово!

Теперь, каждый раз, когда мы встаем утром, мы можем перейти на вкладку ветки, нажать «Обновить» и сразу увидеть обновленную панель мониторинга всего, над чем работает наша команда.

Опять же, вы можете найти весь код здесь. Не стесняйтесь использовать это для своей команды или форк и добавить свои собственные условия для PR-состояния. Оставьте свой отзыв в комментариях ниже. Кроме того, если вы хотите увидеть классные вещи, которые моя команда сделала с помощью описанных выше стратегий ветвления, ознакомьтесь с Mavenlink's Resource Management! Спасибо за чтение!