Skip to content

Commit

Permalink
Add support for publishConfig.registry (#582)
Browse files Browse the repository at this point in the history
* Add support for publishConfig.registry

* Create tidy-cherries-stare.md

Co-authored-by: Mitchell Hamilton <mitchell@hamil.town>
  • Loading branch information
Andarist and emmatown committed May 2, 2021
1 parent 8e03a18 commit e89e28a
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 58 deletions.
6 changes: 6 additions & 0 deletions .changeset/tidy-cherries-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@changesets/cli": patch
"@changesets/types": patch
---

Add support for publishConfig.registry
42 changes: 22 additions & 20 deletions packages/cli/src/commands/publish/npm-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ExitError } from "@changesets/errors";
import { error, info, warn } from "@changesets/logger";
import { PackageJSON } from "@changesets/types";
import pLimit from "p-limit";
import preferredPM from "preferred-pm";
import chalk from "chalk";
Expand All @@ -23,12 +24,13 @@ function jsonParse(input: string) {
}
}

function getCorrectRegistry() {
let registry =
process.env.npm_config_registry === "https://registry.yarnpkg.com"
? undefined
: process.env.npm_config_registry;
return registry;
function getCorrectRegistry(packageJson?: PackageJSON): string {
const registry =
packageJson?.publishConfig?.registry ?? process.env.npm_config_registry;

return !registry || registry === "https://registry.yarnpkg.com"
? "https://registry.npmjs.org"
: registry;
}

async function getPublishTool(
Expand Down Expand Up @@ -69,23 +71,23 @@ export async function getTokenIsRequired() {
return json.tfa.mode === "auth-and-writes";
}

export function getPackageInfo(pkgName: string) {
export function getPackageInfo(packageJson: PackageJSON) {
return npmRequestLimit(async () => {
info(`npm info ${pkgName}`);
info(`npm info ${packageJson.name}`);

// Due to a couple of issues with yarnpkg, we also want to override the npm registry when doing
// npm info.
// Issues: We sometimes get back cached responses, i.e old data about packages which causes
// `publish` to behave incorrectly. It can also cause issues when publishing private packages
// as they will always give a 404, which will tell `publish` to always try to publish.
// See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633
const envOverride = {
npm_config_registry: getCorrectRegistry()
};

let result = await spawn("npm", ["info", pkgName, "--json"], {
env: Object.assign({}, process.env, envOverride)
});
let result = await spawn("npm", [
"info",
packageJson.name,
"--registry",
getCorrectRegistry(packageJson),
"--json"
]);

// Github package registry returns empty string when calling npm info
// for a non-existant package instead of a E404
Expand All @@ -100,17 +102,17 @@ export function getPackageInfo(pkgName: string) {
});
}

export async function infoAllow404(pkgName: string) {
let pkgInfo = await getPackageInfo(pkgName);
if (pkgInfo.error && pkgInfo.error.code === "E404") {
warn(`Received 404 for npm info ${chalk.cyan(`"${pkgName}"`)}`);
export async function infoAllow404(packageJson: PackageJSON) {
let pkgInfo = await getPackageInfo(packageJson);
if (pkgInfo.error?.code === "E404") {
warn(`Received 404 for npm info ${chalk.cyan(`"${packageJson.name}"`)}`);
return { published: false, pkgInfo: {} };
}
if (pkgInfo.error) {
error(
`Received an unknown error code: ${
pkgInfo.error.code
} for npm info ${chalk.cyan(`"${pkgName}"`)}`
} for npm info ${chalk.cyan(`"${packageJson.name}"`)}`
);
error(pkgInfo.error.summary);
if (pkgInfo.error.detail) error(pkgInfo.error.detail);
Expand Down
89 changes: 51 additions & 38 deletions packages/cli/src/commands/publish/publishPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,45 @@ function getReleaseTag(pkgInfo: PkgInfo, preState?: PreState, tag?: string) {
return "latest";
}

const isCustomRegistry = (registry?: string): boolean =>
!!registry &&
registry !== "https://registry.npmjs.org" &&
registry !== "https://registry.yarnpkg.com";

const getTwoFactorState = ({
otp,
publicPackages
}: {
otp?: string;
publicPackages: Package[];
}): TwoFactorState => {
if (otp) {
return {
token: otp,
isRequired: Promise.resolve(true)
};
}

if (
isCI ||
publicPackages.some(pkg =>
isCustomRegistry(pkg.packageJson.publishConfig?.registry)
) ||
isCustomRegistry(process.env.npm_config_registry)
) {
return {
token: null,
isRequired: Promise.resolve(false)
};
}

return {
token: null,
// note: we're not awaiting this here, we want this request to happen in parallel with getUnpublishedPackages
isRequired: npmUtils.getTokenIsRequired()
};
};

export default async function publishPackages({
packages,
access,
Expand All @@ -49,34 +88,10 @@ export default async function publishPackages({
}) {
const packagesByName = new Map(packages.map(x => [x.packageJson.name, x]));
const publicPackages = packages.filter(pkg => !pkg.packageJson.private);
let twoFactorState: TwoFactorState =
otp === undefined
? {
token: null,
isRequired:
isCI ||
publicPackages.some(
x =>
x.packageJson.publishConfig &&
(x.packageJson.publishConfig as any).registry &&
(x.packageJson.publishConfig as any).registry !==
"https://registry.npmjs.org" &&
(x.packageJson.publishConfig as any).registry !==
"https://registry.yarnpkg.com"
) ||
(process.env.npm_config_registry !== undefined &&
process.env.npm_config_registry !==
"https://registry.npmjs.org" &&
process.env.npm_config_registry !==
"https://registry.yarnpkg.com")
? Promise.resolve(false)
: // note: we're not awaiting this here, we want this request to happen in parallel with getUnpublishedPackages
npmUtils.getTokenIsRequired()
}
: {
token: otp,
isRequired: Promise.resolve(true)
};
const twoFactorState: TwoFactorState = getTwoFactorState({
otp,
publicPackages
});
const unpublishedPackagesInfo = await getUnpublishedPackages(
publicPackages,
preState
Expand Down Expand Up @@ -106,15 +121,14 @@ async function publishAPackage(
tag: string
): Promise<PublishedResult> {
const { name, version, publishConfig } = pkg.packageJson;
const localAccess = publishConfig && publishConfig.access;
const localAccess = publishConfig?.access;
info(
`Publishing ${chalk.cyan(`"${name}"`)} at ${chalk.green(`"${version}"`)}`
);

const publishDir =
publishConfig && publishConfig.directory
? join(pkg.dir, publishConfig.directory)
: pkg.dir;
const publishDir = publishConfig?.directory
? join(pkg.dir, publishConfig.directory)
: pkg.dir;

const publishConfirmation = await npmUtils.publish(
name,
Expand All @@ -138,9 +152,8 @@ async function getUnpublishedPackages(
preState: PreState | undefined
) {
const results: Array<PkgInfo> = await Promise.all(
packages.map(async pkg => {
const config = pkg.packageJson;
const response = await npmUtils.infoAllow404(config.name);
packages.map(async ({ packageJson }) => {
const response = await npmUtils.infoAllow404(packageJson);
let publishedState: PublishedState = "never";
if (response.published) {
publishedState = "published";
Expand All @@ -158,8 +171,8 @@ async function getUnpublishedPackages(
}

return {
name: config.name,
localVersion: config.version,
name: packageJson.name,
localVersion: packageJson.version,
publishedState: publishedState,
publishedVersions: response.pkgInfo.versions || []
};
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type PackageJSON = {
publishConfig?: {
access?: AccessType;
directory?: string;
registry?: string;
};
};

Expand Down

0 comments on commit e89e28a

Please sign in to comment.