Skip to content

🛠️ Package Management

Perform write operations on packages on Packagist: create, edit, update (trigger re-fetch). These three methods all require API credentials (Packagist username + API token), used to register your repository to Packagist or update registration after repository address changes.

Credentials Required

All three methods require the client to be configured with username and API token via WithAPICredentials, otherwise will directly return error `"API credentials are required for ...". Credentials are sent as URL query parameters, please read from environment variables, do not hardcode.

When to Use

  • 🛠️ Publish new package: Register your GitHub/GitLab repository to Packagist, let it start fetching versions.
  • 📦 Repository migration: Package source repository address changed, use EditPackage to update registered repository URL.
  • 🔄 Force refresh: Repository has pushed new tag but Packagist hasn't updated yet, use UpdatePackage to trigger re-fetch.
  • 🤖 Automated release pipeline: After release, automatically notify Packagist to update.

Data Models

Request and response structures are defined in pkg/domain/create_package.go.

PackageCreateRequest / Response

go
type PackageCreateRequest struct {
    Repository string `json:"repository"`
}

type PackageCreateResponse struct {
    Status string `json:"status"`
}
FieldTypeDescription
PackageCreateRequest.RepositorystringRepository URL to register (e.g., https://github.com/vendor/package)
PackageCreateResponse.StatusstringOperation status, e.g., success

PackageEditRequest / Response

go
type PackageEditRequest struct {
    Repository string `json:"repository"`
}

type PackageEditResponse struct {
    Status string `json:"status"`
}
FieldTypeDescription
PackageEditRequest.RepositorystringNew repository URL
PackageEditResponse.StatusstringOperation status

PackageUpdateRequest / Response

go
type PackageUpdateRequest struct {
    Repository string `json:"repository"`
}

type PackageUpdateResponse struct {
    Status string `json:"status"`
    Jobs   []string `json:"jobs,omitempty"`
}
FieldTypeDescription
PackageUpdateRequest.RepositorystringRepository URL to update
PackageUpdateResponse.StatusstringOperation status
PackageUpdateResponse.Jobs[]stringTriggered backend job ID list, can be used to track fetch progress

CreatePackage

🛠️ Create (register) a new package on Packagist. Corresponds to POST https://packagist.org/api/create-package?username={user}&apiToken={token}.

Signature

go
func (c *ComposerClient) CreatePackage(ctx context.Context, request *domain.PackageCreateRequest) (*domain.PackageCreateResponse, error)

Parameters

ParameterTypeDescription
ctxcontext.ContextContext for timeout and cancellation
request*domain.PackageCreateRequestContains Repository URL

Return Values

ValueTypeDescription
Result*domain.PackageCreateResponseContains Status
ErrorerrorReturned when credentials missing, HTTP failure, non-200, or JSON parsing failure

Example

go
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"

    "github.com/scagogogo/composer-skills/pkg/client"
    "github.com/scagogogo/composer-skills/pkg/domain"
)

func main() {
    c := client.NewComposerClient(
        30*time.Second,
        client.WithAPICredentials(
            os.Getenv("PACKAGIST_USERNAME"),
            os.Getenv("PACKAGIST_API_TOKEN"),
        ),
    )

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    resp, err := c.CreatePackage(ctx, &domain.PackageCreateRequest{
        Repository: "https://github.com/myorg/my-package",
    })
    if err != nil {
        log.Fatalf("Failed to create package: %v", err)
    }
    fmt.Printf("Status: %s\n", resp.Status)
}

Non-empty Error Message

When resp.StatusCode != 200, SDK will put response body as string into error ("failed to create package: %s"), to see specific reason returned by Packagist (e.g., "repository already registered by another package").


EditPackage

🛠️ Edit repository address of an existing package. Corresponds to PUT https://packagist.org/api/packages/{name}?username={user}&apiToken={token}.

Signature

go
func (c *ComposerClient) EditPackage(ctx context.Context, packageName string, request *domain.PackageEditRequest) (*domain.PackageEditResponse, error)

Parameters

ParameterTypeDescription
ctxcontext.ContextContext
packageNamestringPackage name to edit (vendor/package)
request*domain.PackageEditRequestContains new Repository URL

Return Values

ValueTypeDescription
Result*domain.PackageEditResponseContains Status
ErrorerrorReturned when credentials missing, HTTP failure, non-200, or JSON parsing failure

Example

go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

resp, err := c.EditPackage(ctx, "myorg/my-package", &domain.PackageEditRequest{
    Repository: "https://github.com/myorg/my-package-new-location",
})
if err != nil {
    log.Fatalf("Failed to edit package: %v", err)
}
fmt.Printf("Status: %s\n", resp.Status)

Repository Migration Scenario

When your package source migrates from GitHub to GitLab (or changes org), the repository address in package's composer.json will change. Use EditPackage to change the registered repository URL on Packagist, afterwards Packagist will fetch versions from new repository.


UpdatePackage

🛠️ Trigger Packagist to re-fetch specified repository (force update). Corresponds to POST https://packagist.org/api/update-package?username={user}&apiToken={token}.

Signature

go
func (c *ComposerClient) UpdatePackage(ctx context.Context, request *domain.PackageUpdateRequest) (*domain.PackageUpdateResponse, error)

Parameters

ParameterTypeDescription
ctxcontext.ContextContext
request*domain.PackageUpdateRequestContains Repository URL

Return Values

ValueTypeDescription
Result*domain.PackageUpdateResponseContains Status and Jobs (backend job IDs)
ErrorerrorReturned when credentials missing, HTTP failure, non-200, or JSON parsing failure

Example

go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

resp, err := c.UpdatePackage(ctx, &domain.PackageUpdateRequest{
    Repository: "https://github.com/myorg/my-package",
})
if err != nil {
    log.Fatalf("Failed to update package: %v", err)
}
fmt.Printf("Status: %s\n", resp.Status)
fmt.Printf("Jobs: %v\n", resp.Jobs)

When to Use Update vs Edit

  • EditPackage: Change repository address itself (package points to different source repository).
  • UpdatePackage: Repository address unchanged, but want Packagist to re-fetch immediately (e.g., just pushed new tag, can't wait for automatic fetch).

UpdatePackage returns Jobs which are Packagist backend fetch job IDs, can be used to track whether fetch is complete.

Advanced Topics

Comparison of Three Write Operations

MethodHTTPPathPurpose
CreatePackagePOST/api/create-packageRegister new package
EditPackagePUT/api/packages/{name}Modify package repository address
UpdatePackagePOST/api/update-packageTrigger re-fetch

Automated Release Pipeline

Typical "tag → release" pipeline fragment:

go
// 1. After pushing tag, trigger Packagist update
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

resp, err := c.UpdatePackage(ctx, &domain.PackageUpdateRequest{
    Repository: "https://github.com/myorg/my-package",
})
if err != nil {
    log.Printf("Failed to trigger update: %v\n", err)
    return
}
log.Printf("Update triggered, jobs: %v\n", resp.Jobs)

// 2. Poll GetPackage to confirm new version is indexed
for i := 0; i < 10; i++ {
    time.Sleep(15 * time.Second)
    info, err := c.GetPackage("myorg/my-package")
    if err == nil {
        if _, ok := info.Package.Versions["v1.2.3"]; ok {
            log.Println("New version indexed")
            break
        }
    }
}

Released under the MIT License