Configuration

On this page 42

Buddy can be configured using a buddy.config.ts (or buddy.config.js) file and it will be automatically loaded when running buddy commands.

Buddy automatically detects and updates multiple dependency file formats including package.json, pkgx dependency files (deps.yaml, pkgx.yaml), Launchpad dependency files that use the same registry format, and GitHub Actions workflow dependencies.

Basic Configuration

// buddy.config.ts
import type { BuddyConfig } from '@buddysh/buddy'

const config: BuddyConfig = {
  // Enable verbose logging
  verbose: true,

  // Repository configuration (required for PR creation)
  repository: {
    provider: 'github',
    owner: 'your-org',
    name: 'your-repo',
    baseBranch: 'main', // optional, defaults to 'main'
  },

  // Package update configuration
  packages: {
    strategy: 'patch', // 'major' | 'minor' | 'patch' | 'all'
    ignore: [
      '@types/node', // Ignore specific packages
      'eslint', // Keep manual control
      'actions/checkout', // Ignore specific GitHub Actions
    ],
    pin: {
      react: '^18.0.0', // Pin to specific version ranges
    },
  },

  // Pull request configuration
  pullRequest: {
    reviewers: ['team-lead', 'senior-dev'],
    assignees: ['maintainer'],
    labels: ['dependencies', 'automated'],
    autoMerge: {
      enabled: true,
      strategy: 'squash',
      conditions: ['patch-only'],
    },
  },

  // Scheduling configuration
  schedule: {
    cron: '0 2 _ _ 1', // Weekly on Monday at 2 AM
    timezone: 'UTC',
  },
}

export default config

Advanced Configuration

Dependency File Support

Buddy automatically scans your project for various dependency file formats:

// Buddy automatically detects these file types:
const supportedFiles = [
  'package.json', // npm dependencies
  'deps.yaml', // Launchpad/pkgx dependencies
  'deps.yml', // Launchpad/pkgx dependencies (alternative extension)
  'dependencies.yaml', // Alternative dependency format
  'dependencies.yml', // Alternative dependency format
  'pkgx.yaml', // pkgx-specific dependencies
  'pkgx.yml', // pkgx-specific dependencies
  '.deps.yaml', // Hidden dependency configuration
  '.deps.yml', // Hidden dependency configuration
]

All dependency files are parsed using the ts-pkgx library and updates are applied while preserving formatting, comments, and version prefixes (^, ~, >=, etc.).

Package Groups

Organize related packages for coordinated updates:

const config: BuddyConfig = {
  packages: {
    strategy: 'all',
    groups: [
      {
        name: 'React Ecosystem',
        patterns: ['react', 'react-dom', '@types/react'],
        strategy: 'minor',
      },
      {
        name: 'Build Tools',
        patterns: ['typescript', 'vite', 'rollup'],
        strategy: 'patch',
      },
      {
        name: 'Testing',
        patterns: ['jest', '@types/jest', 'testing-library/*'],
        strategy: 'minor',
      },
    ],
  },
}

Package Rules

Groups handle "these packages travel together". Rules handle everything else: conditional labels, reviewers, auto-merge, priorities and holds, matched on any combination of package name, ecosystem, dependency type, file path, update type and installed version.

Rules are evaluated in order. Later matches override earlier ones per field, so a broad rule can set a default and a narrow one refine it. List effects — labels, reviewers, assignees — accumulate instead of replacing, because a package matching both a "security" and a "frontend" rule should carry both sets rather than whichever happened to be last.

Matchers

All matchers present on a rule must match (AND within a rule).

MatcherMatches on
matchPackagesPackage names or globs (@types/_)
matchEcosystemsnpm, composer, github-actions, docker, pkgx, zig
matchDepTypesdependencies, devDependencies, peerDependencies, …
matchUpdateTypesmajor, minor, patch
matchFilesGlobs on the manifest path, for monorepo directories
matchCurrentVersionSemver range the installed version must satisfy
scheduleCron window during which the rule applies

