Skip to main content
Plantillas listas para copiar y pegar para lenguajes y casos de uso habituales. Cada plantilla es autosuficiente. Combínalas para crear tu configuración completa. Para ver un desglose completo de cada campo, consulta la referencia de plantillas.
Secretos: Las plantillas hacen referencia a los secretos mediante $SECRET_NAME. Configúralos en Settings > Secrets antes de usar una plantilla. Nunca incrustes credenciales directamente en tu plantilla.

Inicio rápido

Plantillas mínimas para las configuraciones más habituales. Copia uno, pégalo en el editor de plantillas y listo.
initialize: |
  npm install -g pnpm

maintenance: |
  pnpm install

knowledge:
  - name: lint
    contents: |
      Ejecuta `pnpm lint` para comprobar si hay errores.
  - name: test
    contents: |
      Ejecuta `pnpm test` para la suite completa.
initialize: |
  curl -LsSf https://astral.sh/uv/install.sh | sh

maintenance: |
  uv sync

knowledge:
  - name: lint
    contents: |
      Ejecuta `uv run ruff check .` para hacer linting.
  - name: test
    contents: |
      Ejecuta `uv run pytest` para la suite completa.
initialize:
  - name: Instalar pnpm
    run: npm install -g pnpm
  - name: Install uv
    run: curl -LsSf https://astral.sh/uv/install.sh | sh

maintenance:
  - name: Dependencias del frontend
    run: (cd frontend && pnpm install)
  - name: Dependencias del backend
    run: (cd backend && uv sync)

knowledge:
  - name: structure
    contents: |
      - `frontend/` — app de React (pnpm)
      - `backend/` — API de Python (uv)
  - name: test
    contents: |
      Frontend: cd frontend && pnpm test
      Backend: cd backend && uv run pytest

Plantillas de repositorios

Pasos de compilación para cada repositorio, gestión de dependencias y entradas de Knowledge. Configúralos en Settings > Configuración de Environment > [tu repo].

Python

Configuración recomendada para proyectos de Python que usan uv para gestionar dependencias.
initialize: |
  curl -LsSf https://astral.sh/uv/install.sh | sh

maintenance: |
  uv sync

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

      Corrección automática:
      uv run ruff check --fix .

  - name: test
    contents: |
      uv run pytest

  - name: build
    contents: |
      uv run python -m build

Node.js

Configuración estándar de Node.js con npm.
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
Usa npm install (no npm ci) en maintenance. Realiza una actualización incremental, mientras que npm ci elimina node_modules y reinstala todo desde cero en cada sesión.

Go

Configuración estándar de Go con módulos.
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 ./...

Java

Configuración de Java con Gradle.
JDK 17 viene preinstalado en la imagen base de Devin. Omite el paso de instalación de JDK si OpenJDK 17 predeterminado es suficiente.
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

Ruby on Rails

Configuración de Rails con PostgreSQL.
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

Rust

Configuración estándar de Rust con Cargo.
Rust (mediante rustup) y Cargo vienen preinstalados en la imagen base de Devin. Omite el paso de instalación si la cadena de herramientas estable predeterminada es suficiente. Solo necesitas descargar las dependencias.
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

Monorepos

Monorepo con un frontend en Node.js y un backend en Python. Cada subproyecto tiene sus propias entradas de Knowledge.
initialize:
  - name: Instalar pnpm
    run: npm install -g pnpm
  - name: Install uv
    run: curl -LsSf https://astral.sh/uv/install.sh | sh

maintenance:
  - name: Instalar dependencias del frontend
    run: (cd packages/frontend && pnpm install)
  - name: Instalar dependencias del backend
    run: (cd packages/backend && uv sync)
  - name: Compilar la biblioteca compartida
    run: (cd packages/shared && pnpm install && pnpm build)

