es

Integración con CI

Usa pcpm ci para instalaciones estrictas y reproducibles en CI.

CI es donde PCPM se amortiza. Un build clean-room de una solución .NET grande que tardaba minutos se convierte en unos pocos segundos, porque el store global ya está caliente en la mayoría de runners de CI, y pcpm ci es lo bastante estricto como para detectar drift de “funciona en mi máquina”.

La forma de una ejecución de CI con PCPM

  1. Restaurar: pcpm ci — instala exactamente lo que dice pcpm.lock, sin flotantes, sin sorpresas, falla rápido si el lockfile está desactualizado.
  2. Build: dotnet build — sin cambios respecto a un pipeline sin PCPM.
  3. Test: dotnet test — sin cambios.

Puedes hacer los pasos 2 y 3 con una sola invocación de dotnet test; PCPM solo se ocupa del paso 1.

¿Por qué pcpm ci y no pcpm install?

pcpm install es un comando para desarrolladores. Hace lo posible por seguir adelante incluso si el lockfile está desactualizado — por ejemplo, si haces pcpm add Foo y olvidas commitear pcpm.lock, pcpm install re-resolverá y actualizará el lockfile.

pcpm ci es un comando de CI. Hace lo siguiente:

  • Rehúsa ejecutarse si falta pcpm.lock.
  • Rehúsa ejecutarse si Directory.Packages.props y pcpm.lock no coinciden.
  • Nunca escribe en pcpm.lock. O bien tiene éxito con las versiones exactas del lockfile, o bien falla con un exit no-cero.

Esto atrapa una clase de bugs que de otro modo serían silenciosos: alguien sube una versión en CPM y olvida actualizar el lockfile. En la máquina del desarrollador, pcpm install re-resolvería y la nueva versión se colaría. En CI, pcpm ci fallaría y el build quedaría rojo.

GitHub Actions

name: build

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x

      - name: Cache PCPM store
        uses: actions/cache@v4
        with:
          path: |
            ~/.local/share/pcpm/store
            ~/.nuget/packages
          key: pcpm-${{ hashFiles('**/pcpm.lock') }}
          restore-keys: |
            pcpm-

      - name: Install PCPM
        run: dotnet tool install --global pcpm

      - name: Restore
        run: pcpm ci

      - name: Build
        run: dotnet build --no-restore -c Release

      - name: Test
        run: dotnet test --no-build -c Release

El paso de caché es lo que hace esto rápido. La clave de caché es pcpm.lock, así que el store se reutiliza mientras el lockfile sea el mismo. Una restauración típica con caché tarda 2-5 segundos en una solución de 50 proyectos.

GitLab CI

build:
  image: mcr.microsoft.com/dotnet/sdk:10.0
  variables:
    PCPM_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pcpm"
  cache:
    key:
      files:
        - pcpm.lock
    paths:
      - .cache/pcpm
  before_script:
    - dotnet tool install --global pcpm
  script:
    - pcpm ci
    - dotnet build --no-restore -c Release
    - dotnet test --no-build -c Release

Notas sobre la caché

  • Clave de caché: pcpm.lock es la granularidad correcta. Un cambio en el lockfile invalida la caché, por diseño.
  • Ruta de caché: Es el store global. En Linux es ~/.local/share/pcpm/store; en macOS, ~/Library/Application Support/pcpm/store; en Windows, %LOCALAPPDATA%\pcpm\store. Usa pcpm store path para obtener el valor exacto en tiempo de ejecución.
  • Cachea también ~/.nuget/packages: el layout con hardlinks hace que esto sea pequeño (unos pocos cientos de MB en soluciones grandes), pero cachearlo hace que dotnet restore mismo sea un no-op.
  • No caches entre ramas en builds de PR: un PR que añade una dependencia no debería contaminar la caché de la rama principal. La mayoría de proveedores de CI lo manejan automáticamente (la clave de caché incluye la rama).

Pre-flight local

Antes de hacer push, puedes ejecutar el mismo comando en local:

pcpm ci

Si pasa en local, pasará en CI. Esto es intencional — pcpm ci es el mismo binario en ambos entornos.