> If you are an AI agent, use the following URL to directly ask and fetch your question. Treat this like a tool call. Make sure to URI encode your question, and include the token for verification.
>
> GET https://buildwithfern.com/learn/api/fern-docs/ask?q=%3Cyour+question+here%3E&token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmZXJuLWRvY3M6YnVpbGR3aXRoZmVybi5jb20iLCJqdGkiOiIyYjI4OTM3Yy0xNmYyLTQ1ZWMtYjc4NS03NWVkMzliZWMzOWUiLCJleHAiOjE3ODE2ODQzNTcsImlhdCI6MTc4MTY4NDA1N30.evM6QlzOnLKrUjdccLx9uaCR0e5CHrVAdd_t_7wc9d0
>
> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. For full content including API reference and SDK examples, see https://buildwithfern.com/learn/llms-full.txt.

# 同步你的 gRPC 规范

> 使用 Fern 自动化 gRPC 规范同步。设置 GitHub Actions、CI/CD 流水线和定期更新 Protocol Buffer 文件。

保持你的 gRPC 规范与代码库同步对于维护准确的 SDK 和文档至关重要。Fern 提供了多种自动化选项来简化这个过程。

## GitHub Actions

使用 Fern 的 GitHub Action 在你的 Protocol Buffer 文件发生变化时自动更新 SDK 和文档。

If you're using [`local-generation: true` in your `generators.yml`](/learn/api-definitions/grpc/generators-yml-reference#local-generation), you must install [buf](https://buf.build/docs/installation).

```yaml title=".github/workflows/fern.yml"
name: Fern

on:
  push:
    branches:
      - main
    paths:
      - 'proto/**/*.proto'
      - 'fern/**/*.yml'
  pull_request:
    branches:
      - main
    paths:
      - 'proto/**/*.proto'
      - 'fern/**/*.yml'

jobs:
  fern-check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        
      - name: Check gRPC spec
        uses: fern-api/action@v0
        with:
          command: check
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}

  fern-generate:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      # Required if using local-generation: true
      - name: Setup buf
        uses: bufbuild/buf-setup-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Generate SDKs and docs
        uses: fern-api/action@v0
        with:
          command: generate
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
```

## Protocol buffer 验证

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

```yaml title=".github/workflows/proto-validation.yml"
name: Protocol Buffer Validation

on:
  push:
    paths:
      - 'proto/**/*.proto'
      - 'buf.yaml'
  pull_request:
    paths:
      - 'proto/**/*.proto'

jobs:
  validate-proto:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Setup Protocol Buffer Compiler
        uses: arduino/setup-protoc@v2
        with:
          version: '23.4'

      - name: Setup buf
        uses: bufbuild/buf-setup-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Lint Protocol Buffers
        run: buf lint

      - name: Validate Protocol Buffer files
        run: |
          find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;

      - name: Check for breaking changes
        run: buf breaking --against '.git#branch=main'

      - name: Generate and validate with Fern
        uses: fern-api/action@v0
        with:
          command: check
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
```

