🚀 cli_basic_usage — Composer CLI Basic Usage
This example demonstrates how to create a Composer instance and execute basic Composer commands via Run / RunWithContext, while setting the working directory and environment variables and triggering a Composer self-update.
🎯 Example Positioning
cli_basic_usage is the first example (and the 8th overall) in the Composer CLI Local Operations series, and the starting point for the entire cli_* series. It breaks "getting a runnable Composer client" down into the smallest runnable unit.
- 📚 What you'll learn: how to use
composer.DefaultOptions()+composer.New()for client initialization, how to useRunto execute arbitrary commands, how to useRunWithContextfor timeout control, how to adjust the execution environment viaSetWorkingDir/SetEnv, and finally how to callSelfUpdateto upgrade Composer itself. - 🔗 Corresponding SDK methods:
composer.New,Composer.Run,Composer.RunWithContext,Composer.SetWorkingDir,Composer.SetEnv,Composer.SelfUpdate. - 💡 Difference from
basic_setup:basic_setuptargets the Packagist remote API client; this example targets the local Composer CLI subprocess. They belong to different subsystems, and this example is the prerequisite for all subsequent CLI examples likecli_package_managementandcli_project_management.
💻 Full Code
package cli_basic_usage
import (
"context"
"fmt"
"log"
"time"
"github.com/scagogogo/composer-skills/pkg/composer"
)
// Example02RunCommands demonstrates how to run basic Composer commands
func Example02RunCommands() {
// Create a Composer instance
options := composer.DefaultOptions()
c, err := composer.New(options)
if err != nil {
log.Fatalf("Failed to create Composer instance: %v", err)
}
// Example 1: Use the Run method to execute a simple command
output, err := c.Run("--version")
if err != nil {
log.Fatalf("Failed to execute command: %v", err)
}
fmt.Printf("Run method output: %s\n", output)
// Example output: Run method output: Composer version 2.5.7 2023-12-01 11:43:14
// Example 2: Execute a command with a timeout-bearing context
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
output, err = c.RunWithContext(ctx, "diagnose")
if err != nil {
log.Printf("Failed to execute command with context: %v", err)
} else {
fmt.Println("RunWithContext executed successfully, output omitted...")
}
// Example 3: Set the working directory before executing a command
c.SetWorkingDir("/path/to/your/project")
fmt.Printf("Working directory set to: %s\n", "/path/to/your/project")
// Example 4: Set environment variables before executing a command
c.SetEnv([]string{"COMPOSER_MEMORY_LIMIT=2G", "COMPOSER_NO_INTERACTION=1"})
fmt.Println("Environment variables set: COMPOSER_MEMORY_LIMIT=2G, COMPOSER_NO_INTERACTION=1")
// Example 5: Run the self-update command
fmt.Println("Running self-update command...")
err = c.SelfUpdate()
if err != nil {
log.Printf("Failed to update Composer: %v", err)
} else {
fmt.Println("Composer self-update succeeded")
}
}🧩 Code Walkthrough
- 🏗️ Initialize the client:
composer.DefaultOptions()provides a set of out-of-the-box default configs, whichcomposer.New(options)then uses to construct the*Composerinstance. If Composer is not detected locally, the SDK triggers an auto-install, soerrmust be checked. - ⚡ Minimal execution:
c.Run("--version")passes the argument straight through to the Composer subprocess and returns the merged stdout as a string. This is the lightest entry point for running any "side-effect-free query command." - ⏱️ Execution with timeout:
context.WithTimeout(..., 30*time.Second)derives a cancellable ctx;c.RunWithContext(ctx, "diagnose")kills the subprocess on timeout or cancellation, preventing time-consuming commands likediagnosefrom hanging the caller.defer cancel()releases the context's resources. - 📁 Switch the working directory:
c.SetWorkingDir("/path/to/your/project")makes all subsequent commands execute in that PHP project's root directory (equivalent tocomposer --working-dir), a key switch when operating across multiple projects. - 🌍 Inject environment variables:
c.SetEnv([]string{...})setsCOMPOSER_MEMORY_LIMIT=2G(relaxing the memory limit) andCOMPOSER_NO_INTERACTION=1(disabling interactive prompts), ensuring commands run silently and stably in CI / daemons. - 🔄 Composer self-update:
c.SelfUpdate()wrapscomposer self-updateto upgrade the local Composer to the latest stable version. It may fail due to permissions or network, so it useslog.Printfto record rather thanlog.Fatalfto abort. - 🛡️ Error handling strategy: query-type commands (
--version) useFatalffor fast exposure; side-effecting commands (diagnose,self-update) uselog.Printffor fault tolerance and continuation — reflecting the "initialization is fatal, runtime is recoverable" layering principle.
▶️ How to Run
This example is written as an Example function and must be run by explicitly invoking the corresponding file from the example directory:
cd /home/cc11001100/github/scagogogo/composer-skills/examples/cli_basic_usage
go run 02_run_commands.go⚠️ CLI examples require PHP and Composer installed locally; if not installed, the SDK will attempt an auto-install.
SelfUpdatewill actually upgrade the local Composer, so run it in a test environment. Before running, replace theSetWorkingDirpath with a real PHP project directory on your machine.
📚 SDK Methods Involved
| Method Name | Package | Purpose | Doc Link |
|---|---|---|---|
New | pkg/composer | Construct a *Composer client instance from options | /sdk/composer/methods/quick-setup |
Run | pkg/composer | Pass-through args to execute a Composer command, returns output string | /sdk/composer/methods/run |
RunWithContext | pkg/composer | Same as Run, but accepts context.Context for timeout/cancellation | /sdk/composer/methods/run-with-context |
SetWorkingDir | pkg/composer | Set the working directory for subsequent commands | /sdk/composer/methods/set-working-dir |
SetEnv | pkg/composer | Set subprocess environment variables | /sdk/composer/environment |
SelfUpdate | pkg/composer | Run composer self-update to upgrade Composer | /sdk/composer/methods/self-update |
📝 Note:
SetEnvinfluences the subprocess by batch-injecting environment variables. Alongside the per-variableSetEnvVariable, it belongs to the environment configuration system, so the link points to the environment overview page for cross-referencing.
🚀 Going Further
- ⏱️ Unified timeouts: extract the
RunWithContexttimeout into a constant and tier it by command (query 5s, install 300s); pair it with arun-with-timeout-style convenience wrapper to avoid handwritingcontext.WithTimeoutevery time. - 📋 Structured output:
Runreturns a raw string; when you need structured data like version info or dependency trees, switch to typed methods likeGetVersionInfoorShowDependencyTreeto skip regex parsing. - 🧪 Testability: inject
*Composerinto business code via an interface, making it easy to stubRunin unit tests without actually spawning a subprocess. - 🔁 Failure retry: wrap commands prone to network fluctuation like
SelfUpdateanddiagnosewith a layer of exponential-backoff retry, and report to monitoring after retries are exhausted. - 🗂️ Multi-project orchestration: when switching between projects with
SetWorkingDir, pair it withc.SetEnvto inject a project-levelCOMPOSERconfig file path (COMPOSER=composer.prod.json), driving multiple deployment manifests with one client. - 🔐 Permissions and isolation: before running
SelfUpdatein production, first callGetVersionInfoto compare the current and target versions; if necessary, lock to a specific version instead of blindly upgrading to latest, to avoid breaking changes.