В ISPsystem на текущий момент три front-end команды разрабатывают три крупных проекта: ISPmanager для управления веб-серверами, VMmanager для работы с виртуализацией и BILLmanager для автоматизации бизнеса хостеров. Команды работают одновременно, в режиме сжатых сроков, поэтому без оптимизации не обойтись. Чтобы сэкономить время, мы применяем единые решения и выносим общие компоненты в отдельные проекты. Такие проекты имеют собственные репозитории, которые поддерживают участники всех команд. Об устройстве этих репозиториев, а также работе с ними и будет эта статья.
npm install git+ssh://[url репозитория]#1.0.0
1.0.0_e5541dc1
"publish:git": "ts-node ./scripts/publish.ts"
import { spawnSync } from 'child_process';
import { mkdirSync, existsSync } from 'fs';
import { join } from 'path';
import chalk from 'chalk';
/**
* Скрипт публикации данного модуля
*/
/**
* Генерация параметров для запускаемых подпроцессов
* @param cwd - директория запуска подпроцесса
* @param stdio - настройка ввода/вывода
*/
const getSpawnOptions = (cwd = process.cwd(), stdio = 'inherit') => ({
cwd,
shell: true,
stdio,
});
/* корневая директория модуля */
const rootDir = join(__dirname, '../');
/* проверка наличия незакоммиченных изменений */
const isDiff = !!spawnSync('git', ['diff'], getSpawnOptions(rootDir, 'pipe')).stdout.toString().trim();
if (isDiff) {
console.log(chalk.red('There are uncommitted changes'));
} else {
/* сборка проекта */
const build = spawnSync('npm', ['run', 'build'], getSpawnOptions(rootDir));
/* проверка статуса выполнения сборки */
if (build.status === 0) {
/* временная директория для разворачивания репозитория сборок */
const tempDir = join(rootDir, 'temp');
if (existsSync(tempDir)) {
spawnSync('rm', ['-rf', 'temp'], getSpawnOptions(rootDir));
}
mkdirSync(tempDir);
/* получение параметров из package.json */
const { name, version, repository } = require(join(rootDir, 'package.json'));
const originUrl = repository.url.replace(`${name}-source`, name);
spawnSync('git', ['init'], getSpawnOptions(tempDir));
spawnSync('git', ['remote', 'add', 'origin', originUrl], getSpawnOptions(tempDir));
/* имя текущей ветки из репозитория исходников модуля */
const branch = spawnSync(
'git',
['symbolic-ref', '--short', 'HEAD'],
getSpawnOptions(rootDir, 'pipe')
).stdout.toString().trim();
/* имя ветки в репозитории сборок */
const buildBranch = branch === 'develop' ? 'master' : branch;
/* сокращенный хеш последнего коммита в репозитории исходников,
используемый при формировании метки нестабильной версии */
const shortSHA = spawnSync(
'git',
['rev-parse', '--short', 'HEAD'],
getSpawnOptions(rootDir, 'pipe')
).stdout.toString().trim();
/* метка */
const tag = buildBranch === 'master' ? version : `${version}_${shortSHA}`;
/* проверка существования сформированной метки в репозитории сборок */
const isTagExists = !!spawnSync(
'git',
['ls-remote', 'origin', `refs/tags/${tag}`],
getSpawnOptions(tempDir, 'pipe')
).stdout.toString().trim();
if (isTagExists) {
console.log(chalk.red(`Tag ${tag} already exists`));
} else {
/* проверка существования ветки в репозитории сборок */
const isBranchExits = !!spawnSync(
'git',
['ls-remote', '--exit-code', 'origin', buildBranch],
getSpawnOptions(tempDir, 'pipe')
).stdout.toString().trim();
if (isBranchExits) {
/* переход в целевую ветку */
spawnSync('git', ['fetch', 'origin', buildBranch], getSpawnOptions(tempDir));
spawnSync('git', ['checkout', buildBranch], getSpawnOptions(tempDir));
} else {
/* переход в ветку master */
spawnSync('git', ['fetch', 'origin', 'master'], getSpawnOptions(tempDir));
spawnSync('git', ['checkout', 'master'], getSpawnOptions(tempDir));
/* создание целевой ветки */
spawnSync('git', ['checkout', '-b', buildBranch], getSpawnOptions(tempDir));
/* создание начального коммита */
spawnSync('git', ['commit', '--allow-empty', '-m', '"Initial commit"'], getSpawnOptions(tempDir));
}
/* удаление старых файлов сборки */
spawnSync(
'rm',
['-rf', 'lib', 'package.json', 'package-lock.json', 'README.md'],
getSpawnOptions(tempDir)
);
/* копирование файлов сборки */
spawnSync('cp', ['-r', 'lib', 'temp/lib'], getSpawnOptions(rootDir));
spawnSync('cp', ['package.json', 'temp/package.json'], getSpawnOptions(rootDir));
spawnSync('cp', ['package-lock.json', 'temp/package-lock.json'], getSpawnOptions(rootDir));
spawnSync('cp', ['README.md', 'temp/README.md'], getSpawnOptions(rootDir));
/* индексация файлов сборки */
spawnSync('git', ['add', '--all'], getSpawnOptions(tempDir));
/* сообщение последнего коммита в репозитории исходников */
const lastCommitMessage = spawnSync(
'git',
['log', '--oneline', '-1'],
getSpawnOptions(rootDir, 'pipe')
).stdout.toString().trim();
/* сообщение коммита в репозитории сборок */
const message = buildBranch === 'master' ? version : lastCommitMessage;
/* создание коммита в репозитории сборок */
spawnSync('git', ['commit', '-m', `"${message}"`], getSpawnOptions(tempDir));
/* создание метки в репозитории сборок */
spawnSync('git', ['tag', tag], getSpawnOptions(tempDir));
/* отправка изменений в удаленный репозиторий */
spawnSync('git', ['push', 'origin', buildBranch], getSpawnOptions(tempDir));
spawnSync('git', ['push', '--tags'], getSpawnOptions(tempDir));
console.log(chalk.green('Published successfully!'));
}
/* удаление временной директории */
spawnSync('rm', ['-rf', 'temp'], getSpawnOptions(rootDir));
} else {
console.log(chalk.red(`Build was exited exited with code ${build.status}`));
}
}
console.log(''); // space
const isDiff = !!spawnSync('git', ['diff'], getSpawnOptions(rootDir, 'pipe')).stdout.toString().trim();
const build = spawnSync('npm', ['run', 'build'], getSpawnOptions(rootDir));
/* временная директория для разворачивания репозитория сборок */
const tempDir = join(rootDir, 'temp');
if (existsSync(tempDir)) {
spawnSync('rm', ['-rf', 'temp'], getSpawnOptions(rootDir));
}
mkdirSync(tempDir);
/* получение параметров из package.json */
const { name, version, repository } = require(join(rootDir, 'package.json'));
const originUrl = repository.url.replace(`${name}-source`, name);
spawnSync('git', ['init'], getSpawnOptions(tempDir));
spawnSync('git', ['remote', 'add', 'origin', originUrl], getSpawnOptions(tempDir));
/* имя текущей ветки из репозитория исходников модуля */
const branch = spawnSync(
'git',
['symbolic-ref', '--short', 'HEAD'],
getSpawnOptions(rootDir, 'pipe')
).stdout.toString().trim();
/* имя ветки в репозитории сборок */
const buildBranch = branch === 'develop' ? 'master' : branch;
<source lang="typescript">/* сокращенный хеш последнего коммита в репозитории исходников,
используемый при формировании метки нестабильной версии */
const shortSHA = spawnSync(
'git',
['rev-parse', '--short', 'HEAD'],
getSpawnOptions(rootDir, 'pipe')
).stdout.toString().trim();
/* метка */
const tag = buildBranch === 'master' ? version : `${version}_${shortSHA}`;
/* проверка существования сформированной метки в репозитории сборок */
const isTagExists = !!spawnSync(
'git',
['ls-remote', 'origin', `refs/tags/${tag}`],
getSpawnOptions(tempDir, 'pipe')
).stdout.toString().trim();
/* проверка существования ветки в репозитории сборок */
const isBranchExits = !!spawnSync(
'git',
['ls-remote', '--exit-code', 'origin', buildBranch],
getSpawnOptions(tempDir, 'pipe')
).stdout.toString().trim();
if (isBranchExits) {
/* переход в целевую ветку */
spawnSync('git', ['fetch', 'origin', buildBranch], getSpawnOptions(tempDir));
spawnSync('git', ['checkout', buildBranch], getSpawnOptions(tempDir));
} else {
/* переход в ветку master */
spawnSync('git', ['fetch', 'origin', 'master'], getSpawnOptions(tempDir));
spawnSync('git', ['checkout', 'master'], getSpawnOptions(tempDir));
/* создание целевой ветки */
spawnSync('git', ['checkout', '-b', buildBranch], getSpawnOptions(tempDir));
/* создание начального коммита */
spawnSync('git', ['commit', '--allow-empty', '-m', '"Initial commit"'], getSpawnOptions(tempDir));
}
/* удаление старых файлов сборки */
spawnSync(
'rm',
['-rf', 'lib', 'package.json', 'package-lock.json', 'README.md'],
getSpawnOptions(tempDir)
);
/* копирование файлов сборки */
spawnSync('cp', ['-r', 'lib', 'temp/lib'], getSpawnOptions(rootDir));
spawnSync('cp', ['package.json', 'temp/package.json'], getSpawnOptions(rootDir));
spawnSync('cp', ['package-lock.json', 'temp/package-lock.json'], getSpawnOptions(rootDir));
spawnSync('cp', ['README.md', 'temp/README.md'], getSpawnOptions(rootDir));
/* индексация файлов сборки */
spawnSync('git', ['add', '--all'], getSpawnOptions(tempDir));
/* сообщение последнего коммита в репозитории исходников */
const lastCommitMessage = spawnSync(
'git',
['log', '--oneline', '-1'],
getSpawnOptions(rootDir, 'pipe')
).stdout.toString().trim();
/* сообщение коммита в репозитории сборок */
const message = buildBranch === 'master' ? version : lastCommitMessage;
/* создание коммита в репозитории сборок */
spawnSync('git', ['commit', '-m', `"${message}"`], getSpawnOptions(tempDir));
/* создание метки в репозитории сборок */
spawnSync('git', ['tag', tag], getSpawnOptions(tempDir));
/* отправка изменений в удаленный репозиторий */
spawnSync('git', ['push', 'origin', buildBranch], getSpawnOptions(tempDir));
spawnSync('git', ['push', '--tags'], getSpawnOptions(tempDir));
spawnSync('rm', ['-rf', 'temp'], getSpawnOptions(rootDir));
К сожалению, не доступен сервер mySQL