Skip to content

Conversation

@marcell-vida
Copy link
Contributor

Checklist

Version

Requires a MAJOR/MINOR/PATCH version update

Context

Changes

Investigation details

Decisions

@bitrise
Copy link
Contributor

bitrise bot commented Dec 1, 2025

Summary

Added tools command with setup and info subcommands to manage tool installation. Implemented tool provider functions to install tools from version files or bitrise config, list installed tools, and parse various version file formats.

Walkthrough

File Summary
cli/commands.go, cli/tools_manager.go Added tools command with setup and info subcommands. Setup installs tools from version files or bitrise config; info displays installed tools information. Includes format conversion for plaintext, JSON, and bash output.
toolprovider/info.go New file with functions to list installed tools from asdf or mise providers, including output parsing and whitespace trimming helpers.
toolprovider/run.go Refactored Run function by extracting core tool installation logic into installTools helper function while preserving existing logic.
toolprovider/setup.go New file with SetupFromVersionFiles function to install tools from version files by searching, parsing, and converting to tool requests.
toolprovider/versionfile/parser.go New file with version file parsing functionality supporting .tool-versions and single-tool formats (.ruby-version, .node-version, etc.) with tool name inference.
Copy link
Contributor

@bitrise bitrise bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an AI-generated review. Please review it carefully.

Actionable comments posted: 1

}
log.Printf("")
log.Infof("Run the following to apply environment changes:")
log.Printf(" export $(bitrise tools setup --from ... | grep export)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Bug

The help text references a --from flag that doesn't exist. The actual flag is --config or -c as defined in the command flags at line 36.

🔄 Suggestion:

Suggested change
log.Printf(" export $(bitrise tools setup --from ... | grep export)")
log.Printf(" export $(bitrise tools setup --config ... | grep export)")
},
cli.StringFlag{
Name: "provider",
Usage: "Tool provider to use (asdf, mise). Default: mise",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO we shouldn't expose it unless/until a valid usecase comes up. Let's just go with the current default provider used elsewhere.

Usage: "Workflow ID to use when installing from bitrise config (optional, uses global tools if not specified)",
},
cli.BoolFlag{
Name: "fast-install",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, I'd rather stick to the default for now, then iterate on it if needed


var toolsCommand = cli.Command{
Name: "tools",
Usage: "Manage development tools.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Usage: "Manage development tools.",
Usage: "Manage available tools from inside the workflow.",
Copy link
Contributor

@bitrise bitrise bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an AI-generated review. Please review it carefully.

Actionable comments posted: 2

var pluginURL *string
if opts.ExtraPlugins != nil {
if url, ok := opts.ExtraPlugins[models.ToolID(tool.ToolName)]; ok {
pluginURL = &url
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Bug

Taking the address of a loop variable that goes out of scope. The pointer will be invalid after the if block completes, leading to undefined behavior when the pluginURL is later dereferenced.

🔄 Suggestion:

Suggested change
pluginURL = &url
urlCopy := url
pluginURL = &urlCopy
Comment on lines +118 to +159
if bitriseConfigPath != "" {
config, warnings, err := CreateBitriseConfigFromCLIParams("", bitriseConfigPath, bitrise.ValidationTypeFull)
if err != nil {
return fmt.Errorf("load config: %w", err)
}

for _, warning := range warnings {
log.Warnf("Config warning: %s", warning)
}

tracker := analytics.NewDefaultTracker()
envs, err := toolprovider.Run(config, tracker, false, workflowID)
if err != nil {
return err
}

output, err := convertToOutputFormat(envs, format)
if err != nil {
return fmt.Errorf("convert to output format: %w", err)
}
fmt.Println(output)
}

opts := toolprovider.SetupOptions{
VersionFiles: configFiles,
ProviderName: provider,
ExperimentalFastInstall: fastInstall,
}

tracker := analytics.NewDefaultTracker()
envs, err := toolprovider.SetupFromVersionFiles(opts, tracker)
if err != nil {
return err
}

output, err := convertToOutputFormat(envs, format)
if err != nil {
return fmt.Errorf("convert to output format: %w", err)
}
// TODO: avoid printing other log output when --bash or --json is used
fmt.Println(output)
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Bug

The function processes a bitrise config if found (lines 118-139), but then unconditionally calls toolprovider.SetupFromVersionFiles with the same configFiles list (lines 141-159). This means both code paths execute when a bitrise config is found, causing duplicate processing and output. Additionally, the bitrise config file will be passed to SetupFromVersionFiles which expects version files, potentially causing errors. The logic should likely use an if-else structure or return after processing the bitrise config.

🤖 Prompt for AI Agents:
In cli/tools_manager.go between lines 118 and 159, Fix the logic to ensure only one code path executes: either process the bitrise config OR process version files, not both. Consider using an if-else structure or returning after processing the bitrise config. 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants