Skip to content

Commit a7e38f7

Browse files
committed
SImplify things
1 parent c177efe commit a7e38f7

File tree

5 files changed

+101
-141
lines changed

5 files changed

+101
-141
lines changed

toolprovider/mise/install.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,51 @@ import (
44
"errors"
55
"fmt"
66

7+
"github.com/bitrise-io/bitrise/v2/log"
78
"github.com/bitrise-io/bitrise/v2/toolprovider/mise/execenv"
9+
"github.com/bitrise-io/bitrise/v2/toolprovider/mise/nixpkgs"
810
"github.com/bitrise-io/bitrise/v2/toolprovider/provider"
911
)
1012

13+
func installRequest(toolRequest provider.ToolRequest, useNix bool) provider.ToolRequest {
14+
if useNix {
15+
return provider.ToolRequest{
16+
// Use Mise's backend plugin convention of pluginID:toolID
17+
ToolName: provider.ToolID(fmt.Sprintf("nixpkgs:%s", toolRequest.ToolName)),
18+
UnparsedVersion: toolRequest.UnparsedVersion,
19+
ResolutionStrategy: toolRequest.ResolutionStrategy,
20+
PluginURL: nil, // Not relevant when using nixpkgs backend plugin
21+
}
22+
} else {
23+
return toolRequest
24+
}
25+
}
26+
27+
func (m *MiseToolProvider) canBeInstalledWithNix(tool provider.ToolRequest) (bool, error) {
28+
if !nixpkgs.ShouldUseBackend(tool) {
29+
return false, nil
30+
}
31+
32+
output, err := m.ExecEnv.RunMisePlugin("install", nixpkgs.PluginName, nixpkgs.PluginGitURL)
33+
if err != nil {
34+
return false, fmt.Errorf("install %s: %s", nixpkgs.PluginGitURL, output)
35+
}
36+
37+
nameWithBackend := provider.ToolID(fmt.Sprintf("nixpkgs:%s", tool.ToolName))
38+
39+
available, err := m.versionExists(nameWithBackend, tool.UnparsedVersion)
40+
if err != nil {
41+
log.Warnf("Error while checking nixpkgs index for %s@%s: %v. Falling back to core plugin installation.", tool.ToolName, tool.UnparsedVersion, err)
42+
return false, nil
43+
}
44+
if !available {
45+
log.Warnf("%s@%s not found in nixpkgs index, doing a source build. This may take some time...", tool.ToolName, tool.UnparsedVersion)
46+
return false, nil
47+
}
48+
49+
return true, nil
50+
}
51+
1152
func (m *MiseToolProvider) installToolVersion(tool provider.ToolRequest) error {
1253
versionString, err := miseVersionString(tool, m.resolveToLatestInstalled)
1354
if err != nil {

toolprovider/mise/install_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func TestMiseVersionString(t *testing.T) {
8686
want: "nixpkgs:[email protected]",
8787
wantErr: false,
8888
},
89+
// TODO: more test cases?
8990
}
9091

9192
for _, tt := range tests {

toolprovider/mise/mise.go

Lines changed: 24 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"fmt"
66
"os"
7-
"os/exec"
87
"path/filepath"
98
"strings"
109

@@ -41,11 +40,6 @@ var miseStableChecksums = map[string]string{
4140
"macos-arm64": "0b5893de7c8c274736867b7c4c7ed565b4429f4d6272521ace802f8a21422319",
4241
}
4342

44-
const (
45-
nixpkgsPluginGitURL = "https://github.com/bitrise-io/mise-nixpkgs-plugin.git"
46-
nixpkgsPluginName = "mise-nixpkgs-plugin"
47-
)
48-
4943
type MiseToolProvider struct {
5044
ExecEnv execenv.ExecEnv
5145
}
@@ -108,49 +102,51 @@ func (m *MiseToolProvider) Bootstrap() error {
108102
}
109103

110104
func (m *MiseToolProvider) InstallTool(tool provider.ToolRequest) (provider.ToolInstallResult, error) {
111-
useNix := m.shouldUseNixPkgs(tool)
105+
useNix, err := m.canBeInstalledWithNix(tool)
106+
if err != nil {
107+
return provider.ToolInstallResult{}, err
108+
}
112109

113-
if useNix {
114-
tool.ToolName = provider.ToolID(fmt.Sprintf("nixpkgs:%s", tool.ToolName))
115-
} else {
116-
err := m.InstallPlugin(tool)
110+
if !useNix {
111+
err = m.InstallPlugin(tool)
117112
if err != nil {
118113
return provider.ToolInstallResult{}, fmt.Errorf("install tool plugin %s: %w", tool.ToolName, err)
119114
}
120-
}
115+
} // else: nixpkgs plugin is already installed in ShouldInstallWithNix()
121116

122-
isAlreadyInstalled, err := isAlreadyInstalled(tool, m.resolveToLatestInstalled)
117+
installRequest := installRequest(tool, useNix)
118+
isAlreadyInstalled, err := isAlreadyInstalled(installRequest, m.resolveToLatestInstalled)
123119
if err != nil {
124120
return provider.ToolInstallResult{}, err
125121
}
126122

127-
if !useNix {
128-
// Nix already checks version existence previously
129-
versionExists, err := m.versionExists(tool.ToolName, tool.UnparsedVersion)
130-
if err != nil {
131-
return provider.ToolInstallResult{}, fmt.Errorf("check if version exists: %w", err)
132-
}
133-
if !versionExists {
134-
return provider.ToolInstallResult{}, provider.ToolInstallError{
135-
ToolName: tool.ToolName,
136-
RequestedVersion: tool.UnparsedVersion,
137-
Cause: fmt.Sprintf("no match for requested version %s", tool.UnparsedVersion),
138-
}
123+
versionExists, err := m.versionExists(tool.ToolName, tool.UnparsedVersion)
124+
if err != nil {
125+
return provider.ToolInstallResult{}, fmt.Errorf("check if version exists: %w", err)
126+
}
127+
if !versionExists {
128+
return provider.ToolInstallResult{}, provider.ToolInstallError{
129+
ToolName: tool.ToolName,
130+
RequestedVersion: tool.UnparsedVersion,
131+
Cause: fmt.Sprintf("no match for requested version %s", tool.UnparsedVersion),
139132
}
140133
}
141134

142-
err = m.installToolVersion(tool)
135+
err = m.installToolVersion(installRequest)
143136
if err != nil {
144137
return provider.ToolInstallResult{}, err
145138
}
146139

147-
concreteVersion, err := m.resolveToConcreteVersionAfterInstall(tool)
140+
concreteVersion, err := m.resolveToConcreteVersionAfterInstall(installRequest)
148141
if err != nil {
149142
return provider.ToolInstallResult{}, fmt.Errorf("resolve exact version after install: %w", err)
150143
}
151144

152145
return provider.ToolInstallResult{
153-
ToolName: tool.ToolName,
146+
// Note: we return installRequest.ToolName instead of the original tool.ToolName.
147+
// This is because installRequest might use a custom backend plugin and the value returned here
148+
// is what gets used in ActivateEnv(), the two should be consistent.
149+
ToolName: installRequest.ToolName,
154150
IsAlreadyInstalled: isAlreadyInstalled,
155151
ConcreteVersion: concreteVersion,
156152
}, nil
@@ -194,112 +190,3 @@ func GetMiseChecksums() map[string]string {
194190
// Fallback to stable version for non-edge stacks
195191
return miseStableChecksums
196192
}
197-
198-
func useNixPkgs(tool provider.ToolRequest) bool {
199-
// Note: Add other tools here if needed
200-
if tool.ToolName != "ruby" {
201-
log.Debugf("[TOOLPROVIDER] Nix packages are only supported for ruby tool, current tool: %s", tool.ToolName)
202-
return false
203-
}
204-
205-
if value, variablePresent := os.LookupEnv("BITRISEIO_MISE_LEGACY_INSTALL"); variablePresent && strings.Contains(value, "1") {
206-
log.Debugf("[TOOLPROVIDER] Using legacy install (non-nix) for tool: %s", tool.ToolName)
207-
return false
208-
}
209-
210-
return true
211-
}
212-
213-
// shouldUseNixPkgs checks if Nix packages should be used for the tool installation.
214-
// It validates that the tool is eligible for Nix, the plugin is available, and the version exists in the index.
215-
// Returns false and logs a warning if any validation fails, falling back to legacy installation.
216-
func (m *MiseToolProvider) shouldUseNixPkgs(tool provider.ToolRequest) bool {
217-
if !useNixPkgs(tool) {
218-
return false
219-
}
220-
221-
if err := m.getNixpkgsPlugin(); err != nil {
222-
log.Warnf("Failed to link nixpkgs plugin: %v. Falling back to legacy installation.", err)
223-
return false
224-
}
225-
226-
nameWithBackend := provider.ToolID(fmt.Sprintf("nixpkgs:%s", tool.ToolName))
227-
228-
available, err := m.versionExists(nameWithBackend, tool.UnparsedVersion)
229-
if err != nil || !available {
230-
log.Warnf("Failed to check nixpkgs index for %s@%s: %v. Falling back to legacy installation.", tool.ToolName, tool.UnparsedVersion, err)
231-
return false
232-
}
233-
234-
return true
235-
}
236-
237-
// findPluginPath finds the nixpkgs plugin directory relative to the bitrise executable.
238-
// It first checks next to the binary, then tries a dev location one directory up.
239-
// Returns the path if found, otherwise returns an error.
240-
func findPluginPath() (string, error) {
241-
execPath, err := os.Executable()
242-
if err != nil {
243-
return "", fmt.Errorf("get executable path: %w", err)
244-
}
245-
246-
execDir := filepath.Dir(execPath)
247-
pluginPath := filepath.Join(execDir, nixpkgsPluginName)
248-
249-
// Check if the plugin exists besides the binary
250-
if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
251-
// Try dev location
252-
pluginPath = filepath.Join(execDir, "..", nixpkgsPluginName)
253-
if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
254-
return "", fmt.Errorf("%s not found", nixpkgsPluginName)
255-
}
256-
}
257-
258-
return pluginPath, nil
259-
}
260-
261-
// getNixpkgsPlugin clones or updates the nixpkgs backend plugin and links it to mise.
262-
// If the plugin directory doesn't exist, it clones from the git URL.
263-
// If it exists, it checks out the specified commit/branch.
264-
func (m *MiseToolProvider) getNixpkgsPlugin() error {
265-
pluginPath, err := findPluginPath()
266-
needsClone := false
267-
if err != nil {
268-
// Plugin doesn't exist, we need to clone it
269-
needsClone = true
270-
execPath, execErr := os.Executable()
271-
if execErr != nil {
272-
return fmt.Errorf("get executable path: %w", execErr)
273-
}
274-
pluginPath = filepath.Join(filepath.Dir(execPath), nixpkgsPluginName)
275-
}
276-
277-
if needsClone {
278-
if err := cloneGitRepo(nixpkgsPluginGitURL, pluginPath); err != nil {
279-
return fmt.Errorf("clone nixpkgs plugin: %w", err)
280-
}
281-
}
282-
283-
// Link the plugin using mise plugin link
284-
_, err = m.ExecEnv.RunMisePlugin("link", "--force", "nixpkgs", pluginPath)
285-
if err != nil {
286-
return fmt.Errorf("link nixpkgs plugin: %w", err)
287-
}
288-
289-
// Enable experimental settings for custom backend
290-
if _, err := m.ExecEnv.RunMise("settings", "experimental=true"); err != nil {
291-
return fmt.Errorf("enable experimental settings: %w", err)
292-
}
293-
294-
return nil
295-
}
296-
297-
// cloneGitRepo clones a git repository to the specified path with minimal history.
298-
func cloneGitRepo(repoURL, destPath string) error {
299-
cmd := exec.Command("git", "clone", "--depth", "1", "--no-tags", repoURL, destPath)
300-
output, err := cmd.CombinedOutput()
301-
if err != nil {
302-
return fmt.Errorf("git clone failed: %w: %s", err, string(output))
303-
}
304-
return nil
305-
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package nixpkgs
2+
3+
import (
4+
"os"
5+
"strings"
6+
7+
"github.com/bitrise-io/bitrise/v2/log"
8+
"github.com/bitrise-io/bitrise/v2/toolprovider/provider"
9+
)
10+
11+
const (
12+
PluginGitURL = "https://github.com/bitrise-io/mise-nixpkgs-plugin.git"
13+
PluginName = "mise-nixpkgs-plugin"
14+
)
15+
16+
// TODO: check if Nix works at all
17+
18+
func ShouldUseBackend(request provider.ToolRequest) bool {
19+
if request.ToolName != "ruby" {
20+
log.Debugf("[TOOLPROVIDER] The mise-nixpkgs backend is only enabled for Ruby for now. Using core plugin to install %s", request.ToolName)
21+
return false
22+
}
23+
24+
value, ok := os.LookupEnv("BITRISE_TOOLSETUP_FAST_INSTALL")
25+
if ok && strings.TrimSpace(value) == "1" {
26+
log.Debugf("[TOOLPROVIDER] Using nixpkgs backend for %s as BITRISE_TOOLSETUP_FAST_INSTALL is set", request.ToolName)
27+
return true
28+
}
29+
log.Debugf("[TOOLPROVIDER] Using core plugin for %s", request.ToolName)
30+
return false
31+
}

toolprovider/mise/resolve.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,19 +103,19 @@ func versionExists(executor MiseExecutor, toolName provider.ToolID, version stri
103103
// Fallback: no installed versions found, fall through to remote (ls-remote) existence check.
104104
}
105105

106-
search := string(toolName)
106+
versionString := string(toolName)
107107
if version != "" && version != "latest" && version != "installed" {
108-
search = fmt.Sprintf("%s@%s", toolName, version)
108+
versionString = fmt.Sprintf("%s@%s", toolName, version)
109109
}
110110

111111
// Notes:
112112
// - ls-remote accepts both fuzzy and concrete versions
113113
// - it can return multiple versions (one per line) when a fuzzy version is provided
114114
// - in case of no matching version, the exit code is still 0, just there is no output
115115
// - in case of a non-existing tool, the exit code is 1, but a non-existing tool ID fails earlier than this check
116-
output, err := executor.RunMiseWithTimeout(execenv.DefaultTimeout, "ls-remote", "--quiet", search)
116+
output, err := executor.RunMiseWithTimeout(execenv.DefaultTimeout, "ls-remote", "--quiet", versionString)
117117
if err != nil {
118-
return false, fmt.Errorf("mise ls-remote %s: %w", search, err)
118+
return false, fmt.Errorf("mise ls-remote %s: %w", versionString, err)
119119
}
120120

121121
return strings.TrimSpace(string(output)) != "", nil

0 commit comments

Comments
 (0)