Содержание урока · 11 разделов
- Модуль: единица публикации
- buf push: публикация и метки версий
- Зависимости: deps, buf dep update и buf.lock
- Приватные модули: visibility, токены, CI
- SDK из реестра: Go и TypeScript без buf у потребителя
- BSR в релизном контуре: breaking против реестра
- BSR сквозь призму npm-мира
- Что дальше по курсу
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
До сих пор ваши схемы жили в одном репозитории, и всё было просто: buf.yaml указывал каталог proto, buf generate собирал код. Но у контрактов есть свойство расти за границы репозитория: сервис профилей нужен двум командам, клиент вашего API хочет схемы без клона вашего сервера, а мобильная команда просит просто «дай нам типы». Копировать .proto между репозиториями — тот же грех, что копировать код: копии расходятся, и через полгода никто не знает, где канон. Buf Schema Registry решает это так же, как npm решило проблему JS-библиотек: единый реестр, имя пакета, версии и зависимости. Каталог buf.build — тот самый buf schema registry — хранит bsr модули: опубликованные пакеты схем, у которых есть история коммитов, метки версий, страница документации и сгенерированные SDK. Вы уже касались его краем: плагины remote из одиннадцатого урока живут там же, а buf breaking из десятого умеет сравнивать с опубликованной версией. План урока: имя модуля, публикация и метки, зависимости с buf.lock, приватность и токены, готовые SDK для Go и TypeScript, и полный релизный контур.
Модуль: единица публикации#
Модуль — это набор протофайлов, публикуемый под именем вида buf.build/acme/notes: владелец (пользователь или организация) плюс название. Модуль в buf.yaml v2 получает имя прямо в записи секции modules — поле name рядом со знакомым path. Имя — это одновременно и адрес в реестре, и часть пути импорта для потребителей: чужая схема обращается к вашей через import с путём внутри модуля, ровно как ваши eliza и notes живут в одном модуле и видят друг друга. Важно не путать модуль с репозиторием: один git-репозиторий может содержать несколько модулей (в v2 они перечисляются в modules списком), и наоборот — модуль можно публиковать из любого CI. Наш проект из курса — один модуль path: proto из второго урока, и чтобы выйти в реестр, ему нужно только имя. Хорошая новость для существующих проектов: добавление имени — обратимо и не ломает локальную работу, buf lint и buf generate продолжают работать точно так же, просто появляется возможность публикации. Если вы публиковали пакеты в npm или crates.io — механика владельца и имени покажется знакомой до звона.
buf.yaml с именем модуля
GOversion: v2
modules:
- name: buf.build/acme/notes
path: proto
lint:
use: [STANDARD]
breaking:
use: [FILE]Единственная правка против конфига из десятого урока — поле name у модуля: buf.build/acme/notes — владелец acme, модуль notes. Этот buf.yaml всё ещё полностью локален: lint и breaking работают без сети. Но теперь buf push знает, под каким именем публиковать схемы из каталога proto.
buf push: публикация и метки версий#
Публикация — одна команда: buf push отправляет текущее состояние модуля в реестр и создаёт коммит. Коммит BSR — это неизменяемый снимок всех схем модуля с дайджестом; редактировать историю нельзя, только добавлять новые коммиты — git-модель без force push. На коммиты указывают метки (labels): метка main двигается на каждый push и всегда указывает на свежую версию, а метки вида v1.0.0, v1.0.1 вы ставите сами — это ваши semantic-версии. Потребитель пинируется на метку и получает предсказуемую схему, пока вы её не подвинете. Реквизиты доступа buf берёт из окружения: buf registry login записывает токен в конфиг локальной машины, а в CI токен передаётся переменной BUF_TOKEN из секретов — об этом ниже. Первому push-у обычно предшествует автоматическая проверка: buf lint и buf breaking против main — публиковать нелинтованную схему значит раздавать её грязь всем потребителям. В реестре у модуля появляется страница: документация из комментариев протофайлов, дерево пакетов, история коммитов и граф зависимостей — то, что в npm-мире вы видели на странице пакета. Замечу и типовую ошибку новичков: метки — не ветки. Метка указывает на один коммит и сдвигается или нет; ветвистую историю версий в protobuf-мире не строят — для несовместимых изменений заводят новую версию пакета notes.v2, как вы проходили в десятом уроке.
Публикация модуля
GO# Разовая авторизация на машине (токен в локальный конфиг)
$ buf registry login
# Первый push: модуль создаётся, ставим метки
$ buf push --label main --label v1.0.0
# Правка схем -> новый коммит; метка main двигается, v1.0.0 не трогаем
$ buf push --label main
$ buf push --label v1.0.1buf push без аргументов публикует модуль из текущего buf.yaml. Метка main по умолчанию двигается на каждый коммит; семантические метки v1.0.0 и v1.0.1 вы назначаете сами — потребители пинируются на них и не замечают свежих правок. Токен из окружения (BUF_TOKEN) подхватывается автоматически.
Потребитель хочет схему ровно версии v1.0.0, пока вы не скажете обновляться. Что ему пинировать?
Зависимости: deps, buf dep update и buf.lock#
Второй дар реестра — зависимости. Схема перестаёт быть островом: импортируйте типы из чужих модулей, и buf сам подтянет их из реестра при сборке. Классический кейс — buf.build/googleapis/googleapis, курируемый (curated) модуль с аннотациями Google: вы уже слышали о нём в контексте REST-совместимости — опции google.api.http, которыми grpc-gateway описывает REST-маршруты, живут именно там. Подключение двухшаговое: перечислить модуль в секции deps вашего buf.yaml, затем buf dep update — buf разрешит зависимости, скачает их в локальный кеш и запишет buf.lock. Файл buf.lock — это ваш package-lock.json: он фиксирует дайджесты (криптографические отпечатки) конкретных версий каждой зависимости, и его коммитят вместе с buf.yaml — сборка становится воспроизводимой: у всех разработчиков и в CI одни и те же байты чужих схем, даже если владелец зависимости выпустил новый коммит. Обновление — сознательный шаг: buf dep update пересматривает зафиксированные версии (в v2 можно ограничить диапазон, например только патчи), diff buf.lock идёт в ревью как любой настоящий апдейт зависимостей. И обратите внимание: зависимости — это тоже модули со своими метками, поэтому пинирование работает одинаково и для ваших зависимостей, и для ваших потребителей.
buf.yaml и buf.lock: зависимость и её фиксация
GO# buf.yaml — то, что вы редактируете
version: v2
modules:
- name: buf.build/acme/notes
path: proto
deps:
- buf.build/googleapis/googleapis
# buf.lock — то, что пишет buf dep update (не редактируется руками)
version: v2
deps:
- name: buf.build/googleapis/googleapis
digest: b5:1582852416…deps перечисляет имена зависимых модулей; buf dep update разрешает их в конкретные дайджесты и пишет buf.lock. Дайджест b5 — отпечаток содержимого: любая правка схем зависимости меняет дайджест, и рассинхрон виден в diff-е. Оба файла коммитятся: buf.yaml — намерение, buf.lock — факт зафиксированных версий.
Чем buf.lock отличается от секции deps в buf.yaml?
Что произойдёт?
Прочитайте код и выберите, что он выведет:
# proto/notes/v1/notes.proto
import "google/api/annotations.proto";
# buf.yaml: секции deps нет
$ buf generateПриватные модули: visibility, токены, CI#
Не всякую схему хочется светить: контракты коммерческого API, внутренние протоколы, схемы с бизнес-логикой в именах полей. BSR закрывает это полем visibility у модуля: public — модуль виден всем (значение по умолчанию), private — доступ только владельцу и приглашённым участникам организации. Доступ к приватным модулям и публикация вообще — по токенам: buf registry login создаёт и сохраняет токен локально, а для автоматизации токен передаётся переменной BUF_TOKEN: buf читает её из окружения и никогда не требует аргументов с секретом. Отсюда железное правило CI: BUF_TOKEN живёт в секретах пайплайна (GitHub Actions secrets, GitLab CI variables) и подставляется в шаги, где buf ходит в реестр — buf dep update, buf generate с удалёнными входами, buf push. Класть токен в buf.yaml, go.mod или код — нельзя: конфиги едут в репозиторий, а токен — это ключ к вашим схемам. Организационная механика тоже на месте: в BSR можно завести организацию, пригласить команду и раздавать права на модули — тонкая настройка ролей у Buf платная, но базовая приватность публичной учетной записи хватает для учебного и пет-проектного цикла. Токены отзываются и перевыпускаются — ротация секрета в CI делается без деплоя кода.
SDK из реестра: Go и TypeScript без buf у потребителя#
Третий дар реестра — сгенерированный код как сервис. BSR умеет сам прогонять плагины по вашим схемам и публиковать готовые SDK: Go-модули в собственном прокси buf.build/gen/go и npm-пакеты для TypeScript. Потребителю не нужен ни buf, ни плагины — обычный go get по адресу вида buf.build/gen/go/acme/notes/protocolbuffers/go (message-типы) и buf.build/gen/go/acme/notes/bufbuild/connect-go (Connect-слой с клиентами) — в разговорной речи такой набор называют bsr go SDK. Импорты в коде честные Go-пути: notesv1 из protocolbuffers-ветки и notesv1connect из bufbuild/connect-go ветки — те же пакеты, которые вы генерировали локально, только с путём реестра. Для TypeScript — npm-пакет @buf/acme_notes.notes.v1 с теми же модулями, что делал protoc-gen-es из девятого урока. Когда это выбирать: внешним потребителям API, у которых нет buf и не должно быть; мобильным и фронтенд-командам, которые хотят просто npm install. Когда нет: внутри команды, где buf уже стоит, локальная генерация из deps даёт больше контроля — вы управляете версиями плагинов и не ждёте, пока SDK реестра прогонится. Версии SDK следуют коммитам и меткам модуля, так что пинирование потребителя работает так же, как для схем.
Клиент на SDK из реестра
GO$ go get buf.build/gen/go/acme/notes/protocolbuffers/go
$ go get buf.build/gen/go/acme/notes/bufbuild/connect-goДва go get — и зависимости в go.mod. Дальше обычный код: типы notesv1 и пакет notesv1connect импортируются по путям реестра, без buf и без плагинов на машине потребителя.
Те же пакеты, другой источник импорта
GOimport (
notesv1 "buf.build/gen/go/acme/notes/protocolbuffers/go/notes/v1"
notesv1connect "buf.build/gen/go/acme/notes/bufbuild/connect-go/notes/v1"
)
client := notesv1connect.NewNoteServiceClient(
http.DefaultClient,
"https://notes.acme.example",
)
res, err := client.GetNote(ctx, connect.NewRequest(
¬esv1.GetNoteRequest{Id: "1"},
))Те же типы notesv1 и notesv1connect, что вы собирали локально со второго урока, — только импорты ведут в модуль прокси buf.build/gen/go. Внутри пакета — полный Connect-слой: интерфейс NoteServiceHandler, конструктор хендлера и типизированный клиент. Это тот же клиент из пятого урока, слово в слово, с другим источником пакетов.
BSR в релизном контуре: breaking против реестра#
Соберём конвейер, который намечался в десятом уроке. Там вы ставили buf breaking --against '.git#branch=main' — сравнение с веткой собственного репозитория. С реестром появляется более честный эталон: buf breaking --against buf.build/acme/notes — сравнение с последней опубликованной версией модуля. Разница принципиальна: main может уйти вперёд от того, что реально скачали потребители, — коммиты в ветке ещё не релиз. Проверка против реестра отвечает на единственный правильный вопрос: сломает ли следующий push тех, кто уже живёт на v1.0.1? Полный цикл релиза выглядит так: PR проходит lint и breaking против main (дисциплина разработки), перед публикацией — breaking против published-версии (дисциплина релиза), затем buf push с меткой. И несовместимые изменения перестают быть проблемой: вы выпускаете notes.v2 рядом с notes.v1 — оба пакета живут в модуле параллельно, потребители переезжают по своему графику, а buf breaking молчит, потому что каждый пакет эволюционирует внутри своей версии. Так вы получаете ту же дисциплину, что проходили в уроке про версионирование gRPC, но с механикой, которая живёт в CI, а не в головах.
BSR сквозь призму npm-мира#
| Понятие BSR | Что это | Аналог из npm |
|---|---|---|
| Модуль | пакет схем под именем buf.build/acme/notes | пакет в registry |
| Коммит модуля | неизменяемый снимок схем с дайджестом | опубликованная версия пакета |
| Метка (label) | имя, указывающее на коммит: main, v1.0.0 | дист-тег latest / конкретная версия |
| deps + buf.lock | зависимости и фиксация дайджестов | dependencies + package-lock.json |
| SDK | сгенерированный Go/npm-код из модуля | готовая библиотека вместо исходников |
| BUF_TOKEN | токен доступа к приватным модулям | npm-токен в окружении CI |
CI собирает проект с приватным модулем BSR. Какого шага не хватает?
Соберите buf.yaml и команды публикации
Восстановите конфиг модуля с зависимостью и две команды жизненного цикла.
Путь модуля: от схемы до клиента
Расставьте события жизненного цикла BSR-модуля по порядку.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Монорепозиторию с одним продуктом и одной командой реестр не обязателен: локальные модули и buf breaking против main закрывают дисциплину дешевле. BSR проявляет себя, когда потребителей схем больше одного репозитория: вторая команда, фронтенд, внешние клиенты — тогда единый источник правды с версиями и SDK стоит своих пяти минут настройки. Заводите реестр в момент, когда впервые хочется скопировать .proto в соседний репозиторий, — это сигнал.
BUF_TOKEN — только в секретах CI и в локальном конфиге после buf registry login. Не в buf.yaml, не в go.mod, не в коде и не в логах шагов пайплайна: значение переменной печатью маскируется, но любой echo-дебаг обходит маскировку. Если токен всё-таки утёк — отзываете его в настройках BSR и перевыпускаете: деплой кода не нужен, ротация секрета в пайплайне делается в один клик.
Команды buf push и buf dep update требуют аккаунта на buf.build — это уже не локальная файловая работа, и браузерная песочница сайта сетевые команды не исполняет, поэтому исполняемых блоков в курсе нет. Интерактивы работают на странице и дают XP, а публикацию и зависимости проверяйте на своём модуле: регистрация бесплатная, приватный модуль для экспериментов создать можно.
Карточки: Buf Schema Registry
Что дальше по курсу#
Конвейер схем собран целиком: buf lint охраняет стиль, buf breaking — совместимость против main и против опубликованных версий, buf generate — код, а BSR — дистрибуцию модулей, зависимостей и SDK. Схемы теперь ходят между машинами и командами — а значит, встаёт вопрос, кто имеет право их вызывать и по какому проводу. В тринадцатом уроке закрываем безопасность: TLS на обычном net/http, аутентификация через интерсепторы из седьмого урока, авторизация по процедурам и ограничение протоколов. Для повторения смежного: полная форма эволюции схем — в десятом уроке, а пара ключей auth connect приведёт в раздел доков Connect, откуда начинается боевой стек безопасности. Программа всех двадцати пяти уроков — на странице курса.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- BSR — реестр модулей схем: npm для протофайлов, где модуль получает имя вида buf.build/acme/notes в секции modules buf.yaml.
- buf push создаёт неизменяемый коммит; метки (main, v1.0.0) указывают на коммиты, потребители пинируются на метки.
- Зависимости: deps в buf.yaml, buf dep update разрешает их и пишет buf.lock с дайджестами; оба файла коммитятся.
- Классическая зависимость — курируемый buf.build/googleapis/googleapis: аннотации google.api.http для REST-маршрутов.
- Приватность: visibility public/private, токены через buf registry login и BUF_TOKEN в секретах CI.
- SDK из реестра: go get buf.build/gen/go/… (protocolbuffers и bufbuild/connect-go), npm @buf/... — потребителю buf не нужен.
- Релизный контур: buf breaking --against buf.build/acme/notes проверяет совместимость с реально опубликованной версией.
Домашнее задание
Проведите модуль из второго урока полный цикл BSR: добавьте в buf.yaml имя вида buf.build/<ваш-логин>/notes и поле visibility по вкусу, зарегистрируйтесь на buf.build и выполните buf registry login, затем buf lint и buf breaking --against '.git#branch=main' — и только после молчания обеих проверок buf push --label main --label v0.1.0. Найдите модуль на buf.build: проверьте страницу документации (комментарии из схем должны быть там), дерево пакетов и историю коммитов. Вторая часть: заведите в отдельном каталоге проект-потребитель — buf.yaml с deps на ваш модуль, buf dep update, buf generate и клиент, который вызывает GetNote из опубликованных схем; убедитесь, что buf.lock появился и закоммитьте его. Третья часть: измените схему совместимо (новое поле), опубликуйте v0.2.0, обновите потребителя через buf dep update и покажите diff buf.lock. Напоследок попробуйте SDK: go get buf.build/gen/go/<ваш-логин>/notes/protocolbuffers/go и замените импорты в клиенте на реестровые. Запишите в заметки, где в этом цикле встали бы lint, breaking и публикация в CI-пайплайне.
Личные заметкиПусто
Вопросы и ответы по уроку
Обязательно ли публиковать схемы в BSR, чтобы пользоваться buf?
Нет: buf lint, buf breaking против git и buf generate полностью локальны. BSR нужен, когда схемы потребляют несколько репозиториев или внешние команды, — тогда он даёт версии, зависимости и SDK. Плата за публикацию — аккаунт и пара строк в buf.yaml.
Чем метки BSR отличаются от git-тегов?
Метки живут в реестре и указывают на коммиты модуля: их видят потребители, у которых может не быть доступа к git. git-теги остаются в репозитории и удобны самому коду (релизные ветки, changelog). Частая практика — двигать обе стороны одной командой: тег в git и метку v1.2.0 в buf push.
Что делать, если ломающее изменение схемы всё-таки нужно?
Не обходить проверку, а выпускать новую версию пакета: notes.v2 рядом с notes.v1 в том же модуле. Потребители переезжают по своему графику, buf breaking молчит, потому что каждый пакет эволюционирует внутри своей версии. Обходить breaking-проверку стоит только осознанно и с уведомлением потребителей — иначе это поломка чужих сборок в тишине.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Сравните с альтернативами
Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.
Изучайте дальше
Следующая ступень учебного пути
Fyne: десктопные и мобильные GUI на Go
Курс Fyne: окна, виджеты, контейнеры, canvas, темы, binding, сборка под мобильные и десктоп. 25 уроков и итоговый менеджер задач.
- Создавать окна и интерфейсы из виджетов
- Управлять компоновкой: Box, Grid, Form
- Рисовать примитивами canvas и строить свои виджеты
- Связывать данные через binding