🚀 Smart Installer
Stack "progress callback + auto retry + context cancellation + post-install verification + system diagnostics" on top of basic installer — making install process observable, controllable, self-healing.
Package path: github.com/scagogogo/composer-skills/pkg/installer
Core Types
InstallProgress
Single progress event, passed to ProgressCallback.
type InstallProgress struct {
Stage InstallStage `json:"stage"` // Current stage
Message string `json:"message"` // Progress message
Percent int `json:"percent"` // Percentage 0-100
Error error `json:"error,omitempty"` // Error (if any)
Timestamp time.Time `json:"timestamp"` // Timestamp
}InstallStage
Install stage enum, used throughout the entire flow.
type InstallStage string
const (
StageCheckingPHP InstallStage = "checking_php"
StageInstallingPHP InstallStage = "installing_php"
StageDetectingDistro InstallStage = "detecting_distro"
StagePackageManager InstallStage = "package_manager"
StageDownloading InstallStage = "downloading"
StageInstalling InstallStage = "installing"
StageVerifying InstallStage = "verifying"
StageConfiguring InstallStage = "configuring"
StageCompleted InstallStage = "completed"
StageFailed InstallStage = "failed"
)ProgressCallback
Progress callback function type, called when install stage changes.
type ProgressCallback func(progress InstallProgress)InstallOptions
SmartInstaller's complete input parameters.
type InstallOptions struct {
Config Config // Installer config (fallback to SmartConfig when empty)
MaxRetries int // Max retry count
RetryDelay time.Duration // Retry interval
ProgressCallback ProgressCallback // Progress callback
Context context.Context // Context for cancellation
SkipVerification bool // Whether to skip post-install verification
OnPHPInstalled func() // Callback after PHP installation complete
}InstallResult
Install result, returned whether success or failure.
type InstallResult struct {
Success bool `json:"success"`
ComposerPath string `json:"composer_path,omitempty"`
Version string `json:"version,omitempty"`
PHPVersion string `json:"php_version,omitempty"`
Method string `json:"method,omitempty"` // package_manager / homebrew / direct_download / already_installed
Duration time.Duration `json:"duration,omitempty"`
Error error `json:"error,omitempty"`
Stages map[InstallStage]time.Duration `json:"stages,omitempty"` // Each stage duration
}SmartInstaller
Smart installer main body.
type SmartInstaller struct {
options InstallOptions
mu sync.Mutex
}
func NewSmartInstaller(options InstallOptions) *SmartInstallerNewSmartInstaller reasonably fills empty fields:
| Field | When Empty, Fills To |
|---|---|
Config.InstallPath | Entire Config fallback to SmartConfig() |
MaxRetries <= 0 | 3 |
RetryDelay <= 0 | 5 * time.Second |
Context == nil | context.Background() |
Install Stage Flow
InstallWithProgress splits install into several stages, each stage reports percentage via ProgressCallback:
Stage1: checking_php (5%)
└─ No PHP and AutoInstallPHP=true → installing_php (10%)
└─ DetectLinuxDistro + InstallPHP + HasPHP verify again
└─ Trigger OnPHPInstalled callback on success
└─ No PHP and AutoInstallPHP=false → error StageFailed return
└─ Record PHPVersion, record checking_php stage duration
Stage2: Loop retry install Composer (max MaxRetries times)
Each attempt:
├─ detecting_distro (20% + attempt*5%) Detect system environment
└─ doInstall(attempt):
├─ package_manager (30%) Try package manager when PreferPackageManager=true
├─ package_manager (40%) Try Homebrew when PreferBrewOnMac=true
├─ downloading (50%) Download Composer installer
└─ installing (70%) Execute install
Success → Record Method, break loop
Failure → StageFailed (0%), wait RetryDelay then retry (cancel if Context cancelled)
Stage3: verifying (90%) When SkipVerification=false
└─ findComposerBinary find composer → Record ComposerPath
└─ CheckComposerVersion → Record Version
└─ Record verifying stage duration
Complete: completed (100%)
└─ Success=true, record total Duration, return InstallResultStage Percentage Meaning
Percentages are "progress visualization" hint values, not strict engineering progress. On retry, percentage increments with attempt (20 + attempt*5), failed retry stage goes back to 0.
Method Signatures
DefaultInstallOptions
Return default install options (SmartConfig + 3 retries + 5s interval + context.Background).
func DefaultInstallOptions() InstallOptionsNewSmartInstaller
Create smart installer, fill empty fields.
func NewSmartInstaller(options InstallOptions) *SmartInstallerInstallWithProgress
Execute smart install with progress report.
func (si *SmartInstaller) InstallWithProgress() (*InstallResult, error)Return value: *InstallResult always non-nil (contains Success/Error/Stages etc. diagnostic info), error is occurred error (nil on success). InstallResult.Error and returned error are consistent on failure.
EnsureComposerInstalled
Convenience method: already installed returns directly, not installed smart installs.
func EnsureComposerInstalled(options *InstallOptions) (*InstallResult, error)- Already installed:
Method="already_installed", fillsComposerPath/Version/PHPVersion`, doesn't execute any install action. - Not installed:
optionsnil usesDefaultInstallOptions(), otherwise uses passedoptions, callsNewSmartInstaller(opts).InstallWithProgress().
IsComposerInstalled
Check if Composer is installed, get path and version in one step.
func IsComposerInstalled() (bool, string, string)Returns: (whether installed, install path, version number). Path and version are empty strings when not installed.
GetSystemInfo
Collect system info for diagnosing install issues.
func GetSystemInfo() map[string]stringReturned map contains keys:
| Key | Description |
|---|---|
php_available | true/false |
php_version | PHP version (when available) |
composer_available | true/false |
composer_path | Composer path (when available) |
composer_version | Composer version (when available) |
distro_id / distro_name / distro_version / package_manager | Linux distro info (Linux only) |
brew_available | true/false |
curl_available | true/false |
Quick Examples
Install with Progress Bar
package main
import (
"fmt"
"log"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
opts := installer.DefaultInstallOptions()
opts.ProgressCallback = func(p installer.InstallProgress) {
fmt.Printf("[%s] %s (%d%%)\n", p.Stage, p.Message, p.Percent)
if p.Error != nil {
fmt.Printf(" ⚠️ Error: %v\n", p.Error)
}
}
si := installer.NewSmartInstaller(opts)
result, err := si.InstallWithProgress()
if err != nil {
log.Fatalf("Installation failed: %v", err)
}
fmt.Printf("✅ Composer %s installed successfully, path: %s, duration: %v\n",
result.Version, result.ComposerPath, result.Duration)
}Cancellable Install
package main
import (
"context"
"log"
"time"
"github.com/scagogogo/composer-skills/pkg/installer"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
opts := installer.DefaultInstallOptions()
opts.Context = ctx
si := installer.NewSmartInstaller(opts)
result, err := si.InstallWithProgress()
if err != nil {
log.Fatalf("Installation failed or cancelled: %v", err)
}
_ = result
}One-line Ensure Installed
result, err := installer.EnsureComposerInstalled(nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Composer path:", result.ComposerPath, "method:", result.Method)Diagnose System Environment
info := installer.GetSystemInfo()
for k, v := range info {
fmt.Printf("%s = %s\n", k, v)
}Advanced
- Method field semantics:
package_manager(Linux package manager install),homebrew(macOS brew install),direct_download(direct download setup.php),already_installed(EnsureComposerInstalledfound already installed). - Stages duration:
InstallResult.Stagesrecordschecking_phpandverifyingtwo stages'time.Duration, useful for performance analysis. Other stages not individually timed. - PHP install callback:
OnPHPInstalledtriggers immediately after PHP auto-install success andHasPHPverification, suitable for logging or notifying user "PHP ready, starting Composer install". - SkipVerification: Set
trueto skip stage 3,InstallResult.ComposerPathandVersionwill be empty — only use when confident install must succeed and no version info needed. - Integration with Composer:
pkg/composer'sEnsureInstalledWithProgress(callback)andQuickSetupWithProgressinternally call this package'sEnsureComposerInstalled/SmartInstaller, passing install progress to upper layer.