composer

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 7 Imported by: 0

README

go-composer

Go Reference

A small, zero-dependency Go library for the Composer ecosystem: read and manipulate Composer files, query or host a Composer repository, and generate SBOMs from lock files.

Features

  • Read, modify and write composer.json while preserving the formatting Composer expects and any fields the library does not model.
  • Read composer.lock to inspect locked packages.
  • Read and write auth.json for registry credentials.
  • Query composer-type repositories (packagist.org, Satis, Private Packagist) over Composer's V2 protocol — versions, requirements, dist/source, search and security advisories.
  • Serve your own repository over the same protocol and wire types, in the style of net/http.
  • Generate a CycloneDX 1.7 JSON Software Bill of Materials from a Composer lock (github.com/shyim/go-composer/sbom submodule).

The library only depends on the Go standard library.

Installation

go get github.com/shyim/go-composer

The CycloneDX SBOM helpers live in a separate module so consumers can pull them independently:

go get github.com/shyim/go-composer/sbom@sbom/vX.Y.Z

This repository is a multi-module workspace (go.work lists . and ./sbom). Local development and CI use the workspace so sbom always sees the checked-out parent module; published consumers resolve a versioned require against the root module instead.

Quick start

package main

import (
	"log"

	"github.com/shyim/go-composer"
)

func main() {
	c, err := composer.ReadJson("composer.json")
	if err != nil {
		log.Fatal(err)
	}

	c.AddPackage("monolog/monolog", "^3.0")

	if err := c.Save(); err != nil {
		log.Fatal(err)
	}
}

Guides

The library covers several use-cases; each has a focused guide:

Documentation

Full API documentation is available on pkg.go.dev.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bool

func Bool(b bool) *bool

Bool returns a pointer to b, for setting the optional tri-state boolean fields (PreferStable, DefaultBranch) where an explicit false must be distinguished from an absent key.

Types

type Archive

type Archive struct {
	Name    string   `json:"name,omitempty"`
	Exclude []string `json:"exclude,omitempty"`
}

Archive models the "archive" object of a composer.json.

type Auth

type Auth struct {
	HTTPBasicAuth  map[string]BasicAuth         `json:"http-basic,omitempty"`
	BearerAuth     map[string]string            `json:"bearer,omitempty"`
	GitlabAuth     map[string]GitlabToken       `json:"gitlab-token,omitempty"`
	GitlabOAuth    map[string]GitlabOAuthToken  `json:"gitlab-oauth,omitempty"`
	GithubOAuth    map[string]string            `json:"github-oauth,omitempty"`
	BitbucketOauth map[string]map[string]string `json:"bitbucket-oauth,omitempty"`
	CustomHeaders  map[string][]string          `json:"custom-headers,omitempty"`
	GitlabDomains  []string                     `json:"gitlab-domains,omitempty"`
	GithubDomains  []string                     `json:"github-domains,omitempty"`

	// Extra holds any top-level auth.json keys not covered by the fields above.
	// It is populated on read and merged back in on write, preserving unknown
	// or future Composer authentication settings verbatim.
	Extra map[string]json.RawMessage `json:"-"`
	// contains filtered or unexported fields
}

Auth represents the contents of a Composer auth.json file.

Any top-level keys that are not modeled by an explicit field are preserved in Extra and written back unchanged, so that authentication methods added by future Composer versions survive a read/modify/write round-trip.

func ReadAuth

func ReadAuth(authFile string) (*Auth, error)

ReadAuth reads a Composer auth.json file from the given path. If the file does not exist, an empty (but usable) auth configuration is returned rather than an error.

ReadAuth reads only the file. To also layer in credentials from the COMPOSER_AUTH environment variable (as the Composer CLI does), call MergeEnv on the result.

func (*Auth) Json

func (a *Auth) Json(formatted bool) ([]byte, error)

Json serializes the auth configuration to JSON, optionally indented.

func (Auth) MarshalJSON

func (a Auth) MarshalJSON() ([]byte, error)

MarshalJSON serializes the known auth.json fields and merges any unknown keys held in Extra back into the output object.

func (*Auth) MergeEnv

func (a *Auth) MergeEnv() error

