mirror of
https://github.com/pezkuwichain/pezkuwi-dev.git
synced 2026-04-22 02:08:01 +00:00
8d28b36f9c
- Package namespace: @polkadot/dev -> @pezkuwi/dev - Repository: polkadot-js/dev -> pezkuwichain/pezkuwi-dev - Author: Pezkuwi Team <team@pezkuwichain.io> Packages: - @pezkuwi/dev (build tools, linting, CI scripts) - @pezkuwi/dev-test (test runner) - @pezkuwi/dev-ts (TypeScript build) Upstream: polkadot-js/dev v0.83.3
541 lines
12 KiB
JavaScript
Executable File
541 lines
12 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
// Copyright 2017-2025 @polkadot/dev authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import process from 'node:process';
|
|
import yargs from 'yargs';
|
|
|
|
import { copyDirSync, copyFileSync, denoCreateDir, execGit, execPm, execSync, exitFatal, GITHUB_REPO, GITHUB_TOKEN_URL, gitSetup, logBin, mkdirpSync, rimrafSync, topoSort } from './util.mjs';
|
|
|
|
/** @typedef {Record<string, any>} ChangelogMap */
|
|
|
|
logBin('polkadot-ci-ghact-build');
|
|
|
|
const DENO_REPO = 'polkadot-js/build-deno.land';
|
|
const BUND_REPO = 'polkadot-js/build-bundle';
|
|
|
|
const repo = `${GITHUB_TOKEN_URL}/${GITHUB_REPO}.git`;
|
|
const denoRepo = `${GITHUB_TOKEN_URL}/${DENO_REPO}.git`;
|
|
const bundRepo = `${GITHUB_TOKEN_URL}/${BUND_REPO}.git`;
|
|
const bundClone = 'build-bundle-clone';
|
|
const denoClone = 'build-deno-clone';
|
|
|
|
let withDeno = false;
|
|
let withBund = false;
|
|
let withNpm = false;
|
|
|
|
/** @type {string[]} */
|
|
const shouldDeno = [];
|
|
/** @type {string[]} */
|
|
const shouldBund = [];
|
|
|
|
const argv = await yargs(process.argv.slice(2))
|
|
.options({
|
|
'skip-beta': {
|
|
description: 'Do not increment as beta',
|
|
type: 'boolean'
|
|
}
|
|
})
|
|
.strict()
|
|
.argv;
|
|
|
|
/**
|
|
* Removes a specific file, returning true if found, false otherwise
|
|
*
|
|
* @param {string} file
|
|
* @returns {boolean}
|
|
*/
|
|
function rmFile (file) {
|
|
if (fs.existsSync(file)) {
|
|
rimrafSync(file);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the path of the root package.json
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
function npmGetJsonPath () {
|
|
return path.resolve(process.cwd(), 'package.json');
|
|
}
|
|
|
|
/**
|
|
* Retrieves the contents of the root package.json
|
|
*
|
|
* @returns {{ name: string; version: string; versions?: { npm?: string; git?: string } }}
|
|
*/
|
|
function npmGetJson () {
|
|
return JSON.parse(
|
|
fs.readFileSync(npmGetJsonPath(), 'utf8')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Writes the contents of the root package.json
|
|
*
|
|
* @param {any} json
|
|
*/
|
|
function npmSetJson (json) {
|
|
fs.writeFileSync(npmGetJsonPath(), `${JSON.stringify(json, null, 2)}\n`);
|
|
}
|
|
|
|
/**
|
|
* Retrieved the current version included in package.json
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
function npmGetVersion () {
|
|
return npmGetJson().version;
|
|
}
|
|
|
|
/**
|
|
* Sets the current to have an -x version specifier (aka beta)
|
|
*/
|
|
function npmAddVersionX () {
|
|
const json = npmGetJson();
|
|
|
|
if (!json.version.endsWith('-x')) {
|
|
json.version = json.version + '-x';
|
|
npmSetJson(json);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the current -x version specifier (aka beta)
|
|
*/
|
|
function npmDelVersionX () {
|
|
const json = npmGetJson();
|
|
|
|
if (json.version.endsWith('-x')) {
|
|
json.version = json.version.replace('-x', '');
|
|
npmSetJson(json);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the {versions: { npm, git } } fields in package.json
|
|
*/
|
|
function npmSetVersionFields () {
|
|
const json = npmGetJson();
|
|
|
|
if (!json.versions) {
|
|
json.versions = {};
|
|
}
|
|
|
|
json.versions.git = json.version;
|
|
|
|
if (!json.version.endsWith('-x')) {
|
|
json.versions.npm = json.version;
|
|
}
|
|
|
|
npmSetJson(json);
|
|
rmFile('.123current');
|
|
}
|
|
|
|
/**
|
|
* Sets the npm token in the home directory
|
|
*/
|
|
function npmSetup () {
|
|
const registry = 'registry.npmjs.org';
|
|
|
|
fs.writeFileSync(path.join(os.homedir(), '.npmrc'), `//${registry}/:_authToken=${process.env['NPM_TOKEN']}`);
|
|
}
|
|
|
|
/**
|
|
* Publishes the current package
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function npmPublish () {
|
|
if (fs.existsSync('.skip-npm') || !withNpm) {
|
|
return;
|
|
}
|
|
|
|
['LICENSE', 'package.json']
|
|
.filter((file) => !fs.existsSync(path.join(process.cwd(), 'build', file)))
|
|
.forEach((file) => copyFileSync(file, 'build'));
|
|
|
|
process.chdir('build');
|
|
|
|
const tag = npmGetVersion().includes('-') ? '--tag beta' : '';
|
|
let count = 1;
|
|
|
|
while (true) {
|
|
try {
|
|
execSync(`npm publish --quiet --access public ${tag}`);
|
|
|
|
break;
|
|
} catch {
|
|
if (count < 5) {
|
|
const end = Date.now() + 15000;
|
|
|
|
console.error(`Publish failed on attempt ${count}/5. Retrying in 15s`);
|
|
count++;
|
|
|
|
while (Date.now() < end) {
|
|
// just spin our wheels
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
process.chdir('..');
|
|
}
|
|
|
|
/**
|
|
* Creates a map of changelog entries
|
|
*
|
|
* @param {string[][]} parts
|
|
* @param {ChangelogMap} result
|
|
* @returns {ChangelogMap}
|
|
*/
|
|
function createChangelogMap (parts, result = {}) {
|
|
for (let i = 0, count = parts.length; i < count; i++) {
|
|
const [n, ...e] = parts[i];
|
|
|
|
if (!result[n]) {
|
|
if (e.length) {
|
|
result[n] = createChangelogMap([e]);
|
|
} else {
|
|
result[n] = { '': {} };
|
|
}
|
|
} else {
|
|
if (e.length) {
|
|
createChangelogMap([e], result[n]);
|
|
} else {
|
|
result[n][''] = {};
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates an array of changelog entries
|
|
*
|
|
* @param {ChangelogMap} map
|
|
* @returns {string[]}
|
|
*/
|
|
function createChangelogArr (map) {
|
|
const result = [];
|
|
const entries = Object.entries(map);
|
|
|
|
for (let i = 0, count = entries.length; i < count; i++) {
|
|
const [name, imap] = entries[i];
|
|
|
|
if (name) {
|
|
if (imap['']) {
|
|
result.push(name);
|
|
}
|
|
|
|
const inner = createChangelogArr(imap);
|
|
|
|
if (inner.length === 1) {
|
|
result.push(`${name}-${inner[0]}`);
|
|
} else if (inner.length) {
|
|
result.push(`${name}-{${inner.join(', ')}}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Adds changelog entries
|
|
*
|
|
* @param {string[]} changelog
|
|
* @returns {string}
|
|
*/
|
|
function addChangelog (changelog) {
|
|
const [version, ...names] = changelog;
|
|
const entry = `${
|
|
createChangelogArr(
|
|
createChangelogMap(
|
|
names
|
|
.sort()
|
|
.map((n) => n.split('-'))
|
|
)
|
|
).join(', ')
|
|
} ${version}`;
|
|
const newInfo = `## master\n\n- ${entry}\n`;
|
|
|
|
if (!fs.existsSync('CHANGELOG.md')) {
|
|
fs.writeFileSync('CHANGELOG.md', `# CHANGELOG\n\n${newInfo}`);
|
|
} else {
|
|
const md = fs.readFileSync('CHANGELOG.md', 'utf-8');
|
|
|
|
fs.writeFileSync('CHANGELOG.md', md.includes('## master\n\n')
|
|
? md.replace('## master\n\n', newInfo)
|
|
: md.replace('# CHANGELOG\n\n', `# CHANGELOG\n\n${newInfo}\n`)
|
|
);
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} repo
|
|
* @param {string} clone
|
|
* @param {string[]} names
|
|
*/
|
|
function commitClone (repo, clone, names) {
|
|
if (names.length) {
|
|
process.chdir(clone);
|
|
|
|
const entry = addChangelog(names);
|
|
|
|
gitSetup();
|
|
execGit('add --all .');
|
|
execGit(`commit --no-status --quiet -m "${entry}"`);
|
|
execGit(`push ${repo}`, true);
|
|
|
|
process.chdir('..');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Publishes a specific package to polkadot-js bundles
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function bundlePublishPkg () {
|
|
const { name, version } = npmGetJson();
|
|
const dirName = name.split('/')[1];
|
|
const bundName = `bundle-polkadot-${dirName}.js`;
|
|
const srcPath = path.join('build', bundName);
|
|
const dstDir = path.join('../..', bundClone);
|
|
|
|
if (!fs.existsSync(srcPath)) {
|
|
return;
|
|
}
|
|
|
|
console.log(`\n *** bundle ${name}`);
|
|
|
|
if (shouldBund.length === 0) {
|
|
shouldBund.push(version);
|
|
}
|
|
|
|
shouldBund.push(dirName);
|
|
|
|
rimrafSync(path.join(dstDir, bundName));
|
|
copyFileSync(srcPath, dstDir);
|
|
}
|
|
|
|
/**
|
|
* Publishes all packages to polkadot-js bundles
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function bundlePublish () {
|
|
const { version } = npmGetJson();
|
|
|
|
if (!withBund && version.includes('-')) {
|
|
return;
|
|
}
|
|
|
|
execGit(`clone ${bundRepo} ${bundClone}`, true);
|
|
|
|
loopFunc(bundlePublishPkg);
|
|
|
|
commitClone(bundRepo, bundClone, shouldBund);
|
|
}
|
|
|
|
/**
|
|
* Publishes a specific package to Deno
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function denoPublishPkg () {
|
|
const { name, version } = npmGetJson();
|
|
|
|
if (fs.existsSync('.skip-deno') || !fs.existsSync('build-deno')) {
|
|
return;
|
|
}
|
|
|
|
console.log(`\n *** deno ${name}`);
|
|
|
|
const dirName = denoCreateDir(name);
|
|
const denoPath = `../../${denoClone}/${dirName}`;
|
|
|
|
if (shouldDeno.length === 0) {
|
|
shouldDeno.push(version);
|
|
}
|
|
|
|
shouldDeno.push(dirName);
|
|
|
|
rimrafSync(denoPath);
|
|
mkdirpSync(denoPath);
|
|
|
|
copyDirSync('build-deno', denoPath);
|
|
}
|
|
|
|
/**
|
|
* Publishes all packages to Deno
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function denoPublish () {
|
|
const { version } = npmGetJson();
|
|
|
|
if (!withDeno && version.includes('-')) {
|
|
return;
|
|
}
|
|
|
|
execGit(`clone ${denoRepo} ${denoClone}`, true);
|
|
|
|
loopFunc(denoPublishPkg);
|
|
|
|
commitClone(denoRepo, denoClone, shouldDeno);
|
|
}
|
|
|
|
/**
|
|
* Retrieves flags based on current specifications
|
|
*/
|
|
function getFlags () {
|
|
withDeno = rmFile('.123deno');
|
|
withBund = rmFile('.123bundle');
|
|
withNpm = rmFile('.123npm');
|
|
}
|
|
|
|
/**
|
|
* Bumps the current version, also applying to all sub-packages
|
|
*/
|
|
function verBump () {
|
|
const { version: currentVersion, versions } = npmGetJson();
|
|
const [version, tag] = currentVersion.split('-');
|
|
const [,, patch] = version.split('.');
|
|
const lastVersion = versions?.npm || currentVersion;
|
|
|
|
if (argv['skip-beta'] || patch === '0') {
|
|
// don't allow beta versions
|
|
execPm('polkadot-dev-version patch');
|
|
withNpm = true;
|
|
} else if (tag || currentVersion === lastVersion) {
|
|
// if we don't want to publish, add an X before passing
|
|
if (!withNpm) {
|
|
npmAddVersionX();
|
|
} else {
|
|
npmDelVersionX();
|
|
}
|
|
|
|
// beta version, just continue the stream of betas
|
|
execPm('polkadot-dev-version pre');
|
|
} else {
|
|
// manually set, got for publish
|
|
withNpm = true;
|
|
}
|
|
|
|
// always ensure we have made some changes, so we can commit
|
|
npmSetVersionFields();
|
|
rmFile('.123trigger');
|
|
|
|
execPm('polkadot-dev-contrib');
|
|
execGit('add --all .');
|
|
}
|
|
|
|
/**
|
|
* Commits and pushes the current version on git
|
|
*/
|
|
function gitPush () {
|
|
const version = npmGetVersion();
|
|
let doGHRelease = false;
|
|
|
|
if (process.env['GH_RELEASE_GITHUB_API_TOKEN']) {
|
|
const changes = fs.readFileSync('CHANGELOG.md', 'utf8');
|
|
|
|
if (changes.includes(`## ${version}`)) {
|
|
doGHRelease = true;
|
|
} else if (version.endsWith('.1')) {
|
|
exitFatal(`Unable to release, no CHANGELOG entry for ${version}`);
|
|
}
|
|
}
|
|
|
|
execGit('add --all .');
|
|
|
|
if (fs.existsSync('docs/README.md')) {
|
|
execGit('add --all -f docs');
|
|
}
|
|
|
|
// add the skip checks for GitHub ...
|
|
execGit(`commit --no-status --quiet -m "[CI Skip] ${version.includes('-x') ? 'bump' : 'release'}/${version.includes('-') ? 'beta' : 'stable'} ${version}
|
|
|
|
|
|
skip-checks: true"`);
|
|
|
|
// Make sure the release commit is on top of the latest master
|
|
execGit(`pull --rebase ${repo} master`);
|
|
|
|
// Now push normally
|
|
execGit(`push ${repo} HEAD:${process.env['GITHUB_REF']}`, true);
|
|
|
|
if (doGHRelease) {
|
|
const files = process.env['GH_RELEASE_FILES']
|
|
? `--assets ${process.env['GH_RELEASE_FILES']}`
|
|
: '';
|
|
|
|
execPm(`polkadot-exec-ghrelease --draft ${files} --yes`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loops through the packages/* (or root), executing the supplied
|
|
* function for each package found
|
|
*
|
|
* @param {() => unknown} fn
|
|
*/
|
|
function loopFunc (fn) {
|
|
if (fs.existsSync('packages')) {
|
|
const dirs = fs
|
|
.readdirSync('packages')
|
|
.filter((dir) => {
|
|
const pkgDir = path.join(process.cwd(), 'packages', dir);
|
|
|
|
return fs.statSync(pkgDir).isDirectory() &&
|
|
fs.existsSync(path.join(pkgDir, 'package.json')) &&
|
|
fs.existsSync(path.join(pkgDir, 'build'));
|
|
});
|
|
|
|
topoSort(dirs)
|
|
.forEach((dir) => {
|
|
process.chdir(path.join('packages', dir));
|
|
fn();
|
|
process.chdir('../..');
|
|
});
|
|
} else {
|
|
fn();
|
|
}
|
|
}
|
|
|
|
// first do infrastructure setup
|
|
gitSetup();
|
|
npmSetup();
|
|
|
|
// get flags immediate, then adjust
|
|
getFlags();
|
|
verBump();
|
|
|
|
// perform the actual CI build
|
|
execPm('polkadot-dev-clean-build');
|
|
execPm('lint');
|
|
execPm('test');
|
|
execPm('build');
|
|
|
|
// publish to all GH repos
|
|
gitPush();
|
|
denoPublish();
|
|
bundlePublish();
|
|
|
|
// publish to npm
|
|
loopFunc(npmPublish);
|