spectrum-cli 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +63 -11
- package/index.js +63 -37
- package/package.json +5 -3
- package/src/changelog.js +96 -57
- package/src/chart.js +821 -0
- package/src/command-executor.js +70 -0
- package/src/development.js +11 -11
- package/src/git.js +59 -40
- package/src/preflight.js +556 -0
- package/src/release.js +102 -55
- package/src/utils.js +47 -9
- package/src/version.js +23 -17
package/src/release.js
CHANGED
|
@@ -1,81 +1,128 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const { goToDevBranch, goToMainBranch, updateCurrentBranch } = require('./git');
|
|
3
3
|
const { changelogChangeHeader, changelogRemoveEmptyChapters, changelogAddUnreleasedBlock, changelogCommit } = require('./changelog');
|
|
4
|
-
const
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { getVersion, logSuccess, logError, execCommand, getCurrentBranch, getMainBranch, getMergeRequestUrl } = require('./utils');
|
|
6
|
+
const { runCommand } = require('./command-executor');
|
|
7
|
+
const {
|
|
8
|
+
requireGitRepo,
|
|
9
|
+
requireCleanWorkingTree,
|
|
10
|
+
requireCurrentBranchUpToDateWithRemote,
|
|
11
|
+
requireMainAndDevBranches,
|
|
12
|
+
requireOnReleaseBranch,
|
|
13
|
+
requireReleaseBranchMissing,
|
|
14
|
+
requirePackageVersion,
|
|
15
|
+
requireFileExists,
|
|
16
|
+
requireChangelogFormatted
|
|
17
|
+
} = require('./preflight');
|
|
5
18
|
|
|
6
19
|
function releaseCreate() {
|
|
7
|
-
goToDevBranch();
|
|
8
|
-
updateCurrentBranch();
|
|
9
|
-
|
|
20
|
+
if (!goToDevBranch()) return false;
|
|
21
|
+
if (!updateCurrentBranch()) return false;
|
|
22
|
+
|
|
10
23
|
const currentVersion = getVersion();
|
|
11
24
|
if (!currentVersion) {
|
|
12
|
-
logError('❌', '
|
|
25
|
+
logError('❌', 'Не удалось получить версию из package.json');
|
|
13
26
|
return false;
|
|
14
27
|
}
|
|
15
|
-
|
|
16
|
-
if (execCommand(`git switch -c release/${currentVersion}
|
|
17
|
-
|
|
18
|
-
return true;
|
|
28
|
+
|
|
29
|
+
if (!execCommand(`git switch -c release/${currentVersion}`)) {
|
|
30
|
+
return false;
|
|
19
31
|
}
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
|
|
33
|
+
logSuccess('🌱', 'Создана новая release-ветка %s.', getCurrentBranch());
|
|
34
|
+
return true;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
function releasePush() {
|
|
25
38
|
const currentBranch = getCurrentBranch();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
logSuccess('📤', `Push release branch ${currentBranch}.`);
|
|
29
|
-
logSuccess('🌐', 'Go to https://gitlab.spectrumdata.tech/ and merge branch manually.');
|
|
30
|
-
goToMainBranch();
|
|
31
|
-
return true;
|
|
39
|
+
if (!execCommand(`git push origin ${currentBranch}`)) {
|
|
40
|
+
return false;
|
|
32
41
|
}
|
|
33
|
-
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
updateCurrentBranch();
|
|
42
|
-
|
|
43
|
-
goToDevBranch();
|
|
44
|
-
updateCurrentBranch();
|
|
45
|
-
|
|
46
|
-
if (execCommand(`git merge ${mainBranch}`, null, null)) {
|
|
47
|
-
logSuccess('🔀', `Merge branch ${getCurrentBranch()} with ${mainBranch}.`);
|
|
48
|
-
|
|
49
|
-
if (execCommand(`git push origin ${getCurrentBranch()} -o ci.skip`, null, null)) {
|
|
50
|
-
logSuccess('📤', `Push branch ${getCurrentBranch()}.`);
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
43
|
+
logSuccess('📤', 'Release-ветка %s отправлена.', currentBranch);
|
|
44
|
+
const mrUrl = getMergeRequestUrl(currentBranch, getMainBranch());
|
|
45
|
+
if (mrUrl) {
|
|
46
|
+
logSuccess('🌐', 'Создать Merge Request: %s', mrUrl);
|
|
53
47
|
}
|
|
54
|
-
|
|
55
|
-
return false;
|
|
48
|
+
return goToMainBranch();
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
function
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{ name: 'Add unreleased block', fn: changelogAddUnreleasedBlock },
|
|
64
|
-
{ name: 'Commit changelog', fn: changelogCommit },
|
|
65
|
-
{ name: 'Push release', fn: releasePush }
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
for (const step of steps) {
|
|
69
|
-
if (!step.fn()) {
|
|
70
|
-
logError('✖', `Failed at step: ${step.name}`);
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
51
|
+
function releaseCheckChangelogLint() {
|
|
52
|
+
const result = requireChangelogFormatted();
|
|
53
|
+
if (!result.ok) {
|
|
54
|
+
logError('❌', result.reason || 'Файл CHANGELOG.md не прошел линтинг.');
|
|
55
|
+
return false;
|
|
73
56
|
}
|
|
74
|
-
|
|
75
|
-
logSuccess('🚀', 'Release started successfully!');
|
|
76
57
|
return true;
|
|
77
58
|
}
|
|
78
59
|
|
|
60
|
+
function releaseClose() {
|
|
61
|
+
return runCommand({
|
|
62
|
+
name: 'release close',
|
|
63
|
+
checks: [
|
|
64
|
+
{ name: 'git-repo', run: requireGitRepo },
|
|
65
|
+
{ name: 'clean-working-tree', run: requireCleanWorkingTree },
|
|
66
|
+
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
67
|
+
{ name: 'on-release-branch', run: requireOnReleaseBranch },
|
|
68
|
+
{ name: 'main-and-dev-branches', run: requireMainAndDevBranches }
|
|
69
|
+
],
|
|
70
|
+
steps: [
|
|
71
|
+
{ name: 'switch-main', run: () => goToMainBranch() },
|
|
72
|
+
{ name: 'update-main', run: () => updateCurrentBranch() },
|
|
73
|
+
{ name: 'switch-dev', run: () => goToDevBranch() },
|
|
74
|
+
{ name: 'update-dev', run: () => updateCurrentBranch() },
|
|
75
|
+
{
|
|
76
|
+
name: 'merge-main-into-dev',
|
|
77
|
+
run: () => {
|
|
78
|
+
const mainBranch = getMainBranch();
|
|
79
|
+
if (!execCommand(`git merge ${mainBranch}`)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
logSuccess('🔀', 'Ветка %s смержена с %s.', getCurrentBranch(), mainBranch);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'push-dev',
|
|
88
|
+
run: () => {
|
|
89
|
+
const currentBranch = getCurrentBranch();
|
|
90
|
+
if (!execCommand(`git push origin ${currentBranch} -o ci.skip`)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
logSuccess('📤', 'Ветка %s отправлена.', currentBranch);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function releaseStart() {
|
|
102
|
+
return runCommand({
|
|
103
|
+
name: 'release start',
|
|
104
|
+
checks: [
|
|
105
|
+
{ name: 'git-repo', run: requireGitRepo },
|
|
106
|
+
{ name: 'clean-working-tree', run: requireCleanWorkingTree },
|
|
107
|
+
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
108
|
+
{ name: 'main-and-dev-branches', run: requireMainAndDevBranches },
|
|
109
|
+
{ name: 'package-version', run: requirePackageVersion },
|
|
110
|
+
{ name: 'changelog-exists', run: () => requireFileExists('CHANGELOG.md') },
|
|
111
|
+
{ name: 'unreleased-template-exists', run: () => requireFileExists(path.join(__dirname, 'UNRELEASED.md')) },
|
|
112
|
+
{ name: 'changelog-prettier-check', run: requireChangelogFormatted },
|
|
113
|
+
{ name: 'release-branch-missing', run: (ctx) => requireReleaseBranchMissing(`release/${ctx.version}`) }
|
|
114
|
+
],
|
|
115
|
+
steps: [
|
|
116
|
+
{ name: 'change-header', run: changelogChangeHeader },
|
|
117
|
+
{ name: 'remove-empty-chapters', run: changelogRemoveEmptyChapters },
|
|
118
|
+
{ name: 'add-unreleased-block', run: changelogAddUnreleasedBlock },
|
|
119
|
+
{ name: 'lint-changelog', run: releaseCheckChangelogLint },
|
|
120
|
+
{ name: 'create-release-branch', run: releaseCreate },
|
|
121
|
+
{ name: 'commit-changelog', run: changelogCommit },
|
|
122
|
+
{ name: 'push-release', run: releasePush }
|
|
123
|
+
]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
79
126
|
|
|
80
127
|
module.exports = {
|
|
81
128
|
releaseCreate,
|
package/src/utils.js
CHANGED
|
@@ -14,12 +14,29 @@ function log(emoji, message, color = colors.green) {
|
|
|
14
14
|
console.log(` ${emoji} ${message}`);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function
|
|
18
|
-
|
|
17
|
+
function formatLogMessage(template, args, color) {
|
|
18
|
+
if (!args || args.length === 0) {
|
|
19
|
+
return template;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let argIndex = 0;
|
|
23
|
+
return template.replace(/%s/g, () => {
|
|
24
|
+
if (argIndex >= args.length) {
|
|
25
|
+
return '%s';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const value = args[argIndex];
|
|
29
|
+
argIndex += 1;
|
|
30
|
+
return `${color}${String(value)}${colors.reset}`;
|
|
31
|
+
});
|
|
19
32
|
}
|
|
20
33
|
|
|
21
|
-
function
|
|
22
|
-
log(emoji,
|
|
34
|
+
function logSuccess(emoji, message, ...args) {
|
|
35
|
+
log(emoji, formatLogMessage(message, args, colors.green));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function logError(emoji, message, ...args) {
|
|
39
|
+
log(emoji, formatLogMessage(message, args, colors.red));
|
|
23
40
|
}
|
|
24
41
|
|
|
25
42
|
function execSilent(command) {
|
|
@@ -30,14 +47,11 @@ function execSilent(command) {
|
|
|
30
47
|
}
|
|
31
48
|
}
|
|
32
49
|
|
|
33
|
-
function execCommand(command
|
|
50
|
+
function execCommand(command) {
|
|
34
51
|
try {
|
|
35
52
|
execSync(command, { stdio: 'pipe' });
|
|
36
53
|
return true;
|
|
37
54
|
} catch (error) {
|
|
38
|
-
if (errorMessage) {
|
|
39
|
-
console.error(errorMessage);
|
|
40
|
-
}
|
|
41
55
|
return false;
|
|
42
56
|
}
|
|
43
57
|
}
|
|
@@ -78,6 +92,28 @@ function getVersion() {
|
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
|
|
95
|
+
function getRemoteUrl() {
|
|
96
|
+
const raw = execSilent('git remote get-url origin');
|
|
97
|
+
if (!raw) return null;
|
|
98
|
+
|
|
99
|
+
const sshMatch = raw.match(/^git@([^:]+):(.+?)(?:\.git)?$/);
|
|
100
|
+
if (sshMatch) return `https://${sshMatch[1]}/${sshMatch[2]}`;
|
|
101
|
+
|
|
102
|
+
const httpsMatch = raw.match(/^https?:\/\/([^/]+)\/(.+?)(?:\.git)?$/);
|
|
103
|
+
if (httpsMatch) return `https://${httpsMatch[1]}/${httpsMatch[2]}`;
|
|
104
|
+
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getMergeRequestUrl(sourceBranch, targetBranch) {
|
|
109
|
+
const baseUrl = getRemoteUrl();
|
|
110
|
+
if (!baseUrl) return null;
|
|
111
|
+
|
|
112
|
+
const source = encodeURIComponent(sourceBranch);
|
|
113
|
+
const target = encodeURIComponent(targetBranch);
|
|
114
|
+
return `${baseUrl}/-/merge_requests/new?merge_request[source_branch]=${source}&merge_request[target_branch]=${target}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
81
117
|
module.exports = {
|
|
82
118
|
colors,
|
|
83
119
|
log,
|
|
@@ -89,5 +125,7 @@ module.exports = {
|
|
|
89
125
|
getMainBranch,
|
|
90
126
|
getDevelopBranch,
|
|
91
127
|
getPackageManager,
|
|
92
|
-
getVersion
|
|
128
|
+
getVersion,
|
|
129
|
+
getRemoteUrl,
|
|
130
|
+
getMergeRequestUrl
|
|
93
131
|
};
|
package/src/version.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const {
|
|
3
|
+
const { logSuccess, logError } = require('./utils');
|
|
4
|
+
const { runCommand } = require('./command-executor');
|
|
5
|
+
const { requireGitRepo, requireCleanWorkingTree, requireFileExists, requirePackageVersion } = require('./preflight');
|
|
4
6
|
|
|
5
7
|
function upVersion(oldVersion, upType) {
|
|
6
8
|
const [major, minor, patch] = oldVersion.split('.').map(Number);
|
|
@@ -16,35 +18,39 @@ function upVersion(oldVersion, upType) {
|
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
function
|
|
20
|
-
try {
|
|
21
|
-
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
22
|
-
return packageJson.version;
|
|
23
|
-
} catch (error) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function setVersionTypescript(versionType) {
|
|
21
|
+
function updateVersionFile(versionType) {
|
|
29
22
|
try {
|
|
30
23
|
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
31
24
|
const oldVersion = packageJson.version;
|
|
32
25
|
const newVersion = upVersion(oldVersion, versionType);
|
|
33
|
-
|
|
26
|
+
|
|
34
27
|
packageJson.version = newVersion;
|
|
35
28
|
fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
|
|
36
|
-
|
|
37
|
-
logSuccess('🔖', `Current version ${oldVersion} up to ${colors.green}${newVersion}${colors.reset}.`);
|
|
29
|
+
logSuccess('🔖', 'Версия обновлена с %s до %s.', oldVersion, newVersion);
|
|
38
30
|
return true;
|
|
39
31
|
} catch (error) {
|
|
40
|
-
logError('❌', '
|
|
32
|
+
logError('❌', 'Ошибка при обновлении версии в package.json');
|
|
41
33
|
return false;
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
36
|
|
|
37
|
+
function setVersion(versionType) {
|
|
38
|
+
return runCommand({
|
|
39
|
+
name: `version up ${versionType}`,
|
|
40
|
+
checks: [
|
|
41
|
+
{ name: 'git-repo', run: requireGitRepo },
|
|
42
|
+
{ name: 'package-json-exists', run: () => requireFileExists('package.json') },
|
|
43
|
+
{ name: 'package-version', run: requirePackageVersion },
|
|
44
|
+
{ name: 'clean-working-tree', run: requireCleanWorkingTree }
|
|
45
|
+
],
|
|
46
|
+
steps: [
|
|
47
|
+
{ name: 'update-package-version', run: () => updateVersionFile(versionType) }
|
|
48
|
+
]
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
|
|
46
53
|
module.exports = {
|
|
47
54
|
upVersion,
|
|
48
|
-
|
|
49
|
-
setVersionTypescript
|
|
55
|
+
setVersion
|
|
50
56
|
};
|