@@ -3,7 +3,6 @@ package providers
3
3
import (
4
4
"encoding/json"
5
5
"fmt"
6
- "log"
7
6
"os"
8
7
"path/filepath"
9
8
"regexp"
@@ -78,9 +77,32 @@ func (p *NPMProvider) generatePackageJSON() bool {
78
77
}
79
78
80
79
type PackageJSON struct {
81
- Name string `json:"name"`
82
- Version string `json:"version"`
83
- Bin map [string ]string `json:"bin"`
80
+ Name string `json:"name"`
81
+ Version string `json:"version"`
82
+ Bin CustomBinField `json:"bin"`
83
+ }
84
+
85
+ type CustomBinField map [string ]string
86
+
87
+ func (cbf * CustomBinField ) UnmarshalJSON (data []byte ) error {
88
+ var m map [string ]string
89
+ if err := json .Unmarshal (data , & m ); err == nil {
90
+ * cbf = m
91
+ return nil
92
+ }
93
+
94
+ var s string
95
+ if err := json .Unmarshal (data , & s ); err == nil {
96
+ // If it's a string, we assume it's a single binary name
97
+ // and create a map with the binary name as the key and the string as the value.
98
+ // This is a common case for npm packages that have a single binary.
99
+ // Also remove the extension if present.
100
+ binName := strings .TrimSuffix (filepath .Base (s ), filepath .Ext (s ))
101
+ * cbf = map [string ]string {binName : s }
102
+ return nil
103
+ }
104
+
105
+ return fmt .Errorf ("bin field must be a string or a map" )
84
106
}
85
107
86
108
func (p * NPMProvider ) readPackageJSON (packagePath string ) (* PackageJSON , error ) {
@@ -96,27 +118,6 @@ func (p *NPMProvider) readPackageJSON(packagePath string) (*PackageJSON, error)
96
118
return & pkg , nil
97
119
}
98
120
99
- func (p * NPMProvider ) createSymlinks (packagePath string , pkg * PackageJSON ) error {
100
- binDir := files .GetAppBinPath ()
101
- for binName , binPath := range pkg .Bin {
102
- actualBinPath := filepath .Join (packagePath , binPath )
103
- symlinkPath := filepath .Join (binDir , binName )
104
- if _ , err := os .Lstat (symlinkPath ); err == nil {
105
- if err := os .Remove (symlinkPath ); err != nil {
106
- log .Printf ("Warning: failed to remove existing symlink %s: %v" , symlinkPath , err )
107
- }
108
- }
109
- if err := os .Symlink (actualBinPath , symlinkPath ); err != nil {
110
- log .Printf ("Error creating symlink for %s: %v" , binName , err )
111
- return err
112
- }
113
- if err := os .Chmod (symlinkPath , 0755 ); err != nil {
114
- log .Printf ("Error setting executable permissions for %s: %v" , binName , err )
115
- }
116
- }
117
- return nil
118
- }
119
-
120
121
func (p * NPMProvider ) removeAllSymlinks () error {
121
122
binDir := files .GetAppBinPath ()
122
123
entries , err := os .ReadDir (binDir )
@@ -130,7 +131,7 @@ func (p *NPMProvider) removeAllSymlinks() error {
130
131
symlinkPath := filepath .Join (binDir , entry .Name ())
131
132
if _ , err := os .Lstat (symlinkPath ); err == nil {
132
133
if err := os .Remove (symlinkPath ); err != nil {
133
- log . Printf ( "Warning: failed to remove symlink %s: %v" , symlinkPath , err )
134
+ Logger . Info ( fmt . Sprintf ( "Warning: failed to remove symlink %s: %v" , symlinkPath , err ) )
134
135
}
135
136
}
136
137
}
@@ -139,10 +140,10 @@ func (p *NPMProvider) removeAllSymlinks() error {
139
140
140
141
func (p * NPMProvider ) Clean () bool {
141
142
if err := p .removeAllSymlinks (); err != nil {
142
- log . Printf ( "Error removing symlinks: %v" , err )
143
+ Logger . Info ( fmt . Sprintf ( "Error removing symlinks: %v" , err ) )
143
144
}
144
145
if err := os .RemoveAll (p .APP_PACKAGES_DIR ); err != nil {
145
- log . Println ( "Error removing directory:" , err )
146
+ Logger . Info ( fmt . Sprintf ( "Error removing directory:" , err ) )
146
147
return false
147
148
}
148
149
return p .Sync ()
@@ -155,7 +156,7 @@ func (p *NPMProvider) Sync() bool {
155
156
return false
156
157
}
157
158
}
158
- log . Printf ( "NPM Sync: Starting sync process" )
159
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Starting sync process" ) )
159
160
packagesFound := p .generatePackageJSON ()
160
161
if ! packagesFound {
161
162
return true
@@ -182,11 +183,11 @@ func (p *NPMProvider) Sync() bool {
182
183
}
183
184
}
184
185
if allInstalled {
185
- log . Printf ( "NPM Sync: All packages already installed correctly, skipping installation" )
186
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: All packages already installed correctly, skipping installation" ) )
186
187
for _ , pkg := range desired {
187
188
name := p .getRepo (pkg .SourceID )
188
189
if err := p .createPackageSymlinks (name ); err != nil {
189
- log . Printf ( "Error creating symlinks for %s: %v" , name , err )
190
+ Logger . Info ( fmt . Sprintf ( "Error creating symlinks for %s: %v" , name , err ) )
190
191
}
191
192
}
192
193
return true
@@ -208,50 +209,50 @@ func (p *NPMProvider) Sync() bool {
208
209
for _ , pkg := range desired {
209
210
name := p .getRepo (pkg .SourceID )
210
211
if err := p .createPackageSymlinks (name ); err != nil {
211
- log . Printf ( "Error creating symlinks for %s: %v" , name , err )
212
+ Logger . Info ( fmt . Sprintf ( "Error creating symlinks for %s: %v" , name , err ) )
212
213
}
213
214
}
214
215
return true
215
216
}
216
217
if needsUpdate {
217
- log . Printf ( "NPM Sync: Attempting npm ci for faster bulk installation" )
218
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Attempting npm ci for faster bulk installation" ) )
218
219
if p .tryNpmCi () {
219
- log . Printf ( "NPM Sync: npm ci completed successfully" )
220
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: npm ci completed successfully" ) )
220
221
return true
221
222
}
222
- log . Printf ( "NPM Sync: npm ci failed, falling back to individual package installation" )
223
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: npm ci failed, falling back to individual package installation" ) )
223
224
if err := os .Remove (lockFile ); err != nil {
224
- log . Printf ( "Warning: failed to remove lock file: %v" , err )
225
+ Logger . Info ( fmt . Sprintf ( "Warning: failed to remove lock file: %v" , err ) )
225
226
}
226
227
}
227
228
}
228
- log . Printf ( "NPM Sync: Installing packages individually" )
229
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Installing packages individually" ) )
229
230
allOk := true
230
231
installedCount := 0
231
232
skippedCount := 0
232
233
for _ , pkg := range desired {
233
234
name := p .getRepo (pkg .SourceID )
234
235
if p .isPackageInstalled (name , pkg .Version ) {
235
- log . Printf ( "NPM Sync: Package %s@%s already installed, skipping" , name , pkg .Version )
236
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Package %s@%s already installed, skipping" , name , pkg .Version ) )
236
237
skippedCount ++
237
238
if err := p .createPackageSymlinks (name ); err != nil {
238
- log . Printf ( "Error creating symlinks for %s: %v" , name , err )
239
+ Logger . Info ( fmt . Sprintf ( "Error creating symlinks for %s: %v" , name , err ) )
239
240
}
240
241
continue
241
242
}
242
- log . Printf ( "NPM Sync: Installing package %s@%s" , name , pkg .Version )
243
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Installing package %s@%s" , name , pkg .Version ) )
243
244
installCode , err := shell_out .ShellOut ("npm" , []string {"install" , name + "@" + pkg .Version }, p .APP_PACKAGES_DIR , nil )
244
245
if err != nil || installCode != 0 {
245
246
fmt .Printf ("Error installing %s@%s: %v\n " , name , pkg .Version , err )
246
247
allOk = false
247
248
} else {
248
249
installedCount ++
249
250
if err := p .createPackageSymlinks (name ); err != nil {
250
- log . Printf ( "Error creating symlinks for %s: %v" , name , err )
251
+ Logger . Info ( fmt . Sprintf ( "Error creating symlinks for %s: %v" , name , err ) )
251
252
}
252
253
}
253
254
}
254
- log . Printf ( "NPM Sync: Completed - %d packages installed, %d packages skipped" , installedCount , skippedCount )
255
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Completed - %d packages installed, %d packages skipped" , installedCount , skippedCount ) )
255
256
return allOk
256
257
}
257
258
@@ -294,8 +295,23 @@ func (p *NPMProvider) createPackageSymlinks(packageName string) error {
294
295
return fmt .Errorf ("error reading package.json for %s: %v" , packageName , err )
295
296
}
296
297
if len (pkg .Bin ) > 0 {
297
- if err := p .createSymlinks (packagePath , pkg ); err != nil {
298
- return fmt .Errorf ("error creating symlinks for %s: %v" , packageName , err )
298
+ binDir := files .GetAppBinPath ()
299
+ for binPath := range pkg .Bin {
300
+ actualBinPath := filepath .Join (nodeModulesPath , ".bin" , binPath )
301
+ symlinkPath := filepath .Join (binDir , binPath )
302
+ if _ , err := os .Lstat (symlinkPath ); err == nil {
303
+ if err := os .Remove (symlinkPath ); err != nil {
304
+ Logger .Info (fmt .Sprintf ("Warning: failed to remove existing symlink %s: %v" , symlinkPath , err ))
305
+ }
306
+ }
307
+ fmt .Printf ("Creating symlink for %s -> %s\n " , symlinkPath , actualBinPath )
308
+ if err := os .Symlink (actualBinPath , symlinkPath ); err != nil {
309
+ Logger .Info (fmt .Sprintf ("Error creating symlink for %s: %v" , binPath , err ))
310
+ return err
311
+ }
312
+ if err := os .Chmod (symlinkPath , 0755 ); err != nil {
313
+ Logger .Info (fmt .Sprintf ("Error setting executable permissions for %s: %v" , binPath , err ))
314
+ }
299
315
}
300
316
}
301
317
return nil
@@ -313,7 +329,7 @@ func (p *NPMProvider) removePackageSymlinks(packageName string) error {
313
329
symlinkPath := filepath .Join (binDir , binName )
314
330
if _ , err := os .Lstat (symlinkPath ); err == nil {
315
331
if err := os .Remove (symlinkPath ); err != nil {
316
- log . Printf ( "Warning: failed to remove symlink %s: %v" , symlinkPath , err )
332
+ Logger . Info ( fmt . Sprintf ( "Warning: failed to remove symlink %s: %v" , symlinkPath , err ) )
317
333
}
318
334
}
319
335
}
@@ -322,44 +338,52 @@ func (p *NPMProvider) removePackageSymlinks(packageName string) error {
322
338
323
339
func (p * NPMProvider ) Install (sourceID , version string ) bool {
324
340
packageName := p .getRepo (sourceID )
341
+ if version == "" || version == "latest" {
342
+ var err error
343
+ version , err = p .getLatestVersion (packageName )
344
+ if err != nil {
345
+ Logger .Info (fmt .Sprintf ("Error getting latest version for %s: %v" , packageName , err ))
346
+ return false
347
+ }
348
+ }
325
349
if err := local_packages_parser .AddLocalPackage (sourceID , version ); err != nil {
326
350
return false
327
351
}
328
352
success := p .Sync ()
329
353
if success {
330
354
if err := p .createPackageSymlinks (packageName ); err != nil {
331
- log . Printf ( "Error creating symlinks for %s: %v" , packageName , err )
355
+ Logger . Info ( fmt . Sprintf ( "Error creating symlinks for %s: %v" , packageName , err ) )
332
356
}
333
357
}
334
358
return success
335
359
}
336
360
337
361
func (p * NPMProvider ) Remove (sourceID string ) bool {
338
362
packageName := p .getRepo (sourceID )
339
- log . Printf ( "NPM Remove: Removing package %s" , packageName )
363
+ Logger . Info ( fmt . Sprintf ( "NPM Remove: Removing package %s" , packageName ) )
340
364
if err := p .removePackageSymlinks (packageName ); err != nil {
341
- log . Printf ( "Error removing symlinks for %s: %v" , packageName , err )
365
+ Logger . Info ( fmt . Sprintf ( "Error removing symlinks for %s: %v" , packageName , err ) )
342
366
}
343
367
if err := local_packages_parser .RemoveLocalPackage (sourceID ); err != nil {
344
- log . Printf ( "Error removing package %s from local packages: %v" , packageName , err )
368
+ Logger . Info ( fmt . Sprintf ( "Error removing package %s from local packages: %v" , packageName , err ) )
345
369
return false
346
370
}
347
- log . Printf ( "NPM Remove: Package %s removed successfully" , packageName )
371
+ Logger . Info ( fmt . Sprintf ( "NPM Remove: Package %s removed successfully" , packageName ) )
348
372
return p .Sync ()
349
373
}
350
374
351
375
func (p * NPMProvider ) Update (sourceID string ) bool {
352
376
repo := p .getRepo (sourceID )
353
377
if repo == "" {
354
- log . Printf ( "Invalid source ID format for NPM provider" )
378
+ Logger . Info ( fmt . Sprintf ( "Invalid source ID format for NPM provider" ) )
355
379
return false
356
380
}
357
381
latestVersion , err := p .getLatestVersion (repo )
358
382
if err != nil {
359
- log . Printf ( "Error getting latest version for %s: %v" , repo , err )
383
+ Logger . Info ( fmt . Sprintf ( "Error getting latest version for %s: %v" , repo , err ) )
360
384
return false
361
385
}
362
- log . Printf ( "NPM Update: Updating %s to version %s" , repo , latestVersion )
386
+ Logger . Info ( fmt . Sprintf ( "NPM Update: Updating %s to version %s" , repo , latestVersion ) )
363
387
return p .Install (sourceID , latestVersion )
364
388
}
365
389
@@ -374,23 +398,16 @@ func (p *NPMProvider) getLatestVersion(packageName string) (string, error) {
374
398
func (p * NPMProvider ) tryNpmCi () bool {
375
399
lockFile := filepath .Join (p .APP_PACKAGES_DIR , "package-lock.json" )
376
400
if _ , err := os .Stat (lockFile ); os .IsNotExist (err ) {
377
- log . Printf ( "NPM Sync: No package-lock.json found, cannot use npm ci" )
401
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: No package-lock.json found, cannot use npm ci" ) )
378
402
return false
379
403
}
380
- log . Printf ( "NPM Sync: Using npm ci for faster bulk installation" )
404
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: Using npm ci for faster bulk installation" ) )
381
405
installCode , err := shell_out .ShellOut ("npm" , []string {"ci" }, p .APP_PACKAGES_DIR , nil )
382
406
if err != nil || installCode != 0 {
383
- log . Printf ( "NPM Sync: npm ci failed, falling back to individual package installation: %v" , err )
407
+ Logger . Info ( fmt . Sprintf ( "NPM Sync: npm ci failed, falling back to individual package installation: %v" , err ) )
384
408
return false
385
409
}
386
- log .Printf ("NPM Sync: npm ci completed successfully, creating symlinks" )
387
- desired := local_packages_parser .GetData (true ).Packages
388
- for _ , pkg := range desired {
389
- name := p .getRepo (pkg .SourceID )
390
- if err := p .createPackageSymlinks (name ); err != nil {
391
- log .Printf ("Error creating symlinks for %s: %v" , name , err )
392
- }
393
- }
410
+ Logger .Info (fmt .Sprintf ("NPM Sync: npm ci completed successfully, creating symlinks" ))
394
411
return true
395
412
}
396
413
0 commit comments