Skip to content

🏭 Satis Private Repository Builder

In 18 minutes, use the Composer SDK's Satis methods to initialize config, add repositories, add require rules, and build a hostable static private Packagist. Requires local PHP + Composer.

Why Satis?

Companies have private packages that can't go on Packagist, but still want unified Composer management. Satis packages your specified private Git repos into a static website (containing packages.json), so the team can pull directly from it during composer install.

Step 1: Initialize Configuration

comp.InitSatis(name, homepage, dir) generates a satis.json skeleton in dir. You can also use CreateSatisConfig(configPath, name, homepage) to explicitly specify the config file path.

Step 2: Add Repositories & Requires

  • AddSatisRepository(configPath, type, url) — Append a repository to the config (type is usually git/vcs).
  • AddSatisRequire(configPath, packageName, version) — Limit which packages to build; omit to use require-all: true.

Step 3: Build

comp.BuildSatis(configPath, outputDir) executes satis build, downloading and packaging all versions into outputDir, returning the build output text.

Complete Runnable Example

go
package main

import (
	"fmt"
	"log"

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

func main() {
	comp, err := composer.New(composer.DefaultOptions())
	if err != nil {
		log.Fatal(err)
	}

	const (
		cfgPath = "/tmp/satis/satis.json"
		outDir  = "/tmp/satis/public"
		home    = "https://composer.internal.mycompany.com"
	)

	// 1️⃣ Initialize configuration
	if err := comp.InitSatis("mycompany/satis", home, "/tmp/satis"); err != nil {
		log.Fatalf("init: %v", err)
	}
	fmt.Println("📄 satis.json generated")

	// 2️⃣ Add private VCS repositories
	repos := []struct{ typ, url string }{
		{"git", "https://git.internal.mycompany.com/team/auth.git"},
		{"git", "https://git.internal.mycompany.com/team/billing.git"},
	}
	for _, r := range repos {
		if err := comp.AddSatisRepository(cfgPath, r.typ, r.url); err != nil {
			log.Fatalf("add repo %s: %v", r.url, err)
		}
		fmt.Printf("🔗 Added repository: %s\n", r.url)
	}

	// 3️⃣ Limit build scope (optional, omit for require-all)
	if err := comp.AddSatisRequire(cfgPath, "mycompany/auth", "*"); err != nil {
		log.Fatalf("add require: %v", err)
	}
	if err := comp.AddSatisRequire(cfgPath, "mycompany/billing", "*"); err != nil {
		log.Fatalf("add require: %v", err)
	}

	// 4️⃣ Build
	output, err := comp.BuildSatis(cfgPath, outDir)
	if err != nil {
		log.Fatalf("build: %v", err)
	}
	fmt.Println("🏗️  Build output:")
	fmt.Println(output)
	fmt.Printf("✅ Static repository generated at %s, containing packages.json\n", outDir)
}

Expected Output

📄 satis.json generated
🔗 Added repository: https://git.internal.mycompany.com/team/auth.git
🔗 Added repository: https://git.internal.mycompany.com/team/billing.git
🏗️  Build output:
Building dev-main
Collecting packages...
✅ Static repository generated at /tmp/satis/public, containing packages.json

Step 4: Let Projects Consume This Repository

Host the built outDir with any static server (nginx / Caddy / object storage). In the consuming PHP project, add the repository and install:

bash
composer config repositories.mycompany composer https://composer.internal.mycompany.com
composer require mycompany/auth:"*"

Advanced: Custom Configuration Structure

pkg/composer.SatisConfig is a public struct that can be directly read/written, then persisted with standard json.MarshalIndent, giving flexible control over minimum-stability, archive (offline zip packaging), providers (V2 sharding), etc.:

go
cfg := composer.SatisConfig{
	Name:             "mycompany/satis",
	Homepage:         home,
	OutputDir:        "public",
	RequireAll:       false,
	MinimumStability: "stable",
	Archive: map[string]interface{}{
		"directory": "dist",
		"format":    "tar",
	},
}
b, _ := json.MarshalIndent(cfg, "", "  ")
os.WriteFile(cfgPath, b, 0644)

require-all vs require

Use AddSatisRequire for precise packaging when you have few private packages — faster builds, smaller output. Use require-all: true (the InitSatis default) when you have many packages and want to expose all.

Satis Requires Separate Installation

Satis is not a built-in Composer command, must first be installed globally: composer global require composer/satis. If not installed, BuildSatis will report command not found.

Next Steps

Released under the MIT License