Skip to content

Commit

Permalink
feat: npmrc support + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Icaruk committed Jun 30, 2024
1 parent 29dd4fa commit 4681032
Show file tree
Hide file tree
Showing 18 changed files with 560 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
dist/
node_modules
.env
.npmrc
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CLI tool written in Go to review and update your NPM dependencies, easy and fast
- 📃 Review the **release notes** for each package to see "what's new" before deciding whether to update.
- 🦘 Selectively **skip** updates for specific packages.
- 🛡️ **Back up** your `package.json` file before updating, ensuring you always have a fallback option if something goes wrong.
- Limited support for `.npmrc` ([read more here](#npmrc-support))


# Usage
Expand Down Expand Up @@ -71,3 +72,16 @@ npm-up --file my-project/package.json
task buid
```
- Binaries will be created in `/dist` folder.



# .npmrc support

*from https://docs.npmjs.com/cli/v10/configuring-npm/npmrc*

The four relevant files are:

- (✅ supported) per-project config file (/path/to/my/project/.npmrc)
- (✅ supported) per-user config file (~/.npmrc)
- (❌ unsupported) global config file ($PREFIX/etc/npmrc)
- (❌ unsupported) npm builtin config file (/path/to/npm/npmrc)
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module github.com/icaruk/up-npm

go 1.21
go 1.22

toolchain go1.21.5
toolchain go1.22.5

require (
github.com/AlecAivazis/survey/v2 v2.3.7
Expand Down
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDj
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
1 change: 1 addition & 0 deletions packageSimple.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"@aws-sdk/lib-storage": "^3.592.0"
},
"devDependencies": {
"@shakers/entities": "0.0.0",
"@logtail/node": "^0.4.20",
"aws-sdk": "^1.1329.0"
}
Expand Down
23 changes: 20 additions & 3 deletions pkg/updater/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/icaruk/up-npm/pkg/utils/cli"
npm "github.com/icaruk/up-npm/pkg/utils/npm"
"github.com/icaruk/up-npm/pkg/utils/npmrc"
packagejson "github.com/icaruk/up-npm/pkg/utils/packagejson"
repositorypkg "github.com/icaruk/up-npm/pkg/utils/repository"
versionpkg "github.com/icaruk/up-npm/pkg/utils/version"
Expand Down Expand Up @@ -187,7 +188,7 @@ func Init(cfg npm.CmdFlags, binVersion string) {

var isFilterFilled bool = cfg.Filter != ""

// New version message
// Check new version
latestRelease, err := repositorypkg.FetchRepositoryLatestRelease("icaruk", "up-npm")

if err == nil {
Expand Down Expand Up @@ -222,6 +223,22 @@ func Init(cfg npm.CmdFlags, binVersion string) {

fmt.Println()

// Check .npmrc
npmrcFiles, _ := npmrc.GetNpmrcTokens()
token, npmrcTokenLevel := npmrc.GetRelevantNpmrcToken(npmrcFiles)

if token != "" {

fmt.Println(
aurora.Green(".npmrc").Hyperlink("https://docs.npmjs.com/cli/v10/configuring-npm/npmrc"),
aurora.Green("has been detected"),
aurora.Faint(fmt.Sprintf("(%s)", npmrcTokenLevel)),
)

fmt.Println()

}

dependencies, devDependencies, jsonFile, err := packagejson.GetDependenciesFromPackageJson(cfg.File, cfg.NoDev)

if err != nil {
Expand All @@ -240,11 +257,11 @@ func Init(cfg npm.CmdFlags, binVersion string) {
var lockedDependencyCount int
var lockedDevDependencyCount int

lockedDependencyCount = npm.ReadDependencies(dependencies, versionComparison, false, bar, cfg)
lockedDependencyCount = npm.FetchDependencies(dependencies, versionComparison, false, token, bar, cfg)

// Process devDependencies
if !cfg.NoDev {
lockedDevDependencyCount = npm.ReadDependencies(devDependencies, versionComparison, true, bar, cfg)
lockedDevDependencyCount = npm.FetchDependencies(devDependencies, versionComparison, true, token, bar, cfg)
}

// Count total dependencies and filtered dependencies
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package npm

import (
"encoding/json"
"fmt"
"strings"
"sync"
Expand All @@ -22,10 +21,11 @@ type CmdFlags struct {

const concurrencyLimit int = 10

func ReadDependencies(
func FetchDependencies(
dependencyList map[string]string,
targetMap map[string]version.VersionComparisonItem,
isDev bool,
token string,
bar *progressbar.ProgressBar,
cfg CmdFlags,
) (lockedDependencyCount int) {
Expand Down Expand Up @@ -66,28 +66,23 @@ func ReadDependencies(
semaphoreChan <- struct{}{}

// Perform get request to npm registry
resp, err := FetchNpmRegistry(dependency)
body, err := FetchNpmRegistry(dependency, token)
if err != nil {
fmt.Println("Failed to fetch", dependency, " from npm registry, skipping...")
resultsChan <- "" // Enviar un resultado vacío para que se tenga en cuenta en la cuenta de resultados
return
}
defer resp.Body.Close()

// Get response data
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)

distTags := result["dist-tags"].(map[string]interface{})
distTags := body["dist-tags"].(map[string]interface{})

var homepage string
if result["homepage"] != nil {
homepage = result["homepage"].(string)
if body["homepage"] != nil {
homepage = body["homepage"].(string)
}

var repositoryData map[string]interface{}
if result["repository"] != nil {
repositoryData = result["repository"].(map[string]interface{})
if body["repository"] != nil {
repositoryData = body["repository"].(map[string]interface{})
}

var repositoryUrl string
Expand Down
35 changes: 32 additions & 3 deletions pkg/utils/npm/registry.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,44 @@
package npm

import (
"encoding/json"
"fmt"
"net/http"
)

func FetchNpmRegistry(dependency string) (*http.Response, error) {
resp, err := http.Get(fmt.Sprintf("https://registry.npmjs.org/%s", dependency))
func FetchNpmRegistry(dependency string, token string) (map[string]interface{}, error) {

client := &http.Client{}

req, err := http.NewRequest("GET", fmt.Sprintf("https://registry.npmjs.org/%s", dependency), nil)
if err != nil {
fmt.Println(err)
return nil, err
}

if token != "" {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
}

resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return nil, err
}

return resp, err
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("package %s, status code: %d", dependency, resp.StatusCode)
}

var result map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&result)

if err != nil {
return nil, err
}

return result, nil

}
74 changes: 74 additions & 0 deletions pkg/utils/npmrc/getNpmrcTokens.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package npmrc

import (
"fmt"
"os"
"path/filepath"
)

const npmrcFilename = ".npmrc"

type NpmrcTokens struct {
Exists bool
Project string
User string
Global string
Builtin string
}

func GetNpmrcTokens() (NpmrcTokens, error) {

/*
The four relevant files are:
· per-project config file (/path/to/my/project/.npmrc)
· per-user config file (~/.npmrc)
· [TODO] global config file ($PREFIX/etc/npmrc)
· [TODO] npm builtin config file (/path/to/npm/npmrc)
*/

npmrcTokens := NpmrcTokens{
Exists: false,
Project: "",
User: "",
Global: "",
Builtin: "",
}

// per-project config file (/path/to/my/project/.npmrc)
if _, err := os.Stat(npmrcFilename); err == nil {

fileContent, err := os.ReadFile(npmrcFilename)
if err != nil {
fmt.Println(err)
}

if token, err := ParseNpmrc(string(fileContent)); err == nil {
npmrcTokens.Exists = true
npmrcTokens.Project = token
}
}

// per-user config file (~/.npmrc)
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Println(err)
}

userNpmrcFilename := filepath.Join(homeDir, npmrcFilename)
if _, err := os.Stat(userNpmrcFilename); err == nil {

fileContent, err := os.ReadFile(userNpmrcFilename)
if err != nil {
fmt.Println(err)
}

if token, err := ParseNpmrc(string(fileContent)); err == nil {
npmrcTokens.Exists = true
npmrcTokens.User = token
}
}

return npmrcTokens, nil

}
33 changes: 33 additions & 0 deletions pkg/utils/npmrc/getRelevantNpmrcToken.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package npmrc

type NpmrcTokenLevel string

const (
Project NpmrcTokenLevel = "project"
User NpmrcTokenLevel = "user"
Global NpmrcTokenLevel = "global"
Builtin NpmrcTokenLevel = "builtin"
)

// Gets the most relevant npmrc token using order preference from https://docs.npmjs.com/cli/v10/configuring-npm/npmrc#files
func GetRelevantNpmrcToken(npmrcTokens NpmrcTokens) (string, NpmrcTokenLevel) {

if npmrcTokens.Project != "" {
return npmrcTokens.Project, Project
}

if npmrcTokens.User != "" {
return npmrcTokens.User, User
}

if npmrcTokens.Global != "" {
return npmrcTokens.Global, Global
}

if npmrcTokens.Builtin != "" {
return npmrcTokens.Builtin, Builtin
}

return "", ""

}
Loading

0 comments on commit 4681032

Please sign in to comment.