⚙️ cli_configuration — Configuration Management
This example demonstrates how to programmatically operate on composer.json, composer config, auth tokens, and repository sources with the SDK — four configuration scenarios in one place.
🎯 Example Positioning
examples/cli_configuration splits "configuration-type" operations into four functions, corresponding to Composer's four configuration systems: 📄 composer.json file operations (dependencies/scripts/autoload/config/top-level properties, without invoking the binary), ⚙️ the composer config command (read/write project-level and global-level config, query the home directory, clear the cache), 🔑 auth management (GitHub/GitLab/Bearer/HTTP Basic credentials), 🗂️ repository management (VCS/Composer/Path/Artifact sources, enable/disable Packagist, stability policy). Mastering this covers all configuration APIs for scaffolding, CI, and private-mirror scenarios.
📜 Full Code
package cli_configuration
import (
"fmt"
"log"
"github.com/scagogogo/composer-skills/pkg/composer"
)
// Example01ComposerJson operates on composer.json (pure file, does not invoke composer)
func Example01ComposerJson() {
c, err := composer.New(composer.DefaultOptions())
if err != nil {
log.Fatalf("Failed to create Composer instance: %v", err)
}
c.SetWorkingDir("/path/to/project")
// 1. Read composer.json
if cj, err := c.ReadComposerJSON(); err != nil {
log.Printf("Read failed: %v", err)
} else {
fmt.Printf("Project: %s / %s / %s\n", cj.Name, cj.Description, cj.Type)
}
// 2. Set top-level properties (only name/description/type/keywords, etc. are supported)
c.SetProperty("name", "myvendor/mypackage")
c.SetProperty("description", "An awesome PHP library")
c.SetProperty("keywords", []string{"php", "library"})
// 3. Add/remove dependencies (only modifies the file, does not run install)
c.AddRequire("symfony/console", "^6.0", false) // require
c.AddRequire("phpunit/phpunit", "^10.0", true) // require-dev
c.RemoveRequire("old/package", false)
// 4. Add/remove scripts
c.AddScript("test", "phpunit", "Run tests")
c.AddScript("post-install-cmd", []string{"php artisan optimize:clear"}, "Clear cache after install")
c.RemoveScript("old-script")
// 5. Autoload (psr-4 / psr-0 / classmap / files)
c.AddAutoload("psr-4", "App\\", "src/", false)
c.AddAutoload("psr-4", "Tests\\", "tests/", true)
// 6. Read/write the config field of composer.json
c.SetConfig("process-timeout", 500)
timeout, _ := c.GetConfig("process-timeout")
fmt.Printf("Process timeout: %v\n", timeout)
}
// Example02Config goes through the composer config command (project-level/global-level)
func Example02Config() {
c, _ := composer.New(composer.DefaultOptions())
c.SetWorkingDir("/path/to/project")
v, _ := c.GetConfigWithGlobal("vendor-dir", false) // false = project-level
fmt.Printf("vendor-dir: %s\n", v)
c.SetConfigWithGlobal("vendor-dir", "vendor", false)
g, _ := c.GetConfigWithGlobal("bin-dir", true) // true = global
fmt.Printf("Global bin-dir: %s\n", g)
home, _ := c.GetComposerHome()
fmt.Printf("Composer home: %s\n", home)
c.ClearCache()
}
// Example03Auth manages auth configuration (auth.json)
func Example03Auth() {
c, _ := composer.New(composer.DefaultOptions())
auth, _ := c.GetAuthConfig()
fmt.Printf("GitHub token count: %d, GitLab token count: %d\n", len(auth.GitHub), len(auth.GitLab))
c.AddGitHubToken("github.com", "your-github-token")
c.AddGitLabToken("gitlab.com", "your-gitlab-token")
c.AddBearerToken("example.com", "your-bearer-token")
c.AddHTTPBasicAuth("example.com", "username", "password")
// authType ∈ github-oauth / gitlab-oauth / bearer / http-basic
token, _ := c.GetToken("github-oauth", "github.com")
fmt.Printf("GitHub token: %s\n", token)
c.RemoveToken("github-oauth", "github.com")
}
// Example04Repository manages repository sources
func Example04Repository() {
c, _ := composer.New(composer.DefaultOptions())
c.SetWorkingDir("/path/to/project")
output, _ := c.ListRepositories()
fmt.Println(output)
c.AddVcsRepository("my-vcs", "https://github.com/myorg/myrepo")
c.AddComposerRepository("private-repo", "https://packages.example.com")
c.AddPathRepository("local-lib", "../my-lib", nil)
c.AddArtifactRepository("my-artifacts", "/path/to/artifacts")
c.RemoveRepository("my-vcs")
c.DisablePackagistRepository()
c.EnablePackagistRepository()
c.SetMinimumStability("stable")
c.SetPreferStable(true)
}🔍 Code Walkthrough
- 🚀 Create the instance:
composer.New(composer.DefaultOptions())gets a*Composer, andSetWorkingDirspecifies the PHP project directory to operate on. If there's no composer locally,AutoInstall=truepulls it automatically. - 📄 Read → modify → write pattern:
ReadComposerJSONreturns a structured*ComposerJSON; subsequentSetProperty/AddRequire/AddScript/AddAutoload/SetConfigall modify the file in memory and atomically write it back, without ever invoking the composer binary. - ⚙️ Distinguish the two config routes:
SetConfig/GetConfig(composer_json.go) only modifies theconfigfield of the JSON file, and the value can be any type;GetConfigWithGlobal/SetConfigWithGlobal(config.go) go through thecomposer configcommand, the value must be a string, and the third paramglobal boolcontrols global vs. project-level. - 🔑 Auth bucketed by type: the four
Add*methods write into different fields ofauth.json; theauthTypeofGetToken/RemoveTokenmust be one ofgithub-oauth/gitlab-oauth/bearer/http-basic. - 🗂️ Repository four-pack: VCS (Git repo), Composer (private mirror), Path (local path, options can be passed as the third param), and Artifact (artifact directory) cover virtually all private-source scenarios;
DisablePackagistRepositoryis suitable for pure-intranet environments. - 🧯 Error handling: the example uses
log.Printfto record failures but keeps going, convenient for running all steps in one pass; in production, return on error.
▶️ How to Run
git clone https://github.com/scagogogo/composer-skills.git
cd composer-skills/examples/cli_configuration
# Requires PHP 7.4+ and Composer 2.0+ locally; otherwise the SDK auto-installs
go run 01_composer_json_config_auth_repo.goWill actually modify files
The /path/to/project in the example is a placeholder path; change it to a real PHP project directory on your machine before running. The auth and repository writes will actually modify that project's composer.json and auth.json, so run it in a test project.
📚 SDK Methods Involved
| Method | Doc Link |
|---|---|
New / DefaultOptions / SetWorkingDir | new · set-working-dir |
ReadComposerJSON / SetProperty / AddRequire / RemoveRequire / AddScript / RemoveScript / AddAutoload | read-composer-json · set-property · add-require · remove-require · add-script · remove-script · add-autoload |
SetConfig / GetConfig | composer.json operations |
GetConfigWithGlobal / SetConfigWithGlobal / GetComposerHome / ClearCache | get-config-with-global · set-config-with-global · get-composer-home · clear-cache |
GetAuthConfig / AddGitHubToken / AddGitLabToken / AddBearerToken / AddHTTPBasicAuth / GetToken / RemoveToken | get-auth-config · add-github-token · add-gitlab-token · add-bearer-token · add-http-basic-auth · auth management |
ListRepositories / AddVcsRepository / AddComposerRepository / AddPathRepository / AddArtifactRepository / RemoveRepository / DisablePackagistRepository / EnablePackagistRepository | list-repositories · add-vcs-repository · add-composer-repository · add-path-repository · remove-repository · repository management |
SetMinimumStability / SetPreferStable | set-minimum-stability · set-prefer-stable |
All methods belong to pkg/composer.
🚀 Going Further
- 🏗️ Scaffolding combo: chain
WriteComposerJSON(write an empty template) +SetProperty+AddRequire+AddAutoloadto programmatically generate a brand-new project'scomposer.json— nocomposer initinteraction needed. - 🔐 CI secure injection: in the pipeline, use
AddBearerToken/AddHTTPBasicAuthto temporarily inject private-source credentials, then clean up withRemoveTokenafterward to avoid persisting credentials. - 🪞 Mirror acceleration:
AddComposerRepositoryto add a domestic mirror, thenDisablePackagistRepositoryto turn off the official source — all dependency pulls go through the mirror. - 🧪 Mock testing:
SetMockComposerJSONmakesReadComposerJSONreturn injected data, so unit tests don't need a real project directory (note that the convenience variantReadComposerJsonis not affected by this mock). - 🔄 File vs. command: use the
composer_json.gofamily for file-only changes (fast, binary-free, free value types); use theconfig.gofamily through commands when you need composer to actually take effect (recompute lock, refresh metadata).