Skip to content

🛠️ Core Runtime

This page covers the "foundation" of pkg/composer — how to create a Composer instance, how to execute arbitrary subcommands, and how to detect and upgrade composer itself. These methods are defined in composer.go and version.go, and are the underlying dependencies for all other methods (Install, RequirePackage, etc.).

🧱 Core Types

Composer Struct

go
type Composer struct {
	executablePath string        // composer executable path
	workingDir    string        // working directory
	autoInstall   bool          // whether to auto-install when not found
	installer     *installer.Installer
	detector      *detector.Detector
	env           []string      // environment variables (KEY=VALUE format)
	defaultTimeout time.Duration
}

Composer is a struct, not an interface; all methods are attached to *Composer. You typically don't construct it directly, but create it via New.

Options Struct

go
type Options struct {
	ExecutablePath string              // explicitly specify composer path
	WorkingDir     string              // working directory
	AutoInstall    bool                // whether to auto-install when not found
	Installer      *installer.Installer
	Detector       *detector.Detector
	Env            []string
	DefaultTimeout time.Duration
}

🛠️ New

Create a new Composer instance. If ExecutablePath is not specified, it first detects composer in the system via detector.Detect(); if not found and AutoInstall=true, it automatically installs and re-detects.

Signature

go
func New(options Options) (*Composer, error)

Parameters

ParameterTypeDescription
optionsOptionsOptions to customize the Composer instance

Return Values

TypeDescription
*ComposerThe created Composer instance
errorReturns ErrComposerNotFound or ErrComposerInstallation on failure

Example

go
package main

import (
	"log"
	"time"

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

func main() {
	options := composer.DefaultOptions()
	options.WorkingDir = "/path/to/project"
	options.DefaultTimeout = 30 * time.Minute
	comp, err := composer.New(options)
	if err != nil {
		log.Fatalf("Failed to initialize Composer: %v", err)
	}
	_ = comp
}

Advanced

  • When ExecutablePath is explicitly specified, New validates the file exists with os.Stat (skipped in test mode).
  • To completely skip auto-install, set AutoInstall to false; detection failure then directly returns ErrComposerNotFound.
  • For a one-step solution (detect + install + create instance), use composer.QuickSetup(workingDir, true).

⚙️ DefaultOptions

Returns Options with default configuration, the most common construction entry point.

Signature

go
func DefaultOptions() Options

Return Values

TypeDescription
OptionsDefault config: WorkingDir="", AutoInstall=true, DefaultTimeout=10*time.Minute

Example

go
options := composer.DefaultOptions()
comp, err := composer.New(options)

Why not just Options{}

Constructing Options{} directly leaves AutoInstall=false and DefaultTimeout=0 (i.e., no timeout), which is error-prone. Always use DefaultOptions() as a starting point and override as needed.


📁 SetWorkingDir

Set the working directory for composer commands. All subsequent commands will execute in this directory.

Signature

go
func (c *Composer) SetWorkingDir(dir string)

Parameters

ParameterTypeDescription
dirstringWorking directory path to set

Example

go
comp.SetWorkingDir("/path/to/php/project")
// All subsequent comp.Install() / comp.Run(...) execute in that directory

Advanced

  • The corresponding getter is GetWorkingDir() string.
  • To execute a single command in another directory, use InstallWithWorkingDir(workingDir, noDev, optimize) (see Dependencies), which restores the original working directory after execution.

🌐 SetEnv

Set environment variables for executing composer commands, commonly used to configure HTTP proxies, COMPOSER_HOME, or authentication information.

Signature

go
func (c *Composer) SetEnv(env []string)

Parameters

ParameterTypeDescription
env[]stringEnvironment variable array, format ["KEY=VALUE", ...]

Example

go
comp.SetEnv([]string{
	"HTTP_PROXY=http://proxy.example.com:8080",
	"HTTPS_PROXY=http://proxy.example.com:8080",
	"COMPOSER_HOME=/custom/composer/home",
})

Advanced

  • The corresponding getter is GetEnv() []string.
  • SetEnv is a full replacement, not append. To inherit the current process environment, concatenate with os.Environ() before passing.
  • For finer-grained environment variable operations (like COMPOSER_PROCESS_TIMEOUT), see the Environment module (SetProcessTimeout, DisableInteraction, etc.).

▶️ Run

Execute any composer subcommand and return the output; this is the core method of the SDK. Uses a 10-minute timeout by default.

Signature

go
func (c *Composer) Run(args ...string) (string, error)

Parameters

ParameterTypeDescription
args...stringCommand arguments; first argument is the composer subcommand (e.g., "install", "require")

Return Values

TypeDescription
stringCombined command output (stdout+stderr)
errorOn failure, returns error wrapping ErrCommandExecution

Example

go
// Execute "composer show"
output, err := comp.Run("show")
if err != nil {
	log.Fatalf("Command execution failed: %v", err)
}
fmt.Println(output)

// Execute command with arguments
output, err = comp.Run("require", "symfony/console", "--dev")

Advanced

  • Run internally calls RunWithTimeout(c.defaultTimeout, args...).
  • In test mode, RunWithContext first checks mockCommandOutput; if hit, it returns directly without actually executing composer.
  • Use RunWithTimeout for timeout control, RunWithContext for cancellation capability.

⏱️ RunWithTimeout

Execute composer command within a specified timeout, suitable for potentially long-running commands.

Signature

go
func (c *Composer) RunWithTimeout(timeout time.Duration, args ...string) (string, error)

Parameters

ParameterTypeDescription
timeouttime.DurationMaximum timeout for command execution
args...stringCommand arguments

Return Values

TypeDescription
stringCombined command output
errorError on failure or timeout

Example

go
// Execute a potentially long installation command with 30-minute timeout
output, err := comp.RunWithTimeout(30*time.Minute, "install")
if err != nil {
	log.Fatalf("Install timed out or failed: %v", err)
}

Advanced

  • Implementation-wise, RunWithTimeout creates a context.WithTimeout and delegates to RunWithContext.
  • The returned error on timeout can be checked with errors.Is(err, context.DeadlineExceeded).

🧩 RunWithContext

Execute composer command within a specified context.Context, providing maximum control — supports timeout, cancellation, and deadlines.

Signature

go
func (c *Composer) RunWithContext(ctx context.Context, args ...string) (string, error)

Parameters

ParameterTypeDescription
ctxcontext.ContextContext, can be used for cancellation or timeout
args...stringCommand arguments

Return Values

TypeDescription
stringCombined command output
errorOn failure, returns error wrapping ErrCommandExecution

Example

go
// Create a context that can be manually cancelled
ctx, cancel := context.WithCancel(context.Background())

// Cancel based on conditions in another goroutine
go func() {
	time.Sleep(5 * time.Second)
	cancel()
}()

// Execute command
output, err := comp.RunWithContext(ctx, "update")
if err != nil {
	if errors.Is(err, context.Canceled) {
		fmt.Println("Command cancelled")
	} else {
		log.Fatalf("Command execution failed: %v", err)
	}
}

Advanced

  • RunWithContext is the underlying implementation of Run and RunWithTimeout, and also the entry point for mock injection: it first calls getMockOutput(args...), returning mock output if hit.
  • The command is constructed via exec.CommandContext(ctx, c.executablePath, args...); when ctx is cancelled, a termination signal is sent to the subprocess.
  • Working directory and environment variables are injected here: cmd.Dir = c.workingDir, cmd.Env = c.env.

🔍 GetExecutablePath

Returns the composer executable path used by the current instance.

Signature

go
func (c *Composer) GetExecutablePath() string

Return Values

TypeDescription
stringFull path to the composer executable; empty string if not set

Example

go
execPath := comp.GetExecutablePath()
fmt.Printf("Composer executable in use: %s\n", execPath)

IsInstalled

Checks whether the current instance points to a valid composer executable path. Only checks if the path is non-empty; does not actually run composer to verify.

Signature

go
func (c *Composer) IsInstalled() bool

Return Values

TypeDescription
boolReturns true if path is non-empty, otherwise false

Example

go
if comp.IsInstalled() {
	fmt.Println("Composer is installed")
} else {
	fmt.Println("Composer is not installed")
}

Note

IsInstalled only checks executablePath != "". To confirm composer actually runs, call GetVersion() or GetVersionInfo() for a more reliable check.


🚀 SelfUpdate

Update composer itself to the latest version, equivalent to composer self-update.

Signature

go
func (c *Composer) SelfUpdate() error

Return Values

TypeDescription
errorOn failure, returns error wrapping ErrSelfUpdateFailed

Example

go
err := comp.SelfUpdate()
if err != nil {
	log.Fatalf("Failed to update Composer: %v", err)
}
fmt.Println("Composer updated to latest version")

Advanced

  • To get a progress callback during update, use SelfUpdateWithProgress() (in auto_install.go), which returns (string, error) where the string is the new version number.
  • Before updating, you can use GetVersion() to record the old version, then compare after.
  • SelfUpdate requires write permission to the composer executable, often requiring sudo (see Environment module for EnableSuperuser).

In addition to the core methods above, composer.go provides several query methods:

MethodSignatureDescription
GetWorkingDirfunc (c *Composer) GetWorkingDir() stringGet current working directory
GetEnvfunc (c *Composer) GetEnv() []stringGet current environment variables

And package-level test helper functions (generally not used in production code):

FunctionSignatureDescription
SetupMockOutputfunc SetupMockOutput(command, output string, err error)Set mock output for a specific command
ClearMockOutputsfunc ClearMockOutputs()Clear all mock outputs

🧭 Next Steps

After mastering the core runtime, learn:

  • 📦 Dependencies — The full Install / Update / DumpAutoload family
  • 🔍 PackagesRequirePackage / Remove / Show / Search
  • 📊 VersionGetVersion / GetVersionInfo structured version

Released under the MIT License