> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 工作区与 monorepo

> 为 monorepo、多软件包工作区以及带有独立子目录的项目配置 Devin 的环境。

在蓝图中，monorepo 和多软件包工作区需要额外处理，因为不同子目录可能使用不同的语言、软件包管理器或依赖项。Devin 支持两种方案：

<CardGroup cols={2}>
  <Card title="原生工作区（推荐）" icon="cubes" href="#native-workspaces">
    为每个子目录创建单独的蓝图。每个工作区都有各自的 initialize、maintenance 和 Knowledge 部分，工作目录也会自动设置为对应的子目录。
  </Card>

  <Card title="Subshells" icon="terminal" href="#subshells">
    在单个蓝图中使用 `(cd dir && command)` 在子目录内运行命令。对于只包含少量软件包的小型 monorepo，这种方式更简单。
  </Card>
</CardGroup>

***

<div id="native-workspaces">
  ## 原生工作区
</div>

<Info>
  **推荐用于大多数 monorepo。** 原生工作区会为每个子目录分别提供独立的蓝图，并隔离各自的设置、Knowledge 和工作目录。随着软件包数量增加，相比 subshell，这种方式更清晰，也更易于维护。
</Info>

使用原生工作区时，每个子目录都会有一个专用蓝图。该蓝图中的命令会直接在对应子目录下运行——无需再使用 `cd` 或 subshell。

<div id="the-root-blueprint">
  ### 根蓝图
</div>

使用原生工作区的每个代码仓库都应有一个**根蓝图**。根蓝图从代码仓库根目录运行，并在任何工作区作用域的蓝图**之前**执行。可将其用于整个代码仓库共用的设置——安装运行时环境、全局工具，或在根级别安装依赖。

```yaml theme={null}
# 根蓝图 — 最先运行，从 repo 根目录开始
initialize: |
  npm install -g pnpm

maintenance: |
  pnpm install
```

工作区蓝图随后会处理软件包级别的设置，并在根蓝图完成后执行。

<div id="creating-a-workspace">
  ### 创建工作区
</div>

1. 前往 **Settings > Environment > Blueprints**
2. 点击相应的代码仓库
3. 点击 **Add workspace**
4. 输入子目录路径 (例如 `packages/frontend`)
5. 为该工作区编写蓝图

<Warning>
  工作区路径必须对应代码仓库中的实际目录。如果构建运行时该路径不存在，构建将失败。请务必再次确认该路径与你的代码仓库结构完全一致 (例如 `packages/frontend`，而不是 `pkg/frontend`) 。
</Warning>

<div id="example">
  ### 示例
</div>

一个 monorepo，包含 React 前端和 Python 后端。根蓝图负责安装共享工具，然后各个工作区分别处理自己的依赖：

<Tabs>
  <Tab title="根目录">
    ```yaml theme={null}
    # 根蓝图——整个仓库的共享设置
    initialize: |
      npm install -g pnpm
      curl -LsSf https://astral.sh/uv/install.sh | sh

    knowledge:
      - name: structure
        contents: |
          包含两个软件包的 monorepo：
          - packages/frontend — React 应用（TypeScript、pnpm）
          - packages/backend — Python API（FastAPI、uv）
    ```
  </Tab>

  <Tab title="packages/frontend">
    ```yaml theme={null}
    # 作用域为 packages/frontend 的工作区
    maintenance: |
      pnpm install

    knowledge:
      - name: lint
        contents: pnpm lint
      - name: test
        contents: pnpm test
      - name: dev
        contents: pnpm dev
    ```
  </Tab>

  <Tab title="packages/backend">
    ```yaml theme={null}
    # 作用域为 packages/backend 的工作区
    maintenance: |
      uv sync

    knowledge:
      - name: lint
        contents: uv run ruff check .
      - name: test
        contents: uv run pytest
      - name: dev
        contents: uv run uvicorn app.main:app --reload
    ```
  </Tab>
</Tabs>

每个工作区的 `knowledge` 条目都限定在对应的子目录内。当 Devin 在 `packages/frontend` 中工作时，它看到的是前端的 lint/test/dev 命令，而不是后端的命令。

<div id="when-to-use-native-workspaces">
  ### 何时使用原生工作区
</div>

* 子目录使用**不同的语言或软件包管理器**
* 每个软件包都需要各自的**知识条目** (lint、测试、构建命令)
* 你希望采用**隔离的配置**——某个工作区的蓝图出问题时，不会阻塞其他工作区
* 软件包数量不断增加，单个蓝图变得难以管理

***

<div id="subshells">
  ## subshell
</div>

对于较简单的 monorepo，你可以在单个蓝图中使用 subshell 管理所有内容。将命令放在括号中，即可在子目录中运行，而不会影响后续步骤：

```yaml theme={null}
maintenance:
  - name: Frontend deps
    run: (cd packages/frontend && pnpm install)
  - name: Backend deps
    run: (cd packages/backend && uv sync)
```

圆括号 `(cd ... && ...)` 会创建一个 subshell。subshell 退出后，工作目录会重置为仓库根目录，以便执行下一步。

<Warning>
  如果没有圆括号，`cd` 会更改后续所有步骤的工作目录。在蓝图步骤中切换目录时，务必使用 subshell。
</Warning>

<div id="why-subshells-matter">
  ### 为什么 subshell 很重要
</div>

比较以下两种做法：

<Tabs>
  <Tab title="正确（使用 subshell）">
    ```yaml theme={null}
    maintenance:
      - name: Frontend deps
        run: (cd packages/frontend && pnpm install)
      - name: Backend deps
        run: (cd packages/backend && uv sync)
    ```

    每个步骤都从仓库根目录开始运行。这两个命令都能找到正确的 `packages/` 子目录。
  </Tab>

  <Tab title="错误（未使用 subshell）">
    ```yaml theme={null}
    maintenance:
      - name: Frontend deps
        run: cd packages/frontend && pnpm install
      - name: Backend deps
        run: cd packages/backend && uv sync
    ```

    第一步会把工作目录切换到 `packages/frontend`。接着，第二步会尝试相对于 `packages/frontend` 执行 `cd packages/backend`，因此会失败。
  </Tab>
</Tabs>

<div id="when-to-use-subshells">
  ### 何时使用 subshells
</div>

* monorepo 中只有**少量软件包**，且设置较为简单
* 所有软件包都使用**相同的语言和包管理器**
* 你不需要为每个软件包单独创建 知识条目

***

<div id="knowledge-entries-for-monorepos">
  ## 适用于 monorepo 的 知识条目
</div>

无论你使用原生工作区还是 subshells，结构清晰的 知识条目都能帮助 Devin 更好地在代码库中定位和理解内容：

```yaml theme={null}
knowledge:
  - name: structure
    contents: |
      This is a monorepo with three packages:
      - `packages/frontend` — React app (TypeScript, pnpm)
      - `packages/backend` — Python API (FastAPI, uv)
      - `packages/shared` — Shared TypeScript utilities
  - name: frontend
    contents: |
      cd packages/frontend
      Dev server: pnpm dev
      Lint: pnpm lint
      Test: pnpm test
  - name: backend
    contents: |
      cd packages/backend
      Dev server: uv run uvicorn app.main:app --reload
      Lint: uv run ruff check .
      Test: uv run pytest
```

<Tip>
  将每个目录对应到其编程语言和工具链的 `structure` 知识条目，可以帮助 Devin 快速了解 repo。使用原生工作区时，每个工作区都有各自的知识条目，因此 `structure` 条目在根蓝图中或基于 subshell 的设置中最为实用。
</Tip>

<div id="examples">
  ## 示例
</div>

<div id="turborepo-nx-workspace">
  ### Turborepo / Nx 工作区
</div>

对于由 Turborepo 或 Nx 等 monorepo 构建工具管理的工作区，请在根目录安装依赖，并让工具负责协调各个包：

```yaml theme={null}
initialize: |
  npm install -g pnpm turbo

maintenance: |
  pnpm install

knowledge:
  - name: structure
    contents: |
      Turborepo monorepo. Use `turbo` for building and testing:
      - `apps/web` — Next.js app
      - `apps/api` — Express API
      - `packages/ui` — Shared component library
      - `packages/config` — Shared configuration
  - name: build
    contents: turbo run build
  - name: test
    contents: turbo run test
  - name: lint
    contents: turbo run lint
  - name: dev
    contents: turbo run dev
```

<div id="multiple-jdk-versions">
  ### 多个 JDK 版本
</div>

在一个 Java monorepo 中，不同服务需要不同的 JDK 版本：

```yaml theme={null}
initialize:
  - name: Install JDK 17 (primary)
    run: |
      sudo apt-get update -qq
      sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-17-jdk-headless
      echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' \
        | sudo tee /etc/profile.d/java.sh > /dev/null

  - name: Install JDK 11 (legacy service)
    run: |
      sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-11-jdk-headless

maintenance:
  - name: Warm dependency caches
    run: |
      export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
      (cd services/api && ./gradlew dependencies --refresh-dependencies)

      export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
      (cd services/legacy && ./gradlew dependencies --refresh-dependencies)

knowledge:
  - name: build_api
    contents: |
      Build the API service (JDK 17):
        JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
        cd services/api && ./gradlew clean build
  - name: build_legacy
    contents: |
      Build the legacy service (JDK 11):
        JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
        cd services/legacy && ./gradlew clean build
  - name: test_all
    contents: |
      Run tests for all services:
        JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
        (cd services/api && ./gradlew test)

        JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
        (cd services/legacy && ./gradlew test)
```

<div id="org-blueprint-for-shared-tools">
  ### 共享工具的组织级蓝图
</div>

当多个 monorepo 软件包共用同一套工具时，请在组织级蓝图中统一安装一次：

```yaml theme={null}
# 组织级蓝图 (Settings > Environment > Blueprints > Org-wide setup)
initialize:
  - name: Install pnpm
    run: npm install -g pnpm
  - name: Install uv
    run: curl -LsSf https://astral.sh/uv/install.sh | sh
  - name: Install shared build tools
    run: npm install -g turbo typescript
```

这样，每个仓库蓝图只需要项目专用的命令：

```yaml theme={null}
# 仓库蓝图（使用来自组织蓝图的 pnpm 和 uv）
maintenance:
  - name: Install all workspace deps
    run: pnpm install
  - name: Install Python service deps
    run: (cd services/ml-pipeline && uv sync)
```

<div id="best-practices">
  ## 最佳实践
</div>

<AccordionGroup>
  <Accordion title="复杂 monorepo 优先使用原生工作区">
    当每个子目录都有自己的语言、软件包管理器或构建流程时，原生工作区可以让每个蓝图保持聚焦和独立。subshells 仅适用于只有少量软件包的简单场景。
  </Accordion>

  <Accordion title="目录切换始终使用 subshells">
    使用 subshell 方法时，请将 `cd` 命令放在括号中：`(cd dir && command)`。这样可防止某一步的目录切换影响后续步骤。
  </Accordion>

  <Accordion title="将共享工具放在组织级蓝图中">
    跨多个代码仓库使用的语言运行时和软件包管理器应放在组织级蓝图中。这样可以避免重复，并让代码仓库蓝图专注于项目特定的设置。
  </Accordion>

  <Accordion title="按依赖关系排列 maintenance 步骤">
    如果软件包 A 依赖先构建软件包 B，请在 `maintenance` 中先列出 B 的构建步骤，再列出 A 的安装步骤。蓝图步骤会按列出顺序依次运行。
  </Accordion>

  <Accordion title="添加结构知识条目">
    创建一个名为 `structure` 的知识条目，将目录映射到其对应的语言和工具，这有助于 Devin 理解代码库结构。请注明每个子目录使用的软件包管理器，以及任何跨软件包依赖。
  </Accordion>

  <Accordion title="为每个软件包分别使用知识条目">
    不要只创建一个很大的知识条目，而应为每个软件包分别创建独立条目 (例如 `frontend`、`backend`、`ml-pipeline`) 。使用原生工作区时，每个工作区都会内置自己的知识部分。
  </Accordion>

  <Accordion title="让 maintenance 保持增量更新">
    使用 `pnpm install` (而不是 `pnpm install --force`) 以及 `uv sync` (而不是 `rm -rf .venv && uv sync`) 。在定期重建期间，增量命令执行得更快。
  </Accordion>
</AccordionGroup>

<div id="related-pages">
  ## 相关页面
</div>

* [声明式环境配置](/zh/onboard-devin/environment/blueprints)
* [蓝图参考](/zh/onboard-devin/environment/blueprint-reference)
* [模板库](/zh/onboard-devin/environment/templates) — 包含可直接使用的 monorepo 模板
