2024-04-30 07:13:05 -07:00
// Package cmd contains the asdf CLI command code
2024-02-16 13:21:19 -07:00
package cmd
2024-02-12 18:00:41 -07:00
import (
2024-06-04 18:03:03 -07:00
"errors"
2024-08-17 13:27:18 -07:00
"fmt"
2024-09-05 19:12:08 -07:00
"io"
2024-02-21 17:52:34 -07:00
"log"
2024-02-12 18:00:41 -07:00
"os"
2024-09-14 13:06:08 -07:00
"path/filepath"
2024-10-15 05:12:22 -07:00
"slices"
2024-08-17 13:27:18 -07:00
"strings"
2024-09-14 13:06:08 -07:00
"text/tabwriter"
2024-02-12 18:00:41 -07:00
2024-09-05 06:31:30 -07:00
"asdf/internal/config"
2024-09-12 06:02:59 -07:00
"asdf/internal/exec"
2024-11-24 09:00:40 -07:00
"asdf/internal/execenv"
"asdf/internal/execute"
2024-09-22 11:40:00 -07:00
"asdf/internal/help"
2024-07-05 17:55:29 -07:00
"asdf/internal/info"
2024-09-05 06:55:13 -07:00
"asdf/internal/installs"
2024-09-05 06:31:30 -07:00
"asdf/internal/plugins"
2024-09-14 13:06:08 -07:00
"asdf/internal/resolve"
2024-09-02 11:42:54 -07:00
"asdf/internal/shims"
2024-10-05 14:49:28 -07:00
"asdf/internal/toolversions"
2024-08-17 13:27:18 -07:00
"asdf/internal/versions"
2024-04-30 07:13:05 -07:00
2024-02-21 17:52:34 -07:00
"github.com/urfave/cli/v2"
2024-02-12 18:00:41 -07:00
)
2024-02-21 17:52:34 -07:00
const usageText = ` The Multiple Runtime Version Manager .
2024-02-12 18:00:41 -07:00
Manage all your runtime versions with one tool !
2024-02-21 17:52:34 -07:00
Complete documentation is available at https : //asdf-vm.com/`
2024-02-12 18:00:41 -07:00
2024-04-30 07:13:05 -07:00
// Execute defines the full CLI API and then runs it
2024-07-05 17:55:29 -07:00
func Execute ( version string ) {
2024-02-29 18:59:12 -07:00
logger := log . New ( os . Stderr , "" , 0 )
log . SetFlags ( 0 )
2024-02-21 17:52:34 -07:00
app := & cli . App {
Name : "asdf" ,
Version : "0.1.0" ,
// Not really sure what I should put here, but all the new Golang code will
// likely be written by me.
Copyright : "(c) 2024 Trevor Brown" ,
Authors : [ ] * cli . Author {
2024-03-20 18:05:03 -07:00
{
2024-02-21 17:52:34 -07:00
Name : "Trevor Brown" ,
} ,
} ,
Usage : "The multiple runtime version manager" ,
UsageText : usageText ,
Commands : [ ] * cli . Command {
2024-09-14 13:06:08 -07:00
{
Name : "current" ,
Action : func ( cCtx * cli . Context ) error {
tool := cCtx . Args ( ) . Get ( 0 )
return currentCommand ( logger , tool )
} ,
} ,
2024-11-24 09:00:40 -07:00
{
Name : "env" ,
Action : func ( cCtx * cli . Context ) error {
shimmedCommand := cCtx . Args ( ) . Get ( 0 )
args := cCtx . Args ( ) . Slice ( )
return envCommand ( logger , shimmedCommand , args )
} ,
} ,
2024-09-12 06:02:59 -07:00
{
Name : "exec" ,
Action : func ( cCtx * cli . Context ) error {
command := cCtx . Args ( ) . Get ( 0 )
args := cCtx . Args ( ) . Slice ( )
return execCommand ( logger , command , args )
} ,
} ,
2024-09-22 11:40:00 -07:00
{
Name : "help" ,
Action : func ( cCtx * cli . Context ) error {
toolName := cCtx . Args ( ) . Get ( 0 )
toolVersion := cCtx . Args ( ) . Get ( 1 )
return helpCommand ( logger , version , toolName , toolVersion )
} ,
} ,
2024-07-05 17:55:29 -07:00
{
Name : "info" ,
Action : func ( _ * cli . Context ) error {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
return infoCommand ( conf , version )
} ,
} ,
2024-08-17 13:27:18 -07:00
{
Name : "install" ,
Action : func ( cCtx * cli . Context ) error {
args := cCtx . Args ( )
return installCommand ( logger , args . Get ( 0 ) , args . Get ( 1 ) )
} ,
} ,
2024-08-20 05:15:05 -07:00
{
Name : "latest" ,
Flags : [ ] cli . Flag {
& cli . BoolFlag {
Name : "all" ,
Usage : "Show latest version of all tools" ,
} ,
} ,
Action : func ( cCtx * cli . Context ) error {
tool := cCtx . Args ( ) . Get ( 0 )
pattern := cCtx . Args ( ) . Get ( 1 )
all := cCtx . Bool ( "all" )
return latestCommand ( logger , all , tool , pattern )
} ,
} ,
2024-10-15 05:12:22 -07:00
{
Name : "list" ,
Action : func ( cCtx * cli . Context ) error {
args := cCtx . Args ( )
return listCommand ( logger , args . Get ( 0 ) , args . Get ( 1 ) , args . Get ( 2 ) )
} ,
} ,
2024-03-20 18:05:03 -07:00
{
2024-02-21 17:52:34 -07:00
Name : "plugin" ,
2024-03-20 18:05:03 -07:00
Action : func ( _ * cli . Context ) error {
2024-07-05 17:55:29 -07:00
logger . Println ( "Unknown command: `asdf plugin`" )
os . Exit ( 1 )
2024-02-21 17:52:34 -07:00
return nil
} ,
Subcommands : [ ] * cli . Command {
2024-03-20 18:05:03 -07:00
{
2024-02-21 17:52:34 -07:00
Name : "add" ,
Action : func ( cCtx * cli . Context ) error {
2024-02-29 18:59:12 -07:00
args := cCtx . Args ( )
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
2024-03-01 06:43:52 -07:00
return err
2024-02-29 18:59:12 -07:00
}
return pluginAddCommand ( cCtx , conf , logger , args . Get ( 0 ) , args . Get ( 1 ) )
2024-02-21 17:52:34 -07:00
} ,
} ,
2024-03-20 18:05:03 -07:00
{
2024-02-21 17:52:34 -07:00
Name : "list" ,
2024-03-01 06:43:52 -07:00
Flags : [ ] cli . Flag {
& cli . BoolFlag {
Name : "urls" ,
Usage : "Show URLs" ,
} ,
& cli . BoolFlag {
Name : "refs" ,
Usage : "Show Refs" ,
} ,
} ,
2024-02-21 17:52:34 -07:00
Action : func ( cCtx * cli . Context ) error {
2024-03-01 06:43:52 -07:00
return pluginListCommand ( cCtx , logger )
2024-02-21 17:52:34 -07:00
} ,
} ,
2024-03-20 18:05:03 -07:00
{
2024-03-07 12:15:24 -07:00
Name : "remove" ,
2024-02-21 17:52:34 -07:00
Action : func ( cCtx * cli . Context ) error {
2024-03-07 12:15:24 -07:00
args := cCtx . Args ( )
return pluginRemoveCommand ( cCtx , logger , args . Get ( 0 ) )
2024-02-21 17:52:34 -07:00
} ,
} ,
2024-03-20 18:05:03 -07:00
{
2024-02-21 17:52:34 -07:00
Name : "update" ,
2024-04-27 17:20:07 -07:00
Flags : [ ] cli . Flag {
& cli . BoolFlag {
Name : "all" ,
Usage : "Update all installed plugins" ,
} ,
} ,
Action : func ( cCtx * cli . Context ) error {
args := cCtx . Args ( )
return pluginUpdateCommand ( cCtx , logger , args . Get ( 0 ) , args . Get ( 1 ) )
2024-02-21 17:52:34 -07:00
} ,
} ,
} ,
} ,
2024-09-02 11:42:54 -07:00
{
Name : "reshim" ,
2024-09-05 19:12:08 -07:00
Action : func ( cCtx * cli . Context ) error {
args := cCtx . Args ( )
return reshimCommand ( logger , args . Get ( 0 ) , args . Get ( 1 ) )
2024-09-02 11:42:54 -07:00
} ,
} ,
2024-10-31 06:20:00 -07:00
{
Name : "shimversions" ,
Action : func ( cCtx * cli . Context ) error {
args := cCtx . Args ( )
return shimVersionsCommand ( logger , args . Get ( 0 ) )
} ,
} ,
2024-10-07 06:06:50 -07:00
{
Name : "uninstall" ,
Action : func ( cCtx * cli . Context ) error {
tool := cCtx . Args ( ) . Get ( 0 )
version := cCtx . Args ( ) . Get ( 1 )
return uninstallCommand ( logger , tool , version )
} ,
} ,
2024-10-05 14:49:28 -07:00
{
Name : "where" ,
Action : func ( cCtx * cli . Context ) error {
tool := cCtx . Args ( ) . Get ( 0 )
version := cCtx . Args ( ) . Get ( 1 )
return whereCommand ( logger , tool , version )
} ,
} ,
2024-09-16 18:11:39 -07:00
{
Name : "which" ,
Action : func ( cCtx * cli . Context ) error {
tool := cCtx . Args ( ) . Get ( 0 )
return whichCommand ( logger , tool )
} ,
} ,
2024-02-21 17:52:34 -07:00
} ,
2024-03-20 18:05:03 -07:00
Action : func ( _ * cli . Context ) error {
2024-02-21 17:52:34 -07:00
// TODO: flesh this out
log . Print ( "Late but latest -- Rajinikanth" )
return nil
} ,
}
2024-02-12 18:00:41 -07:00
2024-02-21 17:52:34 -07:00
err := app . Run ( os . Args )
if err != nil {
2024-02-12 18:00:41 -07:00
os . Exit ( 1 )
}
}
2024-02-29 18:59:12 -07:00
2024-09-14 13:06:08 -07:00
// This function is a whole mess and needs to be refactored
func currentCommand ( logger * log . Logger , tool string ) error {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
currentDir , err := os . Getwd ( )
if err != nil {
logger . Printf ( "unable to get current directory: %s" , err )
return err
}
// settings here to match legacy implementation
w := tabwriter . NewWriter ( os . Stdout , 16 , 0 , 1 , ' ' , 0 )
if tool == "" {
// show all
allPlugins , err := plugins . List ( conf , false , false )
if err != nil {
return err
}
if len ( allPlugins ) < 1 {
fmt . Println ( "No plugins installed" )
return nil
}
for _ , plugin := range allPlugins {
toolversion , versionFound , versionInstalled := getVersionInfo ( conf , plugin , currentDir )
formatCurrentVersionLine ( w , plugin , toolversion , versionFound , versionInstalled , err )
}
w . Flush ( )
return nil
}
// show single tool
plugin := plugins . New ( conf , tool )
err = plugin . Exists ( )
_ , ok := err . ( plugins . PluginMissing )
pluginExists := ! ok
if pluginExists {
toolversion , versionFound , versionInstalled := getVersionInfo ( conf , plugin , currentDir )
formatCurrentVersionLine ( w , plugin , toolversion , versionFound , versionInstalled , err )
w . Flush ( )
if ! versionFound {
os . Exit ( 126 )
}
if ! versionInstalled {
os . Exit ( 1 )
}
} else {
fmt . Printf ( "No such plugin: %s\n" , tool )
return err
}
return nil
}
func getVersionInfo ( conf config . Config , plugin plugins . Plugin , currentDir string ) ( resolve . ToolVersions , bool , bool ) {
toolversion , found , _ := resolve . Version ( conf , plugin , currentDir )
installed := false
if found {
firstVersion := toolversion . Versions [ 0 ]
2024-10-31 06:30:48 -07:00
version := toolversions . Parse ( firstVersion )
installed = installs . IsInstalled ( conf , plugin , version )
2024-09-14 13:06:08 -07:00
}
return toolversion , found , installed
}
func formatCurrentVersionLine ( w * tabwriter . Writer , plugin plugins . Plugin , toolversion resolve . ToolVersions , found bool , installed bool , err error ) error {
if err != nil {
return err
}
fmt . Fprintf ( w , "%s\t%s\t%s\n" , plugin . Name , formatVersions ( toolversion . Versions ) , formatSource ( toolversion , plugin , found , installed ) )
return nil
}
func formatSource ( toolversion resolve . ToolVersions , plugin plugins . Plugin , found bool , installed bool ) string {
if found {
if ! installed {
return fmt . Sprintf ( "Not installed. Run \"asdf install %s %s\"" , plugin . Name , toolversion . Versions [ 0 ] )
}
return filepath . Join ( toolversion . Directory , toolversion . Source )
}
return fmt . Sprintf ( "No version is set. Run \"asdf <global|shell|local> %s <version>\"" , plugin . Name )
}
func formatVersions ( versions [ ] string ) string {
switch len ( versions ) {
case 0 :
return "______"
case 1 :
return versions [ 0 ]
default :
return strings . Join ( versions , " " )
}
}
2024-11-24 09:00:40 -07:00
func envCommand ( logger * log . Logger , shimmedCommand string , args [ ] string ) error {
command := "env"
if shimmedCommand == "" {
logger . Printf ( "usage: asdf env <command>" )
return fmt . Errorf ( "usage: asdf env <command>" )
}
if len ( args ) >= 2 {
command = args [ 1 ]
}
realArgs := [ ] string { }
if len ( args ) > 2 {
realArgs = args [ 2 : ]
}
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
_ , plugin , version , err := getExecutable ( logger , conf , shimmedCommand )
if err != nil {
return err
}
parsedVersion := toolversions . Parse ( version )
execPaths , err := shims . ExecutablePaths ( conf , plugin , parsedVersion )
if err != nil {
return err
}
2024-11-27 09:21:24 -07:00
env := map [ string ] string {
2024-11-24 09:00:40 -07:00
"ASDF_INSTALL_TYPE" : parsedVersion . Type ,
"ASDF_INSTALL_VERSION" : parsedVersion . Value ,
"ASDF_INSTALL_PATH" : installs . InstallPath ( conf , plugin , parsedVersion ) ,
2024-11-27 09:21:24 -07:00
"PATH" : setPath ( execPaths ) ,
2024-11-24 09:00:40 -07:00
}
2024-11-27 09:21:24 -07:00
if parsedVersion . Type != "system" {
env , err = execenv . Generate ( plugin , env )
2024-11-24 09:00:40 -07:00
if _ , ok := err . ( plugins . NoCallbackError ) ; ! ok && err != nil {
return err
}
2024-11-27 09:21:24 -07:00
}
2024-11-24 09:00:40 -07:00
2024-11-27 09:21:24 -07:00
fname , err := shims . ExecutableOnPath ( env [ "PATH" ] , command )
if err != nil {
return err
2024-11-24 09:00:40 -07:00
}
err = exec . Exec ( fname , realArgs , execute . MapToSlice ( env ) )
if err != nil {
fmt . Printf ( "err %#+v\n" , err . Error ( ) )
}
return err
}
2024-11-27 09:21:24 -07:00
func setPath ( paths [ ] string ) string {
return strings . Join ( paths , ":" ) + ":" + os . Getenv ( "PATH" )
2024-11-24 09:00:40 -07:00
}
2024-09-12 06:02:59 -07:00
func execCommand ( logger * log . Logger , command string , args [ ] string ) error {
if command == "" {
2024-09-14 13:06:08 -07:00
logger . Printf ( "usage: asdf exec <command>" )
return fmt . Errorf ( "usage: asdf exec <command>" )
2024-09-12 06:02:59 -07:00
}
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
2024-11-24 09:00:40 -07:00
executable , plugin , version , err := getExecutable ( logger , conf , command )
if err != nil {
return err
}
if len ( args ) > 1 {
args = args [ 1 : ]
} else {
args = [ ] string { }
}
parsedVersion := toolversions . Parse ( version )
2024-11-27 09:21:24 -07:00
execPaths , err := shims . ExecutablePaths ( conf , plugin , parsedVersion )
2024-09-12 06:02:59 -07:00
if err != nil {
return err
}
2024-11-27 09:21:24 -07:00
env := map [ string ] string {
2024-11-24 09:00:40 -07:00
"ASDF_INSTALL_TYPE" : parsedVersion . Type ,
"ASDF_INSTALL_VERSION" : parsedVersion . Value ,
"ASDF_INSTALL_PATH" : installs . InstallPath ( conf , plugin , parsedVersion ) ,
2024-11-27 09:21:24 -07:00
"PATH" : setPath ( execPaths ) ,
}
if parsedVersion . Type != "system" {
env , err = execenv . Generate ( plugin , env )
if _ , ok := err . ( plugins . NoCallbackError ) ; ! ok && err != nil {
return err
}
2024-11-24 09:00:40 -07:00
}
2024-11-27 09:21:24 -07:00
env = execenv . MergeEnv ( execenv . SliceToMap ( os . Environ ( ) ) , env )
2024-11-24 09:00:40 -07:00
return exec . Exec ( executable , args , execute . MapToSlice ( env ) )
}
2024-09-12 06:02:59 -07:00
2024-11-24 09:00:40 -07:00
func getExecutable ( logger * log . Logger , conf config . Config , command string ) ( executable string , plugin plugins . Plugin , version string , err error ) {
currentDir , err := os . Getwd ( )
if err != nil {
logger . Printf ( "unable to get current directory: %s" , err )
return "" , plugins . Plugin { } , "" , err
}
2024-10-31 06:30:48 -07:00
2024-11-24 09:00:40 -07:00
executable , plugin , version , found , err := shims . FindExecutable ( conf , command , currentDir )
2024-09-12 06:02:59 -07:00
if err != nil {
2024-10-31 06:30:48 -07:00
2024-11-24 09:00:40 -07:00
if _ , ok := err . ( shims . NoExecutableForPluginError ) ; ok {
logger . Printf ( "No executable %s found for current version. Please select a different version or install %s manually for the current version" , command , command )
os . Exit ( 1 )
return "" , plugin , version , err
}
2024-10-31 06:30:48 -07:00
shimPath := shims . Path ( conf , command )
toolVersions , _ := shims . GetToolsAndVersionsFromShimFile ( shimPath )
if len ( toolVersions ) > 0 {
if anyInstalled ( conf , toolVersions ) {
logger . Printf ( "No version is set for command %s" , command )
logger . Printf ( "Consider adding one of the following versions in your config file at %s/.tool-versions\n" , currentDir )
} else {
logger . Printf ( "No preset version installed for command %s" , command )
for _ , toolVersion := range toolVersions {
for _ , version := range toolVersion . Versions {
fmt . Printf ( "asdf install %s %s\n" , toolVersion . Name , version )
}
}
fmt . Printf ( "or add one of the following versions in your config file at %s/.tool-versions\n" , currentDir )
}
for _ , toolVersion := range toolVersions {
for _ , version := range toolVersion . Versions {
fmt . Printf ( "%s %s" , toolVersion . Name , version )
}
}
}
os . Exit ( 126 )
2024-11-24 09:00:40 -07:00
return executable , plugins . Plugin { } , "" , err
2024-09-12 06:02:59 -07:00
}
if ! found {
logger . Print ( "executable not found" )
2024-10-31 06:30:48 -07:00
os . Exit ( 126 )
2024-11-24 09:00:40 -07:00
return executable , plugins . Plugin { } , "" , fmt . Errorf ( "executable not found" )
2024-09-12 06:02:59 -07:00
}
2024-11-24 09:00:40 -07:00
return executable , plugin , version , nil
2024-09-12 06:02:59 -07:00
}
2024-10-31 06:30:48 -07:00
func anyInstalled ( conf config . Config , toolVersions [ ] toolversions . ToolVersions ) bool {
for _ , toolVersion := range toolVersions {
for _ , version := range toolVersion . Versions {
version := toolversions . Parse ( version )
plugin := plugins . New ( conf , toolVersion . Name )
if installs . IsInstalled ( conf , plugin , version ) {
return true
}
}
}
return false
}
2024-03-20 18:05:03 -07:00
func pluginAddCommand ( _ * cli . Context , conf config . Config , logger * log . Logger , pluginName , pluginRepo string ) error {
2024-02-29 18:59:12 -07:00
if pluginName == "" {
// Invalid arguments
// Maybe one day switch this to show the generated help
// cli.ShowSubcommandHelp(cCtx)
return cli . Exit ( "usage: asdf plugin add <name> [<git-url>]" , 1 )
}
2024-03-20 18:05:03 -07:00
err := plugins . Add ( conf , pluginName , pluginRepo )
if err != nil {
2024-06-04 18:03:03 -07:00
logger . Printf ( "%s" , err )
var existsErr plugins . PluginAlreadyExists
if errors . As ( err , & existsErr ) {
os . Exit ( 0 )
return nil
}
os . Exit ( 1 )
return nil
2024-03-20 18:05:03 -07:00
}
2024-06-04 18:03:03 -07:00
os . Exit ( 0 )
2024-02-29 18:59:12 -07:00
return nil
}
2024-03-01 06:43:52 -07:00
2024-03-20 18:05:03 -07:00
func pluginRemoveCommand ( _ * cli . Context , logger * log . Logger , pluginName string ) error {
2024-10-19 11:47:04 -07:00
if pluginName == "" {
logger . Print ( "No plugin given" )
os . Exit ( 1 )
return nil
}
2024-03-07 12:15:24 -07:00
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
2024-10-19 11:47:04 -07:00
err = plugins . Remove ( conf , pluginName , os . Stdout , os . Stderr )
2024-03-07 12:15:24 -07:00
if err != nil {
2024-08-22 06:16:28 -07:00
// Needed to match output of old version
logger . Printf ( "%s" , err )
2024-03-07 12:15:24 -07:00
}
2024-10-19 11:47:04 -07:00
// This feels a little hacky but it works, to re-generate shims we delete them
// all and generate them again.
err2 := shims . RemoveAll ( conf )
if err2 != nil {
logger . Printf ( "%s" , err2 )
os . Exit ( 1 )
return err2
}
shims . GenerateAll ( conf , os . Stdout , os . Stderr )
2024-03-07 12:15:24 -07:00
return err
}
2024-03-01 06:43:52 -07:00
func pluginListCommand ( cCtx * cli . Context , logger * log . Logger ) error {
urls := cCtx . Bool ( "urls" )
refs := cCtx . Bool ( "refs" )
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
plugins , err := plugins . List ( conf , urls , refs )
if err != nil {
logger . Printf ( "error loading plugin list: %s" , err )
return err
}
// TODO: Add some sort of presenter logic in another file so we
// don't clutter up this cmd code with conditional presentation
// logic
for _ , plugin := range plugins {
if urls && refs {
2024-03-20 18:05:03 -07:00
logger . Printf ( "%s\t\t%s\t%s\n" , plugin . Name , plugin . URL , plugin . Ref )
2024-03-01 06:43:52 -07:00
} else if refs {
logger . Printf ( "%s\t\t%s\n" , plugin . Name , plugin . Ref )
} else if urls {
2024-03-20 18:05:03 -07:00
logger . Printf ( "%s\t\t%s\n" , plugin . Name , plugin . URL )
2024-03-01 06:43:52 -07:00
} else {
logger . Printf ( "%s\n" , plugin . Name )
}
}
return nil
}
2024-04-27 17:20:07 -07:00
2024-07-05 17:55:29 -07:00
func infoCommand ( conf config . Config , version string ) error {
return info . Print ( conf , version )
}
2024-09-22 11:40:00 -07:00
func helpCommand ( logger * log . Logger , asdfVersion , tool , version string ) error {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
if tool != "" {
if version != "" {
err := help . PrintToolVersion ( conf , tool , version )
if err != nil {
os . Exit ( 1 )
}
return err
}
err := help . PrintTool ( conf , tool )
if err != nil {
os . Exit ( 1 )
}
return err
}
err = help . Print ( asdfVersion )
if err != nil {
os . Exit ( 1 )
}
return err
}
2024-04-27 17:20:07 -07:00
func pluginUpdateCommand ( cCtx * cli . Context , logger * log . Logger , pluginName , ref string ) error {
updateAll := cCtx . Bool ( "all" )
if ! updateAll && pluginName == "" {
return cli . Exit ( "usage: asdf plugin-update {<name> [git-ref] | --all}" , 1 )
}
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
if updateAll {
installedPlugins , err := plugins . List ( conf , false , false )
if err != nil {
logger . Printf ( "failed to get plugin list: %s" , err )
return err
}
for _ , plugin := range installedPlugins {
updatedToRef , err := plugins . Update ( conf , plugin . Name , "" )
formatUpdateResult ( logger , plugin . Name , updatedToRef , err )
}
return nil
}
updatedToRef , err := plugins . Update ( conf , pluginName , ref )
formatUpdateResult ( logger , pluginName , updatedToRef , err )
return err
}
func formatUpdateResult ( logger * log . Logger , pluginName , updatedToRef string , err error ) {
if err != nil {
logger . Printf ( "failed to update %s due to error: %s\n" , pluginName , err )
return
}
logger . Printf ( "updated %s to ref %s\n" , pluginName , updatedToRef )
}
2024-08-17 13:27:18 -07:00
func installCommand ( logger * log . Logger , toolName , version string ) error {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
dir , err := os . Getwd ( )
if err != nil {
return fmt . Errorf ( "unable to fetch current directory: %w" , err )
}
if toolName == "" {
// Install all versions
errs := versions . InstallAll ( conf , dir , os . Stdout , os . Stderr )
if len ( errs ) > 0 {
for _ , err := range errs {
os . Stderr . Write ( [ ] byte ( err . Error ( ) ) )
os . Stderr . Write ( [ ] byte ( "\n" ) )
}
2024-09-02 11:42:54 -07:00
filtered := filterInstallErrors ( errs )
if len ( filtered ) > 0 {
return filtered [ 0 ]
}
return nil
2024-08-17 13:27:18 -07:00
}
} else {
// Install specific version
plugin := plugins . New ( conf , toolName )
if version == "" {
err = versions . Install ( conf , plugin , dir , os . Stdout , os . Stderr )
if err != nil {
return err
}
} else {
2024-10-31 06:30:48 -07:00
parsedVersion := toolversions . ParseFromCliArg ( version )
2024-08-17 13:27:18 -07:00
2024-10-31 06:30:48 -07:00
if parsedVersion . Type == "latest" {
err = versions . InstallVersion ( conf , plugin , version , parsedVersion . Value , os . Stdout , os . Stderr )
2024-08-17 13:27:18 -07:00
} else {
err = versions . InstallOneVersion ( conf , plugin , version , os . Stdout , os . Stderr )
}
if err != nil {
logger . Printf ( "error installing version: %s" , err )
}
}
}
return err
}
2024-09-02 11:42:54 -07:00
func filterInstallErrors ( errs [ ] error ) [ ] error {
var filtered [ ] error
for _ , err := range errs {
if _ , ok := err . ( versions . NoVersionSetError ) ; ! ok {
filtered = append ( filtered , err )
}
}
return filtered
}
2024-08-20 05:15:05 -07:00
func latestCommand ( logger * log . Logger , all bool , toolName , pattern string ) ( err error ) {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
if ! all {
err = latestForPlugin ( conf , toolName , pattern , false )
if err != nil {
os . Exit ( 1 )
}
return err
}
plugins , err := plugins . List ( conf , false , false )
if err != nil {
2024-08-22 06:16:28 -07:00
logger . Printf ( "error loading plugin list: %s" , err )
2024-08-20 05:15:05 -07:00
return err
}
var maybeErr error
// loop over all plugins and show latest for each one.
for _ , plugin := range plugins {
maybeErr = latestForPlugin ( conf , plugin . Name , "" , true )
if maybeErr != nil {
err = maybeErr
}
}
if err != nil {
os . Exit ( 1 )
return maybeErr
}
return nil
}
2024-10-15 05:12:22 -07:00
func listCommand ( logger * log . Logger , first , second , third string ) ( err error ) {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
// Both listAllCommand and listLocalCommand need to be refactored and extracted
// out into another package.
if first == "all" {
return listAllCommand ( logger , conf , second , third )
}
return listLocalCommand ( logger , conf , first , second )
}
func listAllCommand ( logger * log . Logger , conf config . Config , toolName , filter string ) error {
if toolName == "" {
logger . Print ( "No plugin given" )
os . Exit ( 1 )
return nil
}
plugin := plugins . New ( conf , toolName )
var stdout strings . Builder
var stderr strings . Builder
err := plugin . RunCallback ( "list-all" , [ ] string { } , map [ string ] string { } , & stdout , & stderr )
if err != nil {
fmt . Printf ( "Plugin %s's list-all callback script failed with output:\n" , plugin . Name )
// Print to stderr
os . Stderr . WriteString ( stderr . String ( ) )
os . Stderr . WriteString ( stdout . String ( ) )
os . Exit ( 1 )
return err
}
versions := strings . Split ( stdout . String ( ) , " " )
if filter != "" {
versions = filterByExactMatch ( versions , filter )
}
if len ( versions ) == 0 {
logger . Printf ( "No compatible versions available (%s %s)" , plugin . Name , filter )
os . Exit ( 1 )
return nil
}
for _ , version := range versions {
fmt . Printf ( "%s\n" , version )
}
return nil
}
func filterByExactMatch ( allVersions [ ] string , pattern string ) ( versions [ ] string ) {
for _ , version := range allVersions {
if strings . HasPrefix ( version , pattern ) {
versions = append ( versions , version )
}
}
return versions
}
func listLocalCommand ( logger * log . Logger , conf config . Config , pluginName , filter string ) error {
currentDir , err := os . Getwd ( )
if err != nil {
logger . Printf ( "unable to get current directory: %s" , err )
return err
}
if pluginName != "" {
plugin := plugins . New ( conf , pluginName )
versions , _ := installs . Installed ( conf , plugin )
if filter != "" {
versions = filterByExactMatch ( versions , filter )
}
if len ( versions ) == 0 {
logger . Printf ( "No compatible versions installed (%s %s)" , plugin . Name , filter )
os . Exit ( 1 )
return nil
}
currentVersions , _ , err := resolve . Version ( conf , plugin , currentDir )
if err != nil {
os . Exit ( 1 )
return err
}
for _ , version := range versions {
if slices . Contains ( currentVersions . Versions , version ) {
fmt . Printf ( " *%s\n" , version )
} else {
fmt . Printf ( " %s\n" , version )
}
}
return nil
}
allPlugins , err := plugins . List ( conf , false , false )
if err != nil {
logger . Printf ( "unable to list plugins due to error: %s" , err )
return err
}
for _ , plugin := range allPlugins {
fmt . Printf ( "%s\n" , plugin . Name )
versions , _ := installs . Installed ( conf , plugin )
if len ( versions ) > 0 {
currentVersions , _ , err := resolve . Version ( conf , plugin , currentDir )
if err != nil {
os . Exit ( 1 )
return err
}
for _ , version := range versions {
if slices . Contains ( currentVersions . Versions , version ) {
fmt . Printf ( " *%s\n" , version )
} else {
fmt . Printf ( " %s\n" , version )
}
}
} else {
fmt . Print ( " No versions installed\n" )
}
}
return nil
}
2024-09-05 19:12:08 -07:00
func reshimCommand ( logger * log . Logger , tool , version string ) ( err error ) {
2024-09-02 11:42:54 -07:00
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
2024-09-05 19:12:08 -07:00
// if either tool or version are missing just regenerate all shims. This is
// fast enough now.
if tool == "" || version == "" {
err = shims . RemoveAll ( conf )
if err != nil {
return err
}
2024-09-02 11:42:54 -07:00
2024-09-05 19:12:08 -07:00
return shims . GenerateAll ( conf , os . Stdout , os . Stderr )
2024-09-02 11:42:54 -07:00
}
2024-09-05 19:12:08 -07:00
// If provided a specific version it could be something special like a path
// version so we need to generate it manually
return reshimToolVersion ( conf , tool , version , os . Stdout , os . Stderr )
}
2024-10-31 06:20:00 -07:00
func shimVersionsCommand ( logger * log . Logger , shimName string ) error {
2024-11-24 09:00:40 -07:00
if shimName == "" {
logger . Printf ( "usage: asdf shimversions <command>" )
return fmt . Errorf ( "usage: asdf shimversions <command>" )
}
2024-10-31 06:20:00 -07:00
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
shimPath := shims . Path ( conf , shimName )
toolVersions , err := shims . GetToolsAndVersionsFromShimFile ( shimPath )
for _ , toolVersion := range toolVersions {
for _ , version := range toolVersion . Versions {
2024-11-24 09:00:40 -07:00
fmt . Printf ( "%s %s\n" , toolVersion . Name , version )
2024-10-31 06:20:00 -07:00
}
}
return err
}
2024-09-16 18:11:39 -07:00
// This function is a whole mess and needs to be refactored
func whichCommand ( logger * log . Logger , command string ) error {
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
currentDir , err := os . Getwd ( )
if err != nil {
logger . Printf ( "unable to get current directory: %s" , err )
return err
}
if command == "" {
fmt . Println ( "usage: asdf which <command>" )
return errors . New ( "must provide command" )
}
2024-11-24 09:00:40 -07:00
path , _ , _ , _ , err := shims . FindExecutable ( conf , command , currentDir )
2024-09-16 18:11:39 -07:00
if _ , ok := err . ( shims . UnknownCommandError ) ; ok {
logger . Printf ( "unknown command: %s. Perhaps you have to reshim?" , command )
return errors . New ( "command not found" )
}
if _ , ok := err . ( shims . NoExecutableForPluginError ) ; ok {
logger . Printf ( "%s" , err . Error ( ) )
return errors . New ( "no executable for tool version" )
}
if err != nil {
fmt . Printf ( "unexpected error: %s\n" , err . Error ( ) )
return err
}
fmt . Printf ( "%s\n" , path )
return nil
}
2024-10-07 06:06:50 -07:00
func uninstallCommand ( logger * log . Logger , tool , version string ) error {
if tool == "" || version == "" {
logger . Print ( "No plugin given" )
os . Exit ( 1 )
return nil
}
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
os . Exit ( 1 )
return err
}
plugin := plugins . New ( conf , tool )
err = versions . Uninstall ( conf , plugin , version , os . Stdout , os . Stderr )
if err != nil {
logger . Printf ( "%s" , err )
os . Exit ( 1 )
return err
}
// This feels a little hacky but it works, to re-generate shims we delete them
// all and generate them again.
err = shims . RemoveAll ( conf )
if err != nil {
logger . Printf ( "%s" , err )
os . Exit ( 1 )
return err
}
return shims . GenerateAll ( conf , os . Stdout , os . Stderr )
}
2024-10-31 06:30:48 -07:00
func whereCommand ( logger * log . Logger , tool , versionStr string ) error {
2024-10-05 14:49:28 -07:00
conf , err := config . LoadConfig ( )
if err != nil {
logger . Printf ( "error loading config: %s" , err )
return err
}
currentDir , err := os . Getwd ( )
if err != nil {
logger . Printf ( "unable to get current directory: %s" , err )
return err
}
plugin := plugins . New ( conf , tool )
err = plugin . Exists ( )
if err != nil {
if _ , ok := err . ( plugins . PluginMissing ) ; ok {
logger . Printf ( "No such plugin: %s" , tool )
}
return err
}
2024-10-31 06:30:48 -07:00
version := toolversions . Parse ( versionStr )
2024-10-05 14:49:28 -07:00
2024-10-31 06:30:48 -07:00
if version . Type == "system" {
logger . Printf ( "System version is selected" )
return errors . New ( "System version is selected" )
}
if version . Value == "" {
2024-10-05 14:49:28 -07:00
// resolve version
2024-10-31 06:30:48 -07:00
versions , found , err := resolve . Version ( conf , plugin , currentDir )
2024-10-05 14:49:28 -07:00
if err != nil {
fmt . Printf ( "err %#+v\n" , err )
return err
}
2024-10-31 06:30:48 -07:00
if found && len ( versions . Versions ) > 0 {
versionStruct := toolversions . Version { Type : "version" , Value : versions . Versions [ 0 ] }
if installs . IsInstalled ( conf , plugin , versionStruct ) {
installPath := installs . InstallPath ( conf , plugin , versionStruct )
logger . Printf ( "%s" , installPath )
return nil
}
2024-10-05 14:49:28 -07:00
}
// not found
msg := fmt . Sprintf ( "No version is set for %s; please run `asdf <global | shell | local> %s <version>`" , tool , tool )
logger . Print ( msg )
return errors . New ( msg )
}
2024-10-31 06:30:48 -07:00
if ! installs . IsInstalled ( conf , plugin , version ) {
2024-10-05 14:49:28 -07:00
logger . Printf ( "Version not installed" )
return errors . New ( "Version not installed" )
}
2024-10-31 06:30:48 -07:00
installPath := installs . InstallPath ( conf , plugin , version )
2024-10-05 14:49:28 -07:00
logger . Printf ( "%s" , installPath )
return nil
}
2024-10-31 06:30:48 -07:00
func reshimToolVersion ( conf config . Config , tool , versionStr string , out io . Writer , errOut io . Writer ) error {
version := toolversions . Parse ( versionStr )
2024-11-29 13:13:00 -07:00
return shims . GenerateForVersion ( conf , plugins . New ( conf , tool ) , version , out , errOut )
2024-09-02 11:42:54 -07:00
}
2024-08-20 05:15:05 -07:00
func latestForPlugin ( conf config . Config , toolName , pattern string , showStatus bool ) error {
// show single plugin
plugin := plugins . New ( conf , toolName )
latest , err := versions . Latest ( plugin , pattern )
if err != nil && err . Error ( ) != "no latest version found" {
fmt . Printf ( "unable to load latest version: %s\n" , err )
return err
}
if latest == "" {
err := fmt . Errorf ( "No compatible versions available (%s %s)" , toolName , pattern )
fmt . Println ( err . Error ( ) )
return err
}
if showStatus {
2024-10-31 06:30:48 -07:00
installed := installs . IsInstalled ( conf , plugin , toolversions . Version { Type : "version" , Value : latest } )
2024-08-20 05:15:05 -07:00
fmt . Printf ( "%s\t%s\t%s\n" , plugin . Name , latest , installedStatus ( installed ) )
} else {
fmt . Printf ( "%s\n" , latest )
}
return nil
}
func installedStatus ( installed bool ) string {
if installed {
return "installed"
}
return "missing"
}