Лев Аминов

Заметки о технологиях, музыке, дизайне. В меньшей степени о последних двух.

Atlassian Bitbucket WIP Merge check Plugin

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

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

Назначение плагина копирование базового функционала GitLab, а именно – блокирование слияния пулл-реквестов, если их заголовок начинается с “WIP:”:

Merge Check работает как на уровне проектов, так и на уровне репозиториев.

2018   bitbucket   java

Sublime Merge для вызова из консоли

Ни для кого не секрет, что можно сделать алиас команды вызова Sublime Text в используемой командной оболочке (например, Bash):

alias st="/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl"

Теперь при вызове “st ./” из Терминала можно открыть текущий каталог в Sublime Text. Для Sublime Merge есть аналогичный функционал, просто прописываем:

alias sm="/Applications/Sublime\ Merge.app/Contents/SharedSupport/bin/smerge"

И пользуемся:

$ sm --help
Sublime Merge build 1062

Usage: smerge path               Open the given repository
   or: smerge search query       Search for commits in the current repository
   or: smerge blame file [line]  Blame the given file in the current repository
   or: smerge log file           Show the file history in the current repository

  -n or --new-window:      Open a new window
  -b or --background:      Don't activate the application
  -h or --help:            Show help (this message) and exit
  -v or --version:         Show version and exit
2018   os x   Sublime
2017   Music

Профиль: Splice

Splice – облачная платформа для создания музыки и совместной работы, так же предоставляет автоматическое резервное копирование с контролем версий, интегрируется с основными Digital Audio Workstations (DAW), такими как FL Studio, Ableton, ProTools, Garageband.

Дата основания: 2013 г.
Основатели: Стив Мартокси, Мэтт Аимонетти (занимает место CTO)

Стив Мартокси (GroupMe, Blade, Sympact)

Github: https://github.com/smart
Linkedin: https://www.linkedin.com/in/stevemartocci
Twitter: https://twitter.com/smart
AngelList: https://angel.co/steve-martocci
Medium: https://medium.com/@smart
Интервью для TechCrunch: https://www.youtube.com/watch?v=1I7qiEgPnY8

Мэтт Аимонетти (LivingSocial, Sony PlayStation)

Блог: https://matt.aimonetti.net
Github: https://github.com/mattetti
Linkedin: https://www.linkedin.com/in/mattaimonetti
Twitter: https://twitter.com/mattetti
AngelList: https://angel.co/matt-aimonetti
Medium: https://medium.com/@mattetti

Технологии

Уже кажется странным, но у Splice нет своей организации на Github, где я начал искать в первую очередь, они выбрали Bitbucket. На странице https://bitbucket.org/splice/ то немногое, что доступно публично и давно не обновлялось, среди которых, например, форк facebookgo/grace.

Доступен и список членов команды Splice, при желании их можно найти на Github.

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

  • AWS, Azure, EC2
  • Bitbucket, Mercurial (VCS)
  • Golang (APIs, core logic, audio, network)
  • C, C++
  • Obj-C, C# (client)
  • Ruby (web rendering)
  • MySQL, Memcached, Redis, ElasticSearch
  • Docker
  • Jenkins (CI)
  • Angular, TypeScript (UI/UX)
  • Electron (client)
  • Chef, Ansible

Клиент

По-подробнее остановлюсь на MacOS-клиенте Splice:

Клиент позволяет управлять локальными проектами, скачивать проекты для работы, управлять плагинами и семплами купленными в сервисе. Скачиваемый установщик с сайта – dmg-пакет, на самом деле является загрузчиком толстого клиента, который собран на Angular, Electron, Obj-C и, внезапно, Golang. Размер всего приложения около 150 мб, большая часть, конечно, – Electron. Приложение на Go работает в фоне от основного процесса и является gRPC-сервером со всей логикой, к которому обращается JS-клиент.

Получение JS-кода клиента:

cd /Applications/Splice.app/Contents/Resources/
npm install asar
./node_modules/.bin/asar e app.asar ./app

Получение списка строк из хэлпера, написанного на Go:

cd /Applications/Splice.app/Contents/Resources/Splice\ Helper.app/Contents/MacOS/
nm Splice\ Helper | grep github

Первое даст понять более детально, что использовалось для построения и автоматизации интерфейса, второе – информацию об использованных в проекте пакетах, доступных публично, среди них есть разработки Мэтта: github.com/go-audio/aiff, github.com/go-audio/wav, github.com/mattetti/audio. Наверное, это ответ на вопрос: Should a CTO keep on coding? :)

2017   Music   Profile
2017   Арт

Go HTTP-client. Баг или фитча?

В нашем арсенале имеется небольшой сервис, задачей которого является сбор новостей из RSS-лент с нотификацией в Telegram и все было отлично до обновления Go с версии 1.5.1 до 1.8.1. После обновления одна из пары сотен лент перестала работать, если быть точным, то лента “Twitter Engineering Blog” (RSS).

Очевидно, что могла быть ошибка где-то со стороны самого кода, поэтому проблемная область была ограничена до такого вот куска кода:

