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-
4943type MiseToolProvider struct {
5044 ExecEnv execenv.ExecEnv
5145}
@@ -108,24 +102,25 @@ func (m *MiseToolProvider) Bootstrap() error {
108102}
109103
110104func (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
127123 if ! useNix {
128- // Nix already checks version existence previously
129124 versionExists , err := m .versionExists (tool .ToolName , tool .UnparsedVersion )
130125 if err != nil {
131126 return provider.ToolInstallResult {}, fmt .Errorf ("check if version exists: %w" , err )
@@ -137,20 +132,23 @@ func (m *MiseToolProvider) InstallTool(tool provider.ToolRequest) (provider.Tool
137132 Cause : fmt .Sprintf ("no match for requested version %s" , tool .UnparsedVersion ),
138133 }
139134 }
140- }
135+ } // else: version existence is already checked in canBeInstalledWithNix()
141136
142- err = m .installToolVersion (tool )
137+ err = m .installToolVersion (installRequest )
143138 if err != nil {
144139 return provider.ToolInstallResult {}, err
145140 }
146141
147- concreteVersion , err := m .resolveToConcreteVersionAfterInstall (tool )
142+ concreteVersion , err := m .resolveToConcreteVersionAfterInstall (installRequest )
148143 if err != nil {
149144 return provider.ToolInstallResult {}, fmt .Errorf ("resolve exact version after install: %w" , err )
150145 }
151146
152147 return provider.ToolInstallResult {
153- ToolName : tool .ToolName ,
148+ // Note: we return installRequest.ToolName instead of the original tool.ToolName.
149+ // This is because installRequest might use a custom backend plugin and the value returned here
150+ // is what gets used in ActivateEnv(), the two should be consistent.
151+ ToolName : installRequest .ToolName ,
154152 IsAlreadyInstalled : isAlreadyInstalled ,
155153 ConcreteVersion : concreteVersion ,
156154 }, nil
@@ -194,112 +192,3 @@ func GetMiseChecksums() map[string]string {
194192 // Fallback to stable version for non-edge stacks
195193 return miseStableChecksums
196194}
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- }
0 commit comments