A rule with no matchers applies to every update. Configuration validation rejects that unless you write matchPackages: ['_'], because a matcherless rule is almost always a typo'd matcher — and a typo does not disable a rule, it widens it.

Effects

EffectDoes
enabled: falseDrops matching updates entirely
strategyNarrows what may be proposed, independent of the global
groupNamePuts matching updates in a named group
labelsAdds labels to the pull request
reviewersRequests review, unioned with pullRequest.reviewers
assigneesAssigns, unioned with pullRequest.assignees
autoMergeAllows unattended merge — see the caveat below
autoMigrateAttempts the migration for matching majors
minimumReleaseAgeMinutes a version must have been published
prPriorityOrdering within maxPRsPerRun; higher goes first

prPriority is applied before the per-run cap, so a high-priority group survives a cap that would otherwise have cut it.

autoMerge resolves conservatively across a group: every update in the pull request must allow it. One package that must not merge unattended holds back the whole PR containing it. autoMigrate resolves the other way — one package opting in is enough, since migrating what can be migrated leaves everything else exactly as it would have been.

Cookbook

Hold back majors on one package while everything else updates normally:

rules: [
  { matchPackages: ['react', 'react-dom'], matchUpdateTypes: ['major'], enabled: false },
]

Auto-merge types-only patches, keeping everything else manual:

rules: [
  {
    matchPackages: ['@types/*'],
    matchUpdateTypes: ['patch'],
    autoMerge: true,
    labels: ['types', 'automerge'],
  },
]

Per-workspace reviewers in a monorepo:

rules: [
  { matchFiles: ['packages/api/**'], reviewers: ['backend-team'] },
  { matchFiles: ['packages/web/**'], reviewers: ['frontend-team'] },
]

Weekend-only majors — the schedule is a window, not a firing minute, so a Tuesday run holds these back and a Saturday run lets them through:

rules: [
  {
    matchUpdateTypes: ['major'],
    schedule: '0 0-23 _ _ 6,0',
    scheduleTimezone: 'Europe/Berlin',
  },
]

Hold a legacy version series while letting the modern one move:

rules: [
  { matchPackages: ['vue'], matchCurrentVersion: '<3.0.0', enabled: false },
]

Prioritise security-relevant dependencies so they survive the per-run cap:

rules: [
  { matchPackages: ['*'], prPriority: 0 },
  { matchDepTypes: ['dependencies'], prPriority: 10, labels: ['runtime'] },
]

Migrating from Renovate

buddy setup converts Renovate packageRules automatically. matchPackageNames, matchPackagePrefixes, matchDepTypes, matchFileNames, matchManagers, matchCurrentVersion, automerge, labels, reviewers, assignees, groupName, prPriority and minimumReleaseAge all map across.

Three things do not, and the migration report names each one rather than approximating it:

  • Renovate schedules are natural language ("after 10pm every weekday");

    buddy rules take cron. Translating prose would be guessing.

  • Update types Renovate has and buddy does not — digest, pin,

    lockFileMaintenance, rollback, replacement.

  • matchPackagePatterns that use real regex features. Anchored prefixes

    (^@types/) convert exactly; anything else is copied verbatim with a warning, because a rule that silently matches the wrong packages is worse than one you have to rewrite.

Custom PR Templates

Customize pull request formatting:

const config: BuddyConfig = {
  pullRequest: {
    titleFormat: 'chore(deps): {updateType} {packages}',
    commitMessageFormat: 'chore(deps): update {packages}',
    bodyTemplate: `
# 🤖 Automated Dependency Update

{updateTable}

## 📋 Changes
{releaseNotes}

## 🔧 Configuration

- Strategy: {strategy}
- Packages: {packageCount}
- Labels: {labels}

    `,
  },
}

Workflow Generation

Configure GitHub Actions workflow generation:

const config: BuddyConfig = {
  workflows: {
    enabled: true,
    outputDir: '.github/workflows',
    templates: {
      daily: true,
      weekly: true,
      monthly: false,
      comprehensive: true,
      docker: false,
      monorepo: false,
    },
    custom: [
      {
        name: 'Security Updates',
        schedule: '0 _/6 _ _ _', // Every 6 hours
        strategy: 'patch',
        autoMerge: true,
        reviewers: ['security-team'],
        labels: ['security', 'dependencies'],
      },
    ],
  },
}

Configuration Options

Repository Settings

OptionTypeDescriptionDefault
provider'github'Git provider. GitHub is the only implemented providerRequired
ownerstringRepository owner/organizationRequired
namestringRepository nameRequired
baseBranchstringBase branch for PRs'main'
tokenstringAccess token (use env var)undefined
apiUrlstringREST API base URL, for GitHub Enterprise Server$GITHUB_API_URL, else <https://api.github.com>
serverUrlstringWeb base URL used for links$GITHUB_SERVER_URL, else <https://github.com>

Package Settings

OptionTypeDescriptionDefault
strategy'major' | 'minor' | 'patch' | 'all'Update strategy'all'
ignorestring[]Packages to ignore[]
pinRecord<string, string>Pin packages to versions{}
groupsPackageGroup[]Package groupingsundefined

Logging

OptionTypeDescriptionDefault
verbosebooleanShorthand for logLevel: 'debug'false
logLevel'silent' | 'error' | 'warn' | 'info' | 'debug'How much output to emit. Overrides verbose'info'

Set logLevel: 'silent' when embedding Buddy in another tool that owns its own output. BUDDY_LOG_LEVEL sets the same value from the environment.

Registry Settings

For private or self-hosted package registries. When unset, Buddy reads registry= and @scope:registry= from the project and home .npmrc, matching what npm itself would resolve.

OptionTypeDescriptionDefault
registries.npmstringnpm registry base URL.npmrc, else <https://registry.npmjs.org>
registries.npmScopesRecord<string, string>Per-scope registry overrides, keyed by scope including @.npmrc
registries.composerstringComposer/Packagist base URL<https://packagist.org>
const config: BuddyConfig = {
  registries: {
    npm: 'https://npm.internal.acme.com',
    npmScopes: {
      '@acme': 'https://npm.acme.com',
    },
  },
}

Security Settings

Buddy checks every dependency against the OSV.dev advisory database and annotates updates that resolve a known vulnerability.

OptionTypeDescriptionDefault
security.enabledbooleanQuery OSV for known vulnerabilitiestrue
security.prioritizebooleanPut advisory fixes in their own PR, created firsttrue
security.labelstringLabel applied to PRs that resolve an advisory'security'
security.minimumSeverity'low' | 'moderate' | 'high' | 'critical'Ignore advisories below this severity'low'

With prioritize on (the default), vulnerable dependencies are grouped into a single fix(deps): update vulnerable dependencies PR that is created before any routine update, so a maxPRsPerRun cap can never starve a security fix. The PR body and the dependency dashboard both list the advisory ID, severity, and the version that fixes it.

Set security.enabled: false for fully offline runs.

Pull Request Settings

OptionTypeDescriptionDefault
reviewersstring[]GitHub usernames for review[]
assigneesstring[]GitHub usernames to assign[]
labelsstring[]Labels to apply['dependencies']
autoMergeAutoMergeConfigAuto-merge configuration, see Auto-Mergeundefined

Pull Request Templates

titleFormat, commitMessageFormat and bodyTemplate accept {token} placeholders. Unknown tokens are left as-is rather than blanked, so a typo is visible instead of silently dropping content.

