同步你的 gRPC 规范

以 Markdown 格式查看

保持你的 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.

.github/workflows/fern.yml
1name: Fern
2
3on:
4 push:
5 branches:
6 - main
7 paths:
8 - 'proto/**/*.proto'
9 - 'fern/**/*.yml'
10 pull_request:
11 branches:
12 - main
13 paths:
14 - 'proto/**/*.proto'
15 - 'fern/**/*.yml'
16
17jobs:
18 fern-check:
19 runs-on: ubuntu-latest
20 steps:
21 - name: Checkout repo
22 uses: actions/checkout@v4
23
24 - name: Check gRPC spec
25 uses: fern-api/action@v0
26 with:
27 command: check
28 env:
29 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
30
31 fern-generate:
32 runs-on: ubuntu-latest
33 if: github.event_name == 'push' && github.ref == 'refs/heads/main'
34 steps:
35 - name: Checkout repo
36 uses: actions/checkout@v4
37
38 # Required if using local-generation: true
39 - name: Setup buf
40 uses: bufbuild/buf-setup-action@v1
41 with:
42 github_token: ${{ secrets.GITHUB_TOKEN }}
43
44 - name: Generate SDKs and docs
45 uses: fern-api/action@v0
46 with:
47 command: generate
48 env:
49 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}

Protocol buffer 验证

在生成 SDK 之前验证、检查并检测 Protocol Buffer 文件中的破坏性变更。

.github/workflows/proto-validation.yml
1name: Protocol Buffer Validation
2
3on:
4 push:
5 paths:
6 - 'proto/**/*.proto'
7 - 'buf.yaml'
8 pull_request:
9 paths:
10 - 'proto/**/*.proto'
11
12jobs:
13 validate-proto:
14 runs-on: ubuntu-latest
15 steps:
16 - name: Checkout repo
17 uses: actions/checkout@v4
18
19 - name: Setup Protocol Buffer Compiler
20 uses: arduino/setup-protoc@v2
21 with:
22 version: '23.4'
23
24 - name: Setup buf
25 uses: bufbuild/buf-setup-action@v1
26 with:
27 github_token: ${{ secrets.GITHUB_TOKEN }}
28
29 - name: Lint Protocol Buffers
30 run: buf lint
31
32 - name: Validate Protocol Buffer files
33 run: |
34 find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
35
36 - name: Check for breaking changes
37 run: buf breaking --against '.git#branch=main'
38
39 - name: Generate and validate with Fern
40 uses: fern-api/action@v0
41 with:
42 command: check
43 env:
44 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}

如果你正在使用 Buf Schema Registry,可以添加一个步骤来发布你的模式:

.github/workflows/buf-sync.yml
1name: Buf Sync
2
3on:
4 push:
5 paths:
6 - 'proto/**/*.proto'
7 - 'buf.yaml'
8
9jobs:
10 buf-sync:
11 runs-on: ubuntu-latest
12 steps:
13 - name: Checkout repo
14 uses: actions/checkout@v4
15
16 - name: Setup buf
17 uses: bufbuild/buf-setup-action@v1
18 with:
19 github_token: ${{ secrets.GITHUB_TOKEN }}
20
21 - name: Lint Protocol Buffers
22 run: buf lint
23
24 - name: Check for breaking changes
25 run: buf breaking --against '.git#branch=main'
26
27 - name: Generate and push to Buf Registry
28 run: |
29 buf generate
30 buf push
31 env:
32 BUF_TOKEN: ${{ secrets.BUF_TOKEN }}
33
34 - name: Generate SDKs with Fern
35 uses: fern-api/action@v0
36 with:
37 command: generate
38 env:
39 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}

可选择创建一个 buf.yaml 文件来自定义 buf 的检查规则、破坏性变更检测和依赖项:

buf.yaml
1version: v1
2deps:
3 - buf.build/googleapis/googleapis
4 - buf.build/envoyproxy/protoc-gen-validate
5lint:
6 use:
7 - DEFAULT
8 except:
9 - UNARY_RPC
10breaking:
11 use:
12 - FILE