MergeEnv merges credentials from the COMPOSER_AUTH environment variable — a JSON document in auth.json format — on top of the current configuration, the way Composer does. ReadAuth does not call this automatically; opt in with:

auth, err := composer.ReadAuth("auth.json")
// ...
if err := auth.MergeEnv(); err != nil { /* ... */ }

For the per-host methods (http-basic, bearer, gitlab-token, gitlab-oauth, github-oauth, bitbucket-oauth, custom-headers) an environment entry overrides the entry for the same host; a non-empty gitlab-domains or github-domains list in the environment replaces the current list entirely. MergeEnv is a no-op when COMPOSER_AUTH is unset or empty, and returns an error when it is set but not valid JSON.

func (*Auth) Save

func (a *Auth) Save() error

Save writes the auth configuration back to the file it was read from.

func (*Auth) UnmarshalJSON

func (a *Auth) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the known auth.json fields and captures every remaining top-level key in Extra so it can be re-emitted unchanged on marshal.

type Author

type Author struct {
	Name     string `json:"name,omitempty"`
	Email    string `json:"email,omitempty"`
	Homepage string `json:"homepage,omitempty"`
	Role     string `json:"role,omitempty"`
}

type Autoload

type Autoload struct {
	Psr0     map[string]string `json:"psr-0,omitempty"`
	Psr4     map[string]string `json:"psr-4,omitempty"`
	Classmap []string          `json:"classmap,omitempty"`
	Files    []string          `json:"files,omitempty"`
	Exclude  []string          `json:"exclude-from-classmap,omitempty"`
}

func NewAutoload

func NewAutoload(a Autoload) *Autoload

NewAutoload returns a pointer to the given autoload configuration, suitable for the Autoload / AutoloadDev fields.

type BasicAuth

type BasicAuth struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type BoolOrString

type BoolOrString struct {
	Bool   bool
	String string
	// contains filtered or unexported fields
}

BoolOrString decodes a composer.json "abandoned" value, which may be either a boolean or a string naming the recommended replacement package.

func NewAbandonedBool

func NewAbandonedBool(b bool) *BoolOrString

NewAbandonedBool marks a package abandoned (or not) without a replacement.

func NewAbandonedReplacement

func NewAbandonedReplacement(replacement string) *BoolOrString

NewAbandonedReplacement marks a package abandoned in favor of replacement.

func (*BoolOrString) IsAbandoned

func (b *BoolOrString) IsAbandoned() bool

IsAbandoned reports whether the package is abandoned.

func (BoolOrString) MarshalJSON

func (b BoolOrString) MarshalJSON() ([]byte, error)

func (*BoolOrString) Replacement

func (b *BoolOrString) Replacement() string

Replacement returns the recommended replacement package, or "".

func (*BoolOrString) UnmarshalJSON

func (b *BoolOrString) UnmarshalJSON(data []byte) error

type Dist

type Dist struct {
	Type      string           `json:"type,omitempty"`
	URL       string           `json:"url,omitempty"`
	Reference string           `json:"reference,omitempty"`
	Shasum    string           `json:"shasum,omitempty"`
	Mirrors   []map[string]any `json:"mirrors,omitempty"`
}

Dist models the root-level "dist" object of a composer.json.

type ExtraData

type ExtraData map[string]any

ExtraData is the parsed "extra" section of a composer.json. It is a plain map[string]any, so existing map indexing (e.g. data["key"]) keeps working, but it also provides dotted-path accessors for ergonomically reading and manipulating nested values, for example GetString("shopware.plugin-class").

func (ExtraData) Get

func (e ExtraData) Get(path string) (any, bool)

Get returns the value at the given dotted path and whether it was found. Intermediate segments must resolve to objects; a non-object encountered before the final segment yields (nil, false).

func (ExtraData) GetBool

func (e ExtraData) GetBool(path string) (bool, bool)

GetBool returns the bool at the given dotted path and whether a bool was found there.

func (ExtraData) GetFloat

func (e ExtraData) GetFloat(path string) (float64, bool)

GetFloat returns the float at the given dotted path and whether a number was found there.

func (ExtraData) GetInt

func (e ExtraData) GetInt(path string) (int64, bool)

GetInt returns the integer at the given dotted path and whether an integer was found there. JSON numbers decode as float64, so whole-number float64 values are accepted; non-integral floats are rejected.

func (ExtraData) GetMap

func (e ExtraData) GetMap(path string) (map[string]any, bool)

GetMap returns the nested object at the given dotted path as a map and whether an object was found there.

func (ExtraData) GetSlice

func (e ExtraData) GetSlice(path string) ([]any, bool)

GetSlice returns the array at the given dotted path and whether an array was found there.

func (ExtraData) GetString

func (e ExtraData) GetString(path string) (string, bool)

GetString returns the string at the given dotted path and whether a string was found there.

func (ExtraData) GetStringSlice

func (e ExtraData) GetStringSlice(path string) ([]string, bool)

GetStringSlice returns the array at the given dotted path as a []string. It reports false if the path is absent, is not an array, or contains a non-string element.

func (ExtraData) Has

func (e ExtraData) Has(path string) bool

Has reports whether a value exists at the given dotted path.

func (ExtraData) Set

func (e ExtraData) Set(path string, value any)

Set stores value at the given dotted path, creating intermediate objects as needed. An intermediate segment that exists but is not an object is replaced with a new object. Setting an empty path is a no-op.

func (ExtraData) Unset

func (e ExtraData) Unset(path string)

Unset removes the value at the given dotted path. It is a no-op if the path or any intermediate object does not exist.

type Funding

type Funding struct {
	Type string `json:"type,omitempty"`
	URL  string `json:"url,omitempty"`
}

type GitlabOAuthToken

type GitlabOAuthToken struct {
	ExpiresAt    int64  `json:"expires-at,omitempty"`
	RefreshToken string `json:"refresh-token,omitempty"`
	Token        string `json:"token"`
}

func (GitlabOAuthToken) MarshalJSON

func (t GitlabOAuthToken) MarshalJSON() ([]byte, error)

func (*GitlabOAuthToken) UnmarshalJSON

func (t *GitlabOAuthToken) UnmarshalJSON(data []byte) error

type GitlabToken

type GitlabToken struct {
	Username string
	Token    string
}

func (GitlabToken) MarshalJSON

func (t GitlabToken) MarshalJSON() ([]byte, error)

func (*GitlabToken) UnmarshalJSON

func (t *GitlabToken) UnmarshalJSON(data []byte) error

type Json

type Json struct {
	Name               string            `json:"name"`
	Abandoned          *BoolOrString     `json:"abandoned,omitempty"`
	Bin                *StringOrSlice    `json:"bin,omitempty"`
	Description        string            `json:"description,omitempty"`
	Version            string            `json:"version,omitempty"`
	Type               string            `json:"type,omitempty"`
	Keywords           []string          `json:"keywords,omitempty"`
	Homepage           string            `json:"homepage,omitempty"`
	Readme             string            `json:"readme,omitempty"`
	Time               string            `json:"time,omitempty"`
	License            *StringOrSlice    `json:"license,omitempty"`
	MinimumStability   string            `json:"minimum-stability,omitempty"`
	PreferStable       *bool             `json:"prefer-stable,omitempty"`
	Authors            []Author          `json:"authors,omitempty"`
	Support            *Support          `json:"support,omitempty"`
	Funding            []Funding         `json:"funding,omitempty"`
	Require            PackageLink       `json:"require,omitempty"`
	RequireDev         PackageLink       `json:"require-dev,omitempty"`
	Conflict           PackageLink       `json:"conflict,omitempty"`
	Replace            PackageLink       `json:"replace,omitempty"`
	Provide            PackageLink       `json:"provide,omitempty"`
	Autoload           *Autoload         `json:"autoload,omitempty"`
	AutoloadDev        *Autoload         `json:"autoload-dev,omitempty"`
	Repositories       Repositories      `json:"repositories,omitempty"`
	Config             map[string]any    `json:"config,omitempty"`
	Scripts            map[string]any    `json:"scripts,omitempty"`
	Extra              ExtraData         `json:"extra,omitempty"`
	Suggest            map[string]string `json:"suggest,omitempty"`
	NonFeatureBranches []string          `json:"non-feature-branches,omitempty"`

	Source              *Source             `json:"source,omitempty"`
	Dist                *Dist               `json:"dist,omitempty"`
	Archive             *Archive            `json:"archive,omitempty"`
	IncludePath         []string            `json:"include-path,omitempty"`
	TargetDir           string              `json:"target-dir,omitempty"`
	DefaultBranch       *bool               `json:"default-branch,omitempty"`
	PHPExt              map[string]any      `json:"php-ext,omitempty"`
	ScriptsDescriptions map[string]string   `json:"scripts-descriptions,omitempty"`
	ScriptsAliases      map[string][]string `json:"scripts-aliases,omitempty"`

	// AdditionalFields holds any top-level composer.json keys not covered by the
	// fields above. It is populated on read and merged back in on write,
	// preserving unknown or future Composer settings verbatim. Note this is
	// distinct from the modeled "extra" section, which is exposed via Extra.
	AdditionalFields map[string]json.RawMessage `json:"-"`
	// contains filtered or unexported fields
}

