Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 12 из 25 ~20 мин +50 XP

BSR: Buf Schema Registry

buf schema registry — Buf Schema Registry, сокращённо BSR — это npm для протофайлов: публикуете модуль buf push-ом, потребители подключают его зависимостью, версии держат метки, buf.lock фиксирует дайджесты. Разбираем полный путь модуля: имя и публикация, зависимости от googleapis, приватные модули с токенами, готовые SDK для Go и TypeScript и breaking-проверки против опубликованных версий из десятого урока.

Содержание урока · 11 разделов

До сих пор ваши схемы жили в одном репозитории, и всё было просто: 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 с именем модуля

GO
version: 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.1

buf 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 и без плагинов на машине потребителя.

Те же пакеты, другой источник импорта

GO
import (
        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(
        &notesv1.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 и команды публикации

Восстановите конфиг модуля с зависимостью и две команды жизненного цикла.

version: modules: - name: buf.build/acme/notes path: proto deps: - buf.build/googleapis/ # разрешить зависимости и написать buf.lock: $ buf # опубликовать коммит модуля с версией: $ buf --label v1.2.0

Путь модуля: от схемы до клиента

Расставьте события жизненного цикла BSR-модуля по порядку.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: Buf Schema Registry

Термин

Модуль BSR

нажмите, чтобы перевернуть
Определение

Пакет схем под именем buf.build/acme/notes: name в секции modules buf.yaml. Один git-репозиторий может содержать несколько модулей.

1 / 6

Что дальше по курсу#

Конвейер схем собран целиком: 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-пайплайне.

Личные заметкиПусто
Хранятся только в вашем браузере (Connect · BSR: Buf Schema Registry)

Вопросы и ответы по уроку

Обязательно ли публиковать схемы в 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-проверку стоит только осознанно и с уведомлением потребителей — иначе это поломка чужих сборок в тишине.

Термины урока

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

Сравните с альтернативами

Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.

Следующая ступень учебного пути

Fyne25 уроков~13 чпрактика25 уроков уже доступны

Fyne: десктопные и мобильные GUI на Go

Курс Fyne: окна, виджеты, контейнеры, canvas, темы, binding, сборка под мобильные и десктоп. 25 уроков и итоговый менеджер задач.

  • Создавать окна и интерфейсы из виджетов
  • Управлять компоновкой: Box, Grid, Form
  • Рисовать примитивами canvas и строить свои виджеты
  • Связывать данные через binding