Skip to content

🔄 CI/CD pipeline

Composer Skills is designed for CI/CD: typed return values make "failure decisions" a one-line if, and auto-install means you don't need to pre-install Composer on the runner. This page provides a complete example of using it in GitHub Actions.

🎯 Typical pipeline

A PHP project's CI pipeline usually has four steps:

  1. 📥 Auto-install — SDK auto-provisions Composer when missing.
  2. 📦 install — Install project dependencies.
  3. 🔒 audit — Security audit; fail if vulnerabilities found.
  4. validate — Validate composer.json schema.

Below we write these four steps as Go code and pair it with a GitHub Actions workflow.

📝 Go code: pipeline main program

Save the following as ci/main.go. It will auto-install Composer, install dependencies, audit vulnerabilities, validate schema — and exit non-zero on any failure (CI red light).

go
package main

import (
    "fmt"
    "log"
    "os"

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

func main() {
    workingDir := os.Getenv("GITHUB_WORKSPACE")
    if workingDir == "" {
        workingDir = "."
    }

    // 1. Auto-install + create instance
    //    QuickSetup auto-pulls Composer if missing
    comp, err := composer.QuickSetup(workingDir, true)
    if err != nil {
        log.Fatalf("❌ Composer init failed: %v", err)
    }

    // 2. Install dependencies (--no-dev can be adjusted as needed)
    fmt.Println("📦 Installing dependencies...")
    if err := comp.Install(false, true); err != nil {
        log.Fatalf("❌ install failed: %v", err)
    }

    // 3. Security audit
    fmt.Println("🔒 Security audit...")
    result, err := comp.AuditWithJSON()
    if err != nil {
        log.Fatalf("❌ audit failed: %v", err)
    }
    fmt.Printf("   Vulnerabilities found: %d\n", result.Found)
    for _, v := range result.Advisories {
        fmt.Printf("   ⚠ %s: %s (%s)\n", v.Package, v.Title, v.Severity)
    }
    if result.Found > 0 {
        log.Fatalf("❌ %d vulnerabilities present, pipeline failed", result.Found)
    }

    // 4. Validate composer.json
    fmt.Println("✅ Validating composer.json...")
    res, err := comp.ValidateStructured()
    if err != nil {
        log.Fatalf("❌ validate failed: %v", err)
    }
    if !res.Valid {
        for _, e := range res.Errors {
            fmt.Printf("   ❌ Line %d: %s\n", e.Line, e.Message)
        }
        log.Fatal("❌ composer.json validation failed")
    }

    fmt.Println("🎉 Pipeline passed completely")
}

Fail = red light

In CI, a process exiting with a non-zero code marks the job red. We use log.Fatalf to exit immediately on any failure, no extra conditionals needed.

⚙️ GitHub Actions workflow

.github/workflows/composer-ci.yml:

yaml
name: Composer CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  composer-skills:
    runs-on: ubuntu-latest
    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v4

      - name: 🐹 Install Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.23'

      - name: 🐘 Install PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          # Composer Skills can auto-install Composer too; pre-installing saves time
          coverage: none

      - name: 📦 Download Go dependencies
        run: |
          go mod download
          go mod tidy

      - name: 🚀 Run Composer Skills pipeline
        run: go run ./ci/main.go

      - name: 📤 Upload audit report (optional)
        if: always()
        run: |
          # You could call comp.AuditWithJSON and write results to a file, then upload-artifact
          echo "Audit completed"

Why still use setup-php?

Composer Skills can auto-install Composer, but PHP itself needs to be pre-installed or pulled by the SDK's installer.InstallPHP (via apt on Linux). In CI, setup-php is faster and more reliable; SDK auto-install serves as a fallback.

🔒 Pure remote audit (PHP-less runner)

If you just want to pull Packagist security advisories for vulnerability scanning, you don't need PHP at all — use the Packagist API SDK; the runner is smaller:

go
package main

import (
    "fmt"
    "os"

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

func main() {
    c := client.NewComposerClient(30 * 0) // Set a reasonable timeout
    advisories, err := c.GetSecurityAdvisoriesForPackages(
        "monolog/monolog", "symfony/console",
    )
    if err != nil {
        fmt.Printf("❌ %v\n", err)
        os.Exit(1)
    }
    if len(advisories.Advisories) > 0 {
        fmt.Printf("❌ Found %d advisories\n", len(advisories.Advisories))
        os.Exit(1)
    }
    fmt.Println("✅ No security advisories")
}

The corresponding workflow can drop setup-php and keep only Go.

💡 Advanced suggestions

  • 🧪 Cache dependencies: Add actions/cache in the workflow to cache ~/.composer/cache and speed up install.
  • 🔑 Private repo auth: Store GitHub Token as a repo Secret, inject it via workflow env, then in Go code use comp.AddGitHubToken("github.com", os.Getenv("GITHUB_TOKEN")) to access private packages.
  • 📈 Outdated package monitoring: Run comp.GetOutdatedInfo() in a scheduled job (on: schedule) and open an issue if outdated.
  • 🧾 License check: comp.GetLicensesInfo() outputs dependency licenses to avoid non-compliant licenses.

Timeout

Commands in CI can hang due to slow networks. Use Options with a timeout:

go
options := composer.DefaultOptions()
options.DefaultTimeout = 30 * 60 * 1e9 // 30 minutes (nanoseconds)
comp, _ = composer.New(options)

🧭 Next steps

Released under the MIT License