Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add always auth when present in config #76

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npmRegistryServer: "https://registry.yarnpkg.com"
88 changes: 63 additions & 25 deletions lib/yarnrcyml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe("In the YarnRcYml module,", () => {
test("returns an empty array when none of the settings are registreies", () => {
foo.settings = {
"always-auth": "true",
cache: "some/path"
cache: "some/path",
};

expect(foo.getRegistries()).toEqual([]);
Expand All @@ -64,8 +64,8 @@ describe("In the YarnRcYml module,", () => {
npmScopes: {
private: {
npmRegistryServer: myprivateregistry,
}
}
},
},
};

let registries = foo.getRegistries();
Expand All @@ -75,6 +75,33 @@ describe("In the YarnRcYml module,", () => {
expect(registries[0].url).toEqual(myregistry);
expect(registries[1].url).toEqual(myprivateregistry);
});

test("applies global npmAlwaysAuth setting to each registry", () => {
const registryUrl = "https://registry.example.com";
foo.settings = {
npmAlwaysAuth: true,
npmRegistryServer: registryUrl,
};

let registries = foo.getRegistries();

expect(registries).toHaveLength(1);
expect(registries[0]).toBeInstanceOf(YarnRcYmlRegistry);
expect(registries[0].alwaysAuth).toEqual(true);
});

test("does not apply global npmAlwaysAuth when it is not set", () => {
const registryUrl = "https://registry.example.com";
foo.settings = {
npmRegistryServer: registryUrl,
};

let registries = foo.getRegistries();

expect(registries).toHaveLength(1);
expect(registries[0]).toBeInstanceOf(YarnRcYmlRegistry);
expect(registries[0].alwaysAuth).toEqual(false);
});
});

describe("has a method readSettingsFromFile which", () => {
Expand All @@ -85,27 +112,31 @@ describe("In the YarnRcYml module,", () => {

return expect(foo.readSettingsFromFile()).rejects.toHaveProperty(
"code",
"ERROR"
"ERROR",
);
});

test("resolves settings as JSON from .yarnrc.yml file with entries", () => {
const registryName = "//foo.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/";
const registryName =
"//foo.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/";
const token = "foobar";
fs.readFile.mockImplementation((_a: any, _b: any, cb: Function) => {
cb(null, `npmRegistries:\n "${registryName}":\n npmAlwaysAuth: true\n npmAuthToken: ${token}\n`);
cb(
null,
`npmRegistries:\n "${registryName}":\n npmAlwaysAuth: true\n npmAuthToken: ${token}\n`,
);
});

return expect(foo.readSettingsFromFile()).resolves.toHaveProperty(
"settings",
{
{
npmRegistries: {
["//foo.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/"]: {
npmAlwaysAuth: true,
npmAuthToken: "foobar"
}
}
}
npmAuthToken: "foobar",
},
},
},
);
});

Expand All @@ -117,7 +148,7 @@ describe("In the YarnRcYml module,", () => {

return expect(foo.readSettingsFromFile()).resolves.toHaveProperty(
"settings",
{}
{},
);
});

Expand All @@ -128,7 +159,7 @@ describe("In the YarnRcYml module,", () => {

return expect(foo.readSettingsFromFile()).resolves.toHaveProperty(
"settings",
{}
{},
);
});

Expand All @@ -152,7 +183,7 @@ describe("In the YarnRcYml module,", () => {
fs.writeFile.mockImplementation(
(_path: string, _content: string, cb: Function) => {
cb(someError);
}
},
);

return expect(foo.saveSettingsToFile()).rejects.toEqual(someError);
Expand All @@ -164,7 +195,7 @@ describe("In the YarnRcYml module,", () => {
(_path: string, content: string, cb: Function) => {
expect(content).toContain("some: value");
cb(null);
}
},
);
return expect(foo.saveSettingsToFile())
.resolves.toBeUndefined()
Expand Down Expand Up @@ -201,13 +232,14 @@ describe("In the YarnRcYml module,", () => {
});

function generateRegistryTests(name: string, useLegacyUrl: boolean) {

describe(name, () => {
describe("has a constructor which", () => {
describe("constructs an object", () => {
let feed = "npm-mirror";
let project = "foobar";
let fakeRegistry = useLegacyUrl ? `https://${project}.pkgs.visualstudio.com/_packaging/${feed}/npm/registry/` : `https://pkgs.dev.azure.com/${project}/_packaging/${feed}/npm/registry`;
let fakeRegistry = useLegacyUrl
? `https://${project}.pkgs.visualstudio.com/_packaging/${feed}/npm/registry/`
: `https://pkgs.dev.azure.com/${project}/_packaging/${feed}/npm/registry`;
let o: YarnRcYmlRegistry;
beforeAll(() => {
o = new YarnRcYmlRegistry(fakeRegistry);
Expand Down Expand Up @@ -239,8 +271,10 @@ function generateRegistryTests(name: string, useLegacyUrl: boolean) {
*/
let o: YarnRcYmlRegistry;
beforeEach(() => {
o = new YarnRcYmlRegistry(useLegacyUrl ?
"https://foobar.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/" : "https://pkgs.dev.azure.com/foobar/_packaging/npm-mirror/npm/registry/"
o = new YarnRcYmlRegistry(
useLegacyUrl
? "https://foobar.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/"
: "https://pkgs.dev.azure.com/foobar/_packaging/npm-mirror/npm/registry/",
);
});
afterEach(() => (o = undefined));
Expand All @@ -250,27 +284,31 @@ function generateRegistryTests(name: string, useLegacyUrl: boolean) {
test("returns an object with npmAuthIdent set to the username and password if both are populated and there is no token", () => {
o.basicAuthSettings = {
username: "foo",
password: "bar"
password: "bar",
};

let result = o.getAuthSettings();
expect(result.npmAuthIdent).toEqual("foo:bar");
})
});
test("returns a npmRegistries object, containing a key with the 'registry/' suffix", () => {
const fakeToken = "foo";
o.token = fakeToken;

let result = o.getAuthSettings();
const k_withRegistrySuffix: string = useLegacyUrl ?
"//foobar.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/" : "//pkgs.dev.azure.com/foobar/_packaging/npm-mirror/npm/registry/";
const k_withRegistrySuffix: string = useLegacyUrl
? "//foobar.pkgs.visualstudio.com/_packaging/npm-mirror/npm/registry/"
: "//pkgs.dev.azure.com/foobar/_packaging/npm-mirror/npm/registry/";

const npmRegistries = result.npmRegistries as IYarnRcYmlSettings;
expect(Object.getOwnPropertyNames(result)).toHaveLength(1);
expect((npmRegistries[k_withRegistrySuffix] as IYarnRcYmlSettings).npmAuthToken).toEqual(fakeToken);
expect(
(npmRegistries[k_withRegistrySuffix] as IYarnRcYmlSettings)
.npmAuthToken,
).toEqual(fakeToken);
});
});
});
}

generateRegistryTests("(legacy) the YarnRcYmlRegistry class", true);
generateRegistryTests("the YarnRcYmlRegistry class", false);
generateRegistryTests("the YarnRcYmlRegistry class", false);
55 changes: 33 additions & 22 deletions lib/yarnrcyml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ import * as fs from "fs";
import * as yaml from "js-yaml";

export type IYarnRcYmlSettings = {
[key: string]: IYarnRcYmlSettings | string;
}
[key: string]: IYarnRcYmlSettings | string | boolean;
};

/**
* Represents an .yarnrc.yml configuration file and presents an interface
* for interactions with it.
*/
export class YarnrcYml {
export class YarnrcYml {
public filePath: string;
public settings: IYarnRcYmlSettings;

/**
* @param {string} basePath - path to .yarnrc.yml file or directory containing .yarnrc.yml file
*/
constructor(basePath: string) {
constructor(basePath: string) {
if (!basePath) {
throw new Error(
"Yarnrcyml constructor must be called with directory which contains the .yarnrc.yml file"
"Yarnrcyml constructor must be called with directory which contains the .yarnrc.yml file",
);
}

Expand All @@ -37,12 +37,15 @@ export type IYarnRcYmlSettings = {
* Reads npm settings to determine the location of the
* userconfig and creates an YarnrcYml object for it.
*/
static getUserNpmrc(): YarnrcYml {
static getUserNpmrc(): YarnrcYml {
let userConfigPath = execSync("npm config get userconfig")
.toString()
.trim();
if (userConfigPath.endsWith(".npmrc")) {
userConfigPath = path.join(userConfigPath.substring(0, userConfigPath.length - ".npmrc".length), ".yarnrc.yml");
userConfigPath = path.join(
userConfigPath.substring(0, userConfigPath.length - ".npmrc".length),
".yarnrc.yml",
);
} else {
userConfigPath = path.join(userConfigPath, ".yarnrc.yml");
}
Expand All @@ -56,19 +59,24 @@ export type IYarnRcYmlSettings = {
* it finds.
* @returns {Registry[]}
*/
getRegistries(settings?: IYarnRcYmlSettings): Array<YarnRcYmlRegistry> {
getRegistries(settings?: IYarnRcYmlSettings): Array<YarnRcYmlRegistry> {
let settingsKeys = Object.getOwnPropertyNames(settings || this.settings);
let registries: Array<YarnRcYmlRegistry> = [];
let globalAlwaysAuth = this.settings.npmAlwaysAuth || false;

settingsKeys.forEach(key => {
const settingValue = settings ? settings[key] : this.settings[key];
if (typeof settingValue === "object") {
registries.push(...this.getRegistries(settingValue))
registries.push(...this.getRegistries(settingValue));
} else {
if (key.indexOf("npmRegistryServer") > -1) {
registries.push(new YarnRcYmlRegistry(settingValue));
let registry = new YarnRcYmlRegistry(settingValue.toString());
if (globalAlwaysAuth) {
registry.alwaysAuth = true;
}
registries.push(registry);
}
}
}
});

return registries;
Expand All @@ -79,7 +87,7 @@ export type IYarnRcYmlSettings = {
* to this object then parses and initializes settings.
* When finished, returns this object.
*/
async readSettingsFromFile(): Promise<YarnrcYml> {
async readSettingsFromFile(): Promise<YarnrcYml> {
let that = this;

return new Promise<YarnrcYml>((resolve, reject) => {
Expand All @@ -89,7 +97,7 @@ export type IYarnRcYmlSettings = {
} else {
try {
console.log("config from", that.filePath);
that.settings = yaml.load(data || "") as IYarnRcYmlSettings || {};
that.settings = (yaml.load(data || "") as IYarnRcYmlSettings) || {};

if (that.settings[""]) {
delete that.settings[""];
Expand All @@ -109,7 +117,7 @@ export type IYarnRcYmlSettings = {
* writes them to disk at the .yarnrc location
* the object was instantiated from.
*/
async saveSettingsToFile() {
async saveSettingsToFile() {
return new Promise<void>((resolve, reject) => {
fs.writeFile(this.filePath, yaml.dump(this.settings), err => {
if (err) {
Expand All @@ -130,43 +138,45 @@ export interface IYarnRcYmlBasicAuthSettings {
export class YarnRcYmlRegistry {
public url: string;
public token: string;
public alwaysAuth: boolean;
public feed: string;
public project: string;
public basicAuthSettings: IYarnRcYmlBasicAuthSettings;

constructor(registryUrl: string) {
if (!registryUrl) {
throw new Error(
"Registry constructor must be called with url for the given registry"
"Registry constructor must be called with url for the given registry",
);
}

this.url = registryUrl;
this.token = "";
this.alwaysAuth = false;
this.basicAuthSettings = {
username: null,
password: null,
};

let feedResult = /_packaging\/(.*)\/npm\/registry/i.exec(registryUrl);
let projectResult = /https?:\/\/(.*)\.pkgs\.visualstudio/i.exec(
registryUrl
registryUrl,
);

if (projectResult === null) {
projectResult = /https?:\/\/pkgs\.dev\.azure\.com\/(.+?)\//i.exec(
registryUrl
registryUrl,
);
}

this.feed = feedResult && feedResult[1];
this.project = projectResult && projectResult[1];
}

/**
* Returns the auth settings for this Registry
*/
getAuthSettings(): IYarnRcYmlSettings {
getAuthSettings(): IYarnRcYmlSettings {
let result: IYarnRcYmlSettings = {};

if (this.token) {
Expand All @@ -175,8 +185,9 @@ export class YarnRcYmlRegistry {

result.npmRegistries = {
[`${identifier}registry/`]: {
'npmAuthToken': this.token,
}
npmAuthToken: this.token,
npmAlwaysAuth: this.alwaysAuth,
},
};
} else {
if (this.basicAuthSettings.username && this.basicAuthSettings.password) {
Expand All @@ -186,4 +197,4 @@ export class YarnRcYmlRegistry {

return result;
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "better-vsts-npm-auth",
"version": "7.0.1",
"version": "7.1.0",
"description": "Platform agnostic library which provides a robust solution for maintaining credentials in your npmrc files",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
Loading