diff --git a/README.md b/README.md index 41cb658..141a5a4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Go library that wraps package manager CLIs behind a common interface. Part of ## What it does -Translates generic operations (install, add, remove, list, outdated, update, vendor, resolve) into the correct CLI commands for each package manager. Define what you want to do once, and the library figures out the right command for npm, bundler, cargo, go, or any other supported manager. +Translates generic operations (init, install, add, remove, list, outdated, update, vendor, resolve) into the correct CLI commands for each package manager. Define what you want to do once, and the library figures out the right command for npm, bundler, cargo, go, or any other supported manager. ```go translator := managers.NewTranslator() @@ -83,7 +83,7 @@ cmd, _ = translator.BuildCommand("bundler", "add", managers.CommandInput{ | helm | helm | Chart.lock | | brew | homebrew | - | -Most managers support: install, add, remove, list, outdated, update, resolve. Some also support vendor and path. Some managers (maven, gradle, sbt, lein) have limited CLI support for add/remove operations. +Most managers support: init, install, add, remove, list, outdated, update, resolve. Some also support vendor and path. Some managers (maven, gradle, sbt, lein) have limited CLI support for add/remove operations. A few global installers (brew, gem, cpanm) and managers with interactive-only init (maven, pip, sbt) do not have init support. ## Installation @@ -211,6 +211,7 @@ Built-in policies include AllowAllPolicy, DenyAllPolicy, and PackageBlocklistPol | Operation | Description | |-----------|-------------| +| `init` | Initialize a new project with manifest file | | `install` | Install dependencies from lockfile | | `add` | Add a new dependency | | `remove` | Remove a dependency | @@ -229,6 +230,18 @@ Built-in policies include AllowAllPolicy, DenyAllPolicy, and PackageBlocklistPol | `frozen` | Fail if lockfile would change (CI mode) | | `json` | Output in JSON format (where supported) | +### Initializing projects + +The `init` operation creates a new project with the package manager's default manifest file. This is useful for creating temporary projects for dependency resolution or scaffolding new projects. + +```go +manager, _ := managers.Detect("/path/to/empty/dir") +result, _ := manager.Init(ctx) +// Creates package.json (npm), Gemfile (bundler), Cargo.toml (cargo), etc. +``` + +**Managers with init support:** npm, pnpm, yarn, bun, bundler, cargo, gomod, uv, poetry, deno, composer, swift, pub, mix, nuget, gradle, helm, conan, conda, stack, lein, rebar3, cabal, cocoapods, nimble, opam, luarocks, shards, vcpkg, renv + ### Getting package paths The `path` operation returns the filesystem path to an installed package, useful for source exploration or editor integration: diff --git a/definitions/bun.yaml b/definitions/bun.yaml index 876a74b..e668917 100644 --- a/definitions/bun.yaml +++ b/definitions/bun.yaml @@ -18,6 +18,12 @@ detection: priority: 25 # Higher than npm/yarn/pnpm when bun lockfile present commands: + init: + base: [init, -y] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -92,6 +98,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/bundler.yaml b/definitions/bundler.yaml index 5cb11b1..77619d6 100644 --- a/definitions/bundler.yaml +++ b/definitions/bundler.yaml @@ -18,6 +18,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [init] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -125,6 +131,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/cabal.yaml b/definitions/cabal.yaml index aede326..b7e5da1 100644 --- a/definitions/cabal.yaml +++ b/definitions/cabal.yaml @@ -16,6 +16,12 @@ version_detection: pattern: 'cabal-install version (\d+\.\d+\.\d+)' commands: + init: + base: [init, -n, -p, resolve-tmp] + exit_codes: + 0: success + 1: error + install: base: [build, --only-dependencies] flags: @@ -63,6 +69,7 @@ commands: 1: error capabilities: + - init - install - list - outdated diff --git a/definitions/cargo.yaml b/definitions/cargo.yaml index 26af033..83f4a88 100644 --- a/definitions/cargo.yaml +++ b/definitions/cargo.yaml @@ -18,6 +18,12 @@ version_detection: pattern: 'cargo (\d+\.\d+\.\d+)' commands: + init: + base: [init, --lib] + exit_codes: + 0: success + 1: error + # cargo fetch downloads dependencies without building # cargo build would also work but does more than just install install: @@ -129,6 +135,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/cocoapods.yaml b/definitions/cocoapods.yaml index f0a1839..c75a768 100644 --- a/definitions/cocoapods.yaml +++ b/definitions/cocoapods.yaml @@ -17,6 +17,12 @@ detection: priority: 10 commands: + init: + base: [init] + exit_codes: + 0: success + 1: error + install: base: [install] flags: diff --git a/definitions/composer.yaml b/definitions/composer.yaml index 1aaa5e9..e7662f4 100644 --- a/definitions/composer.yaml +++ b/definitions/composer.yaml @@ -17,6 +17,13 @@ detection: priority: 10 commands: + init: + base: [init, -n] + default_flags: [--name, resolve/tmp] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -91,6 +98,7 @@ commands: 1: error capabilities: + - init - install - add - add_dev diff --git a/definitions/conan.yaml b/definitions/conan.yaml index 0de6a7f..159ec4b 100644 --- a/definitions/conan.yaml +++ b/definitions/conan.yaml @@ -16,6 +16,12 @@ version_detection: pattern: 'Conan version (\d+\.\d+\.\d+)' commands: + init: + base: [new, resolve-tmp, -d, basic] + exit_codes: + 0: success + 1: error + install: base: [install, .] flags: @@ -82,6 +88,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/conda.yaml b/definitions/conda.yaml index f162eb2..940665d 100644 --- a/definitions/conda.yaml +++ b/definitions/conda.yaml @@ -16,6 +16,12 @@ version_detection: pattern: 'conda (\d+\.\d+\.\d+)' commands: + init: + base: [create, -n, resolve-tmp, --yes] + exit_codes: + 0: success + 1: error + install: base: [install, --yes] base_overrides: @@ -96,6 +102,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/deno.yaml b/definitions/deno.yaml index cb36a24..d015034 100644 --- a/definitions/deno.yaml +++ b/definitions/deno.yaml @@ -16,6 +16,12 @@ version_detection: pattern: 'deno (\d+\.\d+\.\d+)' commands: + init: + base: [init] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -86,6 +92,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/gomod.yaml b/definitions/gomod.yaml index 6a86c59..292d16b 100644 --- a/definitions/gomod.yaml +++ b/definitions/gomod.yaml @@ -18,6 +18,12 @@ version_detection: pattern: 'go(\d+\.\d+(?:\.\d+)?)' commands: + init: + base: [mod, init, resolve-tmp] + exit_codes: + 0: success + 1: error + # go mod download fetches dependencies install: base: [mod, download] @@ -124,6 +130,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/gradle.yaml b/definitions/gradle.yaml index d7f7062..db3831f 100644 --- a/definitions/gradle.yaml +++ b/definitions/gradle.yaml @@ -16,6 +16,12 @@ version_detection: pattern: 'Gradle (\d+\.\d+(?:\.\d+)?)' commands: + init: + base: [init, --type, java-library, --dsl, groovy] + exit_codes: + 0: success + 1: error + install: base: [dependencies, --write-locks] flags: @@ -77,6 +83,7 @@ commands: 1: error capabilities: + - init - install - list - resolve diff --git a/definitions/helm.yaml b/definitions/helm.yaml index cd8cd30..80332ed 100644 --- a/definitions/helm.yaml +++ b/definitions/helm.yaml @@ -15,6 +15,12 @@ version_detection: pattern: 'v(\d+\.\d+\.\d+)' commands: + init: + base: [create, resolve-tmp] + exit_codes: + 0: success + 1: error + install: base: [dependency, build] flags: @@ -74,6 +80,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/lein.yaml b/definitions/lein.yaml index 89898ff..2b07ddf 100644 --- a/definitions/lein.yaml +++ b/definitions/lein.yaml @@ -14,6 +14,12 @@ version_detection: pattern: 'Leiningen (\d+\.\d+\.\d+)' commands: + init: + base: [new, resolve-tmp] + exit_codes: + 0: success + 1: error + install: base: [deps] exit_codes: @@ -67,6 +73,7 @@ commands: 1: error capabilities: + - init - install - list - resolve diff --git a/definitions/luarocks.yaml b/definitions/luarocks.yaml index e63161f..5724b42 100644 --- a/definitions/luarocks.yaml +++ b/definitions/luarocks.yaml @@ -14,6 +14,12 @@ version_detection: pattern: 'LuaRocks (\d+\.\d+\.\d+)' commands: + init: + base: [init, resolve-tmp] + exit_codes: + 0: success + 1: error + install: base: [install, --deps-only, .] flags: @@ -81,6 +87,7 @@ commands: pattern: 'Installed in:\s+(\S+)' capabilities: + - init - install - add - remove diff --git a/definitions/mix.yaml b/definitions/mix.yaml index 734358c..36de9d4 100644 --- a/definitions/mix.yaml +++ b/definitions/mix.yaml @@ -17,6 +17,12 @@ detection: priority: 10 commands: + init: + base: [new, resolve_tmp] + exit_codes: + 0: success + 1: error + install: base: [deps.get] flags: @@ -86,6 +92,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - list diff --git a/definitions/nimble.yaml b/definitions/nimble.yaml index d262138..41a4819 100644 --- a/definitions/nimble.yaml +++ b/definitions/nimble.yaml @@ -15,6 +15,12 @@ version_detection: pattern: 'nimble v(\d+\.\d+\.\d+)' commands: + init: + base: [init, -y] + exit_codes: + 0: success + 1: error + install: base: [install, --depsOnly] flags: @@ -79,6 +85,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/npm.yaml b/definitions/npm.yaml index 9732d1a..3343d58 100644 --- a/definitions/npm.yaml +++ b/definitions/npm.yaml @@ -19,6 +19,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [init, -y] + exit_codes: + 0: success + 1: error + install: base: [install] base_overrides: @@ -100,6 +106,7 @@ commands: 1: partial capabilities: + - init - install - install_frozen - add diff --git a/definitions/nuget.yaml b/definitions/nuget.yaml index ea9dee0..329e329 100644 --- a/definitions/nuget.yaml +++ b/definitions/nuget.yaml @@ -17,6 +17,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [new, classlib, -n, ResolveTmp] + exit_codes: + 0: success + 1: error + install: base: [restore] base_overrides: @@ -87,6 +93,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/opam.yaml b/definitions/opam.yaml index 7c67f4e..e08b8b8 100644 --- a/definitions/opam.yaml +++ b/definitions/opam.yaml @@ -16,6 +16,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [init, -n, --bare] + exit_codes: + 0: success + 1: error + install: base: [install, --deps-only, .] flags: @@ -79,6 +85,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/pnpm.yaml b/definitions/pnpm.yaml index c80d580..6a39a3b 100644 --- a/definitions/pnpm.yaml +++ b/definitions/pnpm.yaml @@ -18,6 +18,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [init] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -118,6 +124,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/poetry.yaml b/definitions/poetry.yaml index 918229d..9240225 100644 --- a/definitions/poetry.yaml +++ b/definitions/poetry.yaml @@ -20,6 +20,12 @@ detection: priority: 20 # Higher than uv for poetry-specific projects commands: + init: + base: [init, -n] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -94,6 +100,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/pub.yaml b/definitions/pub.yaml index d0b9c10..9170fd1 100644 --- a/definitions/pub.yaml +++ b/definitions/pub.yaml @@ -17,6 +17,12 @@ detection: priority: 10 commands: + init: + base: [create, -t, package, resolve_tmp] + exit_codes: + 0: success + 1: error + install: base: [pub, get] flags: @@ -82,6 +88,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/rebar3.yaml b/definitions/rebar3.yaml index 5e09950..a2598a1 100644 --- a/definitions/rebar3.yaml +++ b/definitions/rebar3.yaml @@ -15,6 +15,12 @@ version_detection: pattern: 'rebar (\d+\.\d+\.\d+)' commands: + init: + base: [new, lib, resolve_tmp] + exit_codes: + 0: success + 1: error + install: base: [get-deps] flags: @@ -90,6 +96,7 @@ commands: 1: error capabilities: + - init - install - list - update diff --git a/definitions/renv.yaml b/definitions/renv.yaml index 9563fa1..312995f 100644 --- a/definitions/renv.yaml +++ b/definitions/renv.yaml @@ -25,6 +25,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [-e, "renv::init(bare = TRUE)"] + exit_codes: + 0: success + 1: error + install: base: [-e, "renv::restore()"] flags: @@ -90,6 +96,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/shards.yaml b/definitions/shards.yaml index a069b64..5dfc520 100644 --- a/definitions/shards.yaml +++ b/definitions/shards.yaml @@ -15,6 +15,12 @@ version_detection: pattern: 'Shards (\d+\.\d+\.\d+)' commands: + init: + base: [init, resolve-tmp] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -78,6 +84,7 @@ commands: pattern: "lib/{package}" capabilities: + - init - install - install_frozen - list diff --git a/definitions/stack.yaml b/definitions/stack.yaml index ddd540b..54d44e4 100644 --- a/definitions/stack.yaml +++ b/definitions/stack.yaml @@ -16,6 +16,12 @@ version_detection: pattern: 'Version (\d+\.\d+\.\d+)' commands: + init: + base: [new, resolve-tmp] + exit_codes: + 0: success + 1: error + install: base: [build, --only-dependencies] flags: @@ -70,6 +76,7 @@ commands: 1: error capabilities: + - init - install - list - outdated diff --git a/definitions/swift.yaml b/definitions/swift.yaml index 554fd94..6a7b452 100644 --- a/definitions/swift.yaml +++ b/definitions/swift.yaml @@ -15,6 +15,12 @@ version_detection: pattern: 'Swift version (\d+\.\d+(?:\.\d+)?)' commands: + init: + base: [package, init, --type, library] + exit_codes: + 0: success + 1: error + install: base: [package, resolve] flags: @@ -77,6 +83,7 @@ commands: 1: error capabilities: + - init - install - add - list diff --git a/definitions/uv.yaml b/definitions/uv.yaml index 762bfd7..2aadf60 100644 --- a/definitions/uv.yaml +++ b/definitions/uv.yaml @@ -21,6 +21,12 @@ version_detection: pattern: 'uv (\d+\.\d+\.\d+)' commands: + init: + base: [init] + exit_codes: + 0: success + 1: error + # uv sync installs from lockfile install: base: [sync] @@ -127,6 +133,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/definitions/vcpkg.yaml b/definitions/vcpkg.yaml index a44d8b6..f9d05f4 100644 --- a/definitions/vcpkg.yaml +++ b/definitions/vcpkg.yaml @@ -15,6 +15,12 @@ version_detection: pattern: 'vcpkg package management program version (\d{4}-\d{2}-\d{2})' commands: + init: + base: [new, --application] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -68,6 +74,7 @@ commands: 1: error capabilities: + - init - install - add - remove diff --git a/definitions/yarn.yaml b/definitions/yarn.yaml index c4be560..748aed4 100644 --- a/definitions/yarn.yaml +++ b/definitions/yarn.yaml @@ -24,6 +24,12 @@ version_detection: pattern: '(\d+\.\d+\.\d+)' commands: + init: + base: [init, -y] + exit_codes: + 0: success + 1: error + install: base: [install] flags: @@ -124,6 +130,7 @@ commands: 1: error capabilities: + - init - install - install_frozen - add diff --git a/generic_manager.go b/generic_manager.go index 7cc7fb9..6b40f20 100644 --- a/generic_manager.go +++ b/generic_manager.go @@ -30,6 +30,20 @@ func (m *GenericManager) Warnings() []string { return m.warnings } +func (m *GenericManager) Init(ctx context.Context) (*Result, error) { + input := CommandInput{ + Args: map[string]string{}, + Flags: map[string]any{}, + } + + cmd, err := m.translator.BuildCommand(m.def.Name, "init", input) + if err != nil { + return nil, err + } + + return m.runner.Run(ctx, m.dir, cmd...) +} + func (m *GenericManager) Install(ctx context.Context, opts InstallOptions) (*Result, error) { input := CommandInput{ Args: map[string]string{}, @@ -61,6 +75,10 @@ func (m *GenericManager) Add(ctx context.Context, pkg string, opts AddOptions) ( }, } + if opts.Version != "" { + input.Args["version"] = opts.Version + } + cmd, err := m.translator.BuildCommand(m.def.Name, "add", input) if err != nil { return nil, err diff --git a/manager.go b/manager.go index 502d667..d451cda 100644 --- a/manager.go +++ b/manager.go @@ -9,6 +9,7 @@ type Manager interface { Name() string Ecosystem() string + Init(ctx context.Context) (*Result, error) Install(ctx context.Context, opts InstallOptions) (*Result, error) Add(ctx context.Context, pkg string, opts AddOptions) (*Result, error) Remove(ctx context.Context, pkg string) (*Result, error) @@ -30,6 +31,7 @@ type InstallOptions struct { } type AddOptions struct { + Version string Dev bool Optional bool Exact bool @@ -66,7 +68,8 @@ const ( type Capability int const ( - CapInstall Capability = iota + CapInit Capability = iota + CapInstall CapInstallFrozen CapInstallClean CapAdd @@ -87,6 +90,7 @@ const ( ) var capabilityNames = map[Capability]string{ + CapInit: "init", CapInstall: "install", CapInstallFrozen: "install_frozen", CapInstallClean: "install_clean",