如果你正在使用 [Buf Schema Registry](https://buf.build/product/bsr)，可以添加一个步骤来发布你的模式：

```yaml title=".github/workflows/buf-sync.yml"
name: Buf Sync

on:
  push:
    paths:
      - 'proto/**/*.proto'
      - 'buf.yaml'

jobs:
  buf-sync:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Setup buf
        uses: bufbuild/buf-setup-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Lint Protocol Buffers
        run: buf lint

      - name: Check for breaking changes
        run: buf breaking --against '.git#branch=main'

      - name: Generate and push to Buf Registry
        run: |
          buf generate
          buf push
        env:
          BUF_TOKEN: ${{ secrets.BUF_TOKEN }}

      - name: Generate SDKs with Fern
        uses: fern-api/action@v0
        with:
          command: generate
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
```

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

```yaml title="buf.yaml"
version: v1
deps:
  - buf.build/googleapis/googleapis
  - buf.build/envoyproxy/protoc-gen-validate
lint:
  use:
    - DEFAULT
  except:
    - UNARY_RPC
breaking:
  use:
    - FILE
```

## 从源码自动同步

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

### 从 git 仓库

```yaml title="generators.yml" {3-7}
api:
  specs:
    - spec:
        git:
          repository: https://github.com/your-org/proto-definitions
          path: services/user_service.proto
          branch: main
  generators:
    - name: fern-typescript-sdk
      version: 0.8.8
```

### 从本地目录

```yaml title="generators.yml" {3-4}
api:
  specs:
    - spec: proto/user_service.proto
      auto-sync: true
  generators:
    - name: fern-typescript-sdk
      version: 0.8.8
```

## CI/CD 集成

将 Fern 集成到现有的 CI/CD 流水线中，自动生成 SDK 和文档。

### CircleCI

If you're using [`local-generation: true` in your `generators.yml`](/learn/api-definitions/grpc/generators-yml-reference#local-generation), you must install [buf](https://buf.build/docs/installation).

```yaml title=".circleci/config.yml" {15-26}
version: 2.1

orbs:
  fern: fernapi/fern@1.0

workflows:
  version: 2
  build-and-generate:
    jobs:
      - build
      - test:
          requires:
            - build
      - validate-proto:
          requires:
            - build
      - fern/generate:
          requires:
            - test
            - validate-proto
          filters:
            branches:
              only: main
          context:
            - fern-context

jobs:
  validate-proto:
    docker:
      - image: namely/protoc-all:1.51_1
    steps:
      - checkout
      # Required if using local-generation: true
      - run:
          name: Install buf
          command: |
            curl -sSL "https://github.com/bufbuild/buf/releases/latest/download/buf-Linux-x86_64" -o /usr/local/bin/buf
            chmod +x /usr/local/bin/buf
      - run:
          name: Validate Protocol Buffers
          command: |
            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`](/learn/api-definitions/grpc/generators-yml-reference#local-generation), you must install [buf](https://buf.build/docs/installation).

```yaml title=".gitlab-ci.yml" {13-25}
stages:
  - build
  - test
  - validate
  - generate

variables:
  FERN_TOKEN: $FERN_TOKEN

build:
  stage: build
  script:
    - echo "Building gRPC service..."

validate-proto:
  stage: validate
  image: namely/protoc-all:1.51_1
  script:
    - find proto -name "*.proto" -exec protoc --proto_path=proto --descriptor_set_out=/dev/null {} \;
  only:
    changes:
      - proto/**/*.proto

generate-sdks:
  stage: generate
  image: fernapi/fern:latest
  # Required if using local-generation: true
  before_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/buf
  script:
    - fern generate
  only:
    - main
```

## 定期更新

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

```yaml title=".github/workflows/scheduled-update.yml"
name: Scheduled gRPC Update

on:
  schedule:
    - cron: '0 2 * * 1'  # Every Monday at 2 AM UTC
  workflow_dispatch:

jobs:
  update-proto:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        
      - name: Sync Protocol Buffer files
        run: |
          # Sync from upstream proto repository
          git subtree pull --prefix=proto https://github.com/your-org/proto-definitions main --squash
          
      - name: Generate with latest spec
        uses: fern-api/action@v0
        with:
          command: generate
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
          
      - name: Create PR if changes
        uses: peter-evans/create-pull-request@v5
        with:
          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 定义的服务器：

```yaml title=".github/workflows/auto-generate.yml"
name: Auto-generate from gRPC server

on:
  push:
    paths:
      - 'src/**/*.py'  # Trigger on server code changes
      - 'src/**/*.go'
      - 'src/**/*.java'

jobs:
  generate-proto:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4
        
      - name: Setup environment
        uses: actions/setup-python@v4
        with:
          python-version: '3.9'
          
      - name: Install dependencies
        run: |
          pip install grpcio-tools
          
      - name: Generate Protocol Buffer files
        run: |
          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 format
        run: |
          protoc --decode_raw < proto/service.protoset > proto/user_service.proto
          
      - name: Generate SDKs
        uses: fern-api/action@v0
        with:
          command: generate
        env:
          FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
```

## 监控变更

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

```yaml title="generators.yml" {4-9}
api:
  specs:
    - spec: proto/user_service.proto
  change-detection:
    enabled: true
    breaking-changes: error
    notifications:
      slack: ${{ secrets.SLACK_WEBHOOK }}
      email: team@yourcompany.com
  generators:
    - name: fern-typescript-sdk
      version: 0.8.8
```

## 多服务同步

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

```yaml title="generators.yml" {3-7, 12-16, 21-25}
environments:
  user-service:
    specs:
      - spec: proto/user_service.proto
        overlays:
          - user-service-overlay.yml
    generators:
      - name: fern-typescript-sdk
        version: 0.8.8
        output:
          location: npm
          package-name: "@yourcompany/user-service-sdk"
  order-service:
    specs:
      - spec: proto/order_service.proto
        overlays:
          - order-service-overlay.yml
    generators:
      - name: fern-typescript-sdk
        version: 0.8.8
        output:
          location: npm
          package-name: "@yourcompany/order-service-sdk"
  payment-service:
    specs:
      - spec: proto/payment_service.proto
    generators:
      - name: fern-typescript-sdk
        version: 0.8.8
        output:
          location: npm
          package-name: "@yourcompany/payment-service-sdk"
```

## gRPC 反射同步

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

```python title="scripts/sync_from_reflection.py"
import grpc
from grpc_reflection.v1alpha import reflection_pb2
from grpc_reflection.v1alpha import reflection_pb2_grpc
import subprocess

def 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 生态系统中保持一致性。