TokenAvailable inValue
{title}titleThe generated title
{message}commit messageThe generated commit message
{group}allUpdate group name, e.g. Non-Major Updates
{count}, {package_count}allNumber of packages in the PR
{strategy}allConfigured update strategy
{update_type}allHighest semver impact in the group
{packages}allComma-separated package names
{updates_table}bodyThe generated dependency tables and release notes
{footer}bodyThe rebase/retry checkbox
pullRequest: {
  titleFormat: '[deps] {title}',
  commitMessageFormat: 'deps: {message}',
  bodyTemplate: '# {group}\n\n{updates_table}\n\n{footer}',
}

A custom bodyTemplate replaces the generated prose, but the rebase checkbox and the machine-readable manifest are always appended — rebasing and auto-closing read the manifest, so a template cannot break the PR lifecycle.

Resolution Drift

packages.detectResolutionDrift (default true) reports packages held below their latest version by a range declared elsewhere in the dependency tree. They appear on the dependency dashboard rather than as pull requests, because no change to this repository can move them.

Pinning Packages

packages.pin holds a package at an exact version. A pin is both a ceiling and a floor: updates past the pin are dropped, and a package sitting somewhere else has an update proposed that brings it back to the pin.

packages: {
  strategy: 'all',
  pin: {
    'typescript': '5.8.2',
    '@types/node': '20.11.0',
  },
}

Use packages.ignore instead when you want a package left alone entirely.

Environment Variables

Buddy uses these environment variables:

# Required for GitHub operations
GITHUB_TOKEN=ghp_xxxxxxxxxxxx

# Optional: Alternative token name
GH_TOKEN=ghp_xxxxxxxxxxxx

# Optional: alternative token, preferred when set (needs `workflow` scope)
BUDDY_TOKEN=ghp_xxxxxxxxxxxx

# Optional: GitHub Enterprise Server. GitHub Actions sets both automatically
GITHUB_API_URL=https://github.acme.com/api/v3
GITHUB_SERVER_URL=https://github.acme.com

# Optional: custom npm registry (also read from .npmrc)
NPM_CONFIG_REGISTRY=https://npm.acme.com

# Optional: Composer registry
COMPOSER_REGISTRY_URL=https://packagist.acme.com

# Optional: lifts Docker Hub's anonymous rate limit
DOCKERHUB_TOKEN=dckr_pat_xxxxxxxxxxxx

# Optional: output verbosity (silent|error|warn|info|debug)
BUDDY_LOG_LEVEL=info

# Optional: per-request HTTP timeout in milliseconds (default: 30000)
BUDDY_HTTP_TIMEOUT_MS=30000

# Optional: Bun configuration
BUN_CONFIG_NO_CACHE=false

GitHub Enterprise Server

Buddy runs unmodified against GitHub Enterprise Server. On a GHES runner, GitHub Actions exports GITHUB_API_URL and GITHUB_SERVER_URL automatically, so no configuration is needed. Outside Actions, set them explicitly:

const config: BuddyConfig = {
  repository: {
    provider: 'github',
    owner: 'acme',
    name: 'app',
    apiUrl: 'https://github.acme.com/api/v3',
    serverUrl: 'https://github.acme.com',
  },
}

Configuration Validation

Buddy validates your configuration when it loads, before any network or git work happens, and reports every problem it finds at once:

buddy scan
Invalid buddy configuration (2 issues):
   packages.strategy: expected one of "major", "minor", "patch", "all", got "minr"
   packages.groups[0].patterns: expected a non-empty array of patterns, got []

Validation covers update strategies, package groups, cron expressions, registry and API URLs, severities, log levels, and the numeric bounds on maxPRsPerRun, minimumReleaseAge, and the release-notes limits.

You can also run it yourself:

import { formatConfigIssues, validateConfig } from '@buddysh/buddy'

const issues = validateConfig(config)
if (issues.length > 0)
  console.error(formatConfigIssues(issues))

Multiple Configurations

For different environments or workflows:

// buddy.config.ts
const isDev = process.env.NODE_ENV === 'development'
const isCI = process.env.CI === 'true'

const config: BuddyConfig = {
  verbose: isDev,
  packages: {
    strategy: isCI ? 'patch' : 'all',
    ignore: isDev ? [] : ['@types/node'],
  },
  pullRequest: {
    autoMerge: {
      enabled: isCI,
      strategy: 'squash',
    },
  },
}

export default config

TypeScript Support

Full TypeScript support with type checking:

import type { BuddyConfig, PackageGroup } from '@buddysh/buddy'

// Type-safe configuration
const config: BuddyConfig = {
  // TypeScript will validate all options
  packages: {
    strategy: 'patch', // ✅ Valid
    // strategy: 'invalid', // ❌ TypeScript error
  },
}

// Custom package groups with types
const groups: PackageGroup[] = [
  {
    name: 'Frontend',
    patterns: ['react', 'vue'],
    strategy: 'minor',
  },
]

Configuration Examples

Conservative Project

export default {
  packages: {
    strategy: 'patch',
    ignore: ['react', 'vue'], // Keep frameworks stable
  },
  pullRequest: {
    reviewers: ['tech-lead'],
    autoMerge: { enabled: false }, // Manual review required
  },
} satisfies BuddyConfig

Aggressive Updates

export default {
  packages: {
    strategy: 'all',
    groups: [
      {
        name: 'Core Dependencies',
        patterns: ['react*', 'vue*'],
        strategy: 'minor', // More conservative for core
      },
    ],
  },
  pullRequest: {
    autoMerge: {
      enabled: true,
      strategy: 'squash',
      conditions: ['patch-only'],
    },
  },
} satisfies BuddyConfig

Then run:

buddy update

Supported Dependency Types

Buddy provides comprehensive dependency management across four categories:

Package Dependencies

npm Ecosystem

  • package.json - Traditional npm, Bun, yarn, pnpm dependencies
  • Managed via bun outdated for accurate version detection

PHP/Composer Ecosystem

  • composer.json - PHP dependencies from Packagist
  • composer.lock - Lock file with exact versions
  • Managed via composer outdated and Packagist API integration

pkgx/Launchpad Ecosystem

  • deps.yaml/deps.yml - Launchpad/pkgx dependency declarations
  • dependencies.yaml/dependencies.yml - Alternative format
  • pkgx.yaml/pkgx.yml - pkgx-specific files
  • .deps.yaml/.deps.yml - Hidden configuration files
  • Managed via ts-pkgx library integration

GitHub Actions

Workflow Files

  • .github/workflows/*.yml - GitHub Actions workflow files
  • .github/workflows/*.yaml - Alternative YAML extension
  • Managed via GitHub releases API

Action Detection

Buddy automatically detects uses: statements in workflow files:

# All these formats are supported
steps:

  - uses: actions/checkout@v4 # Standard format
  - uses: oven-sh/setup-bun@v2 # Quoted
  - uses: actions/cache@v4.1.0 # Single quoted
  - uses: crazy-max/ghaction-docker@v3 # Third-party

Excluded Actions

  • Local actions: ./local-action
  • Docker actions: docker://node:18
  • Actions without versions: actions/checkout

Configuration Examples

Ignore Specific Packages

const config: BuddyConfig = {
  packages: {
    ignore: [
      // npm packages
      'react', // Keep React version stable
      '@types/node', // Manual Node.js type updates

      // Composer packages
      'laravel/framework', // Skip Laravel updates
      'php', // Platform requirement (auto-skipped)

      // GitHub Actions
      'actions/checkout', // Skip action updates
      'oven-sh/setup-bun', // Keep specific version
    ],
  },
}

Strategy Application

Update strategies apply to all dependency types:

const config: BuddyConfig = {
  packages: {
    strategy: 'patch', // Applies to npm, pkgx, AND GitHub Actions
  },
}

Pull Request Integration

All three dependency types appear in separate tables within pull requests, providing clear organization and appropriate metadata for each ecosystem.

To learn more, head over to the documentation.

Suggest a change to this page

Last updated:

Released under the MIT License.