knowledge:
  - name: structure
    contents: |
      Este es un monorepo con tres paquetes:
      - `packages/frontend` — Aplicación de React (TypeScript, pnpm)
      - `packages/backend` — API en Python (FastAPI, uv)
      - `packages/shared` — Utilidades compartidas de TypeScript (deben compilarse antes que el frontend)
  - name: frontend
    contents: |
      Ejecuta `cd packages/frontend && pnpm dev` para iniciar el servidor de desarrollo.
      Ejecuta `cd packages/frontend && pnpm lint` para ejecutar el linter.
      Ejecuta `cd packages/frontend && pnpm test` para ejecutar las pruebas.
  - name: backend
    contents: |
      Ejecuta `cd packages/backend && uv run uvicorn app.main:app --reload` para iniciar la API.
      Ejecuta `cd packages/backend && uv run ruff check .` para ejecutar el linter.
      Ejecuta `cd packages/backend && uv run pytest` para ejecutar las pruebas.
Usa subshells (cd dir && command) en lugar de cd dir && command para que el directorio de trabajo se restablezca entre pasos.

Registros privados de paquetes

Configura los gestores de paquetes para resolver dependencias desde registros privados. Configúralos en Settings > Configuración de Environment > configuración de la organización (o por repositorio si solo un repositorio lo necesita).
La configuración de credenciales corresponde a maintenance, no a initialize. Los pasos que escriben secretos (contraseñas del registro, tokens de autenticación) en archivos de configuración deben usar maintenance para que las credenciales se vuelvan a cargar en cada sesión. Los secretos se eliminan antes de guardar la instantánea, por lo que los archivos de configuración escritos durante initialize no tendrán credenciales válidas cuando se inicien las sesiones.
Si tu registro privado usa una CA corporativa, asegúrate de que el certificado de CA esté instalado primero a nivel de Enterprise. La siguiente configuración asume que la confianza en HTTPS ya está establecida.

Registros de Node.js

