[О блоге] [наверх] [пред] [2023-09-05 10:33:33+03:00] [416c573f83428ea877e5cce8a13d49fea1a9ceb8]
Темы: [git][go][mail][redo]

Как я делаю релизы

Где-то в блогах увидел как кто-то описывает процесс создания релиза СПО
программы. Есть время -- опишу и свой процесс, хотя он во всяких
скриптах закоммиченных в Git и так имеется, тайн нет. Ну и просто мои
мысли о структуре программы.

* .gitignore не должен содержать исключения касающиеся инструментария
  конкретного разработчика. Если кто-то использует Emacs, то у него не
  появятся vim-specific файлы. Если кто-то использует другую реализацию
  redo, то запросто не будет .redo директорий, и т.д.
* Я стараюсь прописывать в .gitignore как можно более точные/узкие
  правила. Если надо проигнорировать директорию, то явно указываю "/",
  так как это директория. Если только в текущей директории что-то, то
  слэш в начале. Прописываю "/foo.out" в foo/.gitignore и "/bar.out" в
  bar/.gitignore, вместо "/*.out" в .gitignore выше foo и bar директорий

* Любой проект должен содержать README по хорошему, где было бы хотя бы
  краткое описание что это, пускай и пара строчек. У меня не во всех
  репозиториях это есть, но они только про маленькие утилиты, где
  документацией является комментарий в начале скрипта
* Никаких README.md и вообще Markdown
  (da160c3c7b3f5393aa37f2d042f9b281264273de,
  9d989dceed050f0e0e8f5ac63c22cd3771cd2d56,
  b8b7029858e6e5cbea511e66dedfb98c4d7340b3)!

