> ## 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.

# 模板库

> 适用于常见语言、私有仓库和企业基础架构的可直接复制粘贴蓝图。

适用于常见语言和使用场景的可直接复制粘贴蓝图。每个模板都可独立使用。组合使用即可构建完整配置。

如需查看每个字段的完整说明，请参阅 [蓝图参考](/zh/onboard-devin/environment/blueprint-reference)。

<Info>
  **Secrets：** 模板使用 `$SECRET_NAME` 引用 secrets。使用模板前，请先在每个蓝图编辑器的 **Secrets** 选项卡中配置好这些 secrets。切勿在蓝图中硬编码凭据。
</Info>

***

<div id="quick-start">
  ## 快速开始
</div>

适用于最常见配置的精简蓝图。复制其中一个，粘贴到蓝图编辑器中即可。

<AccordionGroup>
  <Accordion title="Node.js 项目">
    ```yaml theme={null}
    initialize: |
      npm install -g pnpm

    maintenance: |
      pnpm install

    knowledge:
      - name: lint
        contents: |
          运行 `pnpm lint` 检查错误。
      - name: test
        contents: |
          运行 `pnpm test` 执行完整测试套件。
    ```
  </Accordion>

  <Accordion title="Python 项目">
    ```yaml theme={null}
    initialize: |
      curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance: |
      uv sync

    knowledge:
      - name: lint
        contents: |
          运行 `uv run ruff check .` 进行 lint 检查。
      - name: test
        contents: |
          运行 `uv run pytest` 执行完整测试套件。
    ```
  </Accordion>

  <Accordion title="全栈（Node + Python）">
    ```yaml theme={null}
    initialize:
      - name: Install pnpm
        run: npm install -g pnpm
      - name: Install uv
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance:
      - name: 前端依赖
        run: (cd frontend && pnpm install)
      - name: 后端依赖
        run: (cd backend && uv sync)

    knowledge:
      - name: structure
        contents: |
          - `frontend/` — React 应用（pnpm）
          - `backend/` — Python API（uv）
      - name: test
        contents: |
          前端：cd frontend && pnpm test
          后端：cd backend && uv run pytest
    ```
  </Accordion>
</AccordionGroup>

***

<div id="repository-blueprints">
  ## 仓库蓝图
</div>

按仓库设置的构建步骤、依赖管理和 Knowledge 条目。请在 **Settings > Environment > Blueprints > \[你的仓库]** 中进行设置。

<div id="python">
  ### Python
</div>

<Tabs>
  <Tab title="uv（推荐）">
    适用于使用 [uv](https://docs.astral.sh/uv/) 进行依赖管理的 Python 项目的推荐设置。

    ```yaml theme={null}
    initialize: |
      curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance: |
      uv sync

    knowledge:
      - name: lint
        contents: |
          uv run ruff check .

          Auto-fix:
          uv run ruff check --fix .

      - name: test
        contents: |
          uv run pytest

      - name: build
        contents: |
          uv run python -m build
    ```
  </Tab>

  <Tab title="pip + venv">
    使用 pip 和 venv 的传统 Python 设置。项目依赖 `requirements.txt` 时请使用此配置。

    ```yaml theme={null}
    initialize: |
      python3 -m venv .venv

    maintenance: |
      source .venv/bin/activate
      pip install -r requirements.txt

    knowledge:
      - name: lint
        contents: |
          source .venv/bin/activate
          flake8 .

      - name: test
        contents: |
          source .venv/bin/activate
          pytest

      - name: build
        contents: |
          source .venv/bin/activate
          python -m build
    ```
  </Tab>
</Tabs>

<div id="nodejs">
  ### Node.js
</div>

<Tabs>
  <Tab title="npm">
    使用 npm 的标准 Node.js 配置。

    ```yaml theme={null}
    initialize: |
      nvm install 20
      nvm use 20

    maintenance: |
      npm install

    knowledge:
      - name: lint
        contents: |
          npx eslint .

      - name: test
        contents: |
          npm test

      - name: build
        contents: |
          npm run build
    ```

    <Info>
      在 `maintenance` 中使用 `npm install` (不要用 `npm ci`) 。前者会执行增量更新，而 `npm ci` 会在每个会话中删除 `node_modules`，并从头重新安装。
    </Info>
  </Tab>

  <Tab title="pnpm">
    适用于使用 pnpm 的项目。

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

    maintenance: |
      pnpm install --frozen-lockfile

    knowledge:
      - name: lint
        contents: |
          pnpm lint

      - name: test
        contents: |
          pnpm test

      - name: build
        contents: |
          pnpm build
    ```
  </Tab>
</Tabs>

<div id="go">
  ### Go
</div>

标准 Go 模块配置。

```yaml theme={null}
initialize: |
  GO_VERSION=1.23.5
  ARCH=$(dpkg --print-architecture)
  curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz" \
    | sudo tar -xz -C /usr/local
  echo 'export PATH="/usr/local/go/bin:$HOME/go/bin:$PATH"' \
    | sudo tee /etc/profile.d/golang.sh > /dev/null

  go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

maintenance: |
  go mod download

knowledge:
  - name: lint
    contents: |
      golangci-lint run

  - name: test
    contents: |
      go test ./...

  - name: build
    contents: |
      go build ./...
```

<div id="java">
  ### Java
</div>

<Tabs>
  <Tab title="Gradle">
    使用 Gradle 配置 Java 环境。

    <Info>JDK 17 已**预装**在 Devin 的基础镜像中。如果默认的 OpenJDK 17 已满足需求，可跳过 JDK 安装步骤。</Info>

    ```yaml theme={null}
    initialize:
      - name: Install JDK 17
        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 Gradle
        run: |
          GRADLE_VERSION=8.12
          curl -fsSL "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
            -o /tmp/gradle.zip
          sudo unzip -qo /tmp/gradle.zip -d /opt
          sudo ln -sf /opt/gradle-${GRADLE_VERSION}/bin/gradle /usr/local/bin/gradle
          rm /tmp/gradle.zip

    maintenance: |
      ./gradlew dependencies

    knowledge:
      - name: lint
        contents: |
          ./gradlew check

      - name: test
        contents: |
          ./gradlew test

      - name: build
        contents: |
          ./gradlew build
    ```
  </Tab>

  <Tab title="Maven">
    使用 Maven 配置 Java 环境。

    <Info>JDK 17 已**预装**在 Devin 的基础镜像中。如果默认的 OpenJDK 17 已满足需求，可跳过 JDK 安装步骤。</Info>

    ```yaml theme={null}
    initialize:
      - name: Install JDK 17
        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 Maven
        run: |
          MAVEN_VERSION=3.9.9
          curl -fsSL "https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
            | sudo tar -xz -C /opt
          sudo ln -sf /opt/apache-maven-${MAVEN_VERSION}/bin/mvn /usr/local/bin/mvn

    maintenance: |
      mvn dependency:resolve

    knowledge:
      - name: test
        contents: |
          mvn test

      - name: build
        contents: |
          mvn package
    ```
  </Tab>
</Tabs>

<div id="ruby-on-rails">
  ### Ruby on Rails
</div>

基于 PostgreSQL 的 Rails 配置。

```yaml theme={null}
initialize:
  - name: Install Ruby 3.3
    run: |
      sudo apt-get update -qq
      sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ruby-full libpq-dev postgresql-client

maintenance: |
  bundle install
  rails db:migrate

knowledge:
  - name: lint
    contents: |
      bundle exec rubocop

  - name: test
    contents: |
      bundle exec rspec

  - name: build
    contents: |
      rails assets:precompile
```

<div id="rust">
  ### Rust
</div>

使用 cargo 的标准 Rust 配置。

<Info>**Rust** (通过 rustup) 和 **Cargo**已**预装**在 Devin 的基础镜像中。如果默认的稳定工具链已能满足需求，请跳过安装步骤。你只需拉取依赖。</Info>

```yaml theme={null}
initialize: |
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
  source ~/.cargo/env

maintenance: |
  cargo fetch

knowledge:
  - name: lint
    contents: |
      cargo clippy -- -D warnings

  - name: test
    contents: |
      cargo test

  - name: build
    contents: |
      cargo build --release
```

<div id="monorepos">
  ### Monorepo
</div>

<Tabs>
  <Tab title="多语言">
    包含 Node.js 前端和 Python 后端的 Monorepo。每个子项目都有各自的 knowledge 条目。

    ```yaml theme={null}
    initialize:
      - name: Install pnpm
        run: npm install -g pnpm
      - name: Install uv
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance:
      - name: Install frontend dependencies
        run: (cd packages/frontend && pnpm install)
      - name: Install backend dependencies
        run: (cd packages/backend && uv sync)
      - name: Build shared library
        run: (cd packages/shared && pnpm install && pnpm build)

    knowledge:
      - name: structure
        contents: |
          这是一个包含三个软件包的 monorepo：
          - `packages/frontend` — React 应用（TypeScript、pnpm）
          - `packages/backend` — Python API（FastAPI、uv）
          - `packages/shared` — 共享 TypeScript 工具库（必须先于前端构建）
      - name: frontend
        contents: |
          运行 `cd packages/frontend && pnpm dev` 启动开发服务器。
          运行 `cd packages/frontend && pnpm lint` 执行 lint 检查。
          运行 `cd packages/frontend && pnpm test` 运行测试。
      - name: backend
        contents: |
          运行 `cd packages/backend && uv run uvicorn app.main:app --reload` 启动 API。
          运行 `cd packages/backend && uv run ruff check .` 执行 lint 检查。
          运行 `cd packages/backend && uv run pytest` 运行测试。
    ```

    <Tip>
      使用 subshell `(cd dir && command)`，不要使用 `cd dir && command`，这样每个步骤之间都会重置工作目录。
    </Tip>
  </Tab>

  <Tab title="多个 JDK 版本">
    一个 Java Monorepo，其中不同服务需要不同的 JDK 版本。在设置时安装两个 JDK，然后使用 `knowledge` 条目告诉 Devin 每个服务应使用哪个 `JAVA_HOME`。

    ```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: |
          构建 API 服务（JDK 17）：
            JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
            cd services/api && ./gradlew clean build
      - name: build_legacy
        contents: |
          构建 legacy 服务（JDK 11）：
            JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
            cd services/legacy && ./gradlew clean build
      - name: test_all
        contents: |
          运行所有服务的测试：
            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)
    ```
  </Tab>
</Tabs>

***

<div id="private-package-registries">
  ## 私有包注册表
</div>

将包管理器配置为从私有注册表解析依赖。在 **Settings > Environment > Blueprints > 组织范围设置** 中进行设置 (如果只有一个 仓库 需要，则可按 仓库 单独设置) 。

<Warning>
  **凭据配置应放在 `maintenance` 中，而不是 `initialize` 中。** 将 secrets (注册表密码、身份验证令牌) 写入配置文件的步骤应使用 `maintenance`，这样每个 session 都会重新加载最新凭据。
  secrets 会在保存快照前被移除，因此在 `initialize` 期间写入的配置文件，在 sessions 启动时将不包含有效凭据。
</Warning>

<Info>
  如果你的私有注册表使用 corporate CA，请先确保已在企业级别安装 [CA 证书](#corporate-ca-certificate)。以下配置假定 HTTPS 信任
  已经建立。
</Info>

<div id="nodejs-registries">
  ### Node.js 注册表
</div>

<Tabs>
  <Tab title="npm（作用域）">
    将 npm 配置为从私有 registry 解析带作用域的软件包 (例如 `@myorg/*`) ，同时公开软件包仍从默认的 npm registry 获取。

    <Accordion title="必需的 secrets">- `GITHUB_PACKAGES_TOKEN` — 具有 `read:packages` 作用域的 Personal access token 或 GitHub App 令牌</Accordion>

    ```yaml theme={null}
    maintenance:
      - name: Configure npm scoped registry
        run: |
          npm config set @myorg:registry https://npm.pkg.github.com
          npm config set //npm.pkg.github.com/:_authToken $GITHUB_PACKAGES_TOKEN
    ```

    <Tip>
      将 `@myorg` 替换为你的 npm 作用域。常见的私有 registry URL：

      * **GitHub Packages:** `https://npm.pkg.github.com`
      * **Artifactory:** `https://artifactory.example.com/artifactory/api/npm/npm-virtual`
      * **Nexus:** `https://nexus.example.com/repository/npm-group`
      * **GitLab:** `https://gitlab.example.com/api/v4/packages/npm`
      * **AWS CodeArtifact:** `https://<domain>.d.codeartifact.<region>.amazonaws.com/npm/<repo>`
    </Tip>
  </Tab>

  <Tab title="npm（完整镜像）">
    让**所有** npm 软件包都通过你的私有 registry (而不仅仅是带作用域的软件包) 。

    <Accordion title="必需的 secrets">
      * `NPM_REGISTRY_URL` — 你的 npm registry 的完整 URL (例如 `https://artifactory.example.com/artifactory/api/npm/npm-virtual`)  - `NPM_REGISTRY_HOST` — 仅主机名，不含协议 (例如
        `artifactory.example.com`)  - `REGISTRY_TOKEN` — 该 registry 的 npm 身份验证令牌
    </Accordion>

    ```yaml theme={null}
    maintenance:
      - name: Configure npm to use private registry
        run: |
          npm config set registry $NPM_REGISTRY_URL
          npm config set //${NPM_REGISTRY_HOST}/:_authToken $REGISTRY_TOKEN
          npm config set strict-ssl true
    ```
  </Tab>

  <Tab title="pnpm">
    将 pnpm 配置为从私有 registry 解析软件包。

    <Accordion title="必需的 secrets">
      * `NPM_REGISTRY_URL` — 你的 npm registry 的完整 URL - `NPM_REGISTRY_HOST` — 仅主机名，不含协议 - `REGISTRY_TOKEN` — 该 registry 的 npm 身份验证令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install pnpm
        run: npm install -g pnpm

    maintenance:
      - name: Configure pnpm for private registry
        run: |
          pnpm config set registry $NPM_REGISTRY_URL
          pnpm config set //${NPM_REGISTRY_HOST}/:_authToken $REGISTRY_TOKEN
    ```
  </Tab>

  <Tab title="Yarn">
    将 Yarn (Classic v1 或 Berry v2+) 配置为从私有 registry 解析软件包。

    <Accordion title="必需的 secrets">- `NPM_REGISTRY_URL` — 你的 npm/Yarn registry 的完整 URL - `REGISTRY_TOKEN` — 该 registry 的身份验证令牌 (仅 Berry) </Accordion>

    **Yarn Classic (v1) ：**

    ```yaml theme={null}
    initialize:
      - name: Install Yarn Classic
        run: npm install -g yarn

    maintenance:
      - name: Configure Yarn for private registry
        run: |
          yarn config set registry "$NPM_REGISTRY_URL"
          # 对于带作用域的软件包：
          # yarn config set @myorg:registry "https://npm.pkg.github.com"
    ```

    **Yarn Berry (v2+) ：**

    ```yaml theme={null}
    maintenance:
      - name: Configure Yarn Berry for private registry
        run: |
          yarn config set npmRegistryServer "$NPM_REGISTRY_URL"
          yarn config set npmAuthToken "$REGISTRY_TOKEN"
          # 对于带作用域的软件包：
          # yarn config set npmScopes.myorg.npmRegistryServer "https://npm.pkg.github.com"
          # yarn config set npmScopes.myorg.npmAuthToken "$GITHUB_PACKAGES_TOKEN"
    ```
  </Tab>
</Tabs>

<div id="python-registries">
  ### Python 软件包注册表
</div>

<Tabs>
  <Tab title="pip / uv">
    配置 pip 和 uv，使其从你的私有 PyPI registry (例如 Nexus、Artifactory) 解析软件包。

    <Accordion title="必需的 secrets">
      * `PYPI_REGISTRY_URL` — 你的 PyPI 索引完整 URL，如有需要请包含凭据 (例如 `https://user:token@nexus.example.com/repository/pypi-proxy/simple`)
    </Accordion>

    ```yaml theme={null}
    maintenance:
      - name: Configure pip/uv for private registry
        run: |
          mkdir -p ~/.config/pip
          cat > ~/.config/pip/pip.conf << EOF
          [global]
          index-url = $PYPI_REGISTRY_URL
          EOF

          echo "export UV_INDEX_URL=$PYPI_REGISTRY_URL" \
            | sudo tee /etc/profile.d/uv-registry.sh > /dev/null
    ```

    <Tip>
      常见的 PyPI registry URL 模式：

      * **Artifactory:** `https://artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple`
      * **Nexus:** `https://nexus.example.com/repository/pypi-proxy/simple`
      * **AWS CodeArtifact:** `https://aws:TOKEN@domain-owner.d.codeartifact.region.amazonaws.com/pypi/repo/simple/`
      * **Azure Artifacts:** `https://pkgs.dev.azure.com/org/project/_packaging/feed/pypi/simple`
      * **GitLab:** `https://gitlab.example.com/api/v4/groups/<group-id>/-/packages/pypi/simple`
    </Tip>
  </Tab>

  <Tab title="Poetry">
    配置 Poetry，使其从私有 PyPI registry 解析软件包。

    <Accordion title="必需的 secrets">
      * `POETRY_REGISTRY_URL` — 你的 PyPI 兼容 registry 的完整 URL - `REGISTRY_USER` — registry 用户名 - `REGISTRY_PASS` — registry 密码或 API 令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install Poetry
        run: curl -sSL https://install.python-poetry.org | python3 -

    maintenance:
      - name: Configure Poetry for private registry
        run: |
          poetry config repositories.private "$POETRY_REGISTRY_URL"
          poetry config http-basic.private "$REGISTRY_USER" "$REGISTRY_PASS"
    ```
  </Tab>
</Tabs>

<div id="jvm-registries">
  ### JVM 软件包注册表
</div>

<Tabs>
  <Tab title="Maven">
    安装 JDK，并将 Maven 配置为通过你的私有 registry (例如 Artifactory、Nexus) 镜像所有依赖解析。

    <Info>JDK 17 已在 Devin 的基础镜像中**预装**。如果默认的 OpenJDK 17 足够使用，请跳过安装步骤。你只需安装 Maven 并配置 registry。</Info>

    <Accordion title="必需的 secrets">
      * `MAVEN_REGISTRY_URL` — 你的 Maven registry URL (例如 `https://artifactory.example.com/artifactory/maven-virtual`)  - `REGISTRY_USER` — registry 用户名 - `REGISTRY_PASS` — registry 密码或
        API 令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install JDK 17
        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 Maven
        run: |
          MAVEN_VERSION=3.9.9
          curl -fsSL "https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
            | sudo tar -xz -C /opt
          sudo ln -sf /opt/apache-maven-${MAVEN_VERSION}/bin/mvn /usr/local/bin/mvn

    maintenance:
      - name: Configure Maven for private registry
        run: |
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <mirrors>
              <mirror>
                <id>private-registry</id>
                <mirrorOf>*</mirrorOf>
                <url>$MAVEN_REGISTRY_URL</url>
              </mirror>
            </mirrors>
            <servers>
              <server>
                <id>private-registry</id>
                <username>$REGISTRY_USER</username>
                <password>$REGISTRY_PASS</password>
              </server>
            </servers>
          </settings>
          EOF
    ```

    <Tip>
      Maven 常见的注册表 URL 模式：

      * **Artifactory:** `https://artifactory.example.com/artifactory/maven-virtual`
      * **Nexus:** `https://nexus.example.com/repository/maven-public`
      * **Azure Artifacts:** `https://pkgs.dev.azure.com/org/project/_packaging/feed/maven/v1`
      * **GitHub Packages:** `https://maven.pkg.github.com`
      * **GitLab:** `https://gitlab.example.com/api/v4/groups/<group-id>/-/packages/maven`
      * **AWS CodeArtifact:** `https://<domain>.d.codeartifact.<region>.amazonaws.com/maven/<repo>`
    </Tip>
  </Tab>

  <Tab title="Gradle">
    安装 JDK，并配置 Gradle 以通过你的私有仓库解析所有依赖。

    <Info>Devin 的基础镜像已**预装** JDK 17。如果默认版本已足够，请跳过 JDK 安装步骤。</Info>

    <Accordion title="必需的 secrets">- `GRADLE_REGISTRY_URL` — 你的 Gradle/Maven 仓库 URL - `REGISTRY_USER` — 仓库用户名 - `REGISTRY_PASS` — 仓库密码或 API 令牌</Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install JDK 17
        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 Gradle
        run: |
          GRADLE_VERSION=8.12
          curl -fsSL "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \
            -o /tmp/gradle.zip
          sudo unzip -qo /tmp/gradle.zip -d /opt
          sudo ln -sf /opt/gradle-${GRADLE_VERSION}/bin/gradle /usr/local/bin/gradle
          rm /tmp/gradle.zip

    maintenance:
      - name: Configure Gradle for private registry
        run: |
          mkdir -p ~/.gradle
          cat > ~/.gradle/init.gradle << EOF
          allprojects {
              repositories {
                  maven {
                      url "$GRADLE_REGISTRY_URL"
                      credentials {
                          username = "$REGISTRY_USER"
                          password = "$REGISTRY_PASS"
                      }
                      allowInsecureProtocol = false
                  }
              }
          }
          EOF
    ```
  </Tab>
</Tabs>

<div id="other-registries">
  ### 其他软件包注册表
</div>

<AccordionGroup>
  <Accordion title="Go 模块代理">
    安装 Go，并将其配置为通过私有模块代理 (例如 Athens、Artifactory 或 GOPROXY 端点) 解析模块。

    <Accordion title="必需的 secrets">
      * `GO_PROXY_URL` — 你的 Go 模块代理 URL (例如 `https://athens.corp.internal`)
      * `GIT_TOKEN` — 用于托管 Go 模块的私有 Git repo 的 Personal Access Token
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install Go
        run: |
          GO_VERSION=1.23.5
          ARCH=$(dpkg --print-architecture)
          curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${ARCH}.tar.gz" \
            | sudo tar -xz -C /usr/local
          echo 'export PATH="/usr/local/go/bin:$HOME/go/bin:$PATH"' \
            | sudo tee /etc/profile.d/golang.sh > /dev/null

      - name: Configure Go for private modules
        run: |
          cat << 'GOENV' | sudo tee /etc/profile.d/go-private.sh > /dev/null
          export GOPROXY="$GO_PROXY_URL,direct"
          export GONOSUMCHECK="corp.internal/*,github.com/myorg/*"
          export GOPRIVATE="corp.internal/*,github.com/myorg/*"
          GOENV

    maintenance:
      - name: Authenticate Go private modules
        run: |
          git config --global url."https://$GIT_TOKEN@github.com/myorg/".insteadOf "https://github.com/myorg/"
    ```

    <Tip>
      常见的 Go 代理 URL 格式：

      * **Artifactory:** `https://artifactory.example.com/artifactory/go-virtual`
      * **Nexus:** `https://nexus.example.com/repository/go-proxy`
      * **Athens:** `https://athens.corp.internal`
    </Tip>
  </Accordion>

  <Accordion title=".NET / NuGet">
    配置 NuGet，以从私有源解析软件包。

    <Accordion title="必需的 secrets">
      * `NUGET_SOURCE_URL` — 你的 NuGet 源 URL
      * `NUGET_API_KEY` — 该源的 API key 或 PAT
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install .NET SDK
        run: |
          curl -fsSL https://dot.net/v1/dotnet-install.sh | bash -s -- --channel 8.0
          echo 'export PATH="$HOME/.dotnet:$PATH"' \
            | sudo tee /etc/profile.d/dotnet.sh > /dev/null

    maintenance:
      - name: Configure NuGet for private feed
        run: |
          dotnet nuget add source "$NUGET_SOURCE_URL" \
            --name private \
            --username any \
            --password "$NUGET_API_KEY" \
            --store-password-in-clear-text 2>/dev/null || \
          dotnet nuget update source private \
            --source "$NUGET_SOURCE_URL" \
            --username any \
            --password "$NUGET_API_KEY" \
            --store-password-in-clear-text
    ```
  </Accordion>

  <Accordion title="Docker">
    配置 Docker 从私有容器注册表拉取镜像。

    <Accordion title="必需的 secrets">
      * `DOCKER_MIRROR_URL` *(可选)* — 你的 Docker Hub 镜像源 URL (例如 `https://mirror.corp.internal`)
      * `DOCKER_REGISTRY_URL` — 你的私有容器注册表 URL (例如 `registry.corp.internal:5000`)
      * `DOCKER_REGISTRY_USER` — 注册表用户名
      * `DOCKER_REGISTRY_PASS` — 注册表密码或 API 令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Create Docker config directory
        run: sudo mkdir -p /etc/docker

    maintenance:
      - name: Configure Docker for private registry
        run: |
          # 配置注册表镜像（可选 — 通过你的注册表转发 Docker Hub 拉取请求）
          cat << EOF | sudo tee /etc/docker/daemon.json > /dev/null
          {
            "registry-mirrors": ["$DOCKER_MIRROR_URL"]
          }
          EOF
          sudo systemctl restart docker || true

          # 登录私有容器注册表
          echo "$DOCKER_REGISTRY_PASS" | docker login "$DOCKER_REGISTRY_URL" \
            --username "$DOCKER_REGISTRY_USER" \
            --password-stdin
    ```

    <Tip>
      常见容器注册表 URL：

      * **Amazon ECR:** `<account-id>.dkr.ecr.<region>.amazonaws.com`
      * **Azure Container Registry:** `<name>.azurecr.io`
      * **Google Artifact Registry:** `<region>-docker.pkg.dev`
      * **GitHub Container Registry:** `ghcr.io`
      * **GitLab Container Registry:** `registry.gitlab.example.com`
      * **Nexus:** `https://nexus.example.com:8443`
      * **JFrog:** `<name>.jfrog.io`
    </Tip>
  </Accordion>

  <Accordion title="Rust / Cargo">
    配置 Cargo，以从私有 registry 解析 crates。

    <Info>
      **Rust** (通过 rustup) 和 **Cargo** 已**预装**在 Devin 的基础镜像中。如果默认的 stable 工具链已足够，请跳过安装步骤。你只需要配置 registry。
    </Info>

    <Accordion title="必需的 secrets">
      * `CARGO_REGISTRY_INDEX` — 私有 registry index 的 URL (e.g., `sparse+https://cargo.corp.internal/api/v1/crates/`)
      * `CARGO_REGISTRY_TOKEN` — 私有 registry 的身份验证令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain stable
          echo 'source "$HOME/.cargo/env"' \
            | sudo tee /etc/profile.d/rust.sh > /dev/null

    maintenance:
      - name: Configure Cargo for private registry
        run: |
          mkdir -p ~/.cargo
          cat > ~/.cargo/config.toml << EOF
          [registries.private]
          index = "$CARGO_REGISTRY_INDEX"
          token = "$CARGO_REGISTRY_TOKEN"

          [source.crates-io]
          replace-with = "private"

          [source.private]
          registry = "$CARGO_REGISTRY_INDEX"
          EOF
    ```

    <Tip>
      如果你只想**添加**私有注册表，而不替换 crates.io，请删除 `[source.crates-io]` 和 `[source.private]` 配置段，并使用 `cargo install --registry private`，或在 `Cargo.toml` 中使用 `[dependencies] my-crate = { version = "1.0", registry = "private" }`。
    </Tip>
  </Accordion>

  <Accordion title="Ruby / Bundler">
    安装 Ruby 并配置 Bundler，以便从私有 gem 服务器解析 gem。

    <Accordion title="必需的 secrets">
      * `GEM_SERVER_URL` — 你的私有 gem 服务器的 URL (例如：`https://artifactory.example.com/artifactory/api/gems/gems-virtual`)
      * `REGISTRY_USER` — Registry 用户名
      * `REGISTRY_PASS` — Registry 密码或 API 令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install Ruby
        run: |
          sudo apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ruby-full

    maintenance:
      - name: Configure Bundler for private gem server
        run: |
          bundle config set mirror.https://rubygems.org "$GEM_SERVER_URL"
          bundle config set "$GEM_SERVER_URL" "$REGISTRY_USER:$REGISTRY_PASS"
    ```

    <Tip>
      常见的 gem server URL 格式：

      * **Artifactory:** `https://artifactory.example.com/artifactory/api/gems/gems-virtual`
      * **Nexus:** `https://nexus.example.com/repository/rubygems-proxy`
      * **Gemfury:** `https://gem.fury.io/<org>`
    </Tip>
  </Accordion>

  <Accordion title="PHP / Composer">
    安装 PHP 并配置 Composer，以便从私有 Packagist 或 Satis 注册表解析软件包。

    <Accordion title="必需的 secrets">
      * `COMPOSER_REGISTRY_URL` — 你的私有 Composer 注册表的 URL (例如：`https://repo.packagist.com/<org>`)
      * `REGISTRY_USER` — 注册表用户名
      * `REGISTRY_PASS` — 注册表密码或 API 令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install PHP and Composer
        run: |
          sudo apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
            php-cli php-mbstring php-xml php-curl unzip

          # 安装 Composer
          curl -sS https://getcomposer.org/installer | php
          sudo mv composer.phar /usr/local/bin/composer

    maintenance:
      - name: Configure Composer for private registry
        run: |
          composer config --global repositories.private \
            composer "$COMPOSER_REGISTRY_URL"

          # 向镜像仓库进行身份验证
          composer config --global http-basic.$(echo "$COMPOSER_REGISTRY_URL" \
            | sed 's|https\?://||;s|/.*||') "$REGISTRY_USER" "$REGISTRY_PASS"
    ```

    <Tip>
      常见的 Composer 注册表 URL 格式：

      * **Artifactory:** `https://artifactory.example.com/artifactory/api/composer/packagist-virtual`
      * **Nexus:** `https://nexus.example.com/repository/packagist-proxy`
      * **Private Packagist:** `https://repo.packagist.com/<org>`
      * **Satis:** `https://satis.corp.internal`
    </Tip>
  </Accordion>

  <Accordion title="AWS CodeArtifact 令牌更新">
    AWS CodeArtifact 令牌会在 12 小时后过期。请使用 `maintenance` 设置令牌刷新命令，以便 Agent 在需要时可重新运行该命令。此示例会将 npm、pip 和 Maven 配置为使用 CodeArtifact。

    <Info>
      **`awscli` 已预装**在 Devin 的基础镜像中。你只需要刷新令牌并配置 registry。
    </Info>

    <Accordion title="必需的 secrets">
      * `AWS_ACCESS_KEY_ID` 和 `AWS_SECRET_ACCESS_KEY` — 具有 `codeartifact:GetAuthorizationToken` 和 `sts:GetServiceBearerToken` 权限的 IAM 凭据
      * `CA_DOMAIN` — 你的 CodeArtifact 域名
      * `CA_DOMAIN_OWNER` — 拥有该域的 AWS 账户 ID
      * `CA_REGION` — AWS 区域 (例如 `us-east-1`)
      * `CA_NPM_REPO`, `CA_PYPI_REPO`, `CA_MAVEN_REPO` — 各生态对应的仓库名称
    </Accordion>

    ```yaml theme={null}
    maintenance:
      - name: Refresh CodeArtifact auth token
        run: |
          # 获取新令牌（有效期 12 小时）
          export CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
            --domain $CA_DOMAIN \
            --domain-owner $CA_DOMAIN_OWNER \
            --region $CA_REGION \
            --query authorizationToken \
            --output text)

          CA_ENDPOINT="https://${CA_DOMAIN}-${CA_DOMAIN_OWNER}.d.codeartifact.${CA_REGION}.amazonaws.com"

          # 配置 npm
          npm config set registry "${CA_ENDPOINT}/npm/${CA_NPM_REPO}/"
          npm config set "//${CA_DOMAIN}-${CA_DOMAIN_OWNER}.d.codeartifact.${CA_REGION}.amazonaws.com/npm/${CA_NPM_REPO}/:_authToken" "$CODEARTIFACT_AUTH_TOKEN"

          # 配置 pip
          mkdir -p ~/.config/pip
          cat > ~/.config/pip/pip.conf << EOF
          [global]
          index-url = https://aws:${CODEARTIFACT_AUTH_TOKEN}@${CA_DOMAIN}-${CA_DOMAIN_OWNER}.d.codeartifact.${CA_REGION}.amazonaws.com/pypi/${CA_PYPI_REPO}/simple/
          EOF

          # 配置 Maven（可选）
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <servers>
              <server>
                <id>codeartifact</id>
                <username>aws</username>
                <password>${CODEARTIFACT_AUTH_TOKEN}</password>
              </server>
            </servers>
            <mirrors>
              <mirror>
                <id>codeartifact</id>
                <mirrorOf>*</mirrorOf>
                <url>${CA_ENDPOINT}/maven/${CA_MAVEN_REPO}/</url>
              </mirror>
            </mirrors>
          </settings>
          EOF
    ```
  </Accordion>
</AccordionGroup>

***

<div id="enterprise-infrastructure">
  ## 企业基础架构
</div>

适用于所有组织和代码仓库的机器级基础架构。可在 **Settings > Devin's base environment** (企业范围) 或 **Settings > Environment > Blueprints > Org-wide setup** (组织范围) 中进行设置。

<div id="network-and-connectivity">
  ### 网络与连接
</div>

<AccordionGroup>
  <Accordion title="企业级 CA 证书">
    你的组织为内部服务使用私有证书颁发机构 (CA) 。Devin 需要根证书，才能通过 HTTPS 访问内部制品库和工具。

    <Accordion title="必需的 secrets">- `CORP_ROOT_CA_B64` — 来自你的企业 CA 的 Base64 编码 PEM 证书。可使用以下命令生成：`cat corp-root-ca.crt | base64 -w0`</Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install corporate CA certificate
        run: |
          echo "$CORP_ROOT_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-root-ca.crt > /dev/null
          sudo update-ca-certificates
          echo 'export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root-ca.crt' \
            | sudo tee /etc/profile.d/node-ca.sh > /dev/null
    ```
  </Accordion>

  <Accordion title="多个 CA 证书">
    如果你的组织使用多个 CA 证书 (例如，不同的内部服务分别使用不同的 CA) 。

    <Accordion title="必需的 secrets">- `CORP_ROOT_CA_B64` — Base64 编码的主 CA 证书 - `CORP_INTERMEDIATE_CA_B64` — Base64 编码的中间 CA 证书</Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install corporate CA certificates
        run: |
          echo "$CORP_ROOT_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-root-ca.crt > /dev/null
          echo "$CORP_INTERMEDIATE_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-intermediate-ca.crt > /dev/null
          sudo update-ca-certificates

          # 为需要单个 CA 文件的工具创建合并包
          cat /usr/local/share/ca-certificates/corp-*.crt \
            | sudo tee /usr/local/share/ca-certificates/corp-bundle.crt > /dev/null

          echo 'export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-bundle.crt' \
            | sudo tee /etc/profile.d/node-ca.sh > /dev/null
    ```
  </Accordion>

  <Accordion title="HTTP/HTTPS 代理">
    使所有网络流量都通过企业代理转发。

    <Accordion title="必需的 secrets">
      * `CORP_HTTP_PROXY` — HTTP 代理 URL (例如：`http://proxy.corp.example.com:8080`)  - `CORP_HTTPS_PROXY` — HTTPS 代理 URL - `CORP_NO_PROXY` — 以逗号分隔的绕过代理的主机列表 (例如：
        `localhost,127.0.0.1,.corp.example.com`)
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Configure system-wide proxy
        run: |
          cat << 'PROXY' | sudo tee /etc/profile.d/proxy.sh > /dev/null
          export http_proxy="$CORP_HTTP_PROXY"
          export https_proxy="$CORP_HTTPS_PROXY"
          export no_proxy="$CORP_NO_PROXY"
          export HTTP_PROXY="$CORP_HTTP_PROXY"
          export HTTPS_PROXY="$CORP_HTTPS_PROXY"
          export NO_PROXY="$CORP_NO_PROXY"
          PROXY
          source /etc/profile.d/proxy.sh

    maintenance:
      - name: Configure git proxy
        run: |
          git config --global http.proxy "$CORP_HTTP_PROXY"
          git config --global https.proxy "$CORP_HTTPS_PROXY"

          # 配置 npm 代理
          npm config set proxy "$CORP_HTTP_PROXY"
          npm config set https-proxy "$CORP_HTTPS_PROXY"
    ```
  </Accordion>

  <Accordion title="身份验证代理">
    如果你的企业代理需要通过用户名/密码进行身份验证。

    <Accordion title="必需的 secrets">
      * `PROXY_USER` — 代理用户名 - `PROXY_PASS` — 代理密码 - `PROXY_HOST` — 代理主机名和端口 (例如 `proxy.corp.example.com:8080`)  - `CORP_NO_PROXY` — 不经代理的主机
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Configure authenticated proxy
        run: |
          PROXY_URL="http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}"

          cat << PROXY | sudo tee /etc/profile.d/proxy.sh > /dev/null
          export http_proxy="$PROXY_URL"
          export https_proxy="$PROXY_URL"
          export no_proxy="$CORP_NO_PROXY"
          export HTTP_PROXY="$PROXY_URL"
          export HTTPS_PROXY="$PROXY_URL"
          export NO_PROXY="$CORP_NO_PROXY"
          PROXY
          source /etc/profile.d/proxy.sh

    maintenance:
      - name: Configure git for authenticated proxy
        run: |
          PROXY_URL="http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}"
          git config --global http.proxy "$PROXY_URL"
          git config --global https.proxy "$PROXY_URL"
    ```
  </Accordion>

  <Accordion title="CA 证书 + 代理（组合）">
    适用于同时需要 corporate CA 和代理的环境的组合配置。这在 Enterprise 环境中很常见：内部服务使用私有证书，且所有流量都必须通过代理转发。

    <Accordion title="必需的 secrets">
      * `CORP_ROOT_CA_B64` — Base64 编码的 corporate CA certificate - `CORP_HTTP_PROXY`, `CORP_HTTPS_PROXY` — 代理 URL - `CORP_NO_PROXY` — 不走代理的主机
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install corporate CA certificate
        run: |
          echo "$CORP_ROOT_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-root-ca.crt > /dev/null
          sudo update-ca-certificates
          echo 'export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root-ca.crt' \
            | sudo tee /etc/profile.d/node-ca.sh > /dev/null

      - name: Configure system-wide proxy
        run: |
          cat << 'PROXY' | sudo tee /etc/profile.d/proxy.sh > /dev/null
          export http_proxy="$CORP_HTTP_PROXY"
          export https_proxy="$CORP_HTTPS_PROXY"
          export no_proxy="$CORP_NO_PROXY"
          export HTTP_PROXY="$CORP_HTTP_PROXY"
          export HTTPS_PROXY="$CORP_HTTPS_PROXY"
          export NO_PROXY="$CORP_NO_PROXY"
          PROXY
          source /etc/profile.d/proxy.sh

    maintenance:
      - name: Configure git proxy
        run: |
          git config --global http.proxy "$CORP_HTTP_PROXY"
          git config --global https.proxy "$CORP_HTTPS_PROXY"
    ```
  </Accordion>

  <Accordion title="VPN 连接">
    你的私有仓库、Git 服务器或其他内部服务只能通过 VPN 访问。此模块必须在其他需要通过网络访问内部资源的模块**之前**运行。

    <Accordion title="必需的 secrets">
      **OpenVPN：**

      * `VPN_CONFIG_B64` — 经过 Base64 编码的 OpenVPN 配置文件 (`.ovpn`) 。生成方式：`cat corp.ovpn | base64 -w0`
      * `VPN_AUTH_USER` *(可选)* — VPN 用户名 (如果你的 VPN 需要用户名/密码认证)
      * `VPN_AUTH_PASS` *(可选)* — VPN 密码

      **WireGuard：**

      * `WG_CONFIG_B64` — 经过 Base64 编码的 WireGuard 配置文件。生成方式：`cat wg0.conf | base64 -w0`
    </Accordion>

    **OpenVPN：**

    ```yaml theme={null}
    initialize:
      - name: Install and configure OpenVPN
        run: |
          sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openvpn

          # 写入 VPN 配置
          sudo mkdir -p /etc/openvpn/client
          echo "$VPN_CONFIG_B64" | base64 -d \
            | sudo tee /etc/openvpn/client/corp.conf > /dev/null

          # 如果 VPN 需要用户名/密码认证
          if [ -n "${VPN_AUTH_USER:-}" ] && [ -n "${VPN_AUTH_PASS:-}" ]; then
            printf '%s\n%s\n' "$VPN_AUTH_USER" "$VPN_AUTH_PASS" \
              | sudo tee /etc/openvpn/client/auth.txt > /dev/null
            sudo chmod 600 /etc/openvpn/client/auth.txt
            echo "auth-user-pass /etc/openvpn/client/auth.txt" \
              | sudo tee -a /etc/openvpn/client/corp.conf > /dev/null
          fi

          # 启动 VPN 隧道
          sudo systemctl daemon-reload
          sudo systemctl enable --now openvpn-client@corp

          # 等待隧道建立
          for i in $(seq 1 30); do
            if ip link show tun0 >/dev/null 2>&1; then break; fi
            sleep 1
          done
    ```

    **WireGuard：**

    ```yaml theme={null}
    initialize:
      - name: Install and configure WireGuard
        run: |
          sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq wireguard-tools

          # 写入 WireGuard 配置
          echo "$WG_CONFIG_B64" | base64 -d \
            | sudo tee /etc/wireguard/wg0.conf > /dev/null
          sudo chmod 600 /etc/wireguard/wg0.conf

          # 启动隧道
          sudo systemctl enable --now wg-quick@wg0
    ```

    <Tip>
      有关 VPN 设置的更多信息，请参阅 [VPN Configuration](/zh/onboard-devin/vpn)。
    </Tip>
  </Accordion>

  <Accordion title="自定义 DNS 解析">
    你的内部服务使用公共 DNS 无法解析的私有 DNS 名称。

    ```yaml theme={null}
    initialize:
      - name: Configure custom DNS resolution
        run: |
          # 添加内部主机名
          cat << 'HOSTS' | sudo tee -a /etc/hosts > /dev/null
          10.0.1.50  nexus.corp.internal
          10.0.1.51  git.corp.internal
          10.0.1.52  artifactory.corp.internal
          HOSTS

          # 可选：配置自定义域名服务器
          sudo mkdir -p /etc/systemd/resolved.conf.d
          cat << 'DNS' | sudo tee /etc/systemd/resolved.conf.d/corp.conf > /dev/null
          [Resolve]
          DNS=10.0.0.53 10.0.0.54
          Domains=corp.internal
          DNS
          sudo systemctl restart systemd-resolved || true
    ```
  </Accordion>
</AccordionGroup>

<div id="identity-and-security">
  ### 身份与安全
</div>

<AccordionGroup>
  <Accordion title="GPG 提交签名">
    你的组织要求所有 Git 提交都必须签名，并且你希望 GitHub 将 Devin 的提交标记为 **Verified**。

    <Accordion title="必需的 secrets">
      * `GPG_PRIVATE_KEY_B64` — Base64 编码的 GPG 私钥。生成命令：`gpg --export-secret-keys <key-id> | base64 -w0`
      * `GIT_USER_NAME` — Git 作者名称 (例如：`Devin AI`)
      * `GIT_USER_EMAIL` — Git 作者邮箱。**必须与 GPG 密钥上的某个 UID 匹配**，否则 GitHub 不会验证该签名。
    </Accordion>

    <Note>
      此外，还需要将对应的**公钥**上传到 Devin 用于推送的 GitHub 账户 (位于 [GitHub Settings > SSH and GPG keys](https://github.com/settings/keys)) 。只有当用于签名的公钥已注册到创建该提交的账户上时，GitHub 才会将提交标记为 Verified。
    </Note>

    ```yaml theme={null}
    initialize:
      - name: Prepare GPG and git signing config
        run: |
          # 允许 GPG 在没有 TTY 的情况下运行
          echo 'export GPG_TTY=$(tty)' | sudo tee -a /etc/profile.d/gpg.sh > /dev/null

    maintenance:
      - name: Import GPG key and configure git signing
        run: |
          echo "$GPG_PRIVATE_KEY_B64" | base64 -d | gpg --batch --import 2>/dev/null

          KEY_ID=$(gpg --list-secret-keys --keyid-format long 2>/dev/null \
            | grep sec | head -1 | awk '{print $2}' | cut -d'/' -f2)

          git config --global user.signingkey "$KEY_ID"
          git config --global commit.gpgsign true
          git config --global tag.gpgsign true
          git config --global gpg.program gpg
    ```
  </Accordion>

  <Accordion title="Git 身份和 SSH 密钥">
    配置 Devin 的 Git 身份和 SSH 密钥，以访问私有 Git 服务器。

    <Accordion title="必需的 secrets">
      * `GIT_USER_NAME` — Git 作者名称
      * `GIT_USER_EMAIL` — Git 作者邮箱
      * `SSH_PRIVATE_KEY_B64` — Base64 编码的 SSH 私钥。生成命令：`cat ~/.ssh/id_ed25519 | base64 -w0`
      * `SSH_KNOWN_HOSTS_B64` — Base64 编码的 known hosts 条目。生成命令：`ssh-keyscan git.corp.internal | base64 -w0`
      * `SSH_CONFIG_B64` *(可选)* — Base64 编码的 SSH 配置文件
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Configure git identity
        run: |
          git config --global user.name "$GIT_USER_NAME"
          git config --global user.email "$GIT_USER_EMAIL"

          # 准备 SSH 目录
          mkdir -p ~/.ssh && chmod 700 ~/.ssh

    maintenance:
      - name: Install SSH keys
        run: |
          # 安装 SSH 私钥（放在 maintenance 中，这样每个 session 都会重新加载）
          echo "$SSH_PRIVATE_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519

          # 为你的 Git 服务器添加 known hosts
          echo "$SSH_KNOWN_HOSTS_B64" | base64 -d >> ~/.ssh/known_hosts

          # 可选：安装自定义 SSH 配置
          if [ -n "${SSH_CONFIG_B64:-}" ]; then
            echo "$SSH_CONFIG_B64" | base64 -d > ~/.ssh/config
            chmod 600 ~/.ssh/config
          fi
    ```

    <Tip>
      使用 `ssh-keyscan git.corp.internal | base64 -w0` 为你的 Git 服务器生成 known hosts 条目。
    </Tip>
  </Accordion>
</AccordionGroup>

<div id="system-configuration">
  ### 系统配置
</div>

<AccordionGroup>
  <Accordion title="系统软件包">
    安装默认 Devin 镜像中不包含的系统级软件包 (例如，用于图像处理或 PDF 生成的原生库) 。

    ```yaml theme={null}
    initialize:
      - name: Install system packages
        run: |
          sudo apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
            libpq-dev \
            libmagickwand-dev \
            poppler-utils \
            ffmpeg
    ```
  </Accordion>

  <Accordion title="自定义环境变量">
    设置在每个会话中都可用的持久环境变量。

    推荐的做法是将 `KEY=VALUE` 逐行写入 `$ENVRC` 文件。写入 `$ENVRC` 的变量会自动导出到后续所有步骤以及 Devin 会话中 (类似于 GitHub Actions 的 `$GITHUB_ENV`) 。

    ```yaml theme={null}
    initialize:
      - name: 设置自定义环境变量
        run: |
          echo "CORPORATE_ENV=production" >> $ENVRC
          echo "DEFAULT_REGION=us-east-1" >> $ENVRC
          echo "MAX_RETRIES=3" >> $ENVRC
    ```

    <Tip>
      你也可以将环境变量写入 `/etc/profile.d/` 脚本，使其在整个系统范围内生效：

      ```bash theme={null}
      cat << 'ENVVARS' | sudo tee /etc/profile.d/custom-env.sh > /dev/null
      export CORPORATE_ENV=production
      export DEFAULT_REGION=us-east-1
      ENVVARS
      ```

      这两种方式都可以。`$ENVRC` 更简单，也是大多数情况下推荐的做法。
    </Tip>
  </Accordion>

  <Accordion title="区域设置和时区">
    默认基础镜像的区域设置可能有误。请配置区域设置和时区，以避免构建工具、Java、Python 和 Git 发出警告。

    ```yaml theme={null}
    initialize:
      - name: Configure locale and timezone
        run: |
          sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq locales

          # 生成并设置语言环境
          sudo sed -i 's/^# *en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen
          sudo locale-gen
          sudo update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8

          cat << 'LOCALE' | sudo tee /etc/profile.d/locale.sh > /dev/null
          export LANG="en_US.UTF-8"
          export LC_ALL="en_US.UTF-8"
          LOCALE

          # 设置时区
          sudo timedatectl set-timezone UTC 2>/dev/null || \
            sudo ln -sfn /usr/share/zoneinfo/UTC /etc/localtime
    ```
  </Accordion>

  <Accordion title="资源限制（ulimits）">
    Java、Gradle 和 Node.js 构建经常会达到默认的 1024 个打开文件数限制。请调高该限制，以避免构建失败。

    ```yaml theme={null}
    initialize:
      - name: Raise resource limits
        run: |
          cat << 'LIMITS' | sudo tee /etc/security/limits.d/99-devin.conf > /dev/null
          *    soft    nofile    65536
          *    hard    nofile    65536
          *    soft    nproc     65536
          *    hard    nproc     65536
          LIMITS

          # 同时设置内核最大值
          echo "fs.file-max = 65536" | sudo tee /etc/sysctl.d/99-devin-filemax.conf > /dev/null
          sudo sysctl -p /etc/sysctl.d/99-devin-filemax.conf 2>/dev/null || true
    ```
  </Accordion>

  <Accordion title="替换 APT 镜像源">
    在隔离或受限环境中，将默认的 Ubuntu APT 软件源替换为内部镜像源。

    <Accordion title="必需的 secrets">- `APT_MIRROR_URL` — 你的内部 APT 镜像源的 URL (例如：`https://artifactory.example.com/artifactory/ubuntu-remote`) </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Replace APT sources with internal mirror
        run: |
          # 备份原始源
          sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

          # 将所有 Ubuntu 镜像替换为你的内部镜像
          sudo sed -i "s|http://archive.ubuntu.com/ubuntu|$APT_MIRROR_URL|g" /etc/apt/sources.list
          sudo sed -i "s|http://security.ubuntu.com/ubuntu|$APT_MIRROR_URL|g" /etc/apt/sources.list

          sudo apt-get update -qq
    ```

    <Tip>
      常见的 APT 镜像 URL 格式：

      * **Artifactory:** `https://artifactory.example.com/artifactory/ubuntu-remote`
      * **Nexus:** `https://nexus.example.com/repository/ubuntu-proxy`
    </Tip>
  </Accordion>
</AccordionGroup>

***

<div id="advanced-patterns">
  ## 高级用法
</div>

<AccordionGroup>
  <Accordion title="使用 direnv 管理环境变量">
    Devin 的基础环境中已包含 [direnv](https://direnv.net/)。使用 `initialize` 创建 `.envrc` 文件后，Direnv 会自动加载它们。

    ```yaml theme={null}
    initialize: |
      cat <<'EOF' > .envrc
      export DATABASE_URL=postgresql://localhost:5432/myapp_dev
      export REDIS_URL=redis://localhost:6379
      export APP_ENV=development
      EOF

    maintenance: |
      direnv allow .
    ```

    <Info>direnv 已预先集成到 Devin 的 shell 中，因此 `.envrc` 变量会自动加载。无需手动执行 source。</Info>

    <Tip>
      对于**敏感**环境变量 (API key、令牌、数据库密码) ，请使用 [repo secrets](/zh/product-guides/secrets)，而不是 `.envrc` 文件。repo secrets 会被安全存储，并在会话开始时注入。它们绝不会出现在你的 blueprint 或快照中。
    </Tip>
  </Accordion>

  <Accordion title="按仓库切换 Node 版本">
    使用 nvm (已预装) 通过 `.nvmrc` 为每个仓库切换 Node.js 版本。

    ```yaml theme={null}
    initialize: |
      nvm install 18
      nvm install 20
      nvm install 22

    maintenance: |
      nvm use
    ```

    <Info>
      `nvm use` 会从仓库根目录读取 `.nvmrc`。请确保你的仓库中包含该文件 (例如内容为 `20`) 。
    </Info>
  </Accordion>

  <Accordion title="浏览器身份验证（Playwright）">
    在**会话期间**，Devin 会提供一个 Chrome 浏览器，其 CDP 端点为 `localhost:29229`。使用 Playwright 脚本可自动完成基于浏览器的登录。

    <Warning>
      该浏览器仅在会话期间可用，不适用于快照构建。请在 `initialize` 中安装 Playwright，并将登录脚本保存在你的代码仓库中。
    </Warning>

    ```yaml theme={null}
    initialize: |
      pip install playwright
      playwright install chromium

    maintenance: |
      npm install

    knowledge:
      - name: browser-auth
        contents: |
          This project requires browser authentication.
          Run the login script before interacting with the app:
          python scripts/login.py

          Devin's Chrome browser is accessible via CDP at:
          http://localhost:29229
    ```

    示例登录脚本 (`scripts/login.py`) ：

    ```python theme={null}
    from playwright.sync_api import sync_playwright
    import os

    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp("http://localhost:29229")
        context = browser.contexts[0]
        page = context.pages[0] if context.pages else context.new_page()

        page.goto("https://internal-tool.example.com/login")
        page.fill("#username", os.environ["TOOL_USERNAME"])
        page.fill("#password", os.environ["TOOL_PASSWORD"])
        page.click('button[type="submit"]')
        page.wait_for_url("**/dashboard")
    ```

    <Warning>
      将登录凭据以 secrets 的形式存储，不要写入源代码。对于需要长期保持的身份验证，请将登录脚本提交到 `.agents/skills/`，以便 Devin 自动重新验证身份。
    </Warning>
  </Accordion>

  <Accordion title="自定义系统工具与 PATH">
    在 `initialize` 中安装系统软件包、自定义二进制文件，并配置 PATH。

    ```yaml theme={null}
    initialize:
      - name: Install system packages
        run: |
          apt-get update
          apt-get install -y \
            jq \
            ripgrep \
            fd-find \
            protobuf-compiler \
            libssl-dev

      - name: Install custom CLI tool
        run: |
          curl -L https://github.com/example/tool/releases/download/v1.0/tool-linux-amd64 \
            -o /usr/local/bin/mytool
          chmod +x /usr/local/bin/mytool

      - name: Add custom bin directory to PATH
        run: |
          mkdir -p ~/bin
          echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc

    knowledge:
      - name: tools
        contents: |
          Custom tools available:
          - mytool: installed at /usr/local/bin/mytool
          - Additional binaries can be placed in ~/bin
    ```
  </Accordion>

  <Accordion title="使用 GitHub Actions 设置工具">
    Devin 支持在蓝图中直接运行基于 Node.js 的 GitHub Actions。这对于通过 CI 所使用的同一套 actions 安装特定版本的工具非常有用。

    ```yaml theme={null}
    initialize:
      - name: "Install Node.js 20"
        uses: github.com/actions/setup-node@v4
        with:
          node-version: "20"

      - name: "Install Python 3.12"
        uses: github.com/actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: "Install Go 1.22"
        uses: github.com/actions/setup-go@v5
        with:
          go-version: "1.22"

      - name: "Install Java 21"
        uses: github.com/actions/setup-java@v4
        with:
          java-version: "21"
          distribution: "temurin"

      - name: "Install Gradle"
        uses: github.com/gradle/actions/setup-gradle@v4

      - name: "Install Ruby 3.3"
        uses: github.com/ruby/setup-ruby@v1
        with:
          ruby-version: "3.3"

      - name: "Install additional tools"
        run: |
          pip install uv
          npm install -g pnpm turbo
          go install golang.org/x/tools/gopls@latest

    maintenance: |
      echo "All runtimes are available:"
      node --version
      python --version
      go version
      java --version
    ```

    <Info>
      `setup-node` 和 `setup-python` 这类 action 会修改 PATH 和环境变量。某个 action 安装的二进制文件在后续所有步骤以及 `maintenance` 中都可用。仅支持**基于 Node.js 的** GitHub Actions。不支持复合型 action 和基于 Docker 的 action。
    </Info>

    <Tip>
      你无需依赖 GitHub Actions 来完成基础工具设置。直接使用 shell 命令 (`nvm install 20`、`curl ... | sh`、`apt-get install`) 同样有效，而且通常更简单。只有在你希望与 CI 配置完全一致，或者需要 `setup-java` 这类可处理多个发行版的 action 所带来的便利时，GitHub Actions 才更有用。
    </Tip>
  </Accordion>

  <Accordion title="多个应用的本地 HTTPS 反向代理">
    通过 HTTPS 使用看起来像真实域名的主机名在后端运行多个服务，例如 `app.example.com`、`api.example.com` 和 `admin.example.com`。在 `initialize` 中安装一个反向代理，并将每个主机名路由到不同的本地上游端口。

    [Caddy](https://caddyserver.com/) 可在一个工具中同时处理路由和本地 TLS。Caddyfile 会将每个主机名映射到一个上游，而 `tls internal` 会通过 Caddy 内置的 CA 为每个主机名自动签发受信任的证书。`caddy trust` 会将该 CA 根证书安装到系统信任存储中，再将同一个根证书添加到 NSS 数据库后，浏览器也会信任它。

    通过蓝图编辑器中的 **文件附件** 部分上传你的 Caddyfile；之后即可通过 `$FILE_CADDYFILE` 使用它。

    ```caddyfile Caddyfile theme={null}
    app.example.com {
      tls internal
      reverse_proxy 127.0.0.1:3000
    }

    api.example.com {
      tls internal
      reverse_proxy 127.0.0.1:3001
    }

    admin.example.com {
      tls internal
      reverse_proxy 127.0.0.1:3002
    }
    ```

    ```yaml theme={null}
    initialize:
      - name: Install Caddy and NSS tools
        run: |
          sudo apt-get install -y debian-keyring debian-archive-keyring \
            apt-transport-https curl libnss3-tools
          curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
            | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
          curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
            | sudo tee /etc/apt/sources.list.d/caddy-stable.list
          sudo apt-get update -qq
          sudo apt-get install -y caddy

      - name: Install Caddyfile, hosts entries, and trust internal CA
        run: |
          sudo install -m 0644 "$FILE_CADDYFILE" /etc/caddy/Caddyfile

          for host in app.example.com api.example.com admin.example.com; do
            grep -q " $host$" /etc/hosts \
              || echo "127.0.0.1 $host" | sudo tee -a /etc/hosts > /dev/null
          done

          sudo systemctl enable --now caddy
          sudo caddy trust

          mkdir -p ~/.pki/nssdb
          [ -f ~/.pki/nssdb/cert9.db ] \
            || certutil -d sql:$HOME/.pki/nssdb -N --empty-password

          CADDY_ROOT=/var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt
          for _ in $(seq 1 30); do
            sudo test -f "$CADDY_ROOT" && break
            sleep 1
          done

          if sudo test -f "$CADDY_ROOT" \
             && ! certutil -d sql:$HOME/.pki/nssdb -L | grep -q "Caddy Local Authority"; then
            sudo install -o "$USER" -m 0644 "$CADDY_ROOT" /tmp/caddy-root.crt
            certutil -d sql:$HOME/.pki/nssdb -A -t "C,," \
              -n "Caddy Local Authority" -i /tmp/caddy-root.crt
            rm -f /tmp/caddy-root.crt
          fi
    ```

    <Info>
      `/etc/hosts` 循环会让 `app.example.com` 在会话内解析到 `127.0.0.1`。你在 Caddyfile 中写入的每个 hostname，都要在其中添加一条记录。
    </Info>

    <Tip>
      要添加一个服务，请在 Caddyfile 中追加一个三行代码块，并在 `/etc/hosts` 循环中添加一条记录，然后通过 HTTPS 访问对应的 hostname。Caddy 会在首次请求时签发证书，因此无需为每个 app 单独生成证书。
    </Tip>
  </Accordion>
</AccordionGroup>

***

<div id="full-stack-examples">
  ## 全栈示例
</div>

这些示例展示了企业级和组织级配置如何组合使用。实际使用时，通常会按不同作用域拆分。这里将它们集中展示，供参考。

<AccordionGroup>
  <Accordion title="企业全栈（Artifactory）">
    一个完整的企业环境示例：企业 CA 证书、代理、Java (Maven)、Python (pip/uv)、Node.js (npm) 和 Docker，全部指向同一个 Artifactory 实例。

    <Accordion title="必需的 secrets">
      **网络与信任 (账户级) ：**

      * `CORP_ROOT_CA_B64` — 经过 Base64 编码的企业 CA 证书
      * `CORP_HTTP_PROXY` — HTTP 代理 URL
      * `CORP_HTTPS_PROXY` — HTTPS 代理 URL
      * `CORP_NO_PROXY` — 不走代理的主机

      **注册表凭据 (组织级) ：**

      * `ARTIFACTORY_USER` — Artifactory 用户名
      * `ARTIFACTORY_TOKEN` — Artifactory API 令牌或密码
      * `ARTIFACTORY_MAVEN_URL` — Maven 仓库 URL (例如，`https://artifactory.example.com/artifactory/maven-virtual`)
      * `ARTIFACTORY_PYPI_URL` — PyPI 仓库 URL (例如，`https://user:token@artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple`)
      * `ARTIFACTORY_NPM_URL` — npm 仓库 URL (例如，`https://artifactory.example.com/artifactory/api/npm/npm-virtual`)
      * `ARTIFACTORY_DOCKER_URL` — Docker 注册表 URL (例如，`artifactory.example.com`)
    </Accordion>

    这通常会拆分为三个作用域：

    * **账户级 (`initialize`)：** 证书与代理
    * **组织级 (`initialize`)：** 语言运行时安装
    * **全组织范围 (`maintenance`)：** 注册表凭据 (在构建过程中刷新，并在会话开始时提供给 Agent)

    以下为合并后的完整参考示例：

    ```yaml theme={null}
    initialize:
      # ── 账户级：网络与信任 ──────────────────────────────────────

      - name: Install corporate CA certificate
        run: |
          echo "$CORP_ROOT_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-root-ca.crt > /dev/null
          sudo update-ca-certificates
          echo 'export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root-ca.crt' \
            | sudo tee /etc/profile.d/node-ca.sh > /dev/null

      - name: Configure system-wide proxy
        run: |
          cat << 'PROXY' | sudo tee /etc/profile.d/proxy.sh > /dev/null
          export http_proxy="$CORP_HTTP_PROXY"
          export https_proxy="$CORP_HTTPS_PROXY"
          export no_proxy="$CORP_NO_PROXY"
          export HTTP_PROXY="$CORP_HTTP_PROXY"
          export HTTPS_PROXY="$CORP_HTTPS_PROXY"
          export NO_PROXY="$CORP_NO_PROXY"
          PROXY
          source /etc/profile.d/proxy.sh

      # ── Org-wide：语言运行时 ──────────────────────────────────────────

      - name: Install JDK 17 + Maven
        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

          MAVEN_VERSION=3.9.9
          curl -fsSL "https://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" \
            | sudo tar -xz -C /opt
          sudo ln -sf /opt/apache-maven-${MAVEN_VERSION}/bin/mvn /usr/local/bin/mvn

      - name: Install uv
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance:
      # ── 账户级：git 代理（每次会话刷新）───────────────────

      - name: Configure git proxy
        run: |
          git config --global http.proxy "$CORP_HTTP_PROXY"
          git config --global https.proxy "$CORP_HTTPS_PROXY"

      # ── Org-wide：注册表凭据（每次会话刷新）──────────────────

      - name: Configure Maven → Artifactory
        run: |
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <mirrors>
              <mirror>
                <id>artifactory</id>
                <mirrorOf>*</mirrorOf>
                <url>$ARTIFACTORY_MAVEN_URL</url>
              </mirror>
            </mirrors>
            <servers>
              <server>
                <id>artifactory</id>
                <username>$ARTIFACTORY_USER</username>
                <password>$ARTIFACTORY_TOKEN</password>
              </server>
            </servers>
          </settings>
          EOF

      - name: Configure pip/uv → Artifactory PyPI
        run: |
          mkdir -p ~/.config/pip
          cat > ~/.config/pip/pip.conf << EOF
          [global]
          index-url = $ARTIFACTORY_PYPI_URL
          trusted-host = $(echo "$ARTIFACTORY_PYPI_URL" | sed 's|https\?://||;s|/.*||')
          EOF

          echo "export UV_INDEX_URL=$ARTIFACTORY_PYPI_URL" \
            | sudo tee /etc/profile.d/uv-registry.sh > /dev/null

      - name: Configure npm → Artifactory
        run: |
          npm config set registry "$ARTIFACTORY_NPM_URL"
          REGISTRY_HOST=$(echo "$ARTIFACTORY_NPM_URL" | sed 's|https\?://||;s|/.*||')
          npm config set "//${REGISTRY_HOST}/:_authToken" "$ARTIFACTORY_TOKEN"

      - name: Configure Docker → Artifactory
        run: |
          echo "$ARTIFACTORY_TOKEN" | docker login "$ARTIFACTORY_DOCKER_URL" \
            --username "$ARTIFACTORY_USER" \
            --password-stdin
    ```

    <Info>
      在此示例中，所有仓库都指向同一个 Artifactory 实例，但 URL 路径不同。每种软件包生态系统都有各自的端点格式。即使是同一个仓库，Maven、PyPI、npm 和 Docker 的 URL 也各不相同。
    </Info>
  </Accordion>

  <Accordion title="多语言与不同制品库">
    当不同语言分别使用不同的私有仓库时 (例如，Maven 使用 Nexus，npm 使用 GitHub Packages，Python 使用 Artifactory) 。

    <Accordion title="必需的 secrets">
      * `NEXUS_MAVEN_URL` — Nexus Maven 仓库 URL
      * `NEXUS_USER` — Nexus 用户名
      * `NEXUS_PASS` — Nexus 密码
      * `GITHUB_PACKAGES_TOKEN` — 具有 `read:packages` 作用域的 GitHub 个人访问令牌
      * `ARTIFACTORY_USER` — Artifactory 用户名
      * `ARTIFACTORY_TOKEN` — Artifactory API 令牌
      * `GIT_TOKEN` — 用于 Go 私有模块的个人访问令牌
    </Accordion>

    ```yaml theme={null}
    maintenance:
      # Maven → Nexus
      - name: Configure Maven → Nexus
        run: |
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <mirrors>
              <mirror>
                <id>nexus</id>
                <mirrorOf>*</mirrorOf>
                <url>$NEXUS_MAVEN_URL</url>
              </mirror>
            </mirrors>
            <servers>
              <server>
                <id>nexus</id>
                <username>$NEXUS_USER</username>
                <password>$NEXUS_PASS</password>
              </server>
            </servers>
          </settings>
          EOF

      # npm → GitHub Packages（作用域限定）
      - name: Configure npm → GitHub Packages
        run: |
          npm config set @myorg:registry https://npm.pkg.github.com
          npm config set //npm.pkg.github.com/:_authToken $GITHUB_PACKAGES_TOKEN

      # Python → Artifactory
      - name: Configure pip → Artifactory
        run: |
          mkdir -p ~/.config/pip
          cat > ~/.config/pip/pip.conf << EOF
          [global]
          index-url = https://$ARTIFACTORY_USER:$ARTIFACTORY_TOKEN@artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple
          EOF

      # Go → 通过 git 使用私有模块
      - name: Configure Go private modules
        run: |
          git config --global url."https://$GIT_TOKEN@github.com/myorg/".insteadOf "https://github.com/myorg/"
    ```
  </Accordion>

  <Accordion title="使用私有镜像源的气隙环境">
    在完全气隙环境中，Devin 无法访问任何公共 URL。所有工具、运行时和软件包都必须来自内部镜像源。

    <Accordion title="必需的 secrets">
      **证书：**

      * `CORP_ROOT_CA_B64` — Base64 编码的企业 CA 证书

      **镜像访问：**

      * `APT_MIRROR_URL` — 内部 Ubuntu APT 镜像 URL
      * `MIRROR_USER` — 用于镜像身份验证的用户名
      * `MIRROR_PASS` — 用于镜像身份验证的密码
      * `JDK_TARBALL_URL` — 从内部镜像下载 JDK tarball 的 URL
      * `NODE_TARBALL_URL` — 从内部镜像下载 Node.js tarball 的 URL

      **软件包注册表：**

      * `INTERNAL_MAVEN_URL` — 内部 Maven 注册表 URL
      * `INTERNAL_NPM_URL` — 内部 npm 注册表 URL
      * `INTERNAL_PYPI_URL` — 内部 PyPI 注册表 URL
    </Accordion>

    ```yaml theme={null}
    initialize:
      - name: Install corporate CA certificate
        run: |
          echo "$CORP_ROOT_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-root-ca.crt > /dev/null
          sudo update-ca-certificates

      - name: Replace apt sources with internal mirror
        run: |
          sudo sed -i "s|http://archive.ubuntu.com/ubuntu|$APT_MIRROR_URL|g" /etc/apt/sources.list
          sudo sed -i "s|http://security.ubuntu.com/ubuntu|$APT_MIRROR_URL|g" /etc/apt/sources.list
          sudo apt-get update -qq

      - name: Install JDK from internal mirror
        run: |
          # 从内部制品库下载 JDK 压缩包
          curl -fsSL -u "$MIRROR_USER:$MIRROR_PASS" "$JDK_TARBALL_URL" \
            | sudo tar -xz -C /usr/local
          sudo ln -sf /usr/local/jdk-17.*/bin/java /usr/local/bin/java
          sudo ln -sf /usr/local/jdk-17.*/bin/javac /usr/local/bin/javac
          echo "export JAVA_HOME=$(ls -d /usr/local/jdk-17.*)" \
            | sudo tee /etc/profile.d/java.sh > /dev/null

      - name: Install Node.js from internal mirror
        run: |
          curl -fsSL -u "$MIRROR_USER:$MIRROR_PASS" "$NODE_TARBALL_URL" \
            | sudo tar -xz -C /usr/local --strip-components=1

    maintenance:
      - name: Configure all package managers for internal registry
        run: |
          # Maven
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <mirrors>
              <mirror>
                <id>internal</id>
                <mirrorOf>*</mirrorOf>
                <url>$INTERNAL_MAVEN_URL</url>
              </mirror>
            </mirrors>
            <servers>
              <server>
                <id>internal</id>
                <username>$MIRROR_USER</username>
                <password>$MIRROR_PASS</password>
              </server>
            </servers>
          </settings>
          EOF

          # npm
          npm config set registry "$INTERNAL_NPM_URL"

          # pip
          mkdir -p ~/.config/pip
          cat > ~/.config/pip/pip.conf << EOF
          [global]
          index-url = $INTERNAL_PYPI_URL
          EOF
    ```

    <Warning>
      在气隙环境中，Devin 所需的所有工具 (语言运行时、CLI 工具等) 都必须能从你的内部镜像源获取。公共软件仓库和下载站点均无法访问。
    </Warning>
  </Accordion>

  <Accordion title="VPN + 证书 + 代理 + 编程语言">
    一个综合性的企业级配置示例，集成了 VPN 连接、证书、代理及多语言支持。以下是推荐的操作顺序。

    <Accordion title="必需的 secrets">
      **VPN：**

      * `VPN_CONFIG_B64` — Base64 编码的 OpenVPN 配置文件

      **网络与信任：**

      * `CORP_ROOT_CA_B64` — Base64 编码的企业 CA 证书
      * `CORP_HTTP_PROXY` — HTTP 代理 URL
      * `CORP_HTTPS_PROXY` — HTTPS 代理 URL
      * `CORP_NO_PROXY` — 无需通过代理的主机

      **注册表凭据：**

      * `MAVEN_REGISTRY_URL` — Maven 注册表 URL
      * `NPM_REGISTRY_URL` — npm 注册表 URL
      * `PYPI_REGISTRY_HOST` — PyPI 注册表主机名
      * `REGISTRY_USER` — 注册表用户名 (用于 Maven 和 pip)
      * `REGISTRY_PASS` — 注册表密码 (用于 Maven 和 pip)
      * `REGISTRY_TOKEN` — npm 身份验证令牌
    </Accordion>

    ```yaml theme={null}
    initialize:
      # 1. VPN — 必须首先执行，以确保内部资源可访问
      - name: Establish VPN connection
        run: |
          sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openvpn
          sudo mkdir -p /etc/openvpn/client
          echo "$VPN_CONFIG_B64" | base64 -d \
            | sudo tee /etc/openvpn/client/corp.conf > /dev/null
          sudo systemctl daemon-reload
          sudo systemctl enable --now openvpn-client@corp
          for i in $(seq 1 30); do
            if ip link show tun0 >/dev/null 2>&1; then break; fi
            sleep 1
          done

      # 2. DNS — 解析内部主机名
      - name: Configure DNS
        run: |
          sudo mkdir -p /etc/systemd/resolved.conf.d
          cat << 'DNS' | sudo tee /etc/systemd/resolved.conf.d/corp.conf > /dev/null
          [Resolve]
          DNS=10.0.0.53
          Domains=corp.internal
          DNS
          sudo systemctl restart systemd-resolved || true

      # 3. 证书 — 信任内部 CA
      - name: Install CA certificate
        run: |
          echo "$CORP_ROOT_CA_B64" | base64 -d \
            | sudo tee /usr/local/share/ca-certificates/corp-root-ca.crt > /dev/null
          sudo update-ca-certificates
          echo 'export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root-ca.crt' \
            | sudo tee /etc/profile.d/node-ca.sh > /dev/null

      # 4. 代理 — 通过企业代理路由流量
      - name: Configure proxy
        run: |
          cat << 'PROXY' | sudo tee /etc/profile.d/proxy.sh > /dev/null
          export http_proxy="$CORP_HTTP_PROXY"
          export https_proxy="$CORP_HTTPS_PROXY"
          export no_proxy="$CORP_NO_PROXY"
          export HTTP_PROXY="$CORP_HTTP_PROXY"
          export HTTPS_PROXY="$CORP_HTTPS_PROXY"
          export NO_PROXY="$CORP_NO_PROXY"
          PROXY
          source /etc/profile.d/proxy.sh

      # 5. 语言运行时
      - name: Install JDK 17
        run: |
          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 Node.js tooling
        run: npm install -g pnpm

      - name: Install uv
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance:
      - name: Configure git proxy
        run: |
          git config --global http.proxy "$CORP_HTTP_PROXY"
          git config --global https.proxy "$CORP_HTTPS_PROXY"

      - name: Configure Maven
        run: |
          mkdir -p ~/.m2
          cat > ~/.m2/settings.xml << EOF
          <settings>
            <mirrors>
              <mirror>
                <id>corp</id>
                <mirrorOf>*</mirrorOf>
                <url>$MAVEN_REGISTRY_URL</url>
              </mirror>
            </mirrors>
            <servers>
              <server>
                <id>corp</id>
                <username>$REGISTRY_USER</username>
                <password>$REGISTRY_PASS</password>
              </server>
            </servers>
          </settings>
          EOF

      - name: Configure npm
        run: |
          npm config set registry "$NPM_REGISTRY_URL"
          NPM_HOST=$(echo "$NPM_REGISTRY_URL" | sed 's|https\?://||;s|/.*||')
          npm config set "//${NPM_HOST}/:_authToken" "$REGISTRY_TOKEN"

      - name: Configure pip/uv
        run: |
          mkdir -p ~/.config/pip
          cat > ~/.config/pip/pip.conf << EOF
          [global]
          index-url = https://$REGISTRY_USER:$REGISTRY_PASS@${PYPI_REGISTRY_HOST}/simple
          EOF
          echo "export UV_INDEX_URL=https://$REGISTRY_USER:$REGISTRY_PASS@${PYPI_REGISTRY_HOST}/simple" \
            | sudo tee /etc/profile.d/uv-registry.sh > /dev/null
    ```

    <Info>
      **`initialize` 步骤的顺序很重要。** 必须先配置 VPN (这样才能访问内部主机) ，然后配置 DNS (这样名称才能正确解析) ，再配置证书 (这样 HTTPS 才能正常工作) ，接着配置代理 (这样流量才能正确转发) ，最后配置语言运行时环境 (它们可能会从内部镜像下载) 。
    </Info>
  </Accordion>
</AccordionGroup>

***

<div id="tips-for-writing-good-blueprints">
  ## 编写优质蓝图的技巧
</div>

* **先在会话中测试命令。** 在将命令添加到蓝图之前，先在 Devin 会话中手动运行一遍。这样比等待完整构建周期更快。
* **一次性安装的工具用 `initialize`，依赖项用 `maintenance`。** 任何安装需要几分钟的内容 (编译器、大型二进制文件、全局工具) 都应放在 `initialize` 中。较快的依赖命令 (`npm install`、`uv sync`) 则应放在 `maintenance` 中。
* **让 `maintenance` 命令保持快速。** 尽量控制在 2 分钟以内。这些命令会在构建期间运行，并在会话开始时提供给 Agent。
* **环境变量请使用 `$ENVRC`。** 不要写入 `.bashrc` 或 `.profile`。`$ENVRC` 是在不同步骤和会话之间设置变量的受支持方式。
* **给步骤命名。** 使用带有 `name` 字段的展开形式后，构建日志中的失败会更容易定位。
* **monorepo 请使用 subshell。** `(cd packages/foo && npm install)` 会在 subshell 中运行，因此后续步骤不会受目录变更影响。
* **使用 `npm install`，不要使用 `npm ci`。** `npm ci` 会删除 `node_modules` 并从头重新安装，这对 `maintenance` 来说太慢。
* **敏感信息请使用 repo secrets。** 请在仓库蓝图编辑器的 **Secrets** 选项卡中进行配置，而不要将其硬编码到蓝图中。

有关语法细节，请参阅 [蓝图参考](/zh/onboard-devin/environment/blueprint-reference)。有关构建失败的故障排除，请参阅 [声明式配置 > 故障排除](/zh/onboard-devin/environment/blueprints#troubleshooting-builds)。