Configura npm para resolver paquetes con ámbito (p. ej., @myorg/*) desde un registro privado, mientras que los paquetes públicos se siguen obteniendo del registro predeterminado de npm.
- GITHUB_PACKAGES_TOKEN — Personal Access Token o token de GitHub App con ámbito read:packages
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
Sustituye @myorg por tu ámbito de npm. URLs habituales de registros privados:
  • 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>

Registros de Python

Configura pip y uv para resolver paquetes desde tu registro privado de PyPI (p. ej., Nexus, Artifactory).
  • PYPI_REGISTRY_URL — URL completa de tu índice de PyPI, incluidas las credenciales si son necesarias (p. ej., https://user:token@nexus.example.com/repository/pypi-proxy/simple)
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
Patrones habituales de URL para registros de 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

Registro de JVM

Instala el JDK y configura Maven para redirigir toda la resolución de dependencias a través de tu registro privado (p. ej., Artifactory, Nexus).
JDK 17 está preinstalado en la imagen base de Devin. Omite el paso de instalación si el OpenJDK 17 predeterminado es suficiente. Solo necesitas instalar Maven y configurar el registro.
  • MAVEN_REGISTRY_URL — URL de tu registro de Maven (p. ej., https://artifactory.example.com/artifactory/maven-virtual) - REGISTRY_USER — Nombre de usuario del registro - REGISTRY_PASS — Contraseña del registro o token de API
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
Patrones comunes de URL de registros para 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>

Otros registros

Instala Go y configúralo para resolver módulos mediante un proxy privado de módulos (p. ej., Athens, Artifactory o un endpoint de GOPROXY).
  • GO_PROXY_URL — URL de tu proxy de módulos de Go (p. ej., https://athens.corp.internal) - GIT_TOKEN — token de acceso personal para repositorios de Git privados que alojan módulos de Go
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/"
Patrones de URL habituales para proxies de Go:
  • Artifactory: https://artifactory.example.com/artifactory/go-virtual
  • Nexus: https://nexus.example.com/repository/go-proxy
  • Athens: https://athens.corp.internal
Configura NuGet para resolver paquetes desde un feed privado.
- NUGET_SOURCE_URL — URL de tu feed de NuGet - NUGET_API_KEY — API key o PAT del feed
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
Configura Docker para descargar desde un registro privado de contenedores.
  • DOCKER_MIRROR_URL (opcional) — URL de tu mirror de Docker Hub (p. ej., https://mirror.corp.internal) - DOCKER_REGISTRY_URL — URL de tu registro privado de contenedores (p. ej., registry.corp.internal:5000) - DOCKER_REGISTRY_USER — Nombre de usuario del registro - DOCKER_REGISTRY_PASS — Contraseña del registro o token de API
initialize:
  - name: Create Docker config directory
    run: sudo mkdir -p /etc/docker

maintenance:
  - name: Configure Docker for private registry
    run: |
      # Configurar el mirror del registro (opcional — enruta las descargas de Docker Hub a través de tu registro)
      cat << EOF | sudo tee /etc/docker/daemon.json > /dev/null
      {
        "registry-mirrors": ["$DOCKER_MIRROR_URL"]
      }
      EOF
      sudo systemctl restart docker || true

      # Iniciar sesión en el registro de contenedores privado
      echo "$DOCKER_REGISTRY_PASS" | docker login "$DOCKER_REGISTRY_URL" \
        --username "$DOCKER_REGISTRY_USER" \
        --password-stdin
URLs habituales de registros de contenedores:
  • 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
Configura Cargo para resolver crates desde un registro privado.
Rust (a través de rustup) y Cargo vienen preinstalados en la imagen base de Devin. Omite el paso de instalación si el toolchain estable predeterminado es suficiente. Solo necesitas configurar el registro.
  • CARGO_REGISTRY_INDEX — URL del índice del registro privado (p. ej., sparse+https://cargo.corp.internal/api/v1/crates/) - CARGO_REGISTRY_TOKEN — Token de autenticación para el registro privado
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
Si solo necesitas agregar un registro privado sin sustituir crates.io, elimina las secciones [source.crates-io] y [source.private] y usa cargo install --registry private o [dependencies] my-crate = { version = "1.0", registry = "private" } en Cargo.toml.
Instala Ruby y configura Bundler para resolver las gemas desde un servidor privado de gemas.
  • GEM_SERVER_URL — URL de tu servidor privado de gemas (p. ej., https://artifactory.example.com/artifactory/api/gems/gems-virtual) - REGISTRY_USER — Nombre de usuario del registro - REGISTRY_PASS — Contraseña o token de API del registro
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"
Patrones de URL habituales para servidores de gemas:
  • Artifactory: https://artifactory.example.com/artifactory/api/gems/gems-virtual
  • Nexus: https://nexus.example.com/repository/rubygems-proxy
  • Gemfury: https://gem.fury.io/<org>
Instala PHP y configura Composer para resolver paquetes desde un registro privado de Packagist o Satis.
  • COMPOSER_REGISTRY_URL — URL de tu registro privado de Composer (p. ej., https://repo.packagist.com/<org>)
  • REGISTRY_USER — Nombre de usuario del registro
  • REGISTRY_PASS — Contraseña del registro o token de la API
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

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

      # Autenticarse con el registro
      composer config --global http-basic.$(echo "$COMPOSER_REGISTRY_URL" \
        | sed 's|https\?://||;s|/.*||') "$REGISTRY_USER" "$REGISTRY_PASS"
Patrones de URL comunes para registros de 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
Los tokens de AWS CodeArtifact caducan al cabo de 12 horas. Usa maintenance para renovar el token al inicio de cada sesión. Este ejemplo configura npm, pip y Maven para usar CodeArtifact.
awscli viene preinstalado en la imagen base de Devin. Solo necesitas renovar el token y configurar el registro.
  • AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY — credenciales de IAM con los permisos codeartifact:GetAuthorizationToken y sts:GetServiceBearerToken - CA_DOMAIN — nombre de tu dominio de CodeArtifact
    • CA_DOMAIN_OWNER — ID de la cuenta de AWS propietaria del dominio - CA_REGION — región de AWS (p. ej., us-east-1) - CA_NPM_REPO, CA_PYPI_REPO, CA_MAVEN_REPO — nombres de los repositorios de cada ecosistema
maintenance:
  - name: Refresh CodeArtifact auth token
    run: |
      # Obtener un token nuevo (válido por 12 horas)
      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"

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

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

      # Configurar Maven (opcional)
      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

Infraestructura de Enterprise

Infraestructura a nivel de máquina que se aplica a todas las organizaciones y repositorios. Configúrala en Settings > entorno base de Devin (a nivel Enterprise) o Settings > Configuración de Environment > configuración de la organización (a nivel de organización).

Red y conectividad

Tu organización usa una autoridad certificadora privada para los servicios internos. Devin necesita el certificado raíz para conectarse a los repositorios y herramientas internos mediante HTTPS.
- CORP_ROOT_CA_B64 — Certificado PEM codificado en Base64 de tu CA corporativa. Genéralo con: cat corp-root-ca.crt | base64 -w0
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
Si tu organización usa múltiples certificados de CA (p. ej., CAs separadas para distintos servicios internos).
- CORP_ROOT_CA_B64 — certificado de CA principal codificado en Base64 - CORP_INTERMEDIATE_CA_B64 — certificado de CA intermedio codificado en Base64
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

      # Crear un paquete combinado para herramientas que necesitan un único archivo 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
Dirige todo el tráfico de red a través de un proxy corporativo.
  • CORP_HTTP_PROXY — URL del proxy HTTP (p. ej., http://proxy.corp.example.com:8080) - CORP_HTTPS_PROXY — URL del proxy HTTPS - CORP_NO_PROXY — Lista de hosts separados por comas para omitir el proxy (p. ej., localhost,127.0.0.1,.corp.example.com)
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"

      # Configurar proxy de npm
      npm config set proxy "$CORP_HTTP_PROXY"
      npm config set https-proxy "$CORP_HTTPS_PROXY"
Si tu proxy corporativo requiere autenticación con nombre de usuario y contraseña.
  • PROXY_USER — Nombre de usuario del proxy - PROXY_PASS — Contraseña del proxy - PROXY_HOST — Host y puerto del proxy (p. ej., proxy.corp.example.com:8080) - CORP_NO_PROXY — Hosts que deben omitir el proxy
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"
Configuración combinada para entornos que necesitan tanto una CA corporativa como un proxy. Esto es habitual en entornos empresariales donde los servicios internos usan certificados privados y todo el tráfico debe pasar por un proxy.
  • CORP_ROOT_CA_B64 — Certificado de CA corporativa codificado en Base64 - CORP_HTTP_PROXY, CORP_HTTPS_PROXY — URL del proxy - CORP_NO_PROXY — Hosts que deben omitir el proxy
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"
Solo se puede acceder a tus registros privados, servidores Git u otros servicios internos a través de una VPN. Debe ejecutarse antes que otros módulos que necesiten acceso de red a recursos internos.
OpenVPN:
  • VPN_CONFIG_B64 — Archivo de configuración de OpenVPN codificado en Base64 (.ovpn). Genéralo con: cat corp.ovpn | base64 -w0
  • VPN_AUTH_USER (opcional) — Nombre de usuario de la VPN, si tu VPN requiere autenticación con nombre de usuario y contraseña
  • VPN_AUTH_PASS (opcional) — Contraseña de la VPN
WireGuard:
  • WG_CONFIG_B64 — Archivo de configuración de WireGuard codificado en Base64. Genéralo con: cat wg0.conf | base64 -w0
OpenVPN:
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

      # Escribir la configuración de VPN
      sudo mkdir -p /etc/openvpn/client
      echo "$VPN_CONFIG_B64" | base64 -d \
        | sudo tee /etc/openvpn/client/corp.conf > /dev/null

      # Si la VPN requiere autenticación con usuario/contraseña
      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

      # Iniciar el túnel VPN
      sudo systemctl daemon-reload
      sudo systemctl enable --now openvpn-client@corp

      # Esperar a que el túnel se establezca
      for i in $(seq 1 30); do
        if ip link show tun0 >/dev/null 2>&1; then break; fi
        sleep 1
      done
WireGuard:
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

      # Escribir la configuración de WireGuard
      echo "$WG_CONFIG_B64" | base64 -d \
        | sudo tee /etc/wireguard/wg0.conf > /dev/null
      sudo chmod 600 /etc/wireguard/wg0.conf

      # Iniciar el túnel
      sudo systemctl enable --now wg-quick@wg0
Para más información sobre la configuración de la VPN, consulta VPN Configuration.
Tus servicios internos usan nombres DNS privados que no se pueden resolver mediante DNS público.
initialize:
  - name: Configure custom DNS resolution
    run: |
      # Agregar nombres de host internos
      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

      # Opcionalmente, configurar servidores de nombres personalizados
      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

Identidad y seguridad

Tu organización requiere que todos los commits de Git estén firmados y quieres que GitHub marque los commits de Devin como Verified.
  • GPG_PRIVATE_KEY_B64 — Clave privada de GPG codificada en Base64. Genérala con: gpg --export-secret-keys <key-id> | base64 -w0
  • GIT_USER_NAME — Nombre del autor de Git (p. ej., Devin AI)
  • GIT_USER_EMAIL — Correo electrónico del autor de Git. Debe coincidir con un UID de la clave GPG; de lo contrario, GitHub no verificará la firma.
Sube también la clave pública correspondiente a la cuenta de GitHub cuyas credenciales usa Devin para hacer push (en GitHub Settings > SSH and GPG keys). GitHub solo marca los commits como Verified cuando la clave pública de firma está registrada en la cuenta que creó el commit.
initialize:
  - name: Prepare GPG and git signing config
    run: |
      # Permitir que GPG funcione sin una 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
Configura la identidad de Git y las claves SSH de Devin para acceder a servidores Git privados.
  • GIT_USER_NAME — Nombre del autor de Git - GIT_USER_EMAIL — Correo electrónico del autor de Git - SSH_PRIVATE_KEY_B64 — Clave privada SSH codificada en Base64. Genérala con: cat ~/.ssh/id_ed25519 | base64 -w0 - SSH_KNOWN_HOSTS_B64 — Entradas de hosts conocidos codificadas en Base64. Genéralas con: ssh-keyscan git.corp.internal | base64 -w0 - SSH_CONFIG_B64 (opcional) — Archivo de configuración SSH codificado en Base64
initialize:
  - name: Configure git identity
    run: |
      git config --global user.name "$GIT_USER_NAME"
      git config --global user.email "$GIT_USER_EMAIL"

      # Preparar el directorio SSH
      mkdir -p ~/.ssh && chmod 700 ~/.ssh

maintenance:
  - name: Install SSH keys
    run: |
      # Instalar la clave privada SSH (en maintenance para que se vuelva a cargar en cada sesión)
      echo "$SSH_PRIVATE_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
      chmod 600 ~/.ssh/id_ed25519

      # Agregar hosts conocidos para tu servidor Git
      echo "$SSH_KNOWN_HOSTS_B64" | base64 -d >> ~/.ssh/known_hosts

      # Instalar opcionalmente una configuración SSH personalizada
      if [ -n "${SSH_CONFIG_B64:-}" ]; then
        echo "$SSH_CONFIG_B64" | base64 -d > ~/.ssh/config
        chmod 600 ~/.ssh/config
      fi
Genera la entrada de hosts conocidos para tu servidor Git con ssh-keyscan git.corp.internal | base64 -w0.

Configuración del sistema

Instale paquetes del sistema que no estén en la imagen predeterminada de Devin (p. ej., bibliotecas nativas para el procesamiento de imágenes o la generación de PDF).
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
Establece variables de entorno persistentes que deben estar disponibles en cada sesión.La forma recomendada es escribir líneas KEY=VALUE en el archivo $ENVRC. Las variables escritas en $ENVRC se exportan automáticamente para todos los pasos posteriores y para la sesión de Devin (de forma similar a $GITHUB_ENV de GitHub Actions).
initialize:
  - name: Set custom environment variables
    run: |
      echo "CORPORATE_ENV=production" >> $ENVRC
      echo "DEFAULT_REGION=us-east-1" >> $ENVRC
      echo "MAX_RETRIES=3" >> $ENVRC
También puedes escribir variables de entorno en scripts de /etc/profile.d/ para que estén disponibles en todo el sistema:
cat << 'ENVVARS' | sudo tee /etc/profile.d/custom-env.sh > /dev/null
export CORPORATE_ENV=production
export DEFAULT_REGION=us-east-1
ENVVARS
Ambos métodos funcionan. $ENVRC es más sencillo y se recomienda en la mayoría de los casos.
Las imágenes base predeterminadas pueden tener la configuración regional mal configurada. Configure la configuración regional y la zona horaria para evitar advertencias de las herramientas de compilación, Java, Python y Git.
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

      # Generar y establecer la configuración regional
      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

      # Establecer la zona horaria
      sudo timedatectl set-timezone UTC 2>/dev/null || \
        sudo ln -sfn /usr/share/zoneinfo/UTC /etc/localtime
Las compilaciones de Java, Gradle y Node.js suelen alcanzar el límite predeterminado de 1024 archivos abiertos. Auméntelo para evitar fallos de compilación.
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

      # También establecer el máximo del kernel
      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
En entornos aislados o restringidos, sustituye los repositorios APT predeterminados de Ubuntu por un mirror interno.
- APT_MIRROR_URL — URL de tu mirror interno de APT (p. ej., https://artifactory.example.com/artifactory/ubuntu-remote)
initialize:
  - name: Replace APT sources with internal mirror
    run: |
      # Hacer copia de seguridad de las fuentes originales
      sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

      # Reemplazar todos los mirrors de Ubuntu con tu mirror interno
      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
Patrones comunes de URL para mirrors de APT:
  • Artifactory: https://artifactory.example.com/artifactory/ubuntu-remote
  • Nexus: https://nexus.example.com/repository/ubuntu-proxy

Patrones avanzados

El Environment base de Devin incluye direnv. Usa initialize para crear archivos .envrc. Direnv los carga automáticamente.
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 .
direnv ya viene integrado en el shell de Devin, por lo que las variables de .envrc se cargan automáticamente. No necesitas hacer source manualmente.
Para las variables de entorno sensibles (API keys, tokens, contraseñas de bases de datos), usa secretos del repositorio en lugar de archivos .envrc. Los secretos del repositorio se almacenan de forma segura y se inyectan durante la sesión. Nunca aparecen en tu blueprint ni en tu instantánea.
Usa nvm (preinstalado) para cambiar la versión de Node.js de cada repositorio mediante .nvmrc.
initialize: |
  nvm install 18
  nvm install 20
  nvm install 22

maintenance: |
  nvm use
nvm use lee el archivo .nvmrc de la raíz del repositorio. Asegúrate de que tu repositorio incluya ese archivo (p. ej., con 20).
Devin proporciona un navegador Chrome con un endpoint de CDP en localhost:29229 durante las sesiones. Usa scripts de Playwright para automatizar el inicio de sesión en el navegador.
El navegador solo está disponible durante las sesiones, no en las compilaciones de instantánea. Instala Playwright en initialize y mantén los scripts de inicio de sesión en tu repositorio.
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
Script de ejemplo para iniciar sesión (scripts/login.py):
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")
Guarda las credenciales de inicio de sesión como secretos, no en el código fuente. Para mantener la autenticación a largo plazo, confirma los scripts de inicio de sesión en .agents/skills/ para que Devin pueda autenticarse de nuevo automáticamente.
Instala paquetes del sistema, binarios personalizados y configura la variable PATH en initialize.
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
Devin permite ejecutar GitHub Actions basadas en Node.js directamente en blueprints. Esto es útil para instalar versiones específicas de herramientas mediante las mismas acciones que usa tu CI.
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
Acciones como setup-node y setup-python modifican PATH y las variables de entorno. Los binarios instalados por una acción están disponibles en todos los pasos posteriores y en maintenance. Solo se admiten las GitHub Actions basadas en Node.js. Las acciones compuestas y basadas en Docker no son compatibles.
No necesitas GitHub Actions para la configuración básica de herramientas. Los comandos directos de shell (nvm install 20, curl ... | sh, apt-get install) funcionan igual de bien y suelen ser más sencillos. Las GitHub Actions resultan más útiles cuando quieres reproducir exactamente tu configuración de CI o necesitas la comodidad de acciones como setup-java, que gestionan múltiples distribuciones.

Ejemplos de full stack

Estos ejemplos muestran cómo se combinan las configuraciones de Enterprise y a nivel de org. En la práctica, se dividirían entre distintos ámbitos. Se muestran juntas aquí como referencia.
Un Environment empresarial completo: certificado CA corporativo, proxy, Java (Maven), Python (pip/uv), Node.js (npm) y Docker, todos apuntando a una única instancia de Artifactory.
Red y confianza (para toda la cuenta):
  • CORP_ROOT_CA_B64 — certificado de la CA corporativa codificado en Base64
  • CORP_HTTP_PROXY — URL del proxy HTTP
  • CORP_HTTPS_PROXY — URL del proxy HTTPS
  • CORP_NO_PROXY — hosts que deben omitir el proxy
Credenciales del registro (para toda la organización):
  • ARTIFACTORY_USER — nombre de usuario de Artifactory
  • ARTIFACTORY_TOKEN — token de API o contraseña de Artifactory
  • ARTIFACTORY_MAVEN_URL — URL del repositorio de Maven (p. ej., https://artifactory.example.com/artifactory/maven-virtual)
  • ARTIFACTORY_PYPI_URL — URL del repositorio de PyPI (p. ej., https://user:token@artifactory.example.com/artifactory/api/pypi/pypi-virtual/simple)
  • ARTIFACTORY_NPM_URL — URL del repositorio de npm (p. ej., https://artifactory.example.com/artifactory/api/npm/npm-virtual)
  • ARTIFACTORY_DOCKER_URL — URL del registro de Docker (p. ej., artifactory.example.com)
Esto normalmente se dividiría en tres ámbitos:
  • Para toda la cuenta (initialize): Certificado y proxy
  • En toda la organización (initialize): Instalación del entorno de ejecución del lenguaje
  • Para toda la organización (maintenance): Credenciales del registro (se actualizan en cada sesión)
Se muestra aquí de forma combinada como referencia:
initialize:
  # ── Para toda la cuenta: red y confianza ──────────────────────────────────────

  - 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

  # ── Para toda la organización: entornos de ejecución ──────────────────────────────────────────

  - 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:
  # ── Para toda la cuenta: proxy de git (se actualiza en cada sesión) ───────────────────

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

  # ── Para toda la organización: credenciales de registro (se actualizan en cada sesión) ──────────────

  - 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
En este ejemplo, todos los registries apuntan a la misma instancia de Artifactory, pero usan rutas de URL diferentes. Cada ecosistema de paquetes tiene su propio formato de endpoint. Las URL de Maven, PyPI, npm y Docker son diferentes incluso para el mismo registry.
Cuando distintos lenguajes usan diferentes repositorios privados (p. ej., Maven desde Nexus, npm desde GitHub Packages, Python desde Artifactory).
  • NEXUS_MAVEN_URL — URL del repositorio Maven de Nexus - NEXUS_USER — nombre de usuario de Nexus - NEXUS_PASS — contraseña de Nexus - GITHUB_PACKAGES_TOKEN — token de acceso personal de GitHub con el ámbito read:packages - ARTIFACTORY_USER — nombre de usuario de Artifactory - ARTIFACTORY_TOKEN — token de API de Artifactory - GIT_TOKEN — token de acceso personal para módulos privados de Go
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 (con ámbito)
  - 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 → módulos privados mediante git
  - name: Configure Go private modules
    run: |
      git config --global url."https://$GIT_TOKEN@github.com/myorg/".insteadOf "https://github.com/myorg/"
En un entorno completamente aislado de la red, Devin no puede acceder a ninguna URL pública. Todas las herramientas, los entornos de ejecución y los paquetes deben provenir de mirrors internos.
Certificados:
  • CORP_ROOT_CA_B64 — Certificado de CA corporativa codificado en Base64
Acceso al mirror:
  • APT_MIRROR_URL — URL del mirror interno de Ubuntu APT
  • MIRROR_USER — Nombre de usuario para autenticarse en el mirror
  • MIRROR_PASS — Contraseña para autenticarse en el mirror
  • JDK_TARBALL_URL — URL para descargar el archivo tar del JDK desde el mirror interno
  • NODE_TARBALL_URL — URL para descargar el archivo tar de Node.js desde el mirror interno
Registros de paquetes:
  • INTERNAL_MAVEN_URL — URL del registro interno de Maven
  • INTERNAL_NPM_URL — URL del registro interno de npm
  • INTERNAL_PYPI_URL — URL del registro interno de PyPI
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: |
      # Descargar el tarball de JDK desde el almacén de artefactos interno
      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
En entornos aislados de la red, todas las herramientas que Devin necesita (entornos de ejecución, herramientas de CLI, etc.) deben estar disponibles en tus repositorios espejo internos. No se puede acceder a los repositorios públicos ni a los sitios de descarga.
Una configuración empresarial completa que combina conectividad VPN con certificados, proxy y compatibilidad con varios idiomas. Este es el orden de operaciones recomendado.
VPN:
  • VPN_CONFIG_B64 — Archivo de configuración de OpenVPN codificado en Base64
Red y confianza:
  • CORP_ROOT_CA_B64 — Certificado de la CA corporativa codificado en Base64
  • CORP_HTTP_PROXY — URL del proxy HTTP
  • CORP_HTTPS_PROXY — URL del proxy HTTPS
  • CORP_NO_PROXY — Hosts excluidos del proxy
Credenciales del registro:
  • MAVEN_REGISTRY_URL — URL del registro de Maven
  • NPM_REGISTRY_URL — URL del registro de npm
  • PYPI_REGISTRY_HOST — Nombre de host del registro de PyPI
  • REGISTRY_USER — Nombre de usuario del registro (para Maven y pip)
  • REGISTRY_PASS — Contraseña del registro (para Maven y pip)
  • REGISTRY_TOKEN — Token de autenticación de npm
initialize:
  # 1. VPN — debe ir primero para que los recursos internos sean accesibles
  - 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 — resolver nombres de host internos
  - 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. Certificados — confiar en las CAs internas
  - 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 — enrutar el tráfico a través del proxy corporativo
  - 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. Entornos de ejecución
  - 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
El orden de los pasos de initialize es importante. La VPN debe ir primero (para que los hosts internos sean accesibles), luego el DNS (para que los nombres se resuelvan), después los certificados (para que HTTPS funcione), luego el proxy (para que el tráfico se enrute correctamente) y, por último, los entornos de ejecución, que pueden descargarse desde mirrors internos).

Consejos para escribir buenas plantillas

  • Prueba primero los comandos en una sesión. Ejecuta los comandos manualmente en una sesión de Devin antes de agregarlos a tu plantilla. Es más rápido que esperar un ciclo completo de compilación.
  • Usa initialize para herramientas que se instalan una sola vez y maintenance para dependencias. Todo lo que tarde varios minutos en instalarse (compiladores, binarios grandes, herramientas globales) debe ir en initialize. Los comandos rápidos de dependencias (npm install, uv sync) van en maintenance.
  • Haz que los comandos de maintenance sean rápidos. Procura que tarden menos de 2 minutos. Se ejecutan al inicio de cada sesión.
  • Usa $ENVRC para las variables de entorno. No escribas en .bashrc ni en .profile. $ENVRC es el mecanismo compatible para establecer variables entre pasos y sesiones.
  • Pon nombre a tus pasos. La forma expandida con campos name hace que los fallos en los registros de compilación sean mucho más fáciles de identificar.
  • Usa subshells para monorepos. (cd packages/foo && npm install) se ejecuta en una subshell para que los pasos posteriores no se vean afectados por el cambio de directorio.
  • Usa npm install, no npm ci. npm ci elimina node_modules y reinstala todo desde cero en cada sesión, lo que lo hace lento para maintenance.
  • Usa secretos del repo para los valores sensibles. Configúralos en Settings > Secrets con ámbito de repo, en lugar de incluirlos directamente en las plantillas.
Para conocer los detalles de la sintaxis, consulta la referencia de la plantilla. Para solucionar fallos de compilación, consulta Configuración declarativa > Solución de problemas.