* Лицензия является важной вещью для СПО. Можно быть против copyright,
  против "intellectual property" и подобных вещей, но пока в этом мире
  есть законы стран и по ним ваше творение не может считаться свободным
  ПО, если это явно не отмечено. Я считаю что выбор можно свести либо к
  семейству GPLv3: GPLv3, AGPLv3, LGPLv3, либо public domain. Для новых
  форматов/протоколов GNU сайт также рекомендует обратить внимание на
  permissive лицензии типа Apache License 2.0, однако например OpenBSD
  не считает её допустимой свободной (http://www.openbsd.org/policy.html)
  Те, кто говорят что топят за полную свободу, но при этом используют
  Expat лицензию (которую некорректно называют "MIT"
  (499d8afd7741e9056f0d4c9f969386f49a6a293f)) -- лицемеры, ибо не делают
  public domain (1c19e4cee16c687f33dba569b8b7381527fc3e51). Кто за
  распространение СПО, то GPLv3 более чем отлично продумана и учитывает
  засады типа TiVoization
* В значимых файлах исходного кода стоит, как GPL и гласит, добавлять
  явно информацию о том что файл лицензирован так то. По умолчанию GPLv3
  в своём примере вставки для кода предлагает использовать v3 или более
  позднюю версию. Я не доверяю людям из FSF, только RMS, ибо там шиза у
  них в головах лютейшая и у меня сомнения что они за СПО как RMS,
  поэтому все мои проекты не содержат разрешения (автоматического)
  использования более поздних версий GPL (1afd778850d7ed026677339de509ef3c0c3a5269)
* GNU Coding Standards рекомендуют GNU GPL помещать в COPYING файл, чему
  тоже и следую. И срать я хотел что многие новомодные платформы для
  разработки ПО хотят видеть LICENSE. Не они толкают СПО в мир, они не
  хотят СПО в этом мире, плюс меня бесит американское написание этого
  слова и я написал бы LICENCE

* Go стал обязательно требовать использования системы модулей. И я это
  яро одобряю, ибо они удобны и хороши тем, что жёстко фиксируют
  конкретную версию зависимостей. Может быть даже несколько версий одной
  библиотеки. Вообще нарадоваться не могу этому всему.
* Я убеждён что Make не имеет права на существование, ибо оно просто
  прям вот всем всем хуже и не выполняет задач, не помогает разработчику
  (a79698c8ace58a8c35fa35ba382281a018fea59e). Поэтому никаких
  Makefile-ов! Но и .do файлов я из многих Go проектов поубирал тоже,
  ибо в самом Go собственная система сборки/кэширования и достаточно
  простых скриптов вызывающих go build возможно с передачей нескольких
  флагов
* Я использую свой goredo, как redo реализацию, но .do файлы пишутся
  только с предположением что есть redo-{ifchange,create} команды, если
  это не .do файлы для "внутреннего" использования, не предполагающие
  запуск другим человеком
* Если в Go проекте используется stringer, то я не коммичу
  сгенерированный им файл, ибо репозиторий это для разработки, не
  готовый для использования код, не релиз, не tarball, поэтому и нет
  условия что из него должно всё мочь собираться
* Если программа является демоном, то для неё я или в документации или в
  contrib/ директории описывал как создавать daemontools файл для
  запуска. Никаких systemd-модулей (ведь так же называется?) у меня быть
  не может. Единственно правильным подходом по запуску демонов я считаю
  подход daemontools. Не обязательно буквально его использовать: это
  может быть и runit и s6 какой-нибудь, но принцип у них такой же

* Всякие вспомогательные утилиты для сборки, для разработки я стараюсь
  помещать в utils/. Скрипты/программы без которых в общем и целом можно
  обойтись, но они возможно пригодятся конечному пользователю --
  стараюсь помещать в contrib/. Например это примеры использования той
  или иной программы, скрипты для запуска под daemontools, и т.п..
  Например когда NNCP требовал redo для сборки, то минималистичную
  реализацию redo на POSIX shell я помещал в contrib/do
* Если исполняемый файл подразумевает интерактивный запуск
  пользователем, то я стал стараться опускать расширение файла. Не
  "foo.sh", а а просто "foo". Ведь какая разница на чём он написан? Да и
  имея .py расширение -- не будет понятно Py2 это или Py3. Для
  определения типа файла достаточен вызов file или head
* Если скрипт предполагает source кем-то другим, именно source ("."), а
  не выполнение, то стараюсь добавлять .rc расширение, пускай даже после
  расширения говорящего про тип языка (.tcl.rc, .zsh.rc)
* Абсолютно недопустимо никакого GNU Bash применения или упоминания. Он
  ещё больше не имеет права на существование чем Make
  (http://www.stargrave.org/ZSH-proscons.html). Огромный, монструозный,
  громоздкий, медленный. Имеет смехотворные средства customization, но
  для скриптов это не имеет значения, ok. Не имеет никакой помощи для
  работы хотя бы даже с переменными/файлами содержащими пробелы -- чем
  он тогда лучше POSIX shell? Кроме того, на множестве дистрибутивов он
  не стоит по умолчанию, а значит #!/bin/bash требует подтягивания
  сторонней зависимости, которая для скриптов мало чем лучше будет чем
  POSIX shell
* Почти для всего надо использовать #!/usr/bin/env, а не #!/usr/bin/perl
  какой-нибудь, ибо он может находится (собственно, как и /bin/bash не
  обязан существовать в этом месте) в другом месте
* Не приемлю использование awk (ибо их куча диалектов, а зачем писать на
  узком подмножестве, если можно использовать Perl?). Не забываю про
  разницу между BSD sed и GNU sed, где даже поведение -i аргумента
  отличается. sed использую для относительно простых действий, но вообще
  даже для sed -i частенько применяю perl. В OpenBSD он идёт из коробки.
  Во многих системах он из коробки тоже, хотя в современной FreeBSD это
  отдельный пакет. Но что за система без Perl, который везде одинаково
  работает, ибо нет BSD/GNU/Plan9/whatever диалектов несовместимых между
  собой? Считаю что Perl5 доступен будет всегда, поэтому частенько
  скрипты на нём присутствуют в проектах

* Документацию пишу в Texinfo формате
  (http://www.stargrave.org/InfoRules.html) и считаю Info достаточно
  хорошим форматом для неё. Собственно, Info и является официальным
  форматом документации в GNU, а не какие-нибудь man-страницы. Некоторые
  проекты пишут на подмножестве Texinfo и используют утилиты способные
  их версию сконвертировать в man-страницы (GnuPG и Z shell, насколько
  помню, как минимум). Но я не считаю это оправданным геморроем, ибо
  info команда вполне себе удобна для использования и нет смысла в man
* Частенько использую PlantUML. К сожалению, на их сайте сейчас призывы
  поддерживать терроризм и собирать средства на нацистов, мразей, воров
  и бандитов всяких. Но это не отменяет что прошлые версии их программы
  умеют делать как и растровые, так и Unicode текстовые версии диаграмм.
  Только ради этой программы и держу Java. Отрендеренные изображения не
  коммичу: ведь это же build artifact, как и .o/.a какой-нибудь
* Есть redo правила для сборки как .info, так и .html, как правило.
  Вроде бы во всех случаях я документацию пишу так, чтобы она могла
  выступать как исходник для HTML сайта программы
* Кроме короткого описания что это за программа (аналог README), нужно
  не забывать про информацию со спасибами (thanks), про информацию о
  том, как можно связаться с автором, возможных списков рассылки, как
  стоит отправлять патчи, отчёты об ошибках и и т.п.
* На странице установки/скачивания я стараюсь приводить коротко список
  команд как скачать и собрать программу. wget|fetch, tar xf, go build
  -mod=vendor, и т.д.
* Список tarball-ов оформлен в виде таблицы. Если в проекте ведётся
  старница "новостей", где описаны изменения от релиза к релизу, то
  каждая версия tarball-а является ссылкой на версию страницы NEWS. Не
  забывать указывать дату релиза, его размер
* Когда-то (c3ba3d2f29655d06dffe1ec836c9f0b98daec0c9) я там прописывал
  SHA256 от tarball. Для проектов связанных с ГОСТ криптографией ещё
  добавлял Стрибог-256. А недавно всё это убрал и стал публиковать
  ссылку на Metalink .meta4 XML файл, создаваемый собственной meta4a
  утилитой, которая прописывает кучу видов хэшей, а также URL-ы на
  Yggdrasil (y.что-то-там) версию сайтов и возможно других зеркал.
  Удобно тем, что и GNU Wget это поддерживает и позволяет и автоматом
  проверять целостность и качать с разных мест
* Также раньше я добавлял ссылку на OpenPGP ASCII-armored подпись.
  Недавно (d2890179a8bbb7bc3f2dcd807faa906e6ab78224) стал ещё
  подписывать OpenSSH ключом (c5e9b245012a5a1ac1732b2c94fc89d9e760efe3),
  стал двигаться в сторону *существенно* более простого софта. Теперь в
  таблице tarball-ов есть ссылка на .sig ASCII файлик с подписью
* В репозитории появляется из-за этого файл PUBKEY-PGP.asc с armored
  OpenPGP публичным ключом подписи tarball-ов и Git тэгов проекта. Рядом
  находится PUBKEY-SSH.pub файл с публичным OpenSSH ключом подписи.
  Возможно им я и Git тэги начну подписывать вместо OpenPGP. Этот .pub
  файл содержит ключ в формате пригодном для использования в качестве
  "-f allowed_signers_file". Этот файл также ещё и подписан OpenPGP
  ключом в detached signature файле PUBKEY-SSH.pub.asc. Если у кого-то
  есть доверие к моему основному ключу, то ключ подписи проекта он
  сможет проверить, а им уже проверить подпись под OpenSSH ключом
* .meta4 также содержат и armored версии OpenPGP и OpenSSH подписей tarball-а
* В документации, особенно для установки, часто использую @var{VERSION}
  переменные, которые во время сборки подставляю каким-нибудь
  -D "VERSION `cat ../VERSION`" вызовом. чтобы руками явно ничего не
  прописывать
* В этом году отказался от использования CSS вовсе
  (--set-customization-variable NO_CSS=1). Прежде применял свой
  "фирменный" style.css, со спёртыми цветами из одного из screenshot-ов
  SGI IRIX 4Dwm. Но я всегда хочу видеть границы ячеек в таблицах. Без
  CSS они невидимы. Модифицировал код своего локально установленного
  Texinfo чтобы он добавлял border=1 к таблицам

Собственно, как же сам процесс релиза то выглядит?

* Убеждаюсь в doc/news.texi содержит всю информацию о новом релизе,
  ничего не забыто. В .go файле где зашивается версия программы тоже
  выставлена нужная версия
* Делаю git tag -a -s -u proj-id vX.Y.Z. Если проект содержит отдельные
  ветки master и develop, то перед этим делаю: git checkout master; git
  merge --no-ff develop; git tag ...; git branch -d develop ; git branch
  develop ; git checkout develop
* Далее запускаю ./makedist скрипт. По сути в котором все действия
  автоматизированы. Скрипт рассчитан на запуск на моей системе, поэтому
  там вроде бы точно где-то есть и hard-coded пути. Если это программа
  для работы, то всё всё равно происходит примерно аналогично как и СПО
  версиям. Всё что ниже: делается автоматически
* Клонируется git репозиторий во временную директорию, чтобы никакой
  лишний файл ненароком не мешал
* git checkout vX.Y.Z
* Зависит от проекта, но чаще всего делается redo VERSION, который
  вычисляет версию на основе текущего тэга коммита. Появляется VERSION с
  vX.Y.Z строчкой
* Далее собирается документация. Она всегда использует redo для этого.
  Но перед сборкой я заменяю doc/download.texi файл, содержащий таблицу
  с tarball-ами, на пустышку из нескольких строчек: "tarball вот отсюда
  можно скачать". Запускаю redo/PROJNAME.info
* Делаю отдельный NEWS файл в корне проекта. Создаю временный NEWS.texi
  файл, в который просто копируется содержимое doc/news.texi, без
  @node/@section, которые бы сбили иерархию секций. Отдельным makeinfo
  вызовом собираю ASCII версию NEWS
* Аналогичное повторяю для INSTALL файла и THANKS в корне проекта. Для
  INSTALL файла из-за этого есть ограничения: в нём нельзя сделать
  ссылки на другие страницы документации, так как он собирается без
  ведома остальных файлов
* Исходный код (*.go, go.*) перемещаю в src/
* Выполняю go mod vendor, который создаст vendor/ со всеми необходимыми
  зависимостями для сборки проекта
* Копирует он отнюдь не все файлы что находятся в репозиториях
  зависимостей. Иногда вызываю modvendor утилиту и указываю что ещё надо
  докопировать (типа тестовых векторов например) в vendor/ директорию.
  Чтобы каждая зависимость более-менее была самодостаточна и полноценна
* Выполняю зачистку: удаляю всякие .gitignore find-ом, .travis.yaml,
  тяжёлые *_test.go, возможно что-то касающееся *_windows.go и всё в
  таком духе. Зашиваю я это всё в скрипт makedist заранее. Если я
  обновлял зависимости, то во время релиза, как правило, делаю
  makedist-ом tarball и смотрю что в него попадает, всё ли нормально,
  вменяемого ли размера он получается
* Удаляю свои собственные вспомогательные скрипты и ненужные файлы:
  .redo (redo-cleanup full), makedist, VERSION.do, .git* и т.д.
* Если есть скрипты вызывающие "go build", то sed/perl-ом добавляю в них
  -mod=vendor. Иногда в скриптах есть просто проверка на наличие vendor
  директории и они сами добавляют эту опцию
* Меняю права доступа для всех файлов на 644, для директорий на 755.
  Тем, что нужны права на исполнение: явно добавляю chmod +x. Сам по
  себе то я штатно работаю с umask 077, поэтому у меня права по
  умолчанию для группы и других будут вообще по нулям. Вообще git
  archive например делает 664/775 права, но GNU Coding Standards
  рекомендует 755 для директорий, чему и следую
  (https://www.gnu.org/prep/standards/standards.html#Releases)
* Засовываю всё в архив: tar cf --uid=0 --gid=0 --numeric-owner
  Это делает архив более "детерминированным" при создании. git clone
  тоже выставляет mtime-ы во что-то отталкивающихся от времени коммита
* Я много уделял времени на поиск идеального архиватора/формата
  (d5670fc7a83b5161ebbb4f7b9cdaf34f3edf3a94,
  a3f2621a4986184923161e2e1c65778f61a98086,
  c13068dff5ce5219180a26327a0e0a8bafa20f6f,
  3292f9367343832b03ecc52e17437333d136cab6,
  dd558b2a665788dfa4a15024397060615bd86d98,
  b4b36a45ec721be274a4b6c0f9f243d5672f3a48) и сделал вывод что pax лучше
  всех. Но BSD tar (libarchive-based который) по умолчанию делает как:
  ustar формат если ничего из метаинформации не будет потеряно, pax в
  противном случае. Поэтому я не указываю форсированное использование
  pax (ведь ustar это небольшое его подмножество), и для исходного кода
  на деле получается ustar архив по факту
* Сжимаю Zstandard-ом с -19 силой. Для себя я файлы часто сжимаю с
  --ultra -22 -- максимальной силой, но -22 требует --ultra, а он, в
  свою очередь больше памяти для декомпрессии. Я уверен что для моих
  относительно не больших tarball-ов это вряд ли где будет проблемой, но
  всё же не гоже выходить за пределы рекомендуемого и предъявлять
  повышенные требования к памяти целевой системы. Почему .zst? Да потому
  что невероятно быстрый при декомпрессии, сжимает почти как XZ (сильно
  лучше gzip), давно на него уже переходят во множестве пакетных
  менеджеров. Сжимается один раз -- разжимается часто
* Делаю OpenSSH и OpenPGP подписи:
    ssh-keygen -Y sign -f ~/.ssh/sign/PROJ -n file $tarball
    gpg --armor --detach-sign --sign --local-user PROJ "$tarball"
* Вызываю meta4-create -fn "$tarball" -mtime "$tarball"
    -sig-pgp "$tarball".asc -sig-ssh "$tarball".sig
    http://www.PROJ/download/"$tarball"
    http://y.PROJ/download/"$tarball" < "$tarball" > "$tarball".meta4
* Вывожу сгенерированный кусок Texinfo для вставки в doc/download.texi,
  где есть человекочитаемый размер (в KiB) и пути к сгенерированным файлам
* Переношу это всё в doc/PROJ.html/download/
* mtime .meta4 файла явно выставляется равным tarball-у. Для файлов
  подписей не парюсь с идентичностью mtime, хотя не помешало бы
* Вывожу портянку текста для отправки в письме в рассылку об анонсе
  выхода новой версии PROJ. В письмо подставляются URL-ы и размер
  tarball. Прежде я туда вставлял и хэш
* Далее вывожу команду запуска mutt, в которой указано, что надо
  прикрепить .meta4 файл, а также выставить соответствующую тему. Именно
  благодаря .meta4, я и перестал засовывать хэш в текст письма, ибо оно
  и так всё содержится в Metalink файле
* makedist скрипт на этом завершает своё выполнение
* Добавляю кусок текста о новом tarball для download.texi и собираю
  документацию для сайта. Как правило, это redo doc/www цель.
* Отличается она от просто сборки HTML версии документации тем, что ещё
  вызывает releases.atom.zsh (390632c69d7d0dc7a048befff5b48e1f0b34cf42)
  скрипт для создания releases.atom файла на основе файлов в
  doc/PROJ.html/download. В этот XML файл добавляется URL до нового
  tarball и его .meta4. Время берётся из mtime-а tarball. Затем perl
  скриптом в Install.html файле добавляется <link rel=alternate> ссылка
  до releases.atom. Для файлов/директорий HTML документации выставляется
  644/755 права
* Запускаю ./update скрипт, делающий rsync HTML документации на VPS и
  мой основной сервер
* На серверах запущен godlighty
  (bc087ee7e3be4177e83bc39ae3b1497e336ec5da), который умеет на лету
  парсить .meta4 лежащие рядом и выставлять соответствующие заголовки
  (контрольные суммы, альтернативные URL-ы для скачивания) при попытке
  скачивания tarball-ов
* git push --tags master develop
* Запускаю подготовленный mutt, копирую в него текст из makedist,
  открываю NEWS файл из tarball-а и вставляю в письмо. Когда-то пробовал
  хранить эту информацию в git tag сообщении, но потом забил, ибо
  геморрой, требует аккуратность, не так сложно копировать небольшие
  кусочки текста при не так уж и частых релизах
* Письма подписываются моим основным OpenPGP ключом, в MIME режиме.
  Поэтому подписью покрывается и .meta4 файл со всеми хэшами

Если проект подразумевает использование и чисто через go get/install
(например в нём нет ни contrib, ни чего-либо такого особо интересного
кроме чисто кода), то отдельно ещё иду в директорию с prepro
(f110400deb3082f8a7c551479153143fb2808e25) и добавляю знание о новом
релизе в скрипты подготовки директории с .zip модулями. Запускаю
./update для rsync-а онных на мои сервера.

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

* Через zeasypki (47b11bfd222d9f3f03664035e4fb4ca32cfa75de) создаю X.509
  сертификат для HTTPS версии сайта
* В zdns (d4eadab571bf5e8ea00d19598f129a708a322d15) завожу новый
  поддомен (при этом, если он увидит сертификат для нового домена, то и
  DANE записи автоматом пропишет)
* В godlighty прописываю знание о новом домене и где брать на файловой
  системе статические файлы для него
* собираю его новую версию, вычисляю bsdiff-ом
  (bf0b7e0357b8bf41a874a85ab55f920a0fba7d59) патч, загружаю на сервер
  (это всё одним скриптом делается), запускаю скрипт применяющий
  бинарный патч
* svc -t /var/service/godlighty*
* Для не совсем маленьких и тривиальных проектов завожу отдельные
  OpenPGP и OpenSSH ключи (это происходит уже и в момент создания
  проекта, ведь PUBKEY надо закоммитить и знания прописать в makedist).
  Возможно что OpenPGP перестану заводить уже. Во всех случаях это
  ed25519 ключ -- быстрый, безопасный, компактный, более чем хорошо
  проанализированный
* Для OpenPGP ключей отдельно делаю gpg --edit-key и удаляю ключ
  шифрования, так как всё равно эти ключи только для подписи Git тэгов и
  файлов. Делаю подпись ключа своим основным
* Так как большая часть проектов расположена на
  cypherpunks.ru/stargrave.org доменах, то и идентификатор ключа это
  goredo@cypherpunks.ru какой-нибудь. Поэтому иду в ~w/cypherpunks-www,
  редактирую keys.do, в котором добавляю строчку вида
    mk-wkd-keys goredo@cypherpunks.ru 3A528DDE952C7E93
  mk-wkd-keys команда создаёт файл с armored версией указанного ключа, с
  именем пригодным для использования в WKD системе
* redo all && ./update для пересборки сайта cypherpunks.ru, добавления
  нового ключа в .well-known/openpgpkey, rsync-а на два сервера.
  Аналогично всё и для stargrave.org домена
* Далее иду в файлы настройки Postfix и добавляю знание о новом домене
  и/или новых почтовых адресах, обновляю файлы баз данных, перезапускаю
  Postfix
* А если нужна почтовая рассылка... то иду конфигурировать public-inbox
  (8c5adc4480ecdd5c1e35da40c5f2fddd896ea892) и mlmmj
  (aac872add6b3defe52aef4d70dbb54a6fcddf973). Это всё по документации
  каждый раз вспоминаю. Далее в ~w/lists, добавляю знание о новой
  рассылке в index.texi, redo all && ./update
* Ах, да, ещё добавляю в список ключей на домашней странице знание о
  новом ключе OpenSSH/OpenPGP: cd ~hp ; vi trustanchor.texi. Поменяв
  список ключей, выделяю визуально текст для подписи и делаю
  :'<,'>!gpg --clear-sign -u myself -u myold -- обновляя и clearsign
  подпись. redo all && ./update
* Подготовка git-репозитория публичного: su -l git ; cd ~/pub ; mkdir
  PROJ.git ; cd PROJ.git ; git init --object-format=sha256 --bare ; echo
  description > description ; touch git-daemon-export-ok ; cd
  ~/cypherpunks.ru ; ln -s ../pub/PROJ.git .
  По факту у меня репозитории касающиеся stargrave.org и cypherpunks.ru
  лежат одной кучей в одной директории и только символическими ссылками
  распределяются по этим двум доменам

Забыл ли я чего? Наверняка!

    [оставить комментарий]