同步你的 gRPC 规范
保持你的 gRPC 规范与代码库同步对于维护准确的 SDK 和文档至关重要。Fern 提供了多种自动化选项来简化这个过程。
GitHub Actions
使用 Fern 的 GitHub Action 在你的 Protocol Buffer 文件发生变化时自动更新 SDK 和文档。
If you’re using local-generation: true in your generators.yml, you must install buf.
name: Fernon:push:branches:- mainpaths:- 'proto/**/*.proto'- 'fern/**/*.yml'pull_request:branches:- mainpaths:- 'proto/**/*.proto'- 'fern/**/*.yml'jobs:fern-check:runs-on: ubuntu-lateststeps:- name: Checkout repouses: actions/checkout@v4- name: Check gRPC specuses: fern-api/action@v0with:command: checkenv:FERN_TOKEN: ${{ secrets.FERN_TOKEN }}fern-generate:runs-on: ubuntu-latestif: github.event_name == 'push' && github.ref == 'refs/heads/main'steps:- name: Checkout repouses: actions/checkout@v4# Required if using local-generation: true- name: Setup bufuses: bufbuild/buf-setup-action@v1with:github_token: ${{ secrets.GITHUB_TOKEN }}- name: Generate SDKs and docsuses: fern-api/action@v0with:command: generateenv:FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
Protocol buffer 验证
在生成 SDK 之前验证、检查并检测 Protocol Buffer 文件中的破坏性变更。
name: Protocol Buffer Validationon:push:paths:- 'proto/**/*.proto'- 'buf.yaml'pull_request:paths:- 'proto/**/*.proto'jobs:validate-proto:runs-on: ubuntu-lateststeps:- name: Checkout repouses: actions/checkout@v4- name: Setup Protocol Buffer Compileruses: arduino/setup-protoc@v2with:version: '23.4'- name: Setup bufuses: bufbuild/buf-setup-action@v1with:github_token: ${{ secrets.GITHUB_TOKEN }}- name: Lint Protocol Buffersrun: buf lint- name: Validate Protocol Buffer filesrun: |find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;- name: Check for breaking changesrun: buf breaking --against '.git#branch=main'- name: Generate and validate with Fernuses: fern-api/action@v0with:command: checkenv:FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
可选:推送到 Buf Schema Registry
如果你正在使用 Buf Schema Registry,可以添加一个步骤来发布你的模式:
name: Buf Syncon:push:paths:- 'proto/**/*.proto'- 'buf.yaml'jobs:buf-sync:runs-on: ubuntu-lateststeps:- name: Checkout repouses: actions/checkout@v4- name: Setup bufuses: bufbuild/buf-setup-action@v1with:github_token: ${{ secrets.GITHUB_TOKEN }}- name: Lint Protocol Buffersrun: buf lint- name: Check for breaking changesrun: buf breaking --against '.git#branch=main'- name: Generate and push to Buf Registryrun: |buf generatebuf pushenv:BUF_TOKEN: ${{ secrets.BUF_TOKEN }}- name: Generate SDKs with Fernuses: fern-api/action@v0with:command: generateenv:FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
可选:自定义 buf 配置
可选择创建一个 buf.yaml 文件来自定义 buf 的检查规则、破坏性变更检测和依赖项:
version: v1deps:- buf.build/googleapis/googleapis- buf.build/envoyproxy/protoc-gen-validatelint:use:- DEFAULTexcept:- UNARY_RPCbreaking:use:- FILE
从源码自动同步
配置 Fern 自动从各种源拉取 Protocol Buffer 文件:
从 git 仓库
api:specs:- spec:git:repository: https://github.com/your-org/proto-definitionspath: services/user_service.protobranch: maingenerators:- name: fern-typescript-sdkversion: 0.8.8
从本地目录
api:specs:- spec: proto/user_service.protoauto-sync: truegenerators:- name: fern-typescript-sdkversion: 0.8.8
CI/CD 集成
将 Fern 集成到现有的 CI/CD 流水线中,自动生成 SDK 和文档。
CircleCI
If you’re using local-generation: true in your generators.yml, you must install buf.
version: 2.1orbs:fern: fernapi/fern@1.0workflows:version: 2build-and-generate:jobs:- build- test:requires:- build- validate-proto:requires:- build- fern/generate:requires:- test- validate-protofilters:branches:only: maincontext:- fern-contextjobs:validate-proto:docker:- image: namely/protoc-all:1.51_1steps:- checkout# Required if using local-generation: true- run:name: Install bufcommand: |curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/bufchmod +x /usr/local/bin/buf- run:name: Validate Protocol Bufferscommand: |find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
GitLab CI
If you’re using local-generation: true in your generators.yml, you must install buf.
stages:- build- test- validate- generatevariables:FERN_TOKEN: $FERN_TOKENbuild:stage: buildscript:- echo "Building gRPC service..."validate-proto:stage: validateimage: namely/protoc-all:1.51_1script:- find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;only:changes:- proto/**/*.protogenerate-sdks:stage: generateimage: fernapi/fern:latest# Required if using local-generation: truebefore_script:- curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/buf- chmod +x /usr/local/bin/bufscript:- fern generateonly:- main
定期更新
设置定期更新以确保你的 SDK 保持最新:
name: Scheduled gRPC Updateon:schedule:- cron: '0 2 * * 1' # Every Monday at 2 AM UTCworkflow_dispatch:jobs:update-proto:runs-on: ubuntu-lateststeps:- name: Checkout repouses: actions/checkout@v4- name: Sync Protocol Buffer filesrun: |# Sync from upstream proto repositorygit subtree pull --prefix=proto https://github.com/your-org/proto-definitions main --squash- name: Generate with latest specuses: fern-api/action@v0with:command: generateenv:FERN_TOKEN: ${{ secrets.FERN_TOKEN }}- name: Create PR if changesuses: peter-evans/create-pull-request@v5with:token: ${{ secrets.GITHUB_TOKEN }}commit-message: "chore: update Protocol Buffer definitions"title: "Update Protocol Buffer definitions"body: "Automated update of Protocol Buffer definitions from upstream repository"
从 gRPC 服务器生成代码
对于可以生成自己的 Protocol Buffer 定义的服务器:
name: Auto-generate from gRPC serveron:push:paths:- 'src/**/*.py' # Trigger on server code changes- 'src/**/*.go'- 'src/**/*.java'jobs:generate-proto:runs-on: ubuntu-lateststeps:- name: Checkout repouses: actions/checkout@v4- name: Setup environmentuses: actions/setup-python@v4with:python-version: '3.9'- name: Install dependenciesrun: |pip install grpcio-tools- name: Generate Protocol Buffer filesrun: |python -m grpc_tools.protoc \--proto_path=src/protos \--python_out=. \--grpc_python_out=. \--descriptor_set_out=proto/service.protoset \src/protos/*.proto- name: Convert to Protocol Buffer text formatrun: |protoc --decode_raw < proto/service.protoset > proto/user_service.proto- name: Generate SDKsuses: fern-api/action@v0with:command: generateenv:FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
监控变更
跟踪你的 Protocol Buffer 规范的变更:
api:specs:- spec: proto/user_service.protochange-detection:enabled: truebreaking-changes: errornotifications:slack: ${{ secrets.SLACK_WEBHOOK }}email: team@yourcompany.comgenerators:- name: fern-typescript-sdkversion: 0.8.8
多服务同步
为不同组件同步不同的 Protocol Buffer 服务:
environments:user-service:specs:- spec: proto/user_service.protooverlays:- user-service-overlay.ymlgenerators:- name: fern-typescript-sdkversion: 0.8.8output:location: npmpackage-name: "@yourcompany/user-service-sdk"order-service:specs:- spec: proto/order_service.protooverlays:- order-service-overlay.ymlgenerators:- name: fern-typescript-sdkversion: 0.8.8output:location: npmpackage-name: "@yourcompany/order-service-sdk"payment-service:specs:- spec: proto/payment_service.protogenerators:- name: fern-typescript-sdkversion: 0.8.8output:location: npmpackage-name: "@yourcompany/payment-service-sdk"
gRPC 反射同步
自动从启用了服务器反射的运行中的 gRPC 服务同步 Protocol Buffer 定义:
import grpcfrom grpc_reflection.v1alpha import reflection_pb2from grpc_reflection.v1alpha import reflection_pb2_grpcimport subprocessdef sync_from_grpc_reflection(server_address, output_dir):"""从 gRPC 反射同步 Protocol Buffer 定义"""channel = grpc.insecure_channel(server_address)reflection_stub = reflection_pb2_grpc.ServerReflectionStub(channel)# 列出服务request = reflection_pb2.ServerReflectionRequest(list_services="")response = reflection_stub.ServerReflectionInfo(iter([request]))for resp in response:if resp.HasField('list_services_response'):for service in resp.list_services_response.service:print(f"Found service: {service.name}")# 获取服务的文件描述符file_request = reflection_pb2.ServerReflectionRequest(file_containing_symbol=service.name)file_response = reflection_stub.ServerReflectionInfo(iter([file_request]))for file_resp in file_response:if file_resp.HasField('file_descriptor_response'):# 保存描述符到文件descriptor_path = f"{output_dir}/{service.name}.protoset"with open(descriptor_path, 'wb') as f:f.write(file_resp.file_descriptor_response.file_descriptor_proto[0])# 转换为文本格式proto_path = f"{output_dir}/{service.name}.proto"subprocess.run(['protoc','--decode_raw','--proto_path', output_dir,descriptor_path], stdout=open(proto_path, 'w'))if __name__ == "__main__":sync_from_grpc_reflection("localhost:50051", "proto/")
这确保了你的 gRPC 服务的任何变更都能自动反映在你的 SDK 和文档中,在整个 API 生态系统中保持一致性。