🚀 GetPackageChanges
Fetches incremental change records for Packagist package metadata, used for incremental sync of self-hosted mirrors.
When to use
🛠️ When building a self-hosted Packagist mirror to avoid pulling the full dataset every time; 🔍 when monitoring which packages are added / updated / deleted; ⏱️ for sync jobs that need to be resumable and advance by a time cursor.
Signature
func (c *ComposerClient) GetPackageChanges(ctx context.Context, since int64) (*domain.ChangeTrackingResponse, error)Parameters
| Parameter | Type | Description |
|---|---|---|
ctx | context.Context | Context, used for timeout and cancellation |
since | int64 | Incremental cursor (Unix timestamp); pass 0 to get the current timestamp back with Actions empty, useful for obtaining the initial cursor |
Return value
| Value | Type | Description |
|---|---|---|
| Result | *domain.ChangeTrackingResponse | Contains Timestamp (the next cursor) and an Actions change list whose Type is update/delete |
| Error | error | Returned on HTTP failure, non-200 status, or JSON parse failure |
Corresponding endpoint: GET https://packagist.org/metadata/changes.json?since={timestamp}.
Example
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
func main() {
c := client.NewComposerClient(30 * time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// First sync: obtain a starting cursor
resp, err := c.GetPackageChanges(ctx, 0)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Starting cursor: %d\n", resp.Timestamp)
// Next time, use this cursor for an incremental pull
resp, err = c.GetPackageChanges(ctx, resp.Timestamp)
if err != nil {
log.Fatal(err)
}
for _, a := range resp.Actions {
fmt.Printf("[%s] %s @ %d\n", a.Type, a.Package, a.Time)
}
fmt.Printf("Next cursor: %d\n", resp.Timestamp)
}Advanced
Incremental sync pattern
When since=0, only the current Timestamp is returned without historical changes. The correct approach is: ① first call with since=0 to obtain the starting Timestamp and persist it; ② for every subsequent call, pass the previous Timestamp as since, process the returned Actions, then save the new Timestamp. If since is invalid, the response Error field will contain an error message.
- 📦 For packages with
Type=update, callGetPackageto refresh the local cache; for packages withType=delete, remove them locally. - 🧩 For V2 mirrors, use
GetPackageWithV2Metadata.