从源码自动同步

配置 Fern 自动从各种源拉取 Protocol Buffer 文件:

从 git 仓库

generators.yml
1api:
2 specs:
3 - spec:
4 git:
5 repository: https://github.com/your-org/proto-definitions
6 path: services/user_service.proto
7 branch: main
8 generators:
9 - name: fern-typescript-sdk
10 version: 0.8.8

从本地目录

generators.yml
1api:
2 specs:
3 - spec: proto/user_service.proto
4 auto-sync: true
5 generators:
6 - name: fern-typescript-sdk
7 version: 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.

.circleci/config.yml
1version: 2.1
2
3orbs:
4 fern: fernapi/fern@1.0
5
6workflows:
7 version: 2
8 build-and-generate:
9 jobs:
10 - build
11 - test:
12 requires:
13 - build
14 - validate-proto:
15 requires:
16 - build
17 - fern/generate:
18 requires:
19 - test
20 - validate-proto
21 filters:
22 branches:
23 only: main
24 context:
25 - fern-context
26
27jobs:
28 validate-proto:
29 docker:
30 - image: namely/protoc-all:1.51_1
31 steps:
32 - checkout
33 # Required if using local-generation: true
34 - run:
35 name: Install buf
36 command: |
37 curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/buf
38 chmod +x /usr/local/bin/buf
39 - run:
40 name: Validate Protocol Buffers
41 command: |
42 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.

.gitlab-ci.yml
1stages:
2 - build
3 - test
4 - validate
5 - generate
6
7variables:
8 FERN_TOKEN: $FERN_TOKEN
9
10build:
11 stage: build
12 script:
13 - echo "Building gRPC service..."
14
15validate-proto:
16 stage: validate
17 image: namely/protoc-all:1.51_1
18 script:
19 - find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
20 only:
21 changes:
22 - proto/**/*.proto
23
24generate-sdks:
25 stage: generate
26 image: fernapi/fern:latest
27 # Required if using local-generation: true
28 before_script:
29 - curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/buf
30 - chmod +x /usr/local/bin/buf
31 script:
32 - fern generate
33 only:
34 - main

定期更新

设置定期更新以确保你的 SDK 保持最新:

.github/workflows/scheduled-update.yml
1name: Scheduled gRPC Update
2
3on:
4 schedule:
5 - cron: '0 2 * * 1' # Every Monday at 2 AM UTC
6 workflow_dispatch:
7
8jobs:
9 update-proto:
10 runs-on: ubuntu-latest
11 steps:
12 - name: Checkout repo
13 uses: actions/checkout@v4
14
15 - name: Sync Protocol Buffer files
16 run: |
17 # Sync from upstream proto repository
18 git subtree pull --prefix=proto https://github.com/your-org/proto-definitions main --squash
19
20 - name: Generate with latest spec
21 uses: fern-api/action@v0
22 with:
23 command: generate
24 env:
25 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
26
27 - name: Create PR if changes
28 uses: peter-evans/create-pull-request@v5
29 with:
30 token: ${{ secrets.GITHUB_TOKEN }}
31 commit-message: "chore: update Protocol Buffer definitions"
32 title: "Update Protocol Buffer definitions"
33 body: "Automated update of Protocol Buffer definitions from upstream repository"

从 gRPC 服务器生成代码

对于可以生成自己的 Protocol Buffer 定义的服务器:

.github/workflows/auto-generate.yml
1name: Auto-generate from gRPC server
2
3on:
4 push:
5 paths:
6 - 'src/**/*.py' # Trigger on server code changes
7 - 'src/**/*.go'
8 - 'src/**/*.java'
9
10jobs:
11 generate-proto:
12 runs-on: ubuntu-latest
13 steps:
14 - name: Checkout repo
15 uses: actions/checkout@v4
16
17 - name: Setup environment
18 uses: actions/setup-python@v4
19 with:
20 python-version: '3.9'
21
22 - name: Install dependencies
23 run: |
24 pip install grpcio-tools
25
26 - name: Generate Protocol Buffer files
27 run: |
28 python -m grpc_tools.protoc \
29 --proto_path=src/protos \
30 --python_out=. \
31 --grpc_python_out=. \
32 --descriptor_set_out=proto/service.protoset \
33 src/protos/*.proto
34
35 - name: Convert to Protocol Buffer text format
36 run: |
37 protoc --decode_raw < proto/service.protoset > proto/user_service.proto
38
39 - name: Generate SDKs
40 uses: fern-api/action@v0
41 with:
42 command: generate
43 env:
44 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}

监控变更

跟踪你的 Protocol Buffer 规范的变更:

generators.yml
1api:
2 specs:
3 - spec: proto/user_service.proto
4 change-detection:
5 enabled: true
6 breaking-changes: error
7 notifications:
8 slack: ${{ secrets.SLACK_WEBHOOK }}
9 email: team@yourcompany.com
10 generators:
11 - name: fern-typescript-sdk
12 version: 0.8.8

多服务同步

为不同组件同步不同的 Protocol Buffer 服务:

generators.yml
1environments:
2 user-service:
3 specs:
4 - spec: proto/user_service.proto
5 overlays:
6 - user-service-overlay.yml
7 generators:
8 - name: fern-typescript-sdk
9 version: 0.8.8
10 output:
11 location: npm
12 package-name: "@yourcompany/user-service-sdk"
13 order-service:
14 specs:
15 - spec: proto/order_service.proto
16 overlays:
17 - order-service-overlay.yml
18 generators:
19 - name: fern-typescript-sdk
20 version: 0.8.8
21 output:
22 location: npm
23 package-name: "@yourcompany/order-service-sdk"
24 payment-service:
25 specs:
26 - spec: proto/payment_service.proto
27 generators:
28 - name: fern-typescript-sdk
29 version: 0.8.8
30 output:
31 location: npm
32 package-name: "@yourcompany/payment-service-sdk"

gRPC 反射同步

自动从启用了服务器反射的运行中的 gRPC 服务同步 Protocol Buffer 定义:

scripts/sync_from_reflection.py
1import grpc
2from grpc_reflection.v1alpha import reflection_pb2
3from grpc_reflection.v1alpha import reflection_pb2_grpc
4import subprocess
5
6def sync_from_grpc_reflection(server_address, output_dir):
7 """从 gRPC 反射同步 Protocol Buffer 定义"""
8
9 channel = grpc.insecure_channel(server_address)
10 reflection_stub = reflection_pb2_grpc.ServerReflectionStub(channel)
11
12 # 列出服务
13 request = reflection_pb2.ServerReflectionRequest(
14 list_services=""
15 )
16
17 response = reflection_stub.ServerReflectionInfo(iter([request]))
18
19 for resp in response:
20 if resp.HasField('list_services_response'):
21 for service in resp.list_services_response.service:
22 print(f"Found service: {service.name}")
23
24 # 获取服务的文件描述符
25 file_request = reflection_pb2.ServerReflectionRequest(
26 file_containing_symbol=service.name
27 )
28
29 file_response = reflection_stub.ServerReflectionInfo(iter([file_request]))
30
31 for file_resp in file_response:
32 if file_resp.HasField('file_descriptor_response'):
33 # 保存描述符到文件
34 descriptor_path = f"{output_dir}/{service.name}.protoset"
35 with open(descriptor_path, 'wb') as f:
36 f.write(file_resp.file_descriptor_response.file_descriptor_proto[0])
37
38 # 转换为文本格式
39 proto_path = f"{output_dir}/{service.name}.proto"
40 subprocess.run([
41 'protoc',
42 '--decode_raw',
43 '--proto_path', output_dir,
44 descriptor_path
45 ], stdout=open(proto_path, 'w'))
46
47if __name__ == "__main__":
48 sync_from_grpc_reflection("localhost:50051", "proto/")

这确保了你的 gRPC 服务的任何变更都能自动反映在你的 SDK 和文档中,在整个 API 生态系统中保持一致性。