Json represents the contents of a composer.json file.

Any top-level keys that are not modeled by an explicit field are preserved in AdditionalFields and written back unchanged, so that keys added by future Composer versions survive a read/modify/write round-trip.

func ReadJson

func ReadJson(composerPath string) (*Json, error)

ReadJson reads and parses a composer.json file from the given path.

func (*Json) AddPackage

func (c *Json) AddPackage(name, constraint string)

AddPackage adds or updates a package constraint in the "require" section.

func (*Json) AddPackageDev

func (c *Json) AddPackageDev(name, constraint string)

AddPackageDev adds or updates a package constraint in the "require-dev" section.

func (*Json) AddRepository

func (c *Json) AddRepository(repo Repository)

AddRepository appends a repository to "repositories" unless one with the same URL is already configured.

func (*Json) DisableComposerPlugin added in v0.1.2

func (c *Json) DisableComposerPlugin(name string)

DisableComposerPlugin marks the given plugin as disallowed under config.allow-plugins.

func (*Json) EnableComposerPlugin

func (c *Json) EnableComposerPlugin(name string)

EnableComposerPlugin marks the given plugin as allowed under config.allow-plugins.

func (*Json) EnsurePackage

func (c *Json) EnsurePackage(name, constraint string) bool

EnsurePackage adds name=constraint to "require" only when the package is not already present in either "require" or "require-dev". Returns true when the document was modified.

func (*Json) EnsurePackageDev

func (c *Json) EnsurePackageDev(name, constraint string) bool

EnsurePackageDev adds name=constraint to "require-dev" only when the package is not already present in either "require" or "require-dev". Returns true when the document was modified.

func (*Json) HasConfig

func (c *Json) HasConfig(key string) bool

HasConfig reports whether the given key is present in the "config" section.

func (*Json) HasPackage

func (c *Json) HasPackage(name string) bool

HasPackage reports whether the given package is listed in "require".

func (*Json) HasPackageDev

func (c *Json) HasPackageDev(name string) bool

HasPackageDev reports whether the given package is listed in "require-dev".

func (Json) MarshalJSON

func (c Json) MarshalJSON() ([]byte, error)

MarshalJSON serializes the known composer.json fields, merges any unknown keys held in AdditionalFields, and emits top-level keys in their original document order when known (falling back to a deterministic sorted order for new keys and freshly-created documents).

func (*Json) RemoveComposerPlugin

func (c *Json) RemoveComposerPlugin(name string)

RemoveComposerPlugin removes the given plugin from config.allow-plugins.

func (*Json) RemoveConfig

func (c *Json) RemoveConfig(key string)

RemoveConfig removes a key from the "config" section.

func (*Json) RemovePackage

func (c *Json) RemovePackage(name string)

RemovePackage removes a package from the "require" section.

func (*Json) RemovePackageDev

func (c *Json) RemovePackageDev(name string)

RemovePackageDev removes a package from the "require-dev" section.

func (*Json) RemoveRepository

func (c *Json) RemoveRepository(url string)

RemoveRepository removes every repository whose URL matches the given value.

func (*Json) Save

