angular-cli: "ng update" doesn't support private repos such as FontAwesome 5 Pro

Versions

Angular CLI: 6.0.0
Node: 9.11.1
OS: darwin x64
Angular: 6.0.0
... animations, cli, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router
... service-worker

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.6.0
@angular-devkit/build-angular     0.6.0
@angular-devkit/build-optimizer   0.6.0
@angular-devkit/core              0.6.0
@angular-devkit/schematics        0.6.0
@ngtools/webpack                  6.0.0
@schematics/angular               0.6.0
@schematics/update                0.6.0
rxjs                              6.1.0
typescript                        2.7.2
webpack                           4.6.0

Repro steps

  • Add dependency on a private repo such as FontAwesome 5 Pro
  • Run ng update @angular/core

Observed behavior

Not found : @fortawesome/pro-regular-svg-icons

Mention any other details that might be useful (optional)

This works fine using npm directly because the private repo has already been registered with npm

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 114
  • Comments: 172 (45 by maintainers)

Most upvoted comments

There is definitely a problem when using a private npm repository. I could only run ng update correctly once I removed my .npmrc file, and removed every private package from package.json. After running ng update @angular/cli' it finally correctly generated the angular.json file and I readded all the references I removed before.

@hansl & @filipesilva This thread still has the “need more info” and “low frequency” labels. Based on the thread’s activity, I assume that both are not valid anymore and possibly give a false impression of the severity of this issue.

Same problem with 6.0.7 just now.

We’re using a private repository and get “error 401 unauthorized” (see #10704 which was closed as duplicate of this issue). The problem still exists with @angular/cli@6.1.0-rc.0.


    "@angular/cli@^6.1.0-rc.0":  version "6.1.0-rc.0"

    resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-6.1.0-rc.0.tgz#7ffb203ab429beca19003369647015aa707921b9"
    dependencies:
      "@angular-devkit/architect" "0.7.0-rc.0"
      "@angular-devkit/core" "0.7.0-rc.0"
      "@angular-devkit/schematics" "0.7.0-rc.0"
      "@schematics/angular" "0.7.0-rc.0"
      "@schematics/update" "0.7.0-rc.0"
      opn "^5.3.0"
      rxjs "^6.0.0"
      semver "^5.1.0"
      symbol-observable "^1.2.0"
      yargs-parser "^10.0.0"

The team is aware of the issue and there is a pending PR (https://github.com/angular/angular-cli/pull/12526) which will vastly improve the current situation. Unfortunately, it is currently blocked on an upstream issue (https://github.com/zkat/pacote/issues/163).

While looking at the growing list of bugs relating .npmrc #10704 #10571 #10660 I think this should be fixed asap

+1, can’t we just read the .npmrc on install or have an alternative flag that skips the npm check and simply updates angular?

Why is this need more info and low frequency @hansl? It affects nearly every corporate developer because we all have private npm packages. Not to mention anyone who has spent the time to decouple their application into components.

I really wonder why not using plain npm and make use of the build in authentication. Adding an --auth flag so I have to add authentication again for angular. Very bad idea. Parse .npmrc or just use npm.

Looks like I found a solution, at least for Nexus repository. I already filed this as a pull request, however it would be really good to have some feedback from you guys. Especially would be cool to have some Artifactory users’ feedback.

How to try it right now: replace the your_project/node_modules/@schematics/update/update/npm.js with the following content

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const url = require("url");
const RegistryClient = require('npm-registry-client');
const npmPackageJsonCache = new Map();
const npmConfigOptionCache = new Map();
function _readNpmRc() {
    return new rxjs_1.Observable(subject => {
        // TODO: have a way to read options without using fs directly.
        const path = require('path');
        const fs = require('fs');
        const perProjectNpmrc = path.resolve('.npmrc');
        let npmrc = '';
        if (fs.existsSync(perProjectNpmrc)) {
            npmrc = fs.readFileSync(perProjectNpmrc).toString('utf-8');
        }
        else {
            if (process.platform === 'win32') {
                if (process.env.LOCALAPPDATA) {
                    npmrc = fs.readFileSync(path.join(process.env.LOCALAPPDATA, '.npmrc')).toString('utf-8');
                }
            }
            else {
                if (process.env.HOME) {
                    npmrc = fs.readFileSync(path.join(process.env.HOME, '.npmrc')).toString('utf-8');
                }
            }
        }
        const allOptionsArr = npmrc.split(/\r?\n/).map(x => x.trim());
        const allOptions = {};
        allOptionsArr.forEach(x => {
            const delimiter = '=';
            const split = x.split(delimiter);
            const key = split[0].trim();
            allOptions[key] = split.slice(1).join('=').trim();
        });
        subject.next(allOptions);
        subject.complete();
    }).pipe(operators_1.catchError(() => rxjs_1.of({})), operators_1.shareReplay());
}
function getOptionFromNpmRc(option) {
    return _readNpmRc().pipe(operators_1.map(options => options[option]));
}
function getOptionFromNpmCli(option) {
    return new rxjs_1.Observable(subject => {
        child_process_1.exec(`npm get ${option}`, (error, data) => {
            if (error) {
                throw error;
            }
            else {
                data = data.trim();
                if (!data || data === 'undefined' || data === 'null') {
                    subject.next();
                }
                else {
                    subject.next(data);
                }
            }
            subject.complete();
        });
    }).pipe(operators_1.catchError(() => rxjs_1.of(undefined)), operators_1.shareReplay());
}
function getNpmConfigOption(option, scope, tryWithoutScope) {
    if (scope && tryWithoutScope) {
        return rxjs_1.concat(getNpmConfigOption(option, scope), getNpmConfigOption(option)).pipe(operators_1.filter(result => !!result), operators_1.defaultIfEmpty(), operators_1.first());
    }
    const fullOption = `${scope ? scope + ':' : ''}${option}`;
    let value = npmConfigOptionCache.get(fullOption);
    if (value) {
        return value;
    }
    value = option.startsWith('_')
        ? getOptionFromNpmRc(fullOption)
        : getOptionFromNpmCli(fullOption);
    npmConfigOptionCache.set(fullOption, value);
    return value;
}
function getNpmClientSslOptions(strictSsl, cafile) {
    const sslOptions = {};
    if (strictSsl === 'false') {
        sslOptions.strict = false;
    }
    else if (strictSsl === 'true') {
        sslOptions.strict = true;
    }
    if (cafile) {
        sslOptions.ca = fs_1.readFileSync(cafile);
    }
    return sslOptions;
}
/**
 * Get the NPM repository's package.json for a package. This is p
 * @param {string} packageName The package name to fetch.
 * @param {string} registryUrl The NPM Registry URL to use.
 * @param {LoggerApi} logger A logger instance to log debug information.
 * @returns An observable that will put the pacakge.json content.
 * @private
 */
function getNpmPackageJson(packageName, registryUrl, logger) {
    const scope = packageName.startsWith('@') ? packageName.split('/')[0] : undefined;
    return (registryUrl ? rxjs_1.of(registryUrl) : getNpmConfigOption('registry', scope, true)).pipe(operators_1.map(partialUrl => {
        if (!partialUrl) {
            partialUrl = 'https://registry.npmjs.org/';
        }
        const partial = url.parse(partialUrl);
        let fullUrl = new url.URL(`http://${partial.host}/${packageName.replace(/\//g, '%2F')}`);
        try {
            const registry = new url.URL(partialUrl);
            registry.pathname = (registry.pathname || '')
                .replace(/\/?$/, '/' + packageName.replace(/\//g, '%2F'));
            fullUrl = new url.URL(url.format(registry));
        }
        catch (_a) { }
        logger.debug(`Getting package.json from '${packageName}' (url: ${JSON.stringify(fullUrl)})...`);
        return fullUrl;
    }), operators_1.concatMap(fullUrl => {
        let maybeRequest = npmPackageJsonCache.get(fullUrl.toString());
        if (maybeRequest) {
            return maybeRequest;
        }
        const registryKey = `//${fullUrl.host}/`;
        return rxjs_1.concat(getNpmConfigOption('proxy'), getNpmConfigOption('https-proxy'), getNpmConfigOption('strict-ssl'), getNpmConfigOption('cafile'), getNpmConfigOption('_auth'), getNpmConfigOption('_authToken', registryKey), getNpmConfigOption('username', registryKey, true), getNpmConfigOption('password', registryKey, true), getNpmConfigOption('email', registryKey, true), getNpmConfigOption('always-auth', registryKey, true)).pipe(operators_1.toArray(), operators_1.concatMap(options => {
            const [http, https, strictSsl, cafile, token, authToken, username, password, email, alwaysAuth,] = options;
            const subject = new rxjs_1.ReplaySubject(1);
            const sslOptions = getNpmClientSslOptions(strictSsl, cafile);
            const auth = {};
            if (alwaysAuth !== undefined) {
                auth.alwaysAuth = alwaysAuth === 'false' ? false : !!alwaysAuth;
            }
            if (email) {
                auth.email = email;
            }
            if (authToken) {
                auth.token = authToken;
            }
            else if (token) {
                try {
                    // attempt to parse "username:password" from base64 token
                    // to enable Artifactory / Nexus-like repositories support
                    const delimiter = ':';
                    const parsedToken = Buffer.from(token, 'base64').toString('ascii');
                    const [extractedUsername, ...passwordArr] = parsedToken.split(delimiter);
                    const extractedPassword = passwordArr.join(delimiter);
                    if (extractedUsername && extractedPassword) {
                        auth.username = extractedUsername;
                        auth.password = extractedPassword;
                    }
                    else {
                        throw new Error('Unable to extract username and password from _auth token');
                    }
                }
                catch (ex) {
                    auth.token = token;
                }
            }
            else if (username) {
                auth.username = username;
                auth.password = password;
            }
            const client = new RegistryClient({
                proxy: { http, https },
                ssl: sslOptions,
            });
            client.log.level = 'silent';
            const params = {
                timeout: 30000,
                auth,
            };
            client.get(fullUrl.toString(), params, (error, data) => {
                if (error) {
                    subject.error(error);
                }
                subject.next(data);
                subject.complete();
            });
            maybeRequest = subject.asObservable();
            npmPackageJsonCache.set(fullUrl.toString(), maybeRequest);
            return maybeRequest;
        }));
    }));
}
exports.getNpmPackageJson = getNpmPackageJson;

After that the ng update should start working.

Be careful: after you ng update @angular/cli the file will be overwritten, so don’t forget to replace the content again. Otherwise you will see the lovely 401 😃

Issue still exists inside @angular/cli@6.0.8

I’m also having this same problem with @angular/cli@6.0.3 and VSTS

Confirmation that @angular/cli 6.0.0+ has broken authentication to private npm registries:

$ ng update <package_on_private_registry>
Unable to authenticate, need: Basic realm="Artifactory Realm"

I have tested edge version @angular/cli@6.2.0-beta.3, and same issue. This is a serious bug that will prevent our organisation from upgrading to angular 6.

Please fix asap!

Still failing with 6.1.3:

Angular CLI: 6.1.3
Node: 10.8.0
OS: linux x64
Angular: 6.1.2
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.7.3
@angular-devkit/build-angular     0.7.3
@angular-devkit/build-optimizer   0.7.3
@angular-devkit/build-webpack     0.7.3
@angular-devkit/core              0.7.3
@angular-devkit/schematics        0.7.3
@angular/cli                      6.1.3
@ngtools/webpack                  6.1.3
@schematics/angular               0.7.3
@schematics/update                0.7.3
rxjs                              6.2.2
typescript                        2.9.2
webpack                           4.9.2
$ ng update
401 Unauthorized

We have the same issue now. Does anyone have a solution for this problem?

Same problem here behind a private Nexus repo

Experiencing this issue with @angular/cli@6.0.3 and private Nexus repository

Content of .npmrc looks something like…

registry=
email=
always-auth=true
_auth=

This should now be addressed in 7.3.0+. If anyone is still encountering problems, please open a new issue detailing the issue.

It works now using rc.0 and

ng update @angular/core --registry https://%registry/repository/%group-name%/

Not a solution but a workaround 😃

In additional to the previous PR mentioned above (which is currently available in the 7.1.0.beta.1). Further improvements will be present in the next beta via https://github.com/angular/angular-cli/pull/12871. This PR provides improved discovery of .nmprc files.

Same here with @angular/cli 6.2.1

registry=https://ourNexus/repository/npm/
//ourNexus/repository/npm/:_authToken=000000_DUMMY_00000000

Our workaround was to remove our private package, then run…

ng update --registry=https://registry.npmjs.org/ @angular/cli

and then re-install the private package.

The worst? Or simply the worst? The worst.

This should be fixed sooner than later

In our npmrc we have options like strict-ssl and always-auth which are boolean values when being parsed. The update script in npm.ts tries to call ‘replace’ on the options and fails when the function does not exist (which it doesn’t for bools): options[key] = options[key].replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || ''); We fixed this by checking if the function exists before trying to call it. PR is attached

Now, ng update works as expected also with our private Artifactory

@catull well .npmrc isn’t used when having a git dependency. it’s just a line like this:

"my-package": "git+ssh://git@gitlab.com:my-package.git#master"

in the package.json. No package repo is involved.

fyi

Tested 7.1.0-rc.0 but still get 404 Not Found - GET https://registry.npmjs.org/@myScope%2fmyPackage - Not found where the URL should point to a private registry

Angular CLI: 7.1.0-rc.0
Node: 10.13.0
OS: win32 x64
Angular: 7.0.1
... animations, cdk, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.10.3
@angular-devkit/build-angular      0.10.3
@angular-devkit/build-ng-packagr   0.10.3
@angular-devkit/build-optimizer    0.10.3
@angular-devkit/build-webpack      0.10.3
@angular-devkit/core               7.0.3
@angular-devkit/schematics         7.1.0-rc.0
@angular/cli                       7.1.0-rc.0
@angular/flex-layout               7.0.0-beta.19
@ngtools/json-schema               1.1.0
@ngtools/webpack                   7.0.3
@schematics/angular                7.1.0-rc.0
@schematics/update                 0.11.0-rc.0
ng-packagr                         4.4.0
rxjs                               6.3.3
typescript                         3.1.3
webpack                            4.23.1

Hi @Ionaru @evanjmg @PMoransais

could you try to add to your .npmrc the following line?

always-auth=true

or / and

//npm.fontawesome.com/:always-auth=true

Does this help?

The fix by @smnbbrv did not work for me, I still got 401 Unauthorized when running ng update @angular/cli @angular/core.

My .npmrc is really simple:

@fortawesome:registry=https://npm.fontawesome.com/
//npm.fontawesome.com/:_authToken=SOME_TOKEN_HERE

I had to remove the .npmrc file and all private packages in package.json to run ng update. 😦

$ ng update --registry <url-to-private-registry>

Doesn#t that work?

Not when private registry requires credentials.

Really angular team…still no fix for this?! Stop developing any new stuff and fix this issue. I have same issue, except I don’t use private packages, nexus is used as a proxy for ALL npm repositories. So removing .npmrc doesn’t solve anything (npm requests must go through the nexus proxy or they don’t leave the network), and there is no “private dependencies” to remove, because they are all legit packages …like angular stuff…

still the same issue with @schematics/update@0.7.2 and @angular/cli@6.1.2

Angular CLI: 6.1.2
Node: 8.11.3
OS: darwin x64
Angular: 6.0.7
... common, compiler, compiler-cli, core, forms, http
... platform-browser, platform-browser-dynamic

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.6.8
@angular-devkit/build-angular     0.6.8
@angular-devkit/build-optimizer   0.6.8
@angular-devkit/core              0.0.29
@angular-devkit/schematics        0.0.52
@angular/animations               6.0.9
@angular/cli                      6.1.2
@angular/router                   6.0.9
@ngtools/json-schema              1.1.0
@ngtools/webpack                  6.0.8
@schematics/angular               0.7.2
@schematics/update                0.7.2
rxjs                              6.2.1
typescript                        2.7.2
webpack                           4.8.3
ng update @angular/cli
401 Unauthorized

Workaround that works for me:

  • temporary removing all private dependencies from package.json (no need to uninstall / reinstall them)
  • and ensure there’s no private default registry in .npmrc (usually I got the private mirror with public and private packages there)

my hack/solution:

go to node_modules\@schematics\update\update\npm.js and change line 35-ish (first line of the method)

function getNpmClientSslOptions(strictSsl, cafile) {
    const sslOptions = { strict: false }; // <---- RIGHT HERE.. normally, it's "{}"
    if (strictSsl === 'false') {
        sslOptions.strict = false;
    }
    else if (strictSsl === 'true') {
        sslOptions.strict = true;
    }
    if (cafile) {
        sslOptions.ca = fs_1.readFileSync(cafile);
    }
    return sslOptions;
}

Not sure why it’s not getting the setting from the .npmrc file, but this let me get past the issue

even in official 6.1 it’s still an issue

Any chance the cli could just log not found errors as a warning instead of exiting on them?

I’m also curious, is there anything we can do or provide to speed this issue up?

@hansl any update on this? The issue still carries the “need: more info” label.

Same here, happens with our own private repo ng update returns Not found: @org/package although npm install @org/package works fine and the package is already installed in node_modules.

Currently with 7.3.0 for me it works correctly again.

@Chklang did you try with ng update --registry https://privaterepo.mycompagnie.fr/npm/ ?

This is not only a problem with private repos but in my case also a problem with dependencies from git repos (so not npm based)

Update: after upgrading to 7.2.0-beta.1 message slightly changed but issue persist:

➜  ng update @angular/core
404 Not Found - GET https://registry.npmjs.org/my-private-package-here - Not found

@alan-agius4

This is my output:

    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 7.1.2
Node: 8.12.0
OS: win32 x64
Angular: 7.1.2
... animations, cli, common, compiler, compiler-cli, core, forms
... http, platform-browser, platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.10.7
@angular-devkit/build-angular     0.10.7
@angular-devkit/build-optimizer   0.10.7
@angular-devkit/build-webpack     0.10.7
@angular-devkit/core              7.0.7
@angular-devkit/schematics        7.1.2
@angular/cdk                      7.1.1
@ngtools/webpack                  7.0.7
@schematics/angular               7.1.2
@schematics/update                0.11.2
rxjs                              6.3.3
typescript                        3.1.1
webpack                           4.19.1

Is there an article describing what to do know? Im using Windows and don’t know how to get it working:

  • Use .npmrc in local and/or HOME ?
  • Do I have to use environment variables for auth-keys?

Deleted my local .nmprc to update version of @angular/cli to 7.1.1. Restored the local .npmrc and trying to start ng update

If I then try to start ng update getting also the 404 with scoped package names: like: /@angular-devkit%2fbuild-angular

With 7.1.1 I get 404 errors again. Scoped packages seams not to be recognized correctly.

@smnbbrv ng update still doesn’t work for me. the .npmrc is in the same directory as the angular.json and contains always-auth=true and an artifactory url for registry. there is also the ~/.npmrc which contains the _authToken. i’m using yarn as packageManager but however also removed the packageManager config from angular.json to use npm. installation and updating of dependencies via npm and yarn works fine (so auth data should be correct). it takes some time and outputs then This request requires auth credentials. Run npm login and repeat the request.. Angular CLI version 7.0.2, yarn version v1.10.1. any suggestions?

It might work for font-awesome (i wouldn’t know i dont use it), but it still doesn’t work for a npm proxy that has authentication. E.g when you have a nexus server keeping a “local” (to the office network cache) of the default npm repository - there where all the public stuff like angular gets downloaded from by default (explained in my previous https://github.com/angular/angular-cli/issues/10624#issuecomment-421544428)

Steps to reproduce:

  1. Login to your private npm proxy `npm login --registry=https://nexus.myorg.com/repository/npm"
  2. Now try to upgrade your existing project ng upgrade 2.1) Even try `ng upgrade --registry=https://nexus.myorg.com/repository/npm"

After waiting quite a while it errors with “This request requires auth credentials. Run npm login and repeat the request.” Looking at my nexus logs I can see it never even attempted to download from the registry.

Because ng update doesn’t have a --verbose options, I can’t really tell where it is trying to download from…

Works for me ! Thanks @smnbbrv !

@SV-efi You can probably just change the @angular/... packages to a newer 6.X.X version and run npm install to fix your issue.

Tested updated script from comment above, works 👍Nice one @smnbbrv!

@hansl any idea when this will be released?

@mrmaxsteel could you please check the following:

  1. where is located your npmrc? Project-level or home directory?
  2. does your password contain symbol ‘:’? If yes, try the updated script at https://github.com/angular/angular-cli/issues/10624#issuecomment-421564909
  3. could you remove the spaces around ‘=’ (it looks like current script does not work with spaces yet)? So it should look like this:
registry=<url to artifactory>/api/npm/<repo>
_auth=xxxx
always-auth=true
email=xxxx

Thanks for collaborating! 👍

@dkabul the fix is not merged yet

+1 this really needs a resolution. Due to this issue we have missed our window to upgrade and have to wait till next release cycle in several months time 👎

@ankemp Right! We are also blocked to perform the update to ^6.1.0

@blueiceprj solution might work for one off private repos, but not when your corp caches/replicates npm on jFrog Artifactory.

delete node_modules and run ng upgrade @angular/core

rm -rf node_modules
ng update @angular/core

it works for me

@tengis that’s some great debugging friend! I just tried this out, and it works beautifully 😉

Important notice for people trying out this fix too, you need to have at least version 6.1.0-rc0 of the @angular/cli package, before editing the npm.js file as suggested.

What PR do you suggest to create, one that fixes the issue in npm itself, or one that ‘patches’ it in CLI by fetching it somehow from .npmrc and adding it in manually?

Hi Guys,

The issue related with npm’s get config command itself.

npm get <key> returns all other configs except _auth and _authToken. Even, npm config list doesn’t return them as well.

On update script, it tries to run this command, and this command fails, then actual value became null, so your private registry authentication fails.

https://github.com/angular/angular-cli/blob/6449a753641340d8fc19a752e1a1ced75f974efa/packages/schematics/update/update/npm.ts#L47

So quick and dirty solution is set your auth token manually on ./node_modules/@schematics/update/update/npmjs line number somewhere after 103.

auth = { token: "<your_auth_token", alwaysAuth: true }

FYI: My .npmrc file

strict-ssl=false
registry=https://<company_nexus_npm_registry>
_auth="<base64_token>"
always_auth=true
email=<company_email>

I’ll try to create PR for this if anyone created anything similar.

Cheers

Having the same issue using a JFrog Artifactory (universal artifact manager) as registry in .npmrc. This may be related: https://github.com/angular/devkit/issues/917