Skip to content

🪞 包镜像

10 分钟,用 Packagist API 客户端拉取包索引、列包、统计,搭一个轻量"镜像视图"。纯 Go,无需 PHP

为什么需要镜像视图

做内部开发平台或离线镜像时,常需要一份"Packagist 上都有哪些包、各包什么版本、下载量多少"。手写 HTTP + JSON 解析很啰嗦,Packagist API 客户端把这些封装成方法,直接返回结构体。

数据源方法

用途方法返回
全站统计c.GetStatistics()*StatisticsResponseTotals.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 限定字段。

下一步

基于 MIT 许可证发布