🪞 包镜像
10 分钟,用 Packagist API 客户端拉取包索引、列包、统计,搭一个轻量"镜像视图"。纯 Go,无需 PHP。
为什么需要镜像视图
做内部开发平台或离线镜像时,常需要一份"Packagist 上都有哪些包、各包什么版本、下载量多少"。手写 HTTP + JSON 解析很啰嗦,Packagist API 客户端把这些封装成方法,直接返回结构体。
数据源方法
| 用途 | 方法 | 返回 |
|---|---|---|
| 全站统计 | c.GetStatistics() | *StatisticsResponse(Totals.Packages/Versions/Downloads) |
| 全部包名 | c.ListPackages() | *PackageListResponse |
| 按厂商列包 | c.ListPackagesByVendor(vendor) | *PackageListResponse |
| 按类型列包 | c.ListPackagesByType(type) | *PackageListResponse |
| 受欢迎包 | c.ListPopularPackages(perPage) | *PopularPackagesResponse |
| 包详情 | c.GetPackage(name) | *ComposerPackageInfo |
完整可运行示例
go
package main
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
type MirrorSnapshot struct {
CapturedAt time.Time `json:"captured_at"`
TotalPackages int `json:"total_packages"`
TotalVersions int `json:"total_versions"`
TotalDownloads int64 `json:"total_downloads"`
VendorPackages map[string]int `json:"vendor_packages"`
PopularTop []string `json:"popular_top"`
}
func main() {
c := client.NewComposerClient(60 * time.Second)
snap := MirrorSnapshot{CapturedAt: time.Now(), VendorPackages: map[string]int{}}
// 1️⃣ 全站统计
if stats, err := c.GetStatistics(); err == nil {
snap.TotalPackages = stats.Totals.Packages
snap.TotalVersions = stats.Totals.Versions
snap.TotalDownloads = stats.Totals.Downloads
fmt.Printf("📊 包数: %d | 版本数: %d | 下载: %d\n",
snap.TotalPackages, snap.TotalVersions, snap.TotalDownloads)
}
// 2️⃣ 按厂商统计包数
for _, vendor := range []string{"symfony", "laravel", "guzzlehttp", "psr"} {
if list, err := c.ListPackagesByVendor(vendor); err == nil {
snap.VendorPackages[vendor] = len(list.PackageNames)
fmt.Printf("🏷️ %s/*: %d 个包\n", vendor, len(list.PackageNames))
}
}
// 3️⃣ 受欢迎包 Top N
if popular, err := c.ListPopularPackages(10); err == nil {
for _, p := range popular.Packages {
snap.PopularTop = append(snap.PopularTop, p.Name)
}
fmt.Printf("🌟 热门包 Top %d: %v\n", len(snap.PopularTop), snap.PopularTop)
}
// 4️⃣ 落盘快照
b, _ := json.MarshalIndent(snap, "", " ")
os.WriteFile("mirror-snapshot.json", b, 0644)
fmt.Println("\n✅ 快照已写入 mirror-snapshot.json")
}预期输出
📊 包数: 390000 | 版本数: 3200000 | 下载: 9876543210
🏷️ symfony/*: 156 个包
🏷️ laravel/*: 24 个包
🏷️ guzzlehttp/*: 12 个包
🏷️ psr/*: 18 个包
🌟 热门包 Top 10: [psr/log monolog/monolog symfony/console ...]
✅ 快照已写入 mirror-snapshot.json进阶:增量同步与字段裁剪
c.GetPackageChanges(ctx, since)返回自since时间戳以来变更的包,适合定时增量同步镜像。c.ListPackagesWithData(fields)只拉指定字段(如name,downloads),减少传输量。
go
// 增量同步:只处理过去一小时内变更的包
changes, _ := c.GetPackageChanges(context.Background(), time.Now().Add(-time.Hour).Unix())
fmt.Printf("近 1 小时变更包: %d\n", len(changes.Actions))
// 精简列表:只要包名和下载量
slim, _ := c.ListPackagesWithData([]string{"name", "downloads"})镜像 vs 完整克隆
本教程是"视图级镜像"——拉索引和统计,用于看板/搜索。如果你要建一个能真正 composer install 用的离线镜像,请用 🏭 Satis 私有仓库构建器。
分页与体量
ListPackages() 会返回全量包名列表,体积可达数十 MB。生产环境建议分厂商/分类型拉取,或用 ListPackagesWithData 限定字段。
下一步
- 把镜像快照和依赖健康数据合并展示:📈 依赖仪表盘。
- 镜像之外再做安全扫描:🔒 构建安全审计流水线。