2020-05-09 11:34:50 +02:00
|
|
|
import { exec } from 'child_process';
|
2023-01-10 16:08:24 +01:00
|
|
|
import { remove, pathExists, readJSON } from 'fs-extra';
|
2019-04-25 00:00:10 +02:00
|
|
|
import chalk from 'chalk';
|
2020-05-09 11:34:50 +02:00
|
|
|
import path from 'path';
|
|
|
|
import program from 'commander';
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2023-01-10 16:08:24 +01:00
|
|
|
import { runServer, parseConfigFile } from 'verdaccio';
|
2020-05-09 11:34:50 +02:00
|
|
|
import pLimit from 'p-limit';
|
2023-01-10 16:08:24 +01:00
|
|
|
import type { Server } from 'http';
|
2023-08-25 11:13:26 +02:00
|
|
|
import { mkdir } from 'fs/promises';
|
|
|
|
import { PACKS_DIRECTORY } from './utils/constants';
|
2023-08-28 08:37:52 -07:00
|
|
|
|
2020-09-19 17:36:02 +02:00
|
|
|
import { maxConcurrentTasks } from './utils/concurrency';
|
2023-11-02 09:26:31 +01:00
|
|
|
import { getWorkspaces } from './utils/workspace';
|
2019-04-25 11:44:33 +02:00
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
program
|
|
|
|
.option('-O, --open', 'keep process open')
|
2023-01-10 16:08:24 +01:00
|
|
|
.option('-P, --publish', 'should publish packages');
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
program.parse(process.argv);
|
|
|
|
|
|
|
|
const logger = console;
|
|
|
|
|
2023-01-10 16:08:24 +01:00
|
|
|
const startVerdaccio = async () => {
|
2019-04-25 00:00:10 +02:00
|
|
|
let resolved = false;
|
2019-06-03 20:26:24 +02:00
|
|
|
return Promise.race([
|
2020-05-09 11:34:50 +02:00
|
|
|
new Promise((resolve) => {
|
|
|
|
const cache = path.join(__dirname, '..', '.verdaccio-cache');
|
|
|
|
const config = {
|
2023-01-10 16:08:24 +01:00
|
|
|
...parseConfigFile(path.join(__dirname, 'verdaccio.yaml')),
|
2020-05-09 11:34:50 +02:00
|
|
|
self_path: cache,
|
|
|
|
};
|
|
|
|
|
2023-03-29 12:05:38 +02:00
|
|
|
// @ts-expect-error (verdaccio's interface is wrong)
|
2023-01-10 16:08:24 +01:00
|
|
|
runServer(config).then((app: Server) => {
|
|
|
|
app.listen(6001, () => {
|
2019-06-03 20:26:24 +02:00
|
|
|
resolved = true;
|
2023-01-10 16:08:24 +01:00
|
|
|
resolve(app);
|
2019-06-03 20:26:24 +02:00
|
|
|
});
|
2023-01-10 16:08:24 +01:00
|
|
|
});
|
2019-06-03 20:26:24 +02:00
|
|
|
}),
|
2020-05-18 22:29:50 +02:00
|
|
|
new Promise((_, rej) => {
|
2019-06-03 20:26:24 +02:00
|
|
|
setTimeout(() => {
|
|
|
|
if (!resolved) {
|
|
|
|
resolved = true;
|
2021-02-07 16:29:02 +01:00
|
|
|
rej(new Error(`TIMEOUT - verdaccio didn't start within 10s`));
|
2019-06-03 20:26:24 +02:00
|
|
|
}
|
2021-02-07 16:29:02 +01:00
|
|
|
}, 10000);
|
2019-06-03 20:26:24 +02:00
|
|
|
}),
|
2023-01-10 16:08:24 +01:00
|
|
|
]) as Promise<Server>;
|
2019-04-25 00:00:10 +02:00
|
|
|
};
|
2022-01-26 14:12:34 +01:00
|
|
|
|
2019-04-25 00:00:10 +02:00
|
|
|
const currentVersion = async () => {
|
2023-01-10 16:08:24 +01:00
|
|
|
const { version } = await readJSON(path.join(__dirname, '..', 'code', 'package.json'));
|
2019-04-25 00:00:10 +02:00
|
|
|
return version;
|
|
|
|
};
|
|
|
|
|
2023-08-25 11:13:26 +02:00
|
|
|
const publish = async (packages: { name: string; location: string }[], url: string) => {
|
2020-11-03 21:11:30 +01:00
|
|
|
logger.log(`Publishing packages with a concurrency of ${maxConcurrentTasks}`);
|
|
|
|
|
2020-09-19 17:36:02 +02:00
|
|
|
const limit = pLimit(maxConcurrentTasks);
|
2020-09-14 22:20:57 +02:00
|
|
|
let i = 0;
|
2020-05-09 11:34:50 +02:00
|
|
|
|
2023-08-25 11:13:26 +02:00
|
|
|
/**
|
|
|
|
* We need to "pack" our packages before publishing to npm because our package.json files contain yarn specific version "ranges".
|
|
|
|
* such as "workspace:*"
|
|
|
|
*
|
|
|
|
* We can't publish to npm if the package.json contains these ranges. So with `yarn pack` we create a tarball that we can publish.
|
|
|
|
*
|
|
|
|
* However this bug exists in NPM: https://github.com/npm/cli/issues/4533!
|
|
|
|
* Which causes the NPM CLI to disregard the tarball CLI argument and instead re-create a tarball.
|
|
|
|
* But NPM doesn't replace the yarn version ranges.
|
|
|
|
*
|
|
|
|
* So we create the tarball ourselves and move it to another location on the FS.
|
|
|
|
* Then we change-directory to that directory and publish the tarball from there.
|
|
|
|
*/
|
|
|
|
await mkdir(PACKS_DIRECTORY, { recursive: true }).catch(() => {});
|
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
return Promise.all(
|
|
|
|
packages.map(({ name, location }) =>
|
|
|
|
limit(
|
|
|
|
() =>
|
|
|
|
new Promise((res, rej) => {
|
2022-01-26 14:12:34 +01:00
|
|
|
logger.log(
|
|
|
|
`🛫 publishing ${name} (${location.replace(
|
|
|
|
path.resolve(path.join(__dirname, '..')),
|
|
|
|
'.'
|
|
|
|
)})`
|
|
|
|
);
|
2023-08-25 11:13:26 +02:00
|
|
|
|
|
|
|
const tarballFilename = `${name.replace('@', '').replace('/', '-')}.tgz`;
|
2023-11-03 13:43:10 +01:00
|
|
|
const command = `cd ${path.resolve(
|
|
|
|
'../code',
|
|
|
|
location
|
|
|
|
)} && yarn pack --out=${PACKS_DIRECTORY}/${tarballFilename} && cd ${PACKS_DIRECTORY} && npm publish ./${tarballFilename} --registry ${url} --force --access restricted --ignore-scripts`;
|
2020-05-09 11:34:50 +02:00
|
|
|
exec(command, (e) => {
|
|
|
|
if (e) {
|
|
|
|
rej(e);
|
|
|
|
} else {
|
2020-09-14 22:20:57 +02:00
|
|
|
i += 1;
|
|
|
|
logger.log(`${i}/${packages.length} 🛬 successful publish of ${name}!`);
|
2020-11-26 10:58:09 +01:00
|
|
|
res(undefined);
|
2020-05-09 11:34:50 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
})
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
};
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2022-08-23 17:42:58 +08:00
|
|
|
const addUser = (url: string) =>
|
2023-01-10 16:08:24 +01:00
|
|
|
new Promise<void>((res, rej) => {
|
2022-08-23 17:42:58 +08:00
|
|
|
logger.log(`👤 add temp user to verdaccio`);
|
|
|
|
|
|
|
|
exec(`npx npm-cli-adduser -r "${url}" -a -u user -p password -e user@example.com`, (e) => {
|
|
|
|
if (e) {
|
|
|
|
rej(e);
|
|
|
|
} else {
|
|
|
|
res();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2022-01-26 14:12:34 +01:00
|
|
|
|
2019-04-25 00:00:10 +02:00
|
|
|
const run = async () => {
|
2023-01-10 16:08:24 +01:00
|
|
|
const verdaccioUrl = `http://localhost:6001`;
|
2020-05-09 11:34:50 +02:00
|
|
|
|
2019-04-25 11:44:33 +02:00
|
|
|
logger.log(`📐 reading version of storybook`);
|
|
|
|
logger.log(`🚛 listing storybook packages`);
|
2022-05-30 09:42:03 +02:00
|
|
|
|
|
|
|
if (!process.env.CI) {
|
|
|
|
// when running e2e locally, clear cache to avoid EPUBLISHCONFLICT errors
|
|
|
|
const verdaccioCache = path.resolve(__dirname, '..', '.verdaccio-cache');
|
|
|
|
if (await pathExists(verdaccioCache)) {
|
|
|
|
logger.log(`🗑 cleaning up cache`);
|
|
|
|
await remove(verdaccioCache);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
logger.log(`🎬 starting verdaccio (this takes ±5 seconds, so be patient)`);
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2022-07-21 21:06:23 +02:00
|
|
|
const [verdaccioServer, packages, version] = await Promise.all([
|
2023-01-10 16:08:24 +01:00
|
|
|
startVerdaccio(),
|
2023-11-02 09:26:31 +01:00
|
|
|
getWorkspaces(false),
|
2019-04-25 00:00:10 +02:00
|
|
|
currentVersion(),
|
|
|
|
]);
|
|
|
|
|
2019-04-25 11:44:33 +02:00
|
|
|
logger.log(`🌿 verdaccio running on ${verdaccioUrl}`);
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2022-08-23 17:42:58 +08:00
|
|
|
// in some environments you need to add a dummy user. always try to add & catch on failure
|
|
|
|
try {
|
|
|
|
await addUser(verdaccioUrl);
|
|
|
|
} catch (e) {
|
|
|
|
//
|
|
|
|
}
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
logger.log(`📦 found ${packages.length} storybook packages at version ${chalk.blue(version)}`);
|
2019-04-25 00:00:10 +02:00
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
if (program.publish) {
|
|
|
|
await publish(packages, verdaccioUrl);
|
2019-04-25 00:00:10 +02:00
|
|
|
}
|
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
if (!program.open) {
|
|
|
|
verdaccioServer.close();
|
2019-04-25 11:44:33 +02:00
|
|
|
}
|
2019-04-25 00:00:10 +02:00
|
|
|
};
|
|
|
|
|
2020-05-09 11:34:50 +02:00
|
|
|
run().catch((e) => {
|
|
|
|
logger.error(e);
|
|
|
|
process.exit(1);
|
|
|
|
});
|