Skip to content

Commit 7eccc85

Browse files
committed
feat: add Zig support
1 parent 84b01ca commit 7eccc85

File tree

7 files changed

+950
-10
lines changed

7 files changed

+950
-10
lines changed

README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
# 🐶 Buddy Bot
1010

11-
> The fastest, most intelligent dependency management bot for modern JavaScript and TypeScript projects _(and PHP)_.
11+
> The fastest, most intelligent dependency management bot for modern JavaScript and TypeScript projects _(and PHP, Zig)_.
1212
13-
Buddy Bot is a lightning-fast alternative to Dependabot and Renovate, purpose-built for modern JavaScript, TypeScript, and PHP ecosystems. It intelligently scans your projects, detects outdated & deprecated dependencies across multiple package managers, and creates beautifully formatted pull requests with comprehensive changelogs and metadata.
13+
Buddy Bot is a lightning-fast alternative to Dependabot and Renovate, purpose-built for modern JavaScript, TypeScript, PHP, and Zig ecosystems. It intelligently scans your projects, detects outdated & deprecated dependencies across multiple package managers, and creates beautifully formatted pull requests with comprehensive changelogs and metadata.
1414

1515
![Buddy Bot Pull Request Example](.github/art/screenshot.png)
1616

@@ -24,9 +24,10 @@ Buddy Bot is a lightning-fast alternative to Dependabot and Renovate, purpose-bu
2424

2525
### 📦 **Universal Package Support**
2626

27-
- **Multi-Package Manager**: _Full support for Bun, npm, yarn, pnpm, Composer, pkgx & Launchpad_
27+
- **Multi-Package Manager**: _Full support for Bun, npm, yarn, pnpm, Composer, Zig, pkgx & Launchpad_
2828
- **GitHub Actions**: _Automatically updates workflow dependencies (`actions/checkout@v4`, etc.)_
2929
- **Docker Images**: _Detects and updates Dockerfile base images and versions_
30+
- **Zig Dependencies**: _Manages build.zig.zon dependencies with URL and hash tracking_
3031
- **Lock File Awareness**: _Respects and updates all lock file formats_
3132

3233
### 🎯 **Smart Dependency Management**
@@ -45,7 +46,7 @@ Buddy Bot is a lightning-fast alternative to Dependabot and Renovate, purpose-bu
4546

4647
### 🎨 **Beautiful Pull Requests**
4748

48-
- **Multi-Format Tables**: _Separate sections for npm, PHP/Composer, pkgx/Launchpad, and GitHub Actions_
49+
- **Multi-Format Tables**: _Separate sections for npm, PHP/Composer, Zig, pkgx/Launchpad, and GitHub Actions_
4950
- **Rich Metadata**: _Confidence badges, adoption metrics, age indicators, and download stats_
5051
- **Detailed Changelogs**: _Automatic release notes and breaking change detection_
5152
- **Professional Formatting**: _Clean, readable PR descriptions with proper categorization_
@@ -561,6 +562,7 @@ For the rebase functionality to update GitHub Actions workflow files, you need p
561562
- ✅ **package.json** - npm/yarn/pnpm dependencies
562563
- ✅ **Lock files** - package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb
563564
- ✅ **Dependency files** - deps.yaml, dependencies.yaml, pkgx.yaml
565+
- ✅ **Zig manifests** - build.zig.zon with URL and hash updates
564566
- ✅ **GitHub Actions** - workflow files (with proper permissions)
565567
- ✅ **PR content** - Updated title, body, and metadata
566568
@@ -643,6 +645,7 @@ Buddy automatically detects and updates the following dependency file formats:
643645
- **package.json** - Traditional npm dependencies
644646
- **composer.json** - PHP dependencies from Packagist
645647
- **composer.lock** - PHP lock file with exact versions
648+
- **build.zig.zon** - Zig package manager dependencies with URL and hash tracking
646649
- **deps.yaml** / **deps.yml** - Launchpad/pkgx dependency declarations
647650
- **dependencies.yaml** / **dependencies.yml** - Alternative dependency file format
648651
- **pkgx.yaml** / **pkgx.yml** - pkgx-specific dependency files
@@ -657,7 +660,7 @@ All dependency files are parsed using the `ts-pkgx` library to ensure compatibil
657660
658661
### Pull Request Format
659662
660-
Buddy generates comprehensive pull requests with **three separate dependency tables**:
663+
Buddy generates comprehensive pull requests with **separate dependency tables** for each ecosystem:
661664
662665
#### 1. npm Dependencies
663666
@@ -680,7 +683,17 @@ Focused table for PHP packages from Packagist:
680683
| phpunit/phpunit | ^10.0.0 → ^10.3.0 | composer.json | ✅ Available |
681684
```
682685
683-
#### 3. Launchpad/pkgx Dependencies
686+
#### 3. Zig Dependencies
687+
688+
Focused table for Zig packages with repository links and update types:
689+
690+
```
691+
| Package | Change | Type | File |
692+
|---------|--------|------|------|
693+
| httpz | 0.5.0 → 0.6.0 | 🟡 minor | build.zig.zon |
694+
```
695+
696+
#### 4. Launchpad/pkgx Dependencies
684697
685698
Simplified table focusing on package updates and file locations:
686699
@@ -690,7 +703,7 @@ Simplified table focusing on package updates and file locations:
690703
| bun.com | ^1.2.16 → ^1.2.19 | deps.yaml | ✅ Available |
691704
```
692705
693-
#### 4. GitHub Actions
706+
#### 5. GitHub Actions
694707
695708
Workflow automation updates with direct links to repositories:
696709

src/pr/pr-generator.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ export class PullRequestGenerator {
8484
u.file.endsWith('composer.json') || u.file.endsWith('composer.lock'),
8585
).length
8686

87+
const zigCount = group.updates.filter(u =>
88+
u.file.endsWith('build.zig.zon'),
89+
).length
90+
8791
const systemCount = group.updates.filter(u =>
8892
(u.file.includes('.yaml') || u.file.includes('.yml')) && !u.file.includes('.github/workflows/'),
8993
).length
@@ -97,6 +101,8 @@ export class PullRequestGenerator {
97101
labels.push('npm')
98102
if (composerCount > 0)
99103
labels.push('composer')
104+
if (zigCount > 0)
105+
labels.push('zig')
100106
if (systemCount > 0)
101107
labels.push('system')
102108
if (actionsCount > 0)
@@ -171,6 +177,9 @@ export class PullRequestGenerator {
171177
const composerCount = group.updates.filter(u =>
172178
u.file.endsWith('composer.json') || u.file.endsWith('composer.lock'),
173179
).length
180+
const zigCount = group.updates.filter(u =>
181+
u.file.endsWith('build.zig.zon'),
182+
).length
174183

175184
this.log('📊 Package type counts', {
176185
packageJsonCount,
@@ -210,6 +219,8 @@ export class PullRequestGenerator {
210219
body += `| 🚀 GitHub Actions | ${githubActionsCount} |\n`
211220
if (composerCount > 0)
212221
body += `| 🐘 Composer Packages | ${composerCount} |\n`
222+
if (zigCount > 0)
223+
body += `| ⚡ Zig Packages | ${zigCount} |\n`
213224
body += `| **Total** | **${group.updates.length}** |\n\n`
214225
}
215226

@@ -220,6 +231,9 @@ export class PullRequestGenerator {
220231
const composerUpdates = group.updates.filter(update =>
221232
update.file.endsWith('composer.json') || update.file.endsWith('composer.lock'),
222233
)
234+
const zigUpdates = group.updates.filter(update =>
235+
update.file.endsWith('build.zig.zon'),
236+
)
223237
const dependencyFileUpdates = group.updates.filter(update =>
224238
update.file.includes('.yaml') || update.file.includes('.yml'),
225239
).filter(update => !update.file.includes('.github/workflows/'))
@@ -425,6 +439,59 @@ export class PullRequestGenerator {
425439
body += `\n`
426440
}
427441

442+
// Zig dependencies table
443+
if (zigUpdates.length > 0) {
444+
// Only show section header for multi-package updates or when there are multiple package types
445+
if (isMultiPackageUpdate || zigCount < group.updates.length) {
446+
body += `## ⚡ Zig Dependencies\n\n`
447+
}
448+
449+
body += `![zig](https://img.shields.io/badge/Zig-F7A41D?style=flat&logo=zig&logoColor=white)\n\n`
450+
451+
// Only show count text for multi-package updates
452+
if (zigUpdates.length > 1) {
453+
body += `*${zigUpdates.length} packages will be updated*\n\n`
454+
}
455+
456+
body += `| Package | Change | Type | File |\n`
457+
body += `|---|---|---|---|\n`
458+
459+
for (const update of zigUpdates) {
460+
// Generate package link from metadata if available
461+
let packageCell: string
462+
const metadata = (update as any).metadata
463+
if (metadata?.url) {
464+
// Extract repository URL from tarball URL
465+
const repoMatch = metadata.url.match(/https?:\/\/github\.com\/([^\/]+\/[^\/]+)/)
466+
if (repoMatch) {
467+
const repoUrl = `https://github.com/${repoMatch[1]}`
468+
packageCell = `[${update.name}](${repoUrl})`
469+
}
470+
else {
471+
packageCell = update.name
472+
}
473+
}
474+
else {
475+
packageCell = update.name
476+
}
477+
478+
// Enhanced version change display
479+
const updateType = getUpdateType(update.currentVersion, update.newVersion)
480+
const typeEmoji = updateType === 'major' ? '🔴' : updateType === 'minor' ? '🟡' : '🟢'
481+
const change = this.formatVersionChange(update.currentVersion, update.newVersion)
482+
483+
// File reference
484+
const fileName = update.file.split('/').pop() || update.file
485+
const fileCell = this.config?.repository?.owner && this.config?.repository?.name
486+
? `[\`${fileName}\`](https://github.com/${this.config.repository.owner}/${this.config.repository.name}/blob/main/${update.file})`
487+
: `\`${fileName}\``
488+
489+
body += `| ${packageCell} | ${change} | ${typeEmoji} ${updateType} | ${fileCell} |\n`
490+
}
491+
492+
body += `\n`
493+
}
494+
428495
// Dependency files table (enhanced with more information)
429496
if (dependencyFileUpdates.length > 0) {
430497
// Only show section header for multi-package updates or when there are multiple package types

src/scanner/package-scanner.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { BuddyError } from '../types'
1010
import { isDependencyFile, parseDependencyFile as parseDepFile } from '../utils/dependency-file-parser'
1111
import { isDockerfile, parseDockerfile as parseDockerfileUtil } from '../utils/dockerfile-parser'
1212
import { isGitHubActionsFile, parseGitHubActionsFile } from '../utils/github-actions-parser'
13+
import { isZigManifest, parseZigManifest } from '../utils/zig-parser'
1314

1415
export class PackageScanner {
1516
private ignoreGlobs: Glob[] = []
@@ -110,6 +111,20 @@ export class PackageScanner {
110111
}
111112
}
112113

114+
// Look for Zig manifest files (build.zig.zon)
115+
const zigManifests = await this.findZigManifests()
116+
this.logger.info(`🔍 Found ${zigManifests.length} Zig manifest(s): ${zigManifests.join(', ')}`)
117+
for (const filePath of zigManifests) {
118+
if (this.shouldIgnorePath(filePath)) {
119+
continue
120+
}
121+
const packageFile = await this.parseZigManifestFile(filePath)
122+
if (packageFile) {
123+
packageFiles.push(packageFile)
124+
this.logger.info(`📦 Parsed Zig manifest: ${filePath} with ${packageFile.dependencies.length} dependencies`)
125+
}
126+
}
127+
113128
const duration = Date.now() - startTime
114129
this.logger.success(`Found ${packageFiles.length} package files in ${duration}ms`)
115130

@@ -336,6 +351,40 @@ export class PackageScanner {
336351
}
337352
}
338353

354+
/**
355+
* Find Zig manifest files (build.zig.zon) in the project
356+
*/
357+
private async findZigManifests(): Promise<string[]> {
358+
const zigManifests: string[] = []
359+
360+
try {
361+
// Look for build.zig.zon files
362+
const manifestFiles = await this.findFiles('build.zig.zon')
363+
zigManifests.push(...manifestFiles)
364+
}
365+
catch {
366+
// Ignore if no Zig manifests exist
367+
}
368+
369+
return zigManifests
370+
}
371+
372+
/**
373+
* Parse a Zig manifest file (build.zig.zon)
374+
*/
375+
async parseZigManifestFile(filePath: string): Promise<PackageFile | null> {
376+
try {
377+
const fullPath = join(this.projectPath, filePath)
378+
const content = await readFile(fullPath, 'utf-8')
379+
const result = await parseZigManifest(filePath, content)
380+
return result
381+
}
382+
catch (_error) {
383+
this.logger.warn(`Failed to parse Zig manifest ${filePath}:`, _error)
384+
return null
385+
}
386+
}
387+
339388
/**
340389
* Find Composer files in the project
341390
*/

src/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export interface PackageFile {
156156
/** File path relative to repository root */
157157
path: string
158158
/** Type of package file */
159-
type: 'package.json' | 'bun.lockb' | 'package-lock.json' | 'yarn.lock' | 'pnpm-lock.yaml' | 'deps.yaml' | 'deps.yml' | 'dependencies.yaml' | 'dependencies.yml' | 'pkgx.yaml' | 'pkgx.yml' | '.deps.yaml' | '.deps.yml' | 'composer.json' | 'composer.lock' | 'github-actions' | 'Dockerfile'
159+
type: 'package.json' | 'bun.lockb' | 'package-lock.json' | 'yarn.lock' | 'pnpm-lock.yaml' | 'deps.yaml' | 'deps.yml' | 'dependencies.yaml' | 'dependencies.yml' | 'pkgx.yaml' | 'pkgx.yml' | '.deps.yaml' | '.deps.yml' | 'composer.json' | 'composer.lock' | 'github-actions' | 'Dockerfile' | 'build.zig.zon'
160160
/** Raw file content */
161161
content: string
162162
/** Parsed dependencies */
@@ -169,11 +169,13 @@ export interface Dependency {
169169
/** Current version or range */
170170
currentVersion: string
171171
/** Dependency type */
172-
type: 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'require' | 'require-dev' | 'github-actions' | 'docker-image'
172+
type: 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'require' | 'require-dev' | 'github-actions' | 'docker-image' | 'zig-dependencies'
173173
/** File where dependency is defined */
174174
file: string
175175
/** Line number in file */
176176
line?: number
177+
/** Additional metadata (e.g., URL, hash for Zig dependencies) */
178+
metadata?: Record<string, string>
177179
}
178180

179181
export interface PackageUpdate {
@@ -186,7 +188,7 @@ export interface PackageUpdate {
186188
/** Update type */
187189
updateType: 'major' | 'minor' | 'patch'
188190
/** Dependency type */
189-
dependencyType: 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'require' | 'require-dev' | 'github-actions' | 'docker-image'
191+
dependencyType: 'dependencies' | 'devDependencies' | 'peerDependencies' | 'optionalDependencies' | 'require' | 'require-dev' | 'github-actions' | 'docker-image' | 'zig-dependencies'
190192
/** Source file */
191193
file: string
192194
/** Package metadata from registry */

0 commit comments

Comments
 (0)