func (c *Json) Save() error

Save writes the composer.json back to the path it was read from.

func (*Json) SetConfig

func (c *Json) SetConfig(key string, value any)

SetConfig sets a key in the "config" section, creating the section if needed.

func (*Json) UnmarshalJSON

func (c *Json) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the known composer.json fields and captures every remaining top-level key in AdditionalFields so it can be re-emitted unchanged on marshal.

type Lock

type Lock struct {
	Packages    []LockPackage `json:"packages"`
	PackagesDev []LockPackage `json:"packages-dev"`
}

func ReadLock

func ReadLock(pathToFile string) (*Lock, error)

func (*Lock) GetPackage

func (c *Lock) GetPackage(name string) *LockPackage

type LockPackage

type LockPackage struct {
	Name        string            `json:"name"`
	Version     string            `json:"version"`
	Type        string            `json:"type,omitempty"`
	Require     map[string]string `json:"require"`
	License     []string          `json:"license,omitempty"`
	Description string            `json:"description,omitempty"`
	Homepage    string            `json:"homepage,omitempty"`
	Time        string            `json:"time,omitempty"`
	Dist        LockPackageDist   `json:"dist,omitempty"`
	Source      LockPackageSource `json:"source,omitempty"`
}

type LockPackageDist

type LockPackageDist struct {
	Type      string `json:"type,omitempty"`
	URL       string `json:"url,omitempty"`
	Reference string `json:"reference,omitempty"`
	Shasum    string `json:"shasum,omitempty"`
}

type LockPackageSource

type LockPackageSource struct {
	Type      string `json:"type,omitempty"`
	URL       string `json:"url,omitempty"`
	Reference string `json:"reference,omitempty"`
}
type PackageLink map[string]string

type Repositories

type Repositories []Repository

func (*Repositories) HasRepository

func (r *Repositories) HasRepository(url string) bool

HasRepository reports whether a repository with the given URL is configured.

func (*Repositories) UnmarshalJSON

func (e *Repositories) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a composer.json "repositories" value, which may be either a JSON object keyed by repository name or a plain JSON array.

type Repository

type Repository struct {
	Type    string         `json:"type,omitempty"`
	URL     string         `json:"url,omitempty"`
	Options map[string]any `json:"options,omitempty"`
}

type Source

type Source struct {
	Type      string           `json:"type,omitempty"`
	URL       string           `json:"url,omitempty"`
	Reference string           `json:"reference,omitempty"`
	Mirrors   []map[string]any `json:"mirrors,omitempty"`
}

Source models the root-level "source" object of a composer.json.

type StringOrSlice

type StringOrSlice struct {
	Values []string
	// contains filtered or unexported fields
}

StringOrSlice decodes a composer.json value that may be either a single JSON string or an array of strings, always exposing it as a slice. It remembers whether the source was scalar so it can re-emit the same shape on marshal.

func NewString

func NewString(s string) *StringOrSlice

NewString returns a StringOrSlice that marshals back as a single JSON string.

func NewStrings

func NewStrings(s ...string) *StringOrSlice

NewStrings returns a StringOrSlice that marshals back as a JSON array.

func (*StringOrSlice) First

func (s *StringOrSlice) First() string

First returns the first value, or "" when empty.

func (StringOrSlice) MarshalJSON

func (s StringOrSlice) MarshalJSON() ([]byte, error)

func (*StringOrSlice) Strings

func (s *StringOrSlice) Strings() []string

Strings returns the contained values.

func (*StringOrSlice) UnmarshalJSON

func (s *StringOrSlice) UnmarshalJSON(data []byte) error

type Support

type Support struct {
	Email    string `json:"email,omitempty"`
	Issues   string `json:"issues,omitempty"`
	Forum    string `json:"forum,omitempty"`
	Wiki     string `json:"wiki,omitempty"`
	IRC      string `json:"irc,omitempty"`
	Source   string `json:"source,omitempty"`
	Docs     string `json:"docs,omitempty"`
	RSS      string `json:"rss,omitempty"`
	Chat     string `json:"chat,omitempty"`
	Security string `json:"security,omitempty"`
}

Directories

Path Synopsis
internal
sbom module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL