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

# Bibliothèque de modèles

> Blueprints prêts à l'emploi pour les langages courants, les registres privés et les infrastructures d’entreprise.

Des blueprints prêts à l'emploi pour les langages et cas d'usage courants. Chaque modèle est autonome. Combinez-les pour créer votre configuration complète.

Pour une présentation détaillée de chaque champ, consultez la [Référence des blueprints](/fr/onboard-devin/environment/blueprint-reference).

<Info>
  **Secrets :** Les modèles font référence à des secrets via `$SECRET_NAME`. Configurez-les dans l'onglet **Secrets** de chaque éditeur de blueprint avant d'utiliser un modèle. N'intégrez jamais d'identifiants en dur dans votre blueprint.
</Info>

***

<div id="quick-start">
  ## Démarrage rapide
</div>

Blueprints minimaux pour les configurations les plus courantes. Copiez-en un, collez-le dans l’éditeur de blueprints, et le tour est joué.

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

    maintenance: |
      pnpm install

    knowledge:
      - name: lint
        contents: |
          Exécutez `pnpm lint` pour vérifier les erreurs.
      - name: test
        contents: |
          Exécutez `pnpm test` pour lancer la suite complète.
    ```
  </Accordion>

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

    maintenance: |
      uv sync

    knowledge:
      - name: lint
        contents: |
          Exécutez `uv run ruff check .` pour lancer le lint.
      - name: test
        contents: |
          Exécutez `uv run pytest` pour lancer la suite complète.
    ```
  </Accordion>

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

    maintenance:
      - name: Dépendances frontend
        run: (cd frontend && pnpm install)
      - name: Dépendances backend
        run: (cd backend && uv sync)

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

***

<div id="repository-blueprints">
  ## Blueprints du dépôt
</div>

Étapes de build propres à chaque dépôt, gestion des dépendances et entrées Knowledge. Définissez-les dans **Settings > Environnement > Blueprints > \[votre dépôt]**.

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

<Tabs>
  <Tab title="uv (recommandé)">
    Configuration recommandée pour les projets Python qui utilisent [uv](https://docs.astral.sh/uv/) pour la gestion des dépendances.

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

    maintenance: |
      uv sync

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

          Correction automatique :
          uv run ruff check --fix .

      - name: test
        contents: |
          uv run pytest

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

  <Tab title="pip + venv">
    Configuration Python traditionnelle avec pip et venv. À utiliser lorsque votre projet s’appuie sur `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">
    Configuration Node.js par défaut avec 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>
      Utilisez `npm install` (et non `npm ci`) dans `maintenance`. Cette commande effectue une mise à jour incrémentielle, tandis que `npm ci` supprime `node_modules` et réinstalle tout depuis zéro à chaque session.
    </Info>
  </Tab>

  <Tab title="pnpm">
    Pour les projets qui utilisent 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>

Configuration Go standard avec modules.

```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">
    Configuration de Java avec Gradle.

    <Info>JDK 17 est **préinstallé** sur l’image de base de Devin. Ignorez l’étape d’installation du JDK si l’OpenJDK 17 par défaut vous convient.</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">
    Configuration de Java avec Maven.

    <Info>JDK 17 est **préinstallé** sur l’image de base de Devin. Ignorez l’étape d’installation du JDK si l’OpenJDK 17 par défaut vous convient.</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>

Configuration de Rails avec 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>

Configuration standard de Rust avec Cargo.

<Info>**Rust** (via rustup) et **Cargo** sont **préinstallés** sur l’image de base de Devin. Passez l’étape d’installation si la toolchain stable par défaut vous convient. Il vous suffit de récupérer les dépendances.</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="Multilingue">
    Monorepo avec un frontend Node.js et un backend Python. Chaque sous-projet a ses propres entrées Knowledge.

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

    maintenance:
      - name: Installer les dépendances du frontend
        run: (cd packages/frontend && pnpm install)
      - name: Installer les dépendances du backend
        run: (cd packages/backend && uv sync)
      - name: Build de la bibliothèque partagée
        run: (cd packages/shared && pnpm install && pnpm build)

    knowledge:
      - name: structure
        contents: |
          Il s'agit d'un monorepo avec trois packages :
          - `packages/frontend` — application React (TypeScript, pnpm)
          - `packages/backend` — API Python (FastAPI, uv)
          - `packages/shared` — utilitaires TypeScript partagés (doivent être buildés avant le frontend)
      - name: frontend
        contents: |
          Exécutez `cd packages/frontend && pnpm dev` pour démarrer le serveur de développement.
          Exécutez `cd packages/frontend && pnpm lint` pour lancer le linting.
          Exécutez `cd packages/frontend && pnpm test` pour lancer les tests.
      - name: backend
        contents: |
          Exécutez `cd packages/backend && uv run uvicorn app.main:app --reload` pour démarrer l'API.
          Exécutez `cd packages/backend && uv run ruff check .` pour lancer le linting.
          Exécutez `cd packages/backend && uv run pytest` pour lancer les tests.
    ```

    <Tip>
      Utilisez des subshells `(cd dir && command)` plutôt que `cd dir && command` afin que le répertoire de travail soit réinitialisé entre les étapes.
    </Tip>
  </Tab>

  <Tab title="Versions multiples de JDK">
    Monorepo Java dans lequel différents services nécessitent différentes versions de JDK. Installez les deux JDK lors de l'initialisation, puis utilisez les entrées `knowledge` pour indiquer à Devin quelle valeur de `JAVA_HOME` utiliser pour chaque service.

    ```yaml theme={null}
    initialize:
      - name: Installer JDK 17 (principal)
        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: Installer JDK 11 (service ancien)
        run: |
          sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-11-jdk-headless

    maintenance:
      - name: Préchauffer les caches de dépendances
        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: |
          Builder le service API (JDK 17) :
            JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
            cd services/api && ./gradlew clean build
      - name: build_legacy
        contents: |
          Builder le service ancien (JDK 11) :
            JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 \
            cd services/legacy && ./gradlew clean build
      - name: test_all
        contents: |
          Exécuter les tests pour tous les services :
            JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
            (cd services/api && ./gradlew test)

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

***

<div id="private-package-registries">
  ## Registres privés de packages
</div>

Configurez les gestionnaires de paquets pour résoudre les dépendances depuis des registres privés. Définissez cela dans **Settings > environnement > Blueprints > configuration à l’échelle de l’organisation** (ou au niveau du dépôt si un seul dépôt est concerné).

<Warning>
  **La configuration des identifiants doit se faire dans `maintenance`, pas dans `initialize`.** Les étapes qui écrivent des secrets (mots de passe de registre, jetons d’authentification) dans des fichiers de configuration doivent utiliser `maintenance` afin que les identifiants soient
  rechargés au début de chaque session. Les secrets sont supprimés avant l’enregistrement du snapshot, donc les fichiers de configuration écrits pendant `initialize` n’auront pas d’identifiants valides au démarrage.
</Warning>

<Info>
  Si votre registre privé utilise une autorité de certification d’entreprise, assurez-vous que le [certificat d’autorité de certification](#corporate-ca-certificate) est d’abord installé au niveau Enterprise. La configuration ci-dessous suppose que la confiance HTTPS est
  déjà établie.
</Info>

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

<Tabs>
  <Tab title="npm (scoped)">
    Configurez npm pour récupérer les packages d’un scope (par ex. `@myorg/*`) à partir d’un registre privé, tandis que les packages publics continuent de provenir du registre npm par défaut.

    <Accordion title="Secrets requis">- `GITHUB_PACKAGES_TOKEN` — Jeton d’accès personnel ou jeton GitHub App avec le périmètre `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>
      Remplacez `@myorg` par votre scope npm. URL courantes de registres privés :

      * **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 (miroir complet)">
    Faites passer **tous** les packages npm par votre registre privé (et pas seulement les packages d’un scope).

    <Accordion title="Secrets requis">
      * `NPM_REGISTRY_URL` — URL complète de votre registre npm (par ex. `https://artifactory.example.com/artifactory/api/npm/npm-virtual`) - `NPM_REGISTRY_HOST` — Nom d’hôte uniquement, sans protocole (par ex.
        `artifactory.example.com`) - `REGISTRY_TOKEN` — Jeton d’authentification npm pour le registre
    </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">
    Configurez pnpm pour récupérer des packages à partir d’un registre privé.

    <Accordion title="Secrets requis">
      * `NPM_REGISTRY_URL` — URL complète de votre registre npm - `NPM_REGISTRY_HOST` — Nom d’hôte uniquement, sans protocole - `REGISTRY_TOKEN` — Jeton d’authentification npm pour le registre
    </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">
    Configurez Yarn (Classic v1 ou Berry v2+) pour récupérer des packages à partir d’un registre privé.

    <Accordion title="Secrets requis">- `NPM_REGISTRY_URL` — URL complète de votre registre npm/Yarn - `REGISTRY_TOKEN` — Jeton d’authentification pour le registre (Berry uniquement)</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"
          # Pour les packages d’un 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"
          # Pour les packages d’un 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">
  ### Registres Python
</div>

<Tabs>
  <Tab title="pip / uv">
    Configurez pip et uv pour récupérer les packages depuis votre registre PyPI privé (p. ex., Nexus, Artifactory).

    <Accordion title="Secrets requis">
      * `PYPI_REGISTRY_URL` — URL complète de votre index PyPI, y compris les identifiants si nécessaire (p. ex., `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>
      URL courantes de registres PyPI :

      * **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">
    Configurez Poetry pour récupérer les packages depuis un registre PyPI privé.

    <Accordion title="Secrets requis">
      * `POETRY_REGISTRY_URL` — URL complète de votre registre compatible PyPI - `REGISTRY_USER` — Nom d’utilisateur du registre - `REGISTRY_PASS` — Mot de passe du registre ou jeton d’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">
  ### Registres JVM
</div>

<Tabs>
  <Tab title="Maven">
    Installez le JDK et configurez Maven pour faire transiter toute la résolution des dépendances par votre registre privé (p. ex. Artifactory, Nexus).

    <Info>Le JDK 17 est **préinstallé** sur l’image de base de Devin. Passez l’étape d’installation si l’OpenJDK 17 par défaut vous suffit. Vous n’avez besoin que de l’installation de Maven et de la configuration du registre.</Info>

    <Accordion title="Secrets requis">
      * `MAVEN_REGISTRY_URL` — URL de votre registre Maven (p. ex. `https://artifactory.example.com/artifactory/maven-virtual`) - `REGISTRY_USER` — Nom d’utilisateur du registre - `REGISTRY_PASS` — Mot de passe du registre ou
        jeton d’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>
      Schémas d’URL courants pour les registres 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">
    Installez le JDK et configurez Gradle pour résoudre l’ensemble des dépendances via votre registre privé.

    <Info>Le JDK 17 est **préinstallé** sur l’image de base de Devin. Ignorez l’étape d’installation du JDK si la version par défaut vous convient.</Info>

    <Accordion title="Secrets requis">- `GRADLE_REGISTRY_URL` — URL de votre registre Gradle/Maven - `REGISTRY_USER` — Nom d’utilisateur du registre - `REGISTRY_PASS` — Mot de passe du registre ou jeton d’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">
  ### Autres registres
</div>

<AccordionGroup>
  <Accordion title="Proxy de modules Go">
    Installez Go et configurez-le pour passer par un proxy de modules privé afin de résoudre les modules (par ex., Athens, Artifactory ou un endpoint GOPROXY).

    <Accordion title="Secrets requis">
      * `GO_PROXY_URL` — URL de votre proxy de modules Go (par ex., `https://athens.corp.internal`)
      * `GIT_TOKEN` — jeton d’accès personnel pour les dépôts Git privés qui hébergent des modules Go
    </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>
      Schémas d’URL courants pour les proxys Go :

      * **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">
    Configurez NuGet pour récupérer les packages depuis une source privée.

    <Accordion title="Secrets requis">
      * `NUGET_SOURCE_URL` — URL de votre source NuGet
      * `NUGET_API_KEY` — clé API ou PAT pour la source
    </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">
    Configurez Docker pour récupérer des images depuis un registre de conteneurs privé.

    <Accordion title="Secrets requis">
      * `DOCKER_MIRROR_URL` *(facultatif)* — URL de votre miroir Docker Hub (par exemple, `https://mirror.corp.internal`)
      * `DOCKER_REGISTRY_URL` — URL de votre registre de conteneurs privé (par exemple, `registry.corp.internal:5000`)
      * `DOCKER_REGISTRY_USER` — Nom d’utilisateur du registre
      * `DOCKER_REGISTRY_PASS` — Mot de passe du registre ou jeton d’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: |
          # Configurer le miroir de registre (facultatif — achemine les pulls Docker Hub via votre registre)
          cat << EOF | sudo tee /etc/docker/daemon.json > /dev/null
          {
            "registry-mirrors": ["$DOCKER_MIRROR_URL"]
          }
          EOF
          sudo systemctl restart docker || true

          # Se connecter au registre de conteneurs privé
          echo "$DOCKER_REGISTRY_PASS" | docker login "$DOCKER_REGISTRY_URL" \
            --username "$DOCKER_REGISTRY_USER" \
            --password-stdin
    ```

    <Tip>
      URL courantes des registres de conteneurs :

      * **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">
    Configurez Cargo pour utiliser des crates provenant d’un registre privé.

    <Info>
      **Rust** (via rustup) et **Cargo** sont **préinstallés** sur l’image de base de Devin. Passez l’étape d’installation si la toolchain stable par défaut suffit. Seule la configuration du registre est nécessaire.
    </Info>

    <Accordion title="Secrets requis">
      * `CARGO_REGISTRY_INDEX` — URL de l’index du registre privé (p. ex., `sparse+https://cargo.corp.internal/api/v1/crates/`)
      * `CARGO_REGISTRY_TOKEN` — jeton d’authentification pour le registre privé
    </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>
      Si vous devez simplement **ajouter** un registre privé sans remplacer crates.io, supprimez les sections `[source.crates-io]` et `[source.private]`, puis utilisez `cargo install --registry private` ou `[dependencies] my-crate = { version = "1.0", registry = "private" }` dans `Cargo.toml`.
    </Tip>
  </Accordion>

  <Accordion title="Ruby / Bundler">
    Installez Ruby et configurez Bundler pour récupérer les gems depuis un serveur de gems privé.

    <Accordion title="Secrets requis">
      * `GEM_SERVER_URL` — URL de votre serveur de gems privé (p. ex., `https://artifactory.example.com/artifactory/api/gems/gems-virtual`)
      * `REGISTRY_USER` — Nom d'utilisateur du registre
      * `REGISTRY_PASS` — Mot de passe du registre ou jeton d’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>
      Schémas d’URL courants pour les serveurs de gems :

      * **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">
    Installez PHP et configurez Composer pour récupérer des packages depuis un registre privé Packagist ou Satis.

    <Accordion title="Secrets requis">
      * `COMPOSER_REGISTRY_URL` — URL de votre registre Composer privé (par ex., `https://repo.packagist.com/<org>`)
      * `REGISTRY_USER` — Nom d’utilisateur du registre
      * `REGISTRY_PASS` — Mot de passe du registre ou jeton d’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

          # Installer 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"

          # S'authentifier auprès du registre
          composer config --global http-basic.$(echo "$COMPOSER_REGISTRY_URL" \
            | sed 's|https\?://||;s|/.*||') "$REGISTRY_USER" "$REGISTRY_PASS"
    ```

    <Tip>
      Modèles d’URL courants pour les registres Composer :

      * **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="Renouvellement du jeton AWS CodeArtifact">
    Les jetons AWS CodeArtifact expirent après 12 heures. Utilisez `maintenance` pour configurer la commande de renouvellement du jeton afin que l’agent puisse l’exécuter à nouveau si nécessaire. Cet exemple configure npm, pip et Maven pour utiliser CodeArtifact.

    <Info>
      **`awscli` est préinstallé** sur l’image de base de Devin. Il vous suffit d’actualiser le jeton et de configurer le registre.
    </Info>

    <Accordion title="Secrets requis">
      * `AWS_ACCESS_KEY_ID` et `AWS_SECRET_ACCESS_KEY` — Identifiants IAM avec les autorisations `codeartifact:GetAuthorizationToken` et `sts:GetServiceBearerToken`
      * `CA_DOMAIN` — Nom de votre domaine CodeArtifact
      * `CA_DOMAIN_OWNER` — ID du compte AWS propriétaire du domaine
      * `CA_REGION` — Région AWS (p. ex., `us-east-1`)
      * `CA_NPM_REPO`, `CA_PYPI_REPO`, `CA_MAVEN_REPO` — Noms des dépôts pour chaque écosystème
    </Accordion>

    ```yaml theme={null}
    maintenance:
      - name: Refresh CodeArtifact auth token
        run: |
          # Obtenir un token récent (valide pendant 12 heures)
          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"

          # Configurer 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"

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

          # Configurer Maven (facultatif)
          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">
  ## Infrastructure Enterprise
</div>

Infrastructure au niveau des machines qui s’applique à l’ensemble des organisations et des dépôts. Définissez ces paramètres dans **Settings > environnement de base de Devin** (à l’échelle de l’entreprise) ou **Settings > environnement > Blueprints > configuration à l’échelle de l’organisation** (à l’échelle de l’organisation).

<div id="network-and-connectivity">
  ### Réseau et connectivité
</div>

<AccordionGroup>
  <Accordion title="Certificat d’autorité de certification interne">
    Votre organisation utilise une autorité de certification privée pour ses services internes. Devin a besoin du certificat racine pour accéder aux dépôts et outils internes via HTTPS.

    <Accordion title="Secrets requis">- `CORP_ROOT_CA_B64` — certificat PEM encodé en Base64 issu de votre autorité de certification d’entreprise. Générez-le avec : `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="Plusieurs certificats d’autorité de certification">
    Si votre organisation utilise plusieurs certificats d’autorité de certification (par exemple, des autorités de certification distinctes pour différents services internes).

    <Accordion title="Secrets requis">- `CORP_ROOT_CA_B64` — certificat principal d’autorité de certification encodé en Base64 - `CORP_INTERMEDIATE_CA_B64` — certificat intermédiaire d’autorité de certification encodé en Base64</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

          # Créer un bundle combiné pour les outils nécessitant un seul fichier 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="Proxy HTTP/HTTPS">
    Faites passer tout le trafic réseau par un proxy d’entreprise.

    <Accordion title="Secrets requis">
      * `CORP_HTTP_PROXY` — URL du proxy HTTP (p. ex., `http://proxy.corp.example.com:8080`) - `CORP_HTTPS_PROXY` — URL du proxy HTTPS - `CORP_NO_PROXY` — liste d’hôtes, séparés par des virgules, à ne pas faire passer par le proxy (p. ex.,
        `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"

          # Configurer le proxy npm
          npm config set proxy "$CORP_HTTP_PROXY"
          npm config set https-proxy "$CORP_HTTPS_PROXY"
    ```
  </Accordion>

  <Accordion title="Proxy authentifié">
    Si votre proxy d’entreprise nécessite une authentification par nom d’utilisateur et mot de passe.

    <Accordion title="Secrets requis">
      * `PROXY_USER` — Nom d’utilisateur du proxy - `PROXY_PASS` — Mot de passe du proxy - `PROXY_HOST` — Nom d’hôte et port du proxy (par ex., `proxy.corp.example.com:8080`) - `CORP_NO_PROXY` — Hôtes à exclure du 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="certificat d’autorité de certification + proxy (combinés)">
    Configuration combinée pour les environnements nécessitant à la fois une autorité de certification d’entreprise et un proxy. Ce cas est fréquent dans les environnements d’entreprise où les services internes utilisent des certificats privés et où tout le trafic doit transiter par un proxy.

    <Accordion title="Secrets requis">
      * `CORP_ROOT_CA_B64` — certificat d’autorité de certification d’entreprise encodé en Base64 - `CORP_HTTP_PROXY`, `CORP_HTTPS_PROXY` — URL du proxy - `CORP_NO_PROXY` — hôtes à exclure du 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="Connexion au VPN">
    Vos registres privés, serveurs Git ou autres services internes ne sont accessibles que via un VPN. Ce module doit s’exécuter **avant** les autres modules qui nécessitent un accès réseau aux ressources internes.

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

      * `VPN_CONFIG_B64` — Fichier de configuration OpenVPN encodé en Base64 (`.ovpn`). Générez-le avec : `cat corp.ovpn | base64 -w0`
      * `VPN_AUTH_USER` *(facultatif)* — Nom d’utilisateur du VPN, si votre VPN nécessite une authentification par nom d’utilisateur et mot de passe
      * `VPN_AUTH_PASS` *(facultatif)* — Mot de passe du VPN

      **WireGuard :**

      * `WG_CONFIG_B64` — Fichier de configuration WireGuard encodé en Base64. Générez-le avec : `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

          # Écrire la configuration VPN
          sudo mkdir -p /etc/openvpn/client
          echo "$VPN_CONFIG_B64" | base64 -d \
            | sudo tee /etc/openvpn/client/corp.conf > /dev/null

          # Si le VPN requiert une authentification par nom d'utilisateur/mot de passe
          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

          # Démarrer le tunnel VPN
          sudo systemctl daemon-reload
          sudo systemctl enable --now openvpn-client@corp

          # Attendre que le tunnel soit établi
          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

          # Écrire la configuration WireGuard
          echo "$WG_CONFIG_B64" | base64 -d \
            | sudo tee /etc/wireguard/wg0.conf > /dev/null
          sudo chmod 600 /etc/wireguard/wg0.conf

          # Démarrer le tunnel
          sudo systemctl enable --now wg-quick@wg0
    ```

    <Tip>
      Pour plus d'informations sur la configuration du VPN, consultez [Configuration du VPN](/fr/onboard-devin/vpn).
    </Tip>
  </Accordion>

  <Accordion title="Résolution DNS personnalisée">
    Vos services internes utilisent des noms DNS privés qui ne sont pas résolubles via le DNS public.

    ```yaml theme={null}
    initialize:
      - name: Configure custom DNS resolution
        run: |
          # Ajouter les noms d'hôtes internes
          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

          # Configurer éventuellement des serveurs de noms personnalisés
          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é et sécurité
</div>

<AccordionGroup>
  <Accordion title="Signature GPG des commits">
    Votre organisation exige que tous les commits Git soient signés, et vous souhaitez que GitHub marque les commits de Devin comme **Verified**.

    <Accordion title="Secrets requis">
      * `GPG_PRIVATE_KEY_B64` — Clé privée GPG encodée en Base64. Générez-la avec : `gpg --export-secret-keys <key-id> | base64 -w0`
      * `GIT_USER_NAME` — Nom de l’auteur Git (p. ex., `Devin AI`)
      * `GIT_USER_EMAIL` — Adresse e-mail de l’auteur Git. **Doit correspondre à un UID de la clé GPG**, sinon GitHub ne vérifiera pas la signature.
    </Accordion>

    <Note>
      Importez également la clé **publique** correspondante dans le compte GitHub dont Devin utilise les identifiants pour effectuer le push (dans [GitHub Settings > SSH and GPG keys](https://github.com/settings/keys)). GitHub ne marque les commits comme Verified que lorsque la clé publique de signature est enregistrée sur le compte auteur du commit.
    </Note>

    ```yaml theme={null}
    initialize:
      - name: Prepare GPG and git signing config
        run: |
          # Permettre à GPG de fonctionner sans 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="Identité Git et clés SSH">
    Configurez l’identité Git et les clés SSH de Devin pour accéder à des serveurs Git privés.

    <Accordion title="Secrets requis">
      * `GIT_USER_NAME` — Nom de l’auteur Git - `GIT_USER_EMAIL` — Adresse e-mail de l’auteur Git - `SSH_PRIVATE_KEY_B64` — Clé privée SSH encodée en Base64. Générez-la avec : `cat ~/.ssh/id_ed25519 | base64 -w0` -
        `SSH_KNOWN_HOSTS_B64` — Entrées du fichier known\_hosts encodées en Base64. Générez-les avec : `ssh-keyscan git.corp.internal | base64 -w0` - `SSH_CONFIG_B64` *(facultatif)* — Fichier de configuration SSH encodé en Base64
    </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"

          # Préparer le répertoire SSH
          mkdir -p ~/.ssh && chmod 700 ~/.ssh

    maintenance:
      - name: Install SSH keys
        run: |
          # Installer la clé privée SSH (dans maintenance pour qu'elle soit rechargée à neuf à chaque session)
          echo "$SSH_PRIVATE_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519

          # Ajouter les hôtes connus de votre serveur Git
          echo "$SSH_KNOWN_HOSTS_B64" | base64 -d >> ~/.ssh/known_hosts

          # Installer éventuellement une configuration SSH personnalisée
          if [ -n "${SSH_CONFIG_B64:-}" ]; then
            echo "$SSH_CONFIG_B64" | base64 -d > ~/.ssh/config
            chmod 600 ~/.ssh/config
          fi
    ```

    <Tip>
      Générez l’entrée known\_hosts de votre serveur Git avec `ssh-keyscan git.corp.internal | base64 -w0`.
    </Tip>
  </Accordion>
</AccordionGroup>

<div id="system-configuration">
  ### Configuration du système
</div>

<AccordionGroup>
  <Accordion title="Packages du système">
    Installez les packages système qui ne figurent pas dans l’image Devin par défaut (p. ex., des bibliothèques natives pour le traitement d’images ou la génération de 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="Variables d’environnement personnalisées">
    Définissez des variables d’environnement persistantes qui doivent être disponibles dans chaque session.

    L’approche recommandée consiste à écrire des lignes `KEY=VALUE` dans le fichier `$ENVRC`. Les variables écrites dans `$ENVRC` sont automatiquement exportées pour toutes les étapes suivantes ainsi que pour la session Devin (comme avec le `$GITHUB_ENV` de GitHub Actions).

    ```yaml theme={null}
    initialize:
      - name: Set custom environment variables
        run: |
          echo "CORPORATE_ENV=production" >> $ENVRC
          echo "DEFAULT_REGION=us-east-1" >> $ENVRC
          echo "MAX_RETRIES=3" >> $ENVRC
    ```

    <Tip>
      Vous pouvez également définir des variables d’environnement dans des scripts `/etc/profile.d/` afin de les rendre disponibles à l’échelle du système :

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

      Les deux approches fonctionnent. `$ENVRC` est plus simple et recommandé dans la plupart des cas.
    </Tip>
  </Accordion>

  <Accordion title="Paramètres régionaux et fuseau horaire">
    Les images de base par défaut peuvent avoir des paramètres régionaux incorrects. Configurez les paramètres régionaux et le fuseau horaire pour éviter les avertissements des outils de build, de Java, de Python et de 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

          # Générer et définir la locale
          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

          # Définir le fuseau horaire
          sudo timedatectl set-timezone UTC 2>/dev/null || \
            sudo ln -sfn /usr/share/zoneinfo/UTC /etc/localtime
    ```
  </Accordion>

  <Accordion title="Limites de ressources (ulimits)">
    Les builds Java, Gradle et Node.js atteignent fréquemment la limite par défaut de 1 024 fichiers ouverts. Augmentez-la pour éviter les build failures.

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

          # Définir aussi le maximum du noyau
          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="Remplacement du miroir APT">
    Dans les environnements en air gap ou restreints, remplacez les sources APT Ubuntu par défaut par un miroir interne.

    <Accordion title="Secrets requis">- `APT_MIRROR_URL` — URL de votre miroir APT interne (p. ex., `https://artifactory.example.com/artifactory/ubuntu-remote`)</Accordion>

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

          # Remplacer tous les miroirs Ubuntu par votre miroir interne
          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>
      Schémas d’URL courants pour les miroirs APT :

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

***

<div id="advanced-patterns">
  ## Patterns avancés
</div>

<AccordionGroup>
  <Accordion title="Variables d’environnement avec direnv">
    L’environnement de base de Devin comprend [direnv](https://direnv.net/). Utilisez `initialize` pour créer des fichiers `.envrc`. Direnv les charge automatiquement.

    ```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 est intégré d’emblée au shell de Devin, donc les variables `.envrc` se chargent automatiquement. Aucun chargement manuel n’est nécessaire.
    </Info>

    <Tip>
      Pour les variables d’environnement **sensibles** (API keys, jetons, mots de passe de base de données), utilisez les [secrets du dépôt](/fr/product-guides/secrets) plutôt que des fichiers `.envrc`. Les secrets du dépôt sont stockés de manière sécurisée et injectés au début de la session. Ils n’apparaissent jamais dans votre blueprint ni dans votre snapshot.
    </Tip>
  </Accordion>

  <Accordion title="Sélection de la version de Node par dépôt">
    Utilisez nvm (préinstallé) pour changer de version de Node.js pour chaque dépôt via `.nvmrc`.

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

    maintenance: |
      nvm use
    ```

    <Info>
      `nvm use` lit `.nvmrc` à la racine du dépôt. Assurez-vous que votre dépôt en contient un (p. ex. avec `20`).
    </Info>
  </Accordion>

  <Accordion title="Authentification dans le navigateur (Playwright)">
    Devin fournit un navigateur Chrome avec un endpoint CDP sur `localhost:29229` **pendant les sessions**. Utilisez des scripts Playwright pour automatiser la connexion via le navigateur.

    <Warning>
      Le navigateur n'est disponible que pendant les sessions, pas dans les builds du snapshot. Installez Playwright dans `initialize` et conservez les scripts de connexion dans votre repo.
    </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
    ```

    Exemple de script de connexion (`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>
      Stockez les identifiants de connexion dans les secrets, pas dans le code source. Pour une authentification persistante, versionnez les scripts de connexion dans `.agents/skills/` afin que Devin puisse se réauthentifier automatiquement.
    </Warning>
  </Accordion>

  <Accordion title="Outils système personnalisés et PATH">
    Installez des packages système, des binaires personnalisés et configurez le PATH dans `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 pour configurer l’outil">
    Devin prend en charge l’exécution de GitHub Actions basées sur Node.js directement dans les blueprints. Cela est utile pour installer des versions spécifiques d’outils via les mêmes actions que celles utilisées par votre CI.

    ```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>
      Des actions comme `setup-node` et `setup-python` modifient le PATH et les variables d’environnement. Les binaires installés par une action sont disponibles dans toutes les étapes suivantes et dans `maintenance`. Seules les
      GitHub Actions **basées sur Node.js** sont prises en charge. Les actions composites et celles basées sur Docker ne le sont pas.
    </Info>

    <Tip>
      Vous n’avez pas besoin de GitHub Actions pour la configuration de base des outils. Des commandes shell directes (`nvm install 20`, `curl ... | sh`, `apt-get install`) fonctionnent tout aussi bien et sont souvent plus simples. Les GitHub Actions sont surtout utiles si vous voulez reproduire exactement votre configuration CI ou bénéficier de la praticité d’actions comme `setup-java`, qui gèrent plusieurs distributions.
    </Tip>
  </Accordion>

  <Accordion title="Proxy inverse HTTPS en local pour plusieurs applications">
    Exécutez plusieurs services derrière des noms d’hôte plausibles via HTTPS, comme `app.example.com`, `api.example.com` et `admin.example.com`. Installez un seul proxy inverse dans `initialize` et faites pointer chaque nom d’hôte vers un port upstream local distinct.

    [Caddy](https://caddyserver.com/) gère le routage et le TLS local dans un seul outil. Un Caddyfile associe chaque nom d’hôte à un upstream, et `tls internal` émet automatiquement, pour chaque nom d’hôte, un certificat approuvé à partir de l’autorité de certification intégrée à Caddy. `caddy trust` installe la racine de cette autorité dans le magasin de confiance du système, et l’ajout de cette même racine à la base de données NSS permet au navigateur de l’accepter.

    Importez votre Caddyfile via la section **Pièces jointes** de l’éditeur de blueprint ; il sera alors disponible sous la forme `$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>
      Le bloc `/etc/hosts` permet à `app.example.com` de pointer vers `127.0.0.1` à l’intérieur de la session. Ajoutez une entrée pour chaque hostname que vous mettez dans le Caddyfile.
    </Info>

    <Tip>
      Pour ajouter un service, ajoutez un bloc de trois lignes au Caddyfile et une entrée au bloc `/etc/hosts`, puis accédez à son hostname en HTTPS. Caddy génère un certificat à la première requête ; aucune génération de certificat par application n’est donc nécessaire.
    </Tip>
  </Accordion>
</AccordionGroup>

***

<div id="full-stack-examples">
  ## Exemples full-stack
</div>

Ces exemples montrent comment les configurations Enterprise et celles au niveau de l'org se combinent. En pratique, vous les répartiriez entre plusieurs périmètres. Elles sont regroupées ici à titre de référence.

<AccordionGroup>
  <Accordion title="Stack complète pour entreprise (Artifactory)">
    Un environnement d'entreprise complet : certificat d'autorité de certification d'entreprise, proxy, Java (Maven), Python (pip/uv), Node.js (npm) et Docker, le tout pointant vers une seule instance Artifactory.

    <Accordion title="Secrets requis">
      **Réseau et confiance (échelle du compte) :**

      * `CORP_ROOT_CA_B64` — certificat d’autorité de certification d’entreprise encodé en Base64
      * `CORP_HTTP_PROXY` — URL du proxy HTTP
      * `CORP_HTTPS_PROXY` — URL du proxy HTTPS
      * `CORP_NO_PROXY` — hôtes à ne pas faire passer par le proxy

      **Identifiants du registre (échelle de l’organisation) :**

      * `ARTIFACTORY_USER` — nom d’utilisateur Artifactory
      * `ARTIFACTORY_TOKEN` — jeton d’API Artifactory ou mot de passe
      * `ARTIFACTORY_MAVEN_URL` — URL du dépôt Maven (p. ex., `https://artifactory.example.com/artifactory/maven-virtual`)
      * `ARTIFACTORY_PYPI_URL` — URL du dépôt PyPI (p. ex., `https://user:token@artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple`)
      * `ARTIFACTORY_NPM_URL` — URL du dépôt npm (p. ex., `https://artifactory.example.com/artifactory/api/npm/npm-virtual`)
      * `ARTIFACTORY_DOCKER_URL` — URL du registre Docker (p. ex., `artifactory.example.com`)
    </Accordion>

    Cela serait généralement réparti en trois périmètres :

    * **À l’échelle du compte (`initialize`) :** certificat et proxy
    * **À l’échelle de l’organisation (`initialize`) :** Installation des runtimes de langage
    * **À l’échelle de l’organisation (`maintenance`) :** Identifiants du registre (actualisés pendant les builds, mis à disposition de l’agent au début de la session)

    Présenté ici en version combinée pour référence :

    ```yaml theme={null}
    initialize:
      # ── À l'échelle du compte : réseau et confiance ──────────────────────────────────────

      - 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

      # ── À l'échelle de l'organisation : environnements d'exécution ──────────────────────────────────────────

      - 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:
      # ── À l'échelle du compte : proxy git (actualisé à chaque session) ───────────────────

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

      # ── À l'échelle de l'organisation : identifiants de registre (actualisés à chaque session) ──────────────

      - 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>
      Dans cet exemple, tous les registres pointent vers la même instance Artifactory, mais utilisent des chemins d’URL différents. Chaque écosystème de packages a son propre format d’endpoint. Les URL Maven, PyPI, npm et Docker sont toutes différentes, même pour un même registre.
    </Info>
  </Accordion>

  <Accordion title="Plusieurs langages avec différents dépôts">
    Lorsque différents langages utilisent des registres privés différents (par ex. Maven via Nexus, npm via GitHub Packages, Python via Artifactory).

    <Accordion title="Secrets requis">
      * `NEXUS_MAVEN_URL` — URL du dépôt Maven Nexus
      * `NEXUS_USER` — nom d’utilisateur Nexus
      * `NEXUS_PASS` — mot de passe Nexus
      * `GITHUB_PACKAGES_TOKEN` — jeton d’accès personnel GitHub avec le périmètre `read:packages`
      * `ARTIFACTORY_USER` — nom d’utilisateur Artifactory
      * `ARTIFACTORY_TOKEN` — jeton d’API Artifactory
      * `GIT_TOKEN` — jeton d’accès personnel pour les modules Go privés
    </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 (avec périmètre)
      - 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 → modules privés 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="Environnement en air gap avec des miroirs privés">
    Dans un environnement totalement en air gap, Devin ne peut accéder à aucune URL publique. Tous les outils, environnements d’exécution et packages doivent provenir de miroirs internes.

    <Accordion title="Secrets requis">
      **Certificats :**

      * `CORP_ROOT_CA_B64` — certificat d’autorité de certification de l’entreprise encodé en Base64

      **Accès au miroir :**

      * `APT_MIRROR_URL` — URL du miroir APT Ubuntu interne
      * `MIRROR_USER` — nom d’utilisateur d’authentification au miroir
      * `MIRROR_PASS` — mot de passe d’authentification au miroir
      * `JDK_TARBALL_URL` — URL pour télécharger l’archive tar du JDK depuis le miroir interne
      * `NODE_TARBALL_URL` — URL pour télécharger l’archive tar de Node.js depuis le miroir interne

      **Registres de packages :**

      * `INTERNAL_MAVEN_URL` — URL du registre Maven interne
      * `INTERNAL_NPM_URL` — URL du registre npm interne
      * `INTERNAL_PYPI_URL` — URL du registre PyPI interne
    </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: |
          # Télécharger l'archive JDK depuis le dépôt d'artefacts interne
          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>
      Dans les environnements en air gap, tous les outils dont Devin a besoin (environnement d’exécution, outils CLI, etc.) doivent être disponibles sur vos miroirs internes. Les registres publics et les sites de téléchargement ne sont pas accessibles.
    </Warning>
  </Accordion>

  <Accordion title="VPN + certificats + proxy + langages">
    Une configuration Enterprise complète combinant la connectivité VPN avec des certificats, un proxy et la prise en charge multilingue. Voici l'ordre des opérations recommandé.

    <Accordion title="Secrets requis">
      **VPN :**

      * `VPN_CONFIG_B64` — fichier de configuration OpenVPN encodé en Base64

      **Réseau & confiance :**

      * `CORP_ROOT_CA_B64` — certificat d’autorité de certification d’entreprise encodé en Base64
      * `CORP_HTTP_PROXY` — URL du proxy HTTP
      * `CORP_HTTPS_PROXY` — URL du proxy HTTPS
      * `CORP_NO_PROXY` — hôtes à contourner via le proxy

      **Identifiants du registre :**

      * `MAVEN_REGISTRY_URL` — URL du registre Maven
      * `NPM_REGISTRY_URL` — URL du registre npm
      * `PYPI_REGISTRY_HOST` — nom d’hôte du registre PyPI
      * `REGISTRY_USER` — nom d’utilisateur du registre (pour Maven et pip)
      * `REGISTRY_PASS` — mot de passe du registre (pour Maven et pip)
      * `REGISTRY_TOKEN` — jeton d’authentification npm
    </Accordion>

    ```yaml theme={null}
    initialize:
      # 1. VPN — doit être configuré en premier pour que les ressources internes soient accessibles
      - 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 — résolution des noms d'hôtes internes
      - 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. Certificats — approuver les autorités de certification internes
      - 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 — acheminer le trafic via le proxy d'entreprise
      - 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. Environnements d'exécution
      - 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>
      **L’ordre des étapes `initialize` est important.** Le VPN doit venir en premier (pour que les hôtes internes soient accessibles), puis le DNS (pour que la résolution des noms fonctionne), puis les certificats (pour que HTTPS fonctionne), puis le proxy (pour que le trafic soit correctement acheminé), et enfin les environnements d’exécution (qui peuvent télécharger depuis des miroirs internes).
    </Info>
  </Accordion>
</AccordionGroup>

***

<div id="tips-for-writing-good-blueprints">
  ## Conseils pour rédiger de bons blueprints
</div>

* **Testez d’abord les commandes dans une session.** Exécutez-les manuellement dans une session Devin avant de les ajouter à votre blueprint. C’est plus rapide que d’attendre un cycle de build complet.
* **Utilisez `initialize` pour les outils à installer une seule fois, et `maintenance` pour les dépendances.** Tout ce qui prend plusieurs minutes à installer (compilateurs, gros binaires, outils globaux) doit aller dans `initialize`. Les commandes de dépendances rapides (`npm install`, `uv sync`) vont dans `maintenance`.
* **Veillez à ce que les commandes `maintenance` restent rapides.** Visez moins de 2 minutes. Elles s’exécutent pendant les builds et sont exposées à l’agent au démarrage de la session.
* **Utilisez `$ENVRC` pour les variables d’environnement.** N’écrivez pas dans `.bashrc` ou `.profile`. `$ENVRC` est le mécanisme pris en charge pour définir des variables entre les étapes et les sessions.
* **Nommez vos étapes.** La syntaxe développée avec des champs `name` permet d’identifier beaucoup plus facilement les échecs dans les journaux de build.
* **Utilisez des subshells pour les monorepos.** `(cd packages/foo && npm install)` s’exécute dans un subshell afin que les étapes suivantes ne soient pas affectées par le changement de répertoire.
* **Utilisez `npm install`, pas `npm ci`.** `npm ci` supprime `node_modules` et réinstalle tout depuis zéro, ce qui est trop lent pour `maintenance`.
* **Utilisez les secrets du dépôt pour les valeurs sensibles.** Configurez-les dans l’onglet **Secrets** de l’éditeur de blueprint du dépôt plutôt que de les coder en dur dans les blueprints.

Pour les détails de syntaxe, consultez la [référence des blueprints](/fr/onboard-devin/environment/blueprint-reference). Pour résoudre les échecs de build, consultez [Configuration déclarative > Dépannage](/fr/onboard-devin/environment/blueprints#troubleshooting-builds).
