Skip to content

Commit a57493a

Browse files
authored
fix(providers): npm was not working as expected (#19)
1 parent 8a4ce1e commit a57493a

File tree

1 file changed

+81
-64
lines changed

1 file changed

+81
-64
lines changed

internal/lib/providers/npm_provider.go

Lines changed: 81 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package providers
33
import (
44
"encoding/json"
55
"fmt"
6-
"log"
76
"os"
87
"path/filepath"
98
"regexp"
@@ -78,9 +77,32 @@ func (p *NPMProvider) generatePackageJSON() bool {
7877
}
7978

8079
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")
84106
}
85107

86108
func (p *NPMProvider) readPackageJSON(packagePath string) (*PackageJSON, error) {
@@ -96,27 +118,6 @@ func (p *NPMProvider) readPackageJSON(packagePath string) (*PackageJSON, error)
96118
return &pkg, nil
97119
}
98120

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-
120121
func (p *NPMProvider) removeAllSymlinks() error {
121122
binDir := files.GetAppBinPath()
122123
entries, err := os.ReadDir(binDir)
@@ -130,7 +131,7 @@ func (p *NPMProvider) removeAllSymlinks() error {
130131
symlinkPath := filepath.Join(binDir, entry.Name())
131132
if _, err := os.Lstat(symlinkPath); err == nil {
132133
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))
134135
}
135136
}
136137
}
@@ -139,10 +140,10 @@ func (p *NPMProvider) removeAllSymlinks() error {
139140

140141
func (p *NPMProvider) Clean() bool {
141142
if err := p.removeAllSymlinks(); err != nil {
142-
log.Printf("Error removing symlinks: %v", err)
143+
Logger.Info(fmt.Sprintf("Error removing symlinks: %v", err))
143144
}
144145
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))
146147
return false
147148
}
148149
return p.Sync()
@@ -155,7 +156,7 @@ func (p *NPMProvider) Sync() bool {
155156
return false
156157
}
157158
}
158-
log.Printf("NPM Sync: Starting sync process")
159+
Logger.Info(fmt.Sprintf("NPM Sync: Starting sync process"))
159160
packagesFound := p.generatePackageJSON()
160161
if !packagesFound {
161162
return true
@@ -182,11 +183,11 @@ func (p *NPMProvider) Sync() bool {
182183
}
183184
}
184185
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"))
186187
for _, pkg := range desired {
187188
name := p.getRepo(pkg.SourceID)
188189
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))
190191
}
191192
}
192193
return true
@@ -208,50 +209,50 @@ func (p *NPMProvider) Sync() bool {
208209
for _, pkg := range desired {
209210
name := p.getRepo(pkg.SourceID)
210211
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))
212213
}
213214
}
214215
return true
215216
}
216217
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"))
218219
if p.tryNpmCi() {
219-
log.Printf("NPM Sync: npm ci completed successfully")
220+
Logger.Info(fmt.Sprintf("NPM Sync: npm ci completed successfully"))
220221
return true
221222
}
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"))
223224
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))
225226
}
226227
}
227228
}
228-
log.Printf("NPM Sync: Installing packages individually")
229+
Logger.Info(fmt.Sprintf("NPM Sync: Installing packages individually"))
229230
allOk := true
230231
installedCount := 0
231232
skippedCount := 0
232233
for _, pkg := range desired {
233234
name := p.getRepo(pkg.SourceID)
234235
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))
236237
skippedCount++
237238
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))
239240
}
240241
continue
241242
}
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))
243244
installCode, err := shell_out.ShellOut("npm", []string{"install", name + "@" + pkg.Version}, p.APP_PACKAGES_DIR, nil)
244245
if err != nil || installCode != 0 {
245246
fmt.Printf("Error installing %s@%s: %v\n", name, pkg.Version, err)
246247
allOk = false
247248
} else {
248249
installedCount++
249250
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))
251252
}
252253
}
253254
}
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))
255256
return allOk
256257
}
257258

@@ -294,8 +295,23 @@ func (p *NPMProvider) createPackageSymlinks(packageName string) error {
294295
return fmt.Errorf("error reading package.json for %s: %v", packageName, err)
295296
}
296297
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+
}
299315
}
300316
}
301317
return nil
@@ -313,7 +329,7 @@ func (p *NPMProvider) removePackageSymlinks(packageName string) error {
313329
symlinkPath := filepath.Join(binDir, binName)
314330
if _, err := os.Lstat(symlinkPath); err == nil {
315331
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))
317333
}
318334
}
319335
}
@@ -322,44 +338,52 @@ func (p *NPMProvider) removePackageSymlinks(packageName string) error {
322338

323339
func (p *NPMProvider) Install(sourceID, version string) bool {
324340
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+
}
325349
if err := local_packages_parser.AddLocalPackage(sourceID, version); err != nil {
326350
return false
327351
}
328352
success := p.Sync()
329353
if success {
330354
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))
332356
}
333357
}
334358
return success
335359
}
336360

337361
func (p *NPMProvider) Remove(sourceID string) bool {
338362
packageName := p.getRepo(sourceID)
339-
log.Printf("NPM Remove: Removing package %s", packageName)
363+
Logger.Info(fmt.Sprintf("NPM Remove: Removing package %s", packageName))
340364
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))
342366
}
343367
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))
345369
return false
346370
}
347-
log.Printf("NPM Remove: Package %s removed successfully", packageName)
371+
Logger.Info(fmt.Sprintf("NPM Remove: Package %s removed successfully", packageName))
348372
return p.Sync()
349373
}
350374

351375
func (p *NPMProvider) Update(sourceID string) bool {
352376
repo := p.getRepo(sourceID)
353377
if repo == "" {
354-
log.Printf("Invalid source ID format for NPM provider")
378+
Logger.Info(fmt.Sprintf("Invalid source ID format for NPM provider"))
355379
return false
356380
}
357381
latestVersion, err := p.getLatestVersion(repo)
358382
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))
360384
return false
361385
}
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))
363387
return p.Install(sourceID, latestVersion)
364388
}
365389

@@ -374,23 +398,16 @@ func (p *NPMProvider) getLatestVersion(packageName string) (string, error) {
374398
func (p *NPMProvider) tryNpmCi() bool {
375399
lockFile := filepath.Join(p.APP_PACKAGES_DIR, "package-lock.json")
376400
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"))
378402
return false
379403
}
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"))
381405
installCode, err := shell_out.ShellOut("npm", []string{"ci"}, p.APP_PACKAGES_DIR, nil)
382406
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))
384408
return false
385409
}
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"))
394411
return true
395412
}
396413

0 commit comments

Comments
 (0)