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

# Vorlagenbibliothek

> Blueprints zum Kopieren und Einfügen für gängige Programmiersprachen, private Registries und Enterprise-Infrastruktur.

Blueprints zum Kopieren und Einfügen für gängige Programmiersprachen und Anwendungsfälle. Jede Vorlage ist in sich abgeschlossen. Kombinieren Sie sie zu Ihrer vollständigen Konfiguration.

Eine vollständige Aufschlüsselung aller Felder finden Sie in der [Blueprint-Referenz](/de/onboard-devin/environment/blueprint-reference).

<Info>
  **Secrets:** Vorlagen referenzieren Secrets über `$SECRET_NAME`. Konfigurieren Sie diese im **Secrets**-Tab innerhalb jedes Blueprint-Editors, bevor Sie eine Vorlage verwenden. Hinterlegen Sie Zugangsdaten niemals hartcodiert in Ihrem Blueprint.
</Info>

***

<div id="quick-start">
  ## Schnellstart
</div>

Minimale Blueprints für die gängigsten Setups. Kopieren Sie einen, fügen Sie ihn in den Blueprint-Editor ein, und Sie sind fertig.

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

    maintenance: |
      pnpm install

    knowledge:
      - name: lint
        contents: |
          Führen Sie `pnpm lint` aus, um auf Fehler zu prüfen.
      - name: test
        contents: |
          Führen Sie `pnpm test` für die vollständige Testsuite aus.
    ```
  </Accordion>

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

    maintenance: |
      uv sync

    knowledge:
      - name: lint
        contents: |
          Führen Sie `uv run ruff check .` zum Linten aus.
      - name: test
        contents: |
          Führen Sie `uv run pytest` für die vollständige Testsuite aus.
    ```
  </Accordion>

  <Accordion title="Full-Stack (Node + Python)">
    ```yaml theme={null}
    initialize:
      - name: pnpm installieren
        run: npm install -g pnpm
      - name: Install uv
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

    maintenance:
      - name: Frontend-Abhängigkeiten
        run: (cd frontend && pnpm install)
      - name: Backend-Abhängigkeiten
        run: (cd backend && uv sync)

    knowledge:
      - name: structure
        contents: |
          - `frontend/` — React-App (pnpm)
          - `backend/` — Python-API (uv)
      - name: test
        contents: |
          Frontend: cd frontend && pnpm test
          Backend: cd backend && uv run pytest
    ```
  </Accordion>
</AccordionGroup>

***

<div id="repository-blueprints">
  ## Repository-Blueprints
</div>

Repo-spezifische Build-Schritte, Abhängigkeitsmanagement und Knowledge-Einträge. Lege diese unter **Settings > Environment > Blueprints > \[dein Repo]** fest.

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