С помощью стандартного HTTP-клиента делаем GET-запрос, выводим в консоль то, что было отправлено в запросе и то, что вернулось в ответ.

HTTP-клиент по умолчанию использует стандартный транспорт, который поддерживает gzip-сжатие, работает это так – транспорт, если не установлено `DisableCompression=true` и если пользователь сам не задал `Accept-Encoding`, в каждый запрос добавляет заголовок `Accept-Encoding: gzip`, если в заголовке ответа есть `Content-Encoding: gzip`, то транспорт распаковывает содержимое и удаляет из ответа `Content-Encoding`. Для клиента это выглядит как обычный запрос без сжатия.

Наличие заголовка `Accept-Encoding` не задает жесткого ограничения на ответ – это рекомендация, заголовок определяет, какие типы сжатия и настройки поддерживаются клиентом, тем самым указывая gzip и/или deflate мы должны учитывать, что контент может быть как сжат одним из этих алгоритмов, так и быть без сжатия. Передача ответа в сжатом виде, неподдерживаемом клиентом, возможна, но нарушает принцип работы и назначение самого заголовка `Accept-Encoding`.

И тут мы подходим напрямую к проблеме – при стандартном запросе (с указанием поддержки gzip), получаем контент сжатый алгоритмом Deflate, естественно, транспорт такой ответ не распаковывает, получаем тело ответа, с которым нельзя начать работать без распаковки. Кажется, что решить такую проблему можно проверкой значения `Content-Encoding` и распаковкой тела, но ведь это не решает корень проблемы – клиент не заявлял поддержку этого алгоритма сжатия, а значит, не должен его получать.

Первое, что приходит в голову – ошибка на стороне самого сервиса, например, он неверно трактует заголовок запроса и всегда отдает контент сжатый Deflate.

Проверяем gzip:

curl https://blog.twitter.com/api/blog.rss?name=engineering -sH "Accept-Encoding: gzip" -D - | grep encoding
content-encoding: gzip

Проверяем deflate:

curl https://blog.twitter.com/api/blog.rss?name=engineering -sH "Accept-Encoding: deflate" -D - | grep encoding
content-encoding: deflate

Проверяем identity (без сжатия):

curl https://blog.twitter.com/api/blog.rss?name=engineering -sH "Accept-Encoding: identity" -D - | grep encoding
<?xml version="1.0" encoding="utf-8"?>

Проверяем без передачи заголовка `Accept-Encoding` – это трактуется как отсутствие поддержки каких-либо алгоритмов сжатия:

curl https://blog.twitter.com/api/blog.rss?name=engineering -s -D - | grep encoding
<?xml version="1.0" encoding="utf-8"?>

Никаких противоречий нет. Проверим, как менялось поведения транспорта и самого HTTP-клиента от версии к версии. Для этого я написал небольшой скрипт:

В результате получаем:

1.4.3-alpine: Content-Encoding:
1.5.1-alpine: Content-Encoding:
1.5.2-alpine: Content-Encoding:
1.5.3-alpine: Content-Encoding:
1.5.4-alpine: Content-Encoding:
1.6.0-alpine: Content-Encoding:
1.6.1-alpine: Content-Encoding:
1.6.2-alpine: Content-Encoding: deflate
1.6.3-alpine: Content-Encoding: deflate
1.6.4-alpine: Content-Encoding: deflate
1.7.0-alpine: Content-Encoding: deflate
1.7.1-alpine: Content-Encoding: deflate
1.7.3-alpine: Content-Encoding: deflate
1.7.4-alpine: Content-Encoding: deflate
1.7.5-alpine: Content-Encoding: deflate
1.8.0-alpine: Content-Encoding: deflate
1.8.1-alpine: Content-Encoding: deflate

До версии 1.6.2 транспорт запрашивал gzip и распаковывал без каких-либо проблем. Отключим в транспорте поддержку сжатия и проведем тест еще раз:

1.4.3-alpine: Content-Encoding: gzip
1.5.1-alpine: Content-Encoding: gzip
1.5.2-alpine: Content-Encoding: gzip
1.5.3-alpine: Content-Encoding: gzip
1.5.4-alpine: Content-Encoding: gzip
1.6.0-alpine: Content-Encoding: gzip
1.6.1-alpine: Content-Encoding: gzip
1.6.2-alpine: Content-Encoding: deflate
1.6.3-alpine: Content-Encoding: deflate
1.6.4-alpine: Content-Encoding: deflate
1.7.0-alpine: Content-Encoding: deflate
1.7.1-alpine: Content-Encoding: deflate
1.7.3-alpine: Content-Encoding: deflate
1.7.4-alpine: Content-Encoding: deflate
1.7.5-alpine: Content-Encoding: deflate
1.8.0-alpine: Content-Encoding: deflate
1.8.1-alpine: Content-Encoding: deflate

Запросим контент без сжатия:

1.4.3-alpine: Content-Encoding:
1.5.1-alpine: Content-Encoding:
1.5.2-alpine: Content-Encoding:
1.5.3-alpine: Content-Encoding:
1.5.4-alpine: Content-Encoding:
1.6.0-alpine: Content-Encoding:
1.6.1-alpine: Content-Encoding:
1.6.2-alpine: Content-Encoding: deflate
1.6.3-alpine: Content-Encoding: deflate
1.6.4-alpine: Content-Encoding: deflate
1.7.0-alpine: Content-Encoding: deflate
1.7.1-alpine: Content-Encoding: deflate
1.7.3-alpine: Content-Encoding: deflate
1.7.4-alpine: Content-Encoding: deflate
1.7.5-alpine: Content-Encoding: deflate
1.8.0-alpine: Content-Encoding: deflate
1.8.1-alpine: Content-Encoding: deflate

Проблема явно появилась с версии 1.6.2, хорошо, что между этой и предыдущей версией произошло не так много изменений. Благодаря зоркому глазу Владимиру Смирнову из чата @pro.go, удалось выяснить, что ключевые изменения внес коммит, который включает поддержку HTTP2 по умолчанию в стандартном транспорте.

Сразу возникает предположение, что проблема в работе HTTP2 на стороне сервера Twitter, точнее сервера, который обслуживает блог. Проверим с отключенной поддержкой HTTP2, для этого добавим к строке запуска переменную окружения `GODEBUG=http2client=0`:

docker run --rm -it -v "$PWD":/usr/srv/myapp -w /usr/srv/myapp "golang:${version}" sh -c "GODEBUG=http2client=0 go run main.go"

Запускаем:

1.4.3-alpine: Content-Encoding:
1.5.1-alpine: Content-Encoding:
1.5.2-alpine: Content-Encoding:
1.5.3-alpine: Content-Encoding:
1.5.4-alpine: Content-Encoding:
1.6.0-alpine: Content-Encoding:
1.6.1-alpine: Content-Encoding:
1.6.2-alpine: Content-Encoding:
1.6.3-alpine: Content-Encoding:
1.6.4-alpine: Content-Encoding:
1.7.0-alpine: Content-Encoding:
1.7.1-alpine: Content-Encoding:
1.7.3-alpine: Content-Encoding:
1.7.4-alpine: Content-Encoding:
1.7.5-alpine: Content-Encoding:
1.8.0-alpine: Content-Encoding:
1.8.1-alpine: Content-Encoding:

Curl, собранный с поддержкой протокола HTTP2, подтверждает наличие проблемы со стороны сервиса Twitter. Далее список проблемных ссылок и хостов, которые удалось найти за время всех экспериментов:

Остается только написать в Twitter и ждать исправления проблемы.

Дополнение от 3 мая

Оказалось, что все сервисы Твиттера, поддерживающие HTTP2, сжимают ответ вне зависимости от того, поддерживает клиент сжатие или нет. Где-то только gzip, где-то – deflate. На сервисах работающих по HTTP/1.1 проблем не замечено.

2017   go   golang   http2   twitter

Заметка о клиенте Spotify

Клиент представляет собой приложение на основе Chromium Embedded Framework. Почему не Electron? Потому что свой протокол. В остальном, это HTML + CSS + JavaScript. Логика работы и представление конкретных разделов хранится в spa-файлах (видимо, от Single Page Application), которые, на самом деле, являются zip-файлами:

$ cd /Applications/Spotify.app/Contents/Resources/Apps
$ ls -la
total 44432
drwxr-xr-x  41 levaminov  staff     1394 May  1 16:25 .
drwxr-xr-x  48 levaminov  staff     1632 Apr 10 14:31 ..
-rw-r--r--   1 levaminov  staff   472763 Apr 10 13:59 about.spa
<...>
-rw-r--r--   1 levaminov  staff   358401 Apr 10 14:00 zlogin.spa
$ unzip browse.spa -d browse
$ cd browse
$ ls -la
total 6472
drwxr-xr-x   6 levaminov  staff      204 May  1 16:15 .
drwxr-xr-x  42 levaminov  staff     1428 May  1 16:15 ..
-rw-rw-r--   1 levaminov  staff  3302195 Apr 22 13:32 bundle.js
drwxrwxr-x   4 levaminov  staff      136 Apr 10 13:59 css
-rw-r--r--   1 levaminov  staff      232 Apr 10 13:59 index.html
-rw-rw-r--   1 levaminov  staff     1632 Apr 10 13:59 manifest.json

Модуль zlink описывает общее представление клиента – базу, остальные, в большинстве случаев, описывают контент каких-то отдельных блоков (чарты, о программе, настройки клиента, профиль пользователя).

Кэш, пользовательские данные:

cd ~/Library/Application\ Support/Spotify/

Логи по компонентам интерфейса:

cd ~/Library/Logs/Spotify/

Запуск клиента в режиме отладки:

/Applications/Spotify.app/Contents/MacOS/Spotify --remote-debugging-port=9222

Открываем http://localhost:9222/json, копируем devtoolsFrontendUrl, открываем в Chrome что-то в духе http://localhost:9222/devtools/inspector.html?ws=localhost:9222/devtools/page/7afc75eb-5eb6-4ac7-a300-b5e56cbb59db

Описание технологий, стоящих за клиентом:

Не лишним будет упомянуть и технический блог Spotify Labs.

2017   Spotify
Ctrl + ↓ Earlier