<Tabs>
  <Tab title="uv (empfohlen)">
    Empfohlene Einrichtung für Python-Projekte, die [uv](https://docs.astral.sh/uv/) für die Abhängigkeitsverwaltung verwenden.

    ```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">
    Klassische Python-Einrichtung mit pip und venv. Verwenden Sie diese, wenn Ihr Projekt auf `requirements.txt` basiert.

    ```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">
    Standard-Node.js-Setup mit npm.

    ```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>
      Verwenden Sie in `maintenance` `npm install` (nicht `npm ci`). Dadurch wird ein inkrementelles Update durchgeführt, während `npm ci` `node_modules` löscht und in jeder Sitzung alles von Grund auf neu installiert.
    </Info>
  </Tab>

  <Tab title="pnpm">
    Für Projekte mit 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>

Standard-Setup für Go mit Modulen.

```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">
    Java-Einrichtung mit Gradle.

    <Info>JDK 17 ist **vorinstalliert** im Basis-Image von Devin. Überspringen Sie die JDK-Installation, wenn das Standard-OpenJDK 17 ausreicht.</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">
    Java-Einrichtung mit Maven.

    <Info>JDK 17 ist **vorinstalliert** im Basis-Image von Devin. Überspringen Sie die JDK-Installation, wenn das Standard-OpenJDK 17 ausreicht.</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>

Rails-Setup mit PostgreSQL.

```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>

Standardmäßiges Rust-Setup mit Cargo.

<Info>**Rust** (über rustup) und **Cargo** sind im Basis-Image von Devin **vorinstalliert**. Überspringen Sie den Installationsschritt, wenn die standardmäßige stabile Toolchain ausreicht. Sie müssen nur die Abhängigkeiten abrufen.</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">
  ### Monorepos
</div>

<Tabs>
  <Tab title="Mehrsprachig">
    Monorepo mit einem Node.js-Frontend und einem Python-Backend. Jedes Teilprojekt erhält eigene Knowledge-Einträge.

    ```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: |
          Dies ist ein Monorepo mit drei Packages:
          - `packages/frontend` — React-App (TypeScript, pnpm)
          - `packages/backend` — Python-API (FastAPI, uv)
          - `packages/shared` — Gemeinsame TypeScript-Utilities (muss vor dem Frontend gebaut werden)
      - name: frontend
        contents: |
          Führe `cd packages/frontend && pnpm dev` aus, um den Dev-Server zu starten.
          Führe `cd packages/frontend && pnpm lint` aus, um zu linten.
          Führe `cd packages/frontend && pnpm test` aus, um zu testen.
      - name: backend
        contents: |
          Führe `cd packages/backend && uv run uvicorn app.main:app --reload` aus, um die API zu starten.
          Führe `cd packages/backend && uv run ruff check .` aus, um zu linten.
          Führe `cd packages/backend && uv run pytest` aus, um zu testen.
    ```

    <Tip>
      Verwende Subshells `(cd dir && command)` anstelle von `cd dir && command`, damit das Arbeitsverzeichnis zwischen den Schritten zurückgesetzt wird.
    </Tip>
  </Tab>

  <Tab title="Mehrere JDK-Versionen">
    Java-Monorepo, in dem verschiedene Services unterschiedliche JDK-Versionen erfordern. Installiere bei der Einrichtung beide JDKs und verwende dann `knowledge`-Einträge, um Devin mitzuteilen, welches `JAVA_HOME` für welchen Service verwendet werden soll.

    ```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-Service bauen (JDK 17):
            JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
            cd services/api && ./gradlew clean build
      - name: build_legacy
        contents: |
          Legacy-Service bauen (JDK 11):
            JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
            cd services/legacy && ./gradlew clean build
      - name: test_all
        contents: |
          Tests für alle Services ausführen:
            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">
  ## Private Paket-Registries
</div>

Konfigurieren Sie Paketmanager so, dass sie Abhängigkeiten aus privaten Registries beziehen. Konfigurieren Sie dies unter **Settings > Environment > Blueprints > Org-wide setup** (oder pro Repo, wenn es nur für ein Repo benötigt wird).

<Warning>
  **Die Konfiguration von Zugangsdaten gehört in `maintenance`, nicht in `initialize`.** Schritte, die Secrets (Registry-Passwörter, Auth-Token) in Konfigurationsdateien schreiben, sollten `maintenance` verwenden, damit Zugangsdaten
  in jeder Sitzung neu geladen werden. Secrets werden entfernt, bevor der Snapshot gespeichert wird. Daher enthalten Konfigurationsdateien, die während `initialize` geschrieben werden, beim Start von Sitzungen keine gültigen Zugangsdaten.
</Warning>

<Info>
  Wenn Ihre private Registry eine Unternehmens-CA verwendet, stellen Sie sicher, dass das [CA-Zertifikat](#corporate-ca-certificate) zuerst auf Enterprise-Ebene installiert ist. Die folgende Konfiguration setzt voraus, dass das Vertrauen in HTTPS
  bereits hergestellt ist.
</Info>

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

<Tabs>
  <Tab title="npm (scoped)">
    Konfigurieren Sie npm so, dass Packages mit Scope (z. B. `@myorg/*`) aus einer privaten Registry bezogen werden, während öffentliche Packages weiterhin aus der Standard-npm-Registry kommen.

    <Accordion title="Erforderliche Secrets">- `GITHUB_PACKAGES_TOKEN` — Personal Access Token oder GitHub-App-Token mit dem Geltungsbereich `read:packages`</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>
      Ersetzen Sie `@myorg` durch Ihren npm-Scope. Gängige private Registry-URLs:

      * **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 (full mirror)">
    Leiten Sie **alle** npm-Packages über Ihre private Registry weiter (nicht nur Packages mit Scope).

    <Accordion title="Erforderliche Secrets">
      * `NPM_REGISTRY_URL` — Vollständige URL Ihrer npm-Registry (z. B. `https://artifactory.example.com/artifactory/api/npm/npm-virtual`) - `NPM_REGISTRY_HOST` — Nur der Hostname, ohne Protokoll (z. B.
        `artifactory.example.com`) - `REGISTRY_TOKEN` — npm-Auth-Token für die Registry
    </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">
    Konfigurieren Sie pnpm so, dass Packages aus einer privaten Registry bezogen werden.

    <Accordion title="Erforderliche Secrets">
      * `NPM_REGISTRY_URL` — Vollständige URL Ihrer npm-Registry - `NPM_REGISTRY_HOST` — Nur der Hostname, ohne Protokoll - `REGISTRY_TOKEN` — npm-Auth-Token für die Registry
    </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">
    Konfigurieren Sie Yarn (Classic v1 oder Berry v2+) so, dass Packages aus einer privaten Registry bezogen werden.

    <Accordion title="Erforderliche Secrets">- `NPM_REGISTRY_URL` — Vollständige URL Ihrer npm-/Yarn-Registry - `REGISTRY_TOKEN` — Auth-Token für die Registry (nur 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"
          # Für Packages mit Scope:
          # 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"
          # Für Packages mit Scope:
          # 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-Registries
</div>

<Tabs>
  <Tab title="pip / uv">
    Konfigurieren Sie pip und uv so, dass sie Pakete aus Ihrer privaten PyPI-Registry beziehen (z. B. Nexus, Artifactory).

    <Accordion title="Erforderliche Secrets">
      * `PYPI_REGISTRY_URL` — Vollständige URL Ihres PyPI-Index, einschließlich Anmeldedaten, falls erforderlich (z. B. `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>
      Gängige URL-Muster für PyPI-Registries:

      * **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">
    Konfigurieren Sie Poetry so, dass es Pakete aus einer privaten PyPI-Registry bezieht.

    <Accordion title="Erforderliche Secrets">
      * `POETRY_REGISTRY_URL` — Vollständige URL Ihrer PyPI-kompatiblen Registry - `REGISTRY_USER` — Registry-Nutzername - `REGISTRY_PASS` — Registry-Passwort oder API-Token
    </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-Registries
</div>

<Tabs>
  <Tab title="Maven">
    Installieren Sie das JDK und konfigurieren Sie Maven so, dass die Auflösung aller Abhängigkeiten über Ihre private Registry erfolgt (z. B. Artifactory, Nexus).

    <Info>JDK 17 ist auf Devins Basis-Image **vorinstalliert**. Überspringen Sie den Installationsschritt, wenn das Standard-OpenJDK 17 ausreicht. Sie benötigen nur die Maven-Installation und die Konfiguration der Registry.</Info>

    <Accordion title="Erforderliche Secrets">
      * `MAVEN_REGISTRY_URL` — URL Ihrer Maven-Registry (z. B. `https://artifactory.example.com/artifactory/maven-virtual`) - `REGISTRY_USER` — Registry-Nutzername - `REGISTRY_PASS` — Registry-Passwort oder
        API-Token
    </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>
      Gängige Registry-URL-Muster für Maven:

      * **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">
    Installieren Sie das JDK und konfigurieren Sie Gradle so, dass alle Abhängigkeiten über Ihr privates Registry bezogen werden.

    <Info>JDK 17 ist auf Devins Basis-Image **vorinstalliert**. Überspringen Sie den JDK-Installationsschritt, wenn der Standard ausreicht.</Info>

    <Accordion title="Erforderliche Secrets">- `GRADLE_REGISTRY_URL` — URL Ihres Gradle/Maven-Registrys - `REGISTRY_USER` — Registry-Nutzername - `REGISTRY_PASS` — Registry-Passwort oder API-Token</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">
  ### Weitere Registries
</div>

<AccordionGroup>
  <Accordion title="Go-Modul-Proxy">
    Installieren Sie Go und konfigurieren Sie es so, dass es Module über einen privaten Modul-Proxy bezieht (z. B. Athens, Artifactory oder einen `GOPROXY`-Endpunkt).

    <Accordion title="Erforderliche Secrets">
      * `GO_PROXY_URL` — URL Ihres Go-Modul-Proxys (z. B. `https://athens.corp.internal`)
      * `GIT_TOKEN` — Personal Access Token für private Git-Repos, die Go-Module bereitstellen
    </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>
      Gängige URL-Schemata für Go-Proxys:

      * **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">
    Konfigurieren Sie NuGet so, dass Pakete aus einem privaten Feed bezogen werden können.

    <Accordion title="Erforderliche Secrets">
      * `NUGET_SOURCE_URL` — URL Ihres NuGet-Feeds
      * `NUGET_API_KEY` — API key oder PAT für den Feed
    </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">
    Konfigurieren Sie Docker so, dass Images aus einer privaten Container-Registry abgerufen werden.

    <Accordion title="Erforderliche Secrets">
      * `DOCKER_MIRROR_URL` *(optional)* — URL Ihres Docker-Hub-Mirrors (z. B. `https://mirror.corp.internal`)
      * `DOCKER_REGISTRY_URL` — URL Ihrer privaten Container-Registry (z. B. `registry.corp.internal:5000`)
      * `DOCKER_REGISTRY_USER` — Registry-Nutzername
      * `DOCKER_REGISTRY_PASS` — Registry-Passwort oder API-Token
    </Accordion>

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

    maintenance:
      - name: Configure Docker for private registry
        run: |
          # Registry-Mirror konfigurieren (optional — leitet Docker-Hub-Pulls über Ihre Registry)
          cat << EOF | sudo tee /etc/docker/daemon.json > /dev/null
          {
            "registry-mirrors": ["$DOCKER_MIRROR_URL"]
          }
          EOF
          sudo systemctl restart docker || true

          # Bei der privaten Container-Registry anmelden
          echo "$DOCKER_REGISTRY_PASS" | docker login "$DOCKER_REGISTRY_URL" \
            --username "$DOCKER_REGISTRY_USER" \
            --password-stdin
    ```

    <Tip>
      Gängige Container-Registry-URLs:

      * **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">
    Konfigurieren Sie Cargo so, dass es Crates aus einer privaten Registry bezieht.

    <Info>
      **Rust** (über rustup) und **Cargo** sind im Basis-Image von Devin **vorinstalliert**. Überspringen Sie den Installationsschritt, wenn die stabile **Standard**-Toolchain ausreicht. Sie benötigen nur die Konfiguration der Registry.
    </Info>

    <Accordion title="Erforderliche Secrets">
      * `CARGO_REGISTRY_INDEX` — URL des Index einer privaten Registry (z. B. `sparse+https://cargo.corp.internal/api/v1/crates/`)
      * `CARGO_REGISTRY_TOKEN` — Auth-Token für die private 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>
      Wenn Sie nur eine private Registry **hinzufügen** möchten, ohne `crates.io` zu ersetzen, entfernen Sie die Abschnitte `[source.crates-io]` und `[source.private]` und verwenden Sie `cargo install --registry private` oder `[dependencies] my-crate = { version = "1.0", registry = "private" }` in `Cargo.toml`.
    </Tip>
  </Accordion>

  <Accordion title="Ruby / Bundler">
    Installieren Sie Ruby und konfigurieren Sie Bundler zum Beziehen von Gems von einem privaten Gem-Server.

    <Accordion title="Erforderliche Secrets">
      * `GEM_SERVER_URL` — URL Ihres privaten Gem-Servers (z. B. `https://artifactory.example.com/artifactory/api/gems/gems-virtual`)
      * `REGISTRY_USER` — Registry-Nutzername
      * `REGISTRY_PASS` — Registry-Passwort oder API-Token
    </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>
      Gängige URL-Schemata für Gem-Server:

      * **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">
    Installieren Sie PHP und konfigurieren Sie Composer so, dass Pakete aus einer privaten Packagist- oder Satis-Registry bezogen werden.

    <Accordion title="Erforderliche Secrets">
      * `COMPOSER_REGISTRY_URL` — URL Ihrer privaten Composer-Registry (z. B. `https://repo.packagist.com/<org>`)
      * `REGISTRY_USER` — Registry-Nutzername
      * `REGISTRY_PASS` — Registry-Passwort oder API-Token
    </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 installieren
          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"

          # Mit der Registry authentifizieren
          composer config --global http-basic.$(echo "$COMPOSER_REGISTRY_URL" \
            | sed 's|https\?://||;s|/.*||') "$REGISTRY_USER" "$REGISTRY_PASS"
    ```

    <Tip>
      Gängige URL-Muster für Composer-Registries:

      * **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="Aktualisierung des AWS-CodeArtifact-Tokens">
    AWS-CodeArtifact-Tokens laufen nach 12 Stunden ab. Verwenden Sie `maintenance`, um den Befehl zur Token-Aktualisierung einzurichten, damit der Agent ihn bei Bedarf erneut ausführen kann. Dieses Beispiel konfiguriert npm, pip und Maven für die Verwendung mit CodeArtifact.

    <Info>
      **`awscli` ist im Basis-Image von Devin vorinstalliert**. Sie benötigen nur die Token-Aktualisierung und die Registry-Konfiguration.
    </Info>

    <Accordion title="Erforderliche Secrets">
      * `AWS_ACCESS_KEY_ID` und `AWS_SECRET_ACCESS_KEY` — IAM-Anmeldedaten mit den Berechtigungen `codeartifact:GetAuthorizationToken` und `sts:GetServiceBearerToken`
      * `CA_DOMAIN` — Name Ihrer CodeArtifact-Domain
      * `CA_DOMAIN_OWNER` — AWS-Konto-ID, dem die Domain gehört
      * `CA_REGION` — AWS-Region (z. B. `us-east-1`)
      * `CA_NPM_REPO`, `CA_PYPI_REPO`, `CA_MAVEN_REPO` — Reponamen für jedes Ökosystem
    </Accordion>

    ```yaml theme={null}
    maintenance:
      - name: Refresh CodeArtifact auth token
        run: |
          # Neues Token abrufen (gültig für 12 Stunden)
          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 konfigurieren
          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 konfigurieren
          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 konfigurieren (optional)
          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">
  ## Enterprise-Infrastruktur
</div>

Infrastruktur auf Maschinenebene, die für alle Orgs und Repos gilt. Legen Sie diese in **Settings > Devin's base environment** (unternehmensweit) oder **Settings > Environment > Blueprints > Org-wide setup** (org-weit) fest.

<div id="network-and-connectivity">
  ### Netzwerk und Konnektivität
</div>

<AccordionGroup>
  <Accordion title="Unternehmens-CA-Zertifikat">
    Ihre Organisation verwendet eine private Zertifizierungsstelle für interne Dienste. Devin benötigt das Stammzertifikat, um interne Registries und Tools über HTTPS zu erreichen.

    <Accordion title="Erforderliche Secrets">- `CORP_ROOT_CA_B64` — Base64-kodiertes PEM-Zertifikat von der CA Ihres Unternehmens. erzeugen mit: `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="Mehrere CA-Zertifikate">
    Wenn Ihre Organisation mehrere CA-Zertifikate verwendet (z. B. separate CAs für unterschiedliche interne Dienste).

    <Accordion title="Erforderliche Secrets">- `CORP_ROOT_CA_B64` — Base64-codiertes primäres CA-Zertifikat - `CORP_INTERMEDIATE_CA_B64` — Base64-codiertes Zwischenzertifikat</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

          # Kombiniertes Bundle für Tools erstellen, die eine einzelne CA-Datei benötigen
          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-Proxy">
    Leiten Sie den gesamten Netzwerkverkehr über einen Unternehmensproxy.

    <Accordion title="Erforderliche Secrets">
      * `CORP_HTTP_PROXY` — HTTP-Proxy-URL (z. B. `http://proxy.corp.example.com:8080`) - `CORP_HTTPS_PROXY` — HTTPS-Proxy-URL - `CORP_NO_PROXY` — kommagetrennte Liste der Hosts, die den Proxy umgehen sollen (z. B.
        `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-Proxy konfigurieren
          npm config set proxy "$CORP_HTTP_PROXY"
          npm config set https-proxy "$CORP_HTTPS_PROXY"
    ```
  </Accordion>

  <Accordion title="Proxy mit Authentifizierung">
    Wenn Ihr Unternehmens-Proxy eine Authentifizierung per Nutzername/Passwort erfordert.

    <Accordion title="Erforderliche Secrets">
      * `PROXY_USER` — Proxy-Nutzername - `PROXY_PASS` — Proxy-Passwort - `PROXY_HOST` — Proxy-Hostname und -Port (z. B. `proxy.corp.example.com:8080`) - `CORP_NO_PROXY` — Hosts, für die der Proxy umgangen werden soll
    </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-Zertifikat + Proxy (kombiniert)">
    Kombinierte Einrichtung für Umgebungen, die sowohl eine Unternehmens-CA als auch einen Proxy benötigen. Das ist in Unternehmensumgebungen üblich, in denen interne Dienste private Zertifikate verwenden und der gesamte Datenverkehr über einen Proxy geleitet werden muss.

    <Accordion title="Erforderliche Secrets">
      * `CORP_ROOT_CA_B64` — Base64-kodiertes Unternehmens-CA-Zertifikat - `CORP_HTTP_PROXY`, `CORP_HTTPS_PROXY` — Proxy-URLs - `CORP_NO_PROXY` — Hosts, die den Proxy umgehen
    </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-Verbindung">
    Ihre privaten Registries, Git-Server oder anderen internen Dienste sind nur über ein VPN erreichbar. Dieses Modul muss **vor** anderen Modulen ausgeführt werden, die Netzwerkzugriff auf interne Ressourcen benötigen.

    <Accordion title="Erforderliche Secrets">
      **OpenVPN:**

      * `VPN_CONFIG_B64` — Base64-kodierte OpenVPN-Konfigurationsdatei (`.ovpn`). Erzeugen mit: `cat corp.ovpn | base64 -w0`
      * `VPN_AUTH_USER` *(optional)* — VPN-Nutzername, falls Ihr VPN eine Authentifizierung per Nutzername/Passwort erfordert
      * `VPN_AUTH_PASS` *(optional)* — VPN-Passwort

      **WireGuard:**

      * `WG_CONFIG_B64` — Base64-kodierte WireGuard-Konfigurationsdatei. Erzeugen mit: `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-Konfiguration schreiben
          sudo mkdir -p /etc/openvpn/client
          echo "$VPN_CONFIG_B64" | base64 -d \
            | sudo tee /etc/openvpn/client/corp.conf > /dev/null

          # Falls VPN Nutzername/Passwort-Authentifizierung erfordert
          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-Tunnel starten
          sudo systemctl daemon-reload
          sudo systemctl enable --now openvpn-client@corp

          # Warten, bis der Tunnel aktiv ist
          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-Konfiguration schreiben
          echo "$WG_CONFIG_B64" | base64 -d \
            | sudo tee /etc/wireguard/wg0.conf > /dev/null
          sudo chmod 600 /etc/wireguard/wg0.conf

          # Tunnel starten
          sudo systemctl enable --now wg-quick@wg0
    ```

    <Tip>
      Weitere Informationen zur VPN-Einrichtung finden Sie unter [VPN-Konfiguration](/de/onboard-devin/vpn).
    </Tip>
  </Accordion>

  <Accordion title="Benutzerdefinierte DNS-Namensauflösung">
    Ihre internen Dienste verwenden private DNS-Namen, die über öffentliches DNS nicht aufgelöst werden können.

    ```yaml theme={null}
    initialize:
      - name: Configure custom DNS resolution
        run: |
          # Interne Hostnamen hinzufügen
          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

          # Optional benutzerdefinierte Nameserver konfigurieren
          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">
  ### Identität und Sicherheit
</div>

<AccordionGroup>
  <Accordion title="GPG-Commit-Signierung">
    Ihre Organisation verlangt, dass alle Git-Commits signiert sind, und Sie möchten, dass GitHub Devins Commits als **Verified** kennzeichnet.

    <Accordion title="Erforderliche Secrets">
      * `GPG_PRIVATE_KEY_B64` — Base64-kodierter privater GPG-Schlüssel. Erzeugen mit: `gpg --export-secret-keys <key-id> | base64 -w0`
      * `GIT_USER_NAME` — Name des Git-Autors (z. B. `Devin AI`)
      * `GIT_USER_EMAIL` — E-Mail-Adresse des Git-Autors. **Muss mit einer UID im GPG-Schlüssel übereinstimmen**, sonst verifiziert GitHub die Signatur nicht.
    </Accordion>

    <Note>
      Laden Sie außerdem den zugehörigen **öffentlichen** Schlüssel in das GitHub-Konto hoch, dessen Anmeldedaten Devin zum Pushen verwendet (unter [GitHub Settings > SSH and GPG keys](https://github.com/settings/keys)). GitHub kennzeichnet Commits nur dann als Verified, wenn der öffentliche Signaturschlüssel für das Konto registriert ist, das den Commit erstellt hat.
    </Note>

    ```yaml theme={null}
    initialize:
      - name: Prepare GPG and git signing config
        run: |
          # GPG die Ausführung ohne TTY ermöglichen
          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-Identität und SSH-Schlüssel">
    Konfigurieren Sie Devins Git-Identität und SSH-Schlüssel für den Zugriff auf private Git-Server.

    <Accordion title="Erforderliche Secrets">
      * `GIT_USER_NAME` — Name des Git-Autors - `GIT_USER_EMAIL` — E-Mail-Adresse des Git-Autors - `SSH_PRIVATE_KEY_B64` — Base64-kodierter privater SSH-Schlüssel. Erzeugen mit: `cat ~/.ssh/id_ed25519 | base64 -w0` -
        `SSH_KNOWN_HOSTS_B64` — Base64-kodierte Known-Hosts-Einträge. Erzeugen mit: `ssh-keyscan git.corp.internal | base64 -w0` - `SSH_CONFIG_B64` *(optional)* — Base64-kodierte SSH-Konfigurationsdatei
    </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-Verzeichnis vorbereiten
          mkdir -p ~/.ssh && chmod 700 ~/.ssh

    maintenance:
      - name: Install SSH keys
        run: |
          # Privaten SSH-Schlüssel installieren (in maintenance, damit er in jeder Sitzung neu geladen wird)
          echo "$SSH_PRIVATE_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519

          # Known Hosts für Ihren Git-Server hinzufügen
          echo "$SSH_KNOWN_HOSTS_B64" | base64 -d >> ~/.ssh/known_hosts

          # Optional eine benutzerdefinierte SSH-Konfiguration installieren
          if [ -n "${SSH_CONFIG_B64:-}" ]; then
            echo "$SSH_CONFIG_B64" | base64 -d > ~/.ssh/config
            chmod 600 ~/.ssh/config
          fi
    ```

    <Tip>
      Erzeugen Sie den Known-Hosts-Eintrag für Ihren Git-Server mit `ssh-keyscan git.corp.internal | base64 -w0`.
    </Tip>
  </Accordion>
</AccordionGroup>

<div id="system-configuration">
  ### Systemkonfiguration
</div>

<AccordionGroup>
  <Accordion title="Systempakete">
    Installieren Sie Systempakete, die im Standard-Image von Devin nicht enthalten sind (z. B. native Bibliotheken für die Bildverarbeitung oder PDF-Generierung).

    ```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="Benutzerdefinierte Umgebungsvariablen">
    Legen Sie persistente Umgebungsvariablen fest, die in jeder Sitzung verfügbar sein sollen.

    Es wird empfohlen, Zeilen im Format `KEY=VALUE` in die Datei `$ENVRC` zu schreiben. In `$ENVRC` geschriebene Variablen werden automatisch für alle nachfolgenden Schritte und die Devin-Sitzung exportiert (ähnlich wie bei `$GITHUB_ENV` in GitHub Actions').

    ```yaml theme={null}
    initialize:
      - name: Benutzerdefinierte Umgebungsvariablen festlegen
        run: |
          echo "CORPORATE_ENV=production" >> $ENVRC
          echo "DEFAULT_REGION=us-east-1" >> $ENVRC
          echo "MAX_RETRIES=3" >> $ENVRC
    ```

    <Tip>
      Sie können Umgebungsvariablen auch in Skripte unter `/etc/profile.d/` schreiben, damit sie systemweit verfügbar sind:

      ```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
      ```

      Beide Ansätze funktionieren. `$ENVRC` ist jedoch einfacher und wird in den meisten Fällen empfohlen.
    </Tip>
  </Accordion>

  <Accordion title="Region und Zeitzone">
    Standard-Basis-Images können fehlerhafte Locale-Einstellungen aufweisen. Konfigurieren Sie Locale und Zeitzone, um Warnungen durch Build-Tools, Java, Python und Git zu vermeiden.

    ```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

          # Locale generieren und festlegen
          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

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

  <Accordion title="Ressourcenlimits (ulimits)">
    Java-, Gradle- und Node.js-Builds stoßen häufig an das Standardlimit von 1024 offenen Dateien. Erhöhen Sie es, um Build-Fehler zu vermeiden.

    ```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

          # Auch das Kernel-Maximum setzen
          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-Mirror ersetzen">
    In Air-Gap- oder eingeschränkten Umgebungen ersetzen Sie die Standard-Ubuntu-APT-Quellen durch einen internen Mirror.

    <Accordion title="Erforderliche Secrets">- `APT_MIRROR_URL` — URL Ihres internen APT-Mirrors (z. B. `https://artifactory.example.com/artifactory/ubuntu-remote`)</Accordion>

    ```yaml theme={null}
    initialize:
      - name: Replace APT sources with internal mirror
        run: |
          # Originale Quellen sichern
          sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

          # Alle Ubuntu-Mirrors durch den internen Mirror ersetzen
          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>
      Gängige URL-Muster für APT-Mirror:

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

***

<div id="advanced-patterns">
  ## Fortgeschrittene Nutzungsmuster
</div>

<AccordionGroup>
  <Accordion title="Umgebungsvariablen mit direnv">
    Devins Basisumgebung enthält [direnv](https://direnv.net/). Verwenden Sie `initialize`, um `.envrc`-Dateien zu erstellen. Direnv lädt sie automatisch.

    ```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 ist in Devins Shell bereits eingebunden, sodass `.envrc`-Variablen automatisch geladen werden. Ein manuelles Sourcing ist nicht nötig.
    </Info>

    <Tip>
      Für **vertrauliche** Umgebungsvariablen (API-Schlüssel, Tokens, Datenbankpasswörter) sollten Sie statt `.envrc`-Dateien [Repo-Secrets](/de/product-guides/secrets) verwenden. Repo-Secrets werden sicher gespeichert und beim Start der Sitzung eingebunden. Sie erscheinen niemals in Ihrem Blueprint oder Snapshot.
    </Tip>
  </Accordion>

  <Accordion title="Node-Version pro Repository wechseln">
    Verwenden Sie das vorinstallierte nvm, um Node.js-Versionen pro Repository über `.nvmrc` zu wechseln.

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

    maintenance: |
      nvm use
    ```

    <Info>
      `nvm use` liest die `.nvmrc` aus dem Stammverzeichnis des Repo. Stellen Sie sicher, dass Ihr Repository eine solche Datei enthält (z. B. mit `20` als Inhalt).
    </Info>
  </Accordion>

  <Accordion title="Authentifizierung im Browser (Playwright)">
    Devin stellt **während Sitzungen** einen Chrome-Browser mit einem CDP-Endpunkt unter `localhost:29229` bereit. Verwenden Sie Playwright-Skripte, um browserbasierte Anmeldungen zu automatisieren.

    <Warning>
      Der Browser ist nur während Sitzungen verfügbar, nicht in Snapshot-Builds. Installieren Sie Playwright in `initialize` und legen Sie die Anmeldeskripte in Ihrem Repo ab.
    </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
    ```

    Beispielskript für die Anmeldung (`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>
      Speichern Sie Zugangsdaten als Secrets, nicht im Quellcode. Für eine dauerhafte Authentifizierung committen Sie Login-Skripte in `.agents/skills/`, damit Devin sich automatisch erneut authentifizieren kann.
    </Warning>
  </Accordion>

  <Accordion title="Benutzerdefinierte System-Tools und PATH">
    Installieren Sie Systempakete, benutzerdefinierte Binärdateien und konfigurieren Sie den PATH in `initialize`.

    ```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 zum Einrichten von Tools">
    Devin unterstützt das direkte Ausführen von Node.js-basierten GitHub Actions in Blueprints. Das ist nützlich, um bestimmte Tool-Versionen über dieselben Actions zu installieren, die auch in Ihrer CI verwendet werden.

    ```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>
      Actions wie `setup-node` und `setup-python` ändern PATH und Umgebungsvariablen. Von einer Action installierte Binärdateien sind in allen nachfolgenden Schritten und in `maintenance` verfügbar. Nur
      **Node.js-basierte** GitHub Actions werden unterstützt. Composite- und Docker-basierte Actions werden nicht unterstützt.
    </Info>

    <Tip>
      Für die grundlegende Einrichtung von Tools benötigen Sie keine GitHub Actions. Direkte Shell-Befehle (`nvm install 20`, `curl ... | sh`, `apt-get install`) funktionieren genauso gut und sind oft einfacher. GitHub Actions sind besonders nützlich, wenn Sie Ihr CI-Setup exakt nachbilden möchten oder den Komfort von Actions wie `setup-java` nutzen wollen, die mehrere Distributionen unterstützen.
    </Tip>
  </Accordion>

  <Accordion title="Lokaler HTTPS-Reverse-Proxy für mehrere Apps">
    Betreiben Sie mehrere Services unter realistisch wirkenden Hostnames per HTTPS, etwa `app.example.com`, `api.example.com` und `admin.example.com`. Installieren Sie in `initialize` einen einzelnen Reverse-Proxy und leiten Sie jeden Hostname an einen anderen lokalen Upstream-Port weiter.

    [Caddy](https://caddyserver.com/) übernimmt Routing und lokales TLS in einem einzigen Tool. Eine Caddyfile weist jeden Hostname einem Upstream zu, und `tls internal` stellt automatisch für jeden Hostname ein vertrauenswürdiges Zertifikat von Caddys integrierter CA aus. `caddy trust` installiert dieses CA-Root im System-Trust-Store, und wenn Sie dasselbe Root auch zur NSS-Datenbank hinzufügen, akzeptiert es der Browser ebenfalls.

    Laden Sie Ihre Caddyfile über den Abschnitt **Dateianhänge** im Blueprint-Editor hoch; sie ist dann als `$FILE_CADDYFILE` verfügbar.

    ```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>
      Die `/etc/hosts`-Schleife sorgt dafür, dass `app.example.com` innerhalb der Sitzung auf `127.0.0.1` verweist. Fügen Sie für jeden Hostnamen, den Sie in die Caddyfile eintragen, einen Eintrag hinzu.
    </Info>

    <Tip>
      Um einen Service hinzuzufügen, hängen Sie einen dreizeiligen Block an die Caddyfile und fügen Sie der `/etc/hosts`-Schleife einen Eintrag hinzu. Rufen Sie dann den Hostnamen über HTTPS auf. Caddy stellt beim ersten Request ein Zertifikat aus, sodass keine Zertifikatserstellung pro App nötig ist.
    </Tip>
  </Accordion>
</AccordionGroup>

***

<div id="full-stack-examples">
  ## Full-Stack-Beispiele
</div>

Diese Beispiele zeigen, wie Konfigurationen auf Enterprise- und Org-Ebene zusammenwirken. In der Praxis würden Sie diese auf verschiedene Geltungsbereiche aufteilen. Sie sind hier der Übersicht halber zusammen dargestellt.

<AccordionGroup>
  <Accordion title="Kompletter Enterprise-Stack (Artifactory)">
    Eine vollständige Unternehmensumgebung: Unternehmens-CA-Zertifikat, Proxy, Java (Maven), Python (pip/uv), Node.js (npm) und Docker – alles auf eine einzige Artifactory-Instanz ausgerichtet.

    <Accordion title="Benötigte Secrets">
      **Netzwerk & Vertrauenseinstellungen (kontoweit):**

      * `CORP_ROOT_CA_B64` — Base64-kodiertes Unternehmens-CA-Zertifikat
      * `CORP_HTTP_PROXY` — HTTP-Proxy-URL
      * `CORP_HTTPS_PROXY` — HTTPS-Proxy-URL
      * `CORP_NO_PROXY` — Hosts, die den Proxy umgehen sollen

      **Registry-Anmeldedaten (organisationsweit):**

      * `ARTIFACTORY_USER` — Artifactory-Nutzername
      * `ARTIFACTORY_TOKEN` — Artifactory-API-Token oder Passwort
      * `ARTIFACTORY_MAVEN_URL` — Maven-Repository-URL (z. B. `https://artifactory.example.com/artifactory/maven-virtual`)
      * `ARTIFACTORY_PYPI_URL` — PyPI-Repository-URL (z. B. `https://user:token@artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple`)
      * `ARTIFACTORY_NPM_URL` — npm-Repository-URL (z. B. `https://artifactory.example.com/artifactory/api/npm/npm-virtual`)
      * `ARTIFACTORY_DOCKER_URL` — Docker-Registry-URL (z. B. `artifactory.example.com`)
    </Accordion>

    Dies würde typischerweise auf drei Geltungsbereiche verteilt:

    * **Kontoweit (`initialize`):** Zertifikat und Proxy
    * **Organisationsweit (`initialize`):** Installationen von Laufzeitumgebungen für Programmiersprachen
    * **Organisationsweit (`maintenance`):** Registry-Anmeldedaten (werden während der Builds aktualisiert und dem Agenten zu Sitzungsbeginn bereitgestellt)

    Hier zur Übersicht zusammengefasst:

    ```yaml theme={null}
    initialize:
      # ── Kontoweit: Netzwerk und Vertrauen ────────────────────────────────────

      - 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

      # ── Organisationsweit: Language Runtimes ─────────────────────────────────

      - 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:
      # ── Kontoweit: Git-Proxy (wird jede Sitzung aktualisiert) ──────────────

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

      # ── Organisationsweit: Registry-Anmeldedaten (werden jede Sitzung aktualisiert) ──

      - 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>
      In diesem Beispiel verweisen alle Registries auf dieselbe Artifactory-Instanz, verwenden jedoch unterschiedliche URL-Pfade. Jedes Paket-Ökosystem verwendet ein eigenes Endpunktformat. Maven-, PyPI-, npm- und Docker-URLs unterscheiden sich selbst bei derselben Registry.
    </Info>
  </Accordion>

  <Accordion title="Mehrsprachigkeit mit verschiedenen Registries">
    Wenn verschiedene Sprachen unterschiedliche private Registries verwenden (z. B. Maven über Nexus, npm über GitHub Packages, Python über Artifactory).

    <Accordion title="Erforderliche Secrets">
      * `NEXUS_MAVEN_URL` — URL des Nexus-Maven-Repositorys
      * `NEXUS_USER` — Nexus-Nutzername
      * `NEXUS_PASS` — Nexus-Passwort
      * `GITHUB_PACKAGES_TOKEN` — GitHub Personal Access Token mit dem Geltungsbereich `read:packages`
      * `ARTIFACTORY_USER` — Artifactory-Nutzername
      * `ARTIFACTORY_TOKEN` — Artifactory-API-Token
      * `GIT_TOKEN` — Personal Access Token für private Go-Module
    </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 (mit Scope)
      - 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 → private Module via 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="Air-Gap-Umgebung mit privaten Mirrors">
    In einer vollständig air-gapped Umgebung kann Devin keine öffentlichen URLs erreichen. Alle Tools, Laufzeitumgebungen und Pakete müssen aus internen Mirrorn stammen.

    <Accordion title="Erforderliche Secrets">
      **Zertifikate:**

      * `CORP_ROOT_CA_B64` — Base64-kodiertes Unternehmens-CA-Zertifikat

      **Mirror-Zugriff:**

      * `APT_MIRROR_URL` — URL des internen Ubuntu-APT-Mirrors
      * `MIRROR_USER` — Nutzername für die Authentifizierung am Mirror
      * `MIRROR_PASS` — Passwort für die Authentifizierung am Mirror
      * `JDK_TARBALL_URL` — URL zum Herunterladen des JDK-Tarballs vom internen Mirror
      * `NODE_TARBALL_URL` — URL zum Herunterladen des Node.js-Tarballs vom internen Mirror

      **Paket-Registries:**

      * `INTERNAL_MAVEN_URL` — URL der internen Maven-Registry
      * `INTERNAL_NPM_URL` — URL der internen npm-Registry
      * `INTERNAL_PYPI_URL` — URL der internen PyPI-Registry
    </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-Tarball aus internem Artefakt-Store herunterladen
          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>
      In Air-Gap-Umgebungen müssen alle Tools, die Devin benötigt (Laufzeitumgebungen, CLI-Tools usw.), auf Ihren internen Mirrors verfügbar sein. Öffentliche Registries und Downloadseiten sind nicht erreichbar.
    </Warning>
  </Accordion>

  <Accordion title="VPN + Zertifikate + Proxy + Sprachen">
    Ein umfassendes Enterprise-Setup, das VPN-Konnektivität mit Zertifikaten, Proxy und Mehrsprachigkeitsunterstützung kombiniert. Dies ist die empfohlene Vorgehensreihenfolge.

    <Accordion title="Benötigte Secrets">
      **VPN:**

      * `VPN_CONFIG_B64` — Base64-kodierte OpenVPN-Konfigurationsdatei

      **Netzwerk & Vertrauenseinstellungen:**

      * `CORP_ROOT_CA_B64` — Base64-kodiertes Unternehmens-CA-Zertifikat
      * `CORP_HTTP_PROXY` — HTTP-Proxy-URL
      * `CORP_HTTPS_PROXY` — HTTPS-Proxy-URL
      * `CORP_NO_PROXY` — Hosts, die den Proxy umgehen

      **Registry-Anmeldedaten:**

      * `MAVEN_REGISTRY_URL` — Maven-Registry-URL
      * `NPM_REGISTRY_URL` — npm-Registry-URL
      * `PYPI_REGISTRY_HOST` — PyPI-Registry-Hostname
      * `REGISTRY_USER` — Registry-Nutzername (für Maven und pip)
      * `REGISTRY_PASS` — Registry-Passwort (für Maven und pip)
      * `REGISTRY_TOKEN` — npm-Auth-Token
    </Accordion>

    ```yaml theme={null}
    initialize:
      # 1. VPN — muss zuerst kommen, damit interne Ressourcen erreichbar sind
      - 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 — interne Hostnamen auflösen
      - 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. Zertifikate — internen CAs vertrauen
      - 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. Proxy — Datenverkehr über den Unternehmens-Proxy leiten
      - 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. Sprachlaufzeitumgebungen
      - 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>
      **Die Reihenfolge ist bei `initialize`-Schritten wichtig.** Zuerst muss das VPN eingerichtet werden (damit interne Hosts erreichbar sind), dann DNS (damit Namen aufgelöst werden können), dann Zertifikate (damit HTTPS funktioniert), dann der Proxy (damit der Datenverkehr korrekt weitergeleitet wird) und schließlich Laufzeitumgebungen für Programmiersprachen (die möglicherweise aus internen Mirrors herunterladen).
    </Info>
  </Accordion>
</AccordionGroup>

***

<div id="tips-for-writing-good-blueprints">
  ## Tipps zum Schreiben guter Blueprints
</div>

* **Teste Befehle zuerst in einer Sitzung.** Führe Befehle manuell in einer Devin-Sitzung aus, bevor du sie deinem Blueprint hinzufügst. Das geht schneller, als auf einen vollständigen Build-Zyklus zu warten.
* **Verwende `initialize` für Tools, die nur einmal installiert werden, und `maintenance` für Abhängigkeiten.** Alles, was mehrere Minuten für die Installation braucht (Compiler, große Binärdateien, globale Tools), gehört in `initialize`. Schnelle Befehle für Abhängigkeiten (`npm install`, `uv sync`) gehören in `maintenance`.
* **Halte `maintenance`-Befehle schnell.** Sie sollten nach Möglichkeit in unter 2 Minuten laufen. Diese Befehle werden während Builds ausgeführt und dem Agenten zu Sitzungsbeginn angezeigt.
* **Verwende `$ENVRC` für Umgebungsvariablen.** Schreibe nicht in `.bashrc` oder `.profile`. `$ENVRC` ist der unterstützte Mechanismus, um Variablen schritt- und sitzungsübergreifend zu setzen.
* **Gib deinen Schritten Namen.** Die erweiterte Form mit `name`-Feldern macht Fehler in Build-Logs deutlich leichter auffindbar.
* **Verwende Subshells für Monorepos.** `(cd packages/foo && npm install)` läuft in einer Subshell, sodass nachfolgende Schritte nicht von der Verzeichnisänderung betroffen sind.
* **Verwende `npm install`, nicht `npm ci`.** `npm ci` löscht `node_modules` und installiert alles von Grund auf neu, was für `maintenance` zu langsam ist.
* **Verwende Repo-Secrets für sensible Werte.** Konfiguriere sie im **Secrets**-Tab des Blueprint-Editors des Repos, statt sie fest in Blueprints zu hinterlegen.

Details zur Syntax findest du in der [Blueprint-Referenz](/de/onboard-devin/environment/blueprint-reference). Hilfe zur Behebung von Build-Fehlern findest du unter [Deklarative Konfiguration > Fehlerbehebung](/de/onboard-devin/environment/blueprints#troubleshooting-builds).
