@vv0rkz/js-template 1.7.0 → 1.8.2
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/README.md +224 -150
- package/bin/cli.js +227 -196
- package/bin/init.js +244 -236
- package/package.json +41 -41
- package/templates/.husky/commit-msg +3 -67
- package/templates/.husky/post-commit +3 -25
- package/templates/.husky/pre-push +3 -17
- package/templates/changelog.config.js +16 -9
- package/templates/commitlint.config.js +13 -6
- package/templates/jst.config.js +120 -0
- package/tools-gh/check-demo-for-release.js +36 -30
- package/tools-gh/config.js +125 -0
- package/tools-gh/post-commit-hook.js +40 -0
- package/tools-gh/release.js +124 -102
- package/tools-gh/report-issue.js +30 -0
- package/tools-gh/setup-deps.js +59 -0
- package/tools-gh/setup-labels.js +53 -58
- package/tools-gh/update-readme.js +45 -8
- package/tools-gh/upgrade.js +72 -0
- package/tools-gh/validate-branch.js +33 -0
- package/tools-gh/validate-commit.js +86 -0
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
5
|
-
REPO_URL=$(git remote get-url origin)
|
|
6
|
-
|
|
7
|
-
# Исправляем URL
|
|
8
|
-
if echo "$REPO_URL" | grep -q "git@github.com"; then
|
|
9
|
-
REPO_URL=$(echo "$REPO_URL" | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
|
|
10
|
-
else
|
|
11
|
-
REPO_URL=$(echo "$REPO_URL" | sed 's/\.git$//')
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
# Пуш коммита
|
|
15
|
-
git push origin HEAD
|
|
16
|
-
|
|
17
|
-
# Закрытие issues при feat: #номер И fix: #номер
|
|
18
|
-
if echo "$COMMIT_MSG" | grep -q "^\(feat\|fix\): #[0-9]"; then
|
|
19
|
-
ISSUE_NUMBER=$(echo "$COMMIT_MSG" | grep -o '#[0-9]*' | sed 's/#//')
|
|
20
|
-
echo "🔧 Закрываю issue #$ISSUE_NUMBER..."
|
|
21
|
-
|
|
22
|
-
COMMIT_LINK="$REPO_URL/commit/$COMMIT_HASH"
|
|
23
|
-
|
|
24
|
-
gh issue close "$ISSUE_NUMBER" --comment "✅ Завершено в коммите: [$COMMIT_HASH]($COMMIT_LINK) - $COMMIT_MSG"
|
|
25
|
-
fi
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
|
|
3
|
+
node ./node_modules/@vv0rkz/js-template/tools-gh/post-commit-hook.js
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if [ "$CURRENT_BRANCH" = "main" ]; then
|
|
5
|
-
echo "✅ Разрешённая ветка: $CURRENT_BRANCH"
|
|
6
|
-
exit 0
|
|
7
|
-
fi
|
|
8
|
-
|
|
9
|
-
# Простая проверка: начинается с v и есть дефис
|
|
10
|
-
if ! echo "$CURRENT_BRANCH" | grep -q "^v.*-.*"; then
|
|
11
|
-
echo "❌ Неправильный формат ветки: $CURRENT_BRANCH"
|
|
12
|
-
echo "✅ Должен быть: vX.Y.Z-description"
|
|
13
|
-
echo "📝 Пример: v2.3.0-normalize-operators"
|
|
14
|
-
exit 1
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
echo "✅ Формат ветки правильный: $CURRENT_BRANCH"
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
|
|
3
|
+
node ./node_modules/@vv0rkz/js-template/tools-gh/validate-branch.js
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
1
|
+
let changelogTypes = {
|
|
2
|
+
feat: { title: '✨ Новые фичи', semver: 'minor' },
|
|
3
|
+
fix: { title: '🐛 Исправления', semver: 'patch' },
|
|
4
|
+
refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
|
|
5
|
+
perf: { title: '⚡ Оптимизация', semver: 'patch' },
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const { default: config } = await import('./jst.config.js')
|
|
10
|
+
if (config.changelog?.types) changelogTypes = config.changelog.types
|
|
11
|
+
} catch {}
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
types: changelogTypes,
|
|
15
|
+
contributors: false,
|
|
16
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
let types = ['feat', 'fix', 'refactor', 'build', 'chore', 'docs', 'perf']
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
const { default: config } = await import('./jst.config.js')
|
|
5
|
+
if (config.commits?.types) types = config.commits.types
|
|
6
|
+
} catch {}
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
extends: ['@commitlint/config-conventional'],
|
|
10
|
+
rules: {
|
|
11
|
+
'type-enum': [2, 'always', types],
|
|
12
|
+
},
|
|
13
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JST Configuration
|
|
3
|
+
* Документация: https://github.com/vv0rkz/js-template
|
|
4
|
+
*
|
|
5
|
+
* Все значения ниже — дефолтные. Измени нужные или закомментируй.
|
|
6
|
+
*/
|
|
7
|
+
export default {
|
|
8
|
+
/**
|
|
9
|
+
* Правила именования веток
|
|
10
|
+
*/
|
|
11
|
+
branch: {
|
|
12
|
+
/** Имя главной ветки ('main' | 'master') */
|
|
13
|
+
main: 'main',
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Допустимые форматы веток (массив шаблонов)
|
|
17
|
+
*
|
|
18
|
+
* Плейсхолдеры:
|
|
19
|
+
* {version} → семвер (X.Y.Z)
|
|
20
|
+
* {issue} → номер issue (цифры)
|
|
21
|
+
* {name} → описание (буквы, цифры, дефисы, подчёркивания)
|
|
22
|
+
* * → что угодно
|
|
23
|
+
*
|
|
24
|
+
* Примеры наборов:
|
|
25
|
+
*
|
|
26
|
+
* ['v{version}-{name}']
|
|
27
|
+
* ✅ v2.3.0-normalize-operators
|
|
28
|
+
*
|
|
29
|
+
* ['release/v{version}', 'feature/#{issue}-{name}', 'fix/#{issue}-{name}']
|
|
30
|
+
* ✅ release/v2.3.0
|
|
31
|
+
* ✅ feature/#12-dark-theme
|
|
32
|
+
* ✅ fix/#7-validation-bug
|
|
33
|
+
*
|
|
34
|
+
* ['feature/*', 'fix/*', 'release/*']
|
|
35
|
+
* ✅ feature/anything-goes-here
|
|
36
|
+
* ✅ fix/urgent-patch
|
|
37
|
+
*
|
|
38
|
+
* ['*']
|
|
39
|
+
* ✅ любое имя (без ограничений)
|
|
40
|
+
*/
|
|
41
|
+
patterns: ['v{version}-{name}'],
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* GitHub Labels
|
|
46
|
+
* Используются при `jst setup-labels` и `jst create-*`
|
|
47
|
+
*
|
|
48
|
+
* Цвета — 6-символьный hex без #
|
|
49
|
+
*/
|
|
50
|
+
labels: [
|
|
51
|
+
{ name: 'task', color: '0E8A16', description: 'Новая фича', emoji: '✨' },
|
|
52
|
+
{ name: 'bug', color: 'D73A4A', description: 'Баг', emoji: '🐛' },
|
|
53
|
+
{ name: 'refactor', color: 'FEF2C0', description: 'Рефакторинг/техдолг', emoji: '♻️' },
|
|
54
|
+
{ name: 'perf', color: '007bff', description: 'Оптимизация производительности', emoji: '⚡' },
|
|
55
|
+
],
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Правила коммитов
|
|
59
|
+
*/
|
|
60
|
+
commits: {
|
|
61
|
+
/**
|
|
62
|
+
* Разрешённые типы коммитов
|
|
63
|
+
* Должны совпадать с commitlint и changelog
|
|
64
|
+
*/
|
|
65
|
+
types: ['feat', 'fix', 'refactor', 'build', 'chore', 'docs', 'perf'],
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Типы, требующие номер issue (#N)
|
|
69
|
+
* Остальные типы — свободный формат
|
|
70
|
+
*/
|
|
71
|
+
requireIssue: ['feat', 'fix'],
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Ключевое слово для закрытия issue в коммите
|
|
75
|
+
*
|
|
76
|
+
* С ним: feat: close #9 описание → закроет issue #9
|
|
77
|
+
* Без: feat: #9 описание → просто ссылка, issue остаётся открытым
|
|
78
|
+
*/
|
|
79
|
+
closeKeyword: 'close',
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Настройки релиза (`jst release`)
|
|
84
|
+
*/
|
|
85
|
+
release: {
|
|
86
|
+
/** Требовать GIF/PNG демо перед релизом (true | false) */
|
|
87
|
+
requireDemo: true,
|
|
88
|
+
|
|
89
|
+
/** Директория для демо-файлов */
|
|
90
|
+
demoDir: 'docs',
|
|
91
|
+
|
|
92
|
+
/** Допустимые форматы демо-файлов */
|
|
93
|
+
demoFormats: ['gif', 'png'],
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Настройки changelog (changelogen)
|
|
98
|
+
* title — заголовок секции в CHANGELOG.md
|
|
99
|
+
* semver — тип версионирования ('major' | 'minor' | 'patch')
|
|
100
|
+
*/
|
|
101
|
+
changelog: {
|
|
102
|
+
types: {
|
|
103
|
+
feat: { title: '✨ Новые фичи', semver: 'minor' },
|
|
104
|
+
fix: { title: '🐛 Исправления', semver: 'patch' },
|
|
105
|
+
refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
|
|
106
|
+
perf: { title: '⚡ Оптимизация', semver: 'patch' },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Автообновление зависимостей
|
|
112
|
+
* @type {'dependabot' | 'renovate' | false}
|
|
113
|
+
*
|
|
114
|
+
* 'dependabot' — создаст .github/dependabot.yml
|
|
115
|
+
* 'renovate' — создаст renovate.json
|
|
116
|
+
* false — отключено
|
|
117
|
+
*/
|
|
118
|
+
depUpdater: false,
|
|
119
|
+
|
|
120
|
+
}
|
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { execSync } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from 'fs'
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
import { loadConfig } from './config.js'
|
|
5
|
+
|
|
6
|
+
const config = await loadConfig()
|
|
7
|
+
|
|
8
|
+
if (!config.release.requireDemo) {
|
|
9
|
+
console.log('⏭️ Проверка демо отключена')
|
|
10
|
+
process.exit(0)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const packageJson = JSON.parse(readFileSync('package.json', 'utf8'))
|
|
14
|
+
const [major, minor, patch] = packageJson.version.split('.').map(Number)
|
|
15
|
+
|
|
16
|
+
const commitMessages = execSync('git log --oneline -10', { encoding: 'utf8' })
|
|
17
|
+
|
|
18
|
+
let nextVersion
|
|
19
|
+
if (commitMessages.includes('feat:')) {
|
|
20
|
+
nextVersion = `v${major}.${minor + 1}.0`
|
|
21
|
+
} else {
|
|
22
|
+
nextVersion = `v${major}.${minor}.${patch + 1}`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(`📦 Предполагаемая следующая версия: ${nextVersion}`)
|
|
26
|
+
|
|
27
|
+
const { demoDir, demoFormats } = config.release
|
|
28
|
+
const hasDemo = demoFormats.some((fmt) => existsSync(`${demoDir}/${nextVersion}.${fmt}`))
|
|
29
|
+
|
|
30
|
+
if (!hasDemo) {
|
|
31
|
+
console.log(`❌ Релиз ${nextVersion} требует демо!`)
|
|
32
|
+
console.log(`📸 Создай: ${demoDir}/${nextVersion}.${demoFormats[0]}`)
|
|
33
|
+
process.exit(1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(`✅ Демо для ${nextVersion} готово!`)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { pathToFileURL } from 'url'
|
|
4
|
+
|
|
5
|
+
const DEFAULTS = {
|
|
6
|
+
branch: {
|
|
7
|
+
main: 'main',
|
|
8
|
+
patterns: ['v{version}-{name}'],
|
|
9
|
+
},
|
|
10
|
+
labels: [
|
|
11
|
+
{ name: 'task', color: '0E8A16', description: 'Новая фича', emoji: '✨' },
|
|
12
|
+
{ name: 'bug', color: 'D73A4A', description: 'Баг', emoji: '🐛' },
|
|
13
|
+
{ name: 'refactor', color: 'FEF2C0', description: 'Рефакторинг/техдолг', emoji: '♻️' },
|
|
14
|
+
{ name: 'perf', color: '007bff', description: 'Оптимизация производительности', emoji: '⚡' },
|
|
15
|
+
],
|
|
16
|
+
commits: {
|
|
17
|
+
types: ['feat', 'fix', 'refactor', 'build', 'chore', 'docs', 'perf'],
|
|
18
|
+
requireIssue: ['feat', 'fix'],
|
|
19
|
+
closeKeyword: 'close',
|
|
20
|
+
},
|
|
21
|
+
release: {
|
|
22
|
+
requireDemo: true,
|
|
23
|
+
demoDir: 'docs',
|
|
24
|
+
demoFormats: ['gif', 'png'],
|
|
25
|
+
},
|
|
26
|
+
changelog: {
|
|
27
|
+
types: {
|
|
28
|
+
feat: { title: '✨ Новые фичи', semver: 'minor' },
|
|
29
|
+
fix: { title: '🐛 Исправления', semver: 'patch' },
|
|
30
|
+
refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
|
|
31
|
+
perf: { title: '⚡ Оптимизация', semver: 'patch' },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
depUpdater: false,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- Branch pattern template engine ---
|
|
38
|
+
|
|
39
|
+
const BRANCH_PLACEHOLDERS = {
|
|
40
|
+
'{version}': '\\d+\\.\\d+\\.\\d+',
|
|
41
|
+
'{issue}': '\\d+',
|
|
42
|
+
'{name}': '[a-zA-Z0-9_-]+',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function compileTemplate(template) {
|
|
46
|
+
const parts = template.split(/(\{[a-z]+\}|\*)/g)
|
|
47
|
+
return parts
|
|
48
|
+
.map((part) => {
|
|
49
|
+
if (BRANCH_PLACEHOLDERS[part]) return BRANCH_PLACEHOLDERS[part]
|
|
50
|
+
if (part === '*') return '.+'
|
|
51
|
+
return part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
52
|
+
})
|
|
53
|
+
.join('')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Compiles branch config into a RegExp.
|
|
58
|
+
* Supports template patterns (new) and raw regex (legacy).
|
|
59
|
+
*
|
|
60
|
+
* Templates:
|
|
61
|
+
* {version} → X.Y.Z (semver)
|
|
62
|
+
* {issue} → issue number (digits)
|
|
63
|
+
* {name} → branch description (letters, digits, hyphens, underscores)
|
|
64
|
+
* * → anything
|
|
65
|
+
*/
|
|
66
|
+
export function compileBranchRegex(branchConfig) {
|
|
67
|
+
if (branchConfig.patterns && Array.isArray(branchConfig.patterns)) {
|
|
68
|
+
const compiled = branchConfig.patterns.map(compileTemplate)
|
|
69
|
+
return new RegExp(`^(${compiled.join('|')})$`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (branchConfig.pattern) {
|
|
73
|
+
return new RegExp(branchConfig.pattern)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return new RegExp('^v.*-.*')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// --- Deep merge ---
|
|
80
|
+
|
|
81
|
+
function deepMerge(target, source) {
|
|
82
|
+
const result = { ...target }
|
|
83
|
+
for (const key of Object.keys(source)) {
|
|
84
|
+
if (
|
|
85
|
+
source[key] &&
|
|
86
|
+
typeof source[key] === 'object' &&
|
|
87
|
+
!Array.isArray(source[key]) &&
|
|
88
|
+
target[key] &&
|
|
89
|
+
typeof target[key] === 'object' &&
|
|
90
|
+
!Array.isArray(target[key])
|
|
91
|
+
) {
|
|
92
|
+
result[key] = deepMerge(target[key], source[key])
|
|
93
|
+
} else {
|
|
94
|
+
result[key] = source[key]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Loads config from jst.config.js (preferred) or jst.config.json (fallback)
|
|
102
|
+
* Deep-merges user config with defaults
|
|
103
|
+
*/
|
|
104
|
+
export async function loadConfig() {
|
|
105
|
+
const jsPath = join(process.cwd(), 'jst.config.js')
|
|
106
|
+
const jsonPath = join(process.cwd(), 'jst.config.json')
|
|
107
|
+
let userConfig = {}
|
|
108
|
+
|
|
109
|
+
if (existsSync(jsPath)) {
|
|
110
|
+
try {
|
|
111
|
+
const mod = await import(pathToFileURL(jsPath).href)
|
|
112
|
+
userConfig = mod.default || {}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.warn('⚠️ Ошибка чтения jst.config.js:', e.message)
|
|
115
|
+
}
|
|
116
|
+
} else if (existsSync(jsonPath)) {
|
|
117
|
+
try {
|
|
118
|
+
userConfig = JSON.parse(readFileSync(jsonPath, 'utf8'))
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.warn('⚠️ Ошибка чтения jst.config.json:', e.message)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return deepMerge(DEFAULTS, userConfig)
|
|
125
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import { loadConfig } from './config.js'
|
|
4
|
+
|
|
5
|
+
const config = await loadConfig()
|
|
6
|
+
const { closeKeyword, requireIssue } = config.commits
|
|
7
|
+
|
|
8
|
+
const commitMsg = execSync('git log -1 --pretty=%B', { encoding: 'utf8' }).trim()
|
|
9
|
+
const commitHash = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim()
|
|
10
|
+
|
|
11
|
+
let repoUrl = execSync('git remote get-url origin', { encoding: 'utf8' }).trim()
|
|
12
|
+
if (repoUrl.startsWith('git@github.com:')) {
|
|
13
|
+
repoUrl = repoUrl.replace('git@github.com:', 'https://github.com/').replace(/\.git$/, '')
|
|
14
|
+
} else {
|
|
15
|
+
repoUrl = repoUrl.replace(/\.git$/, '')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
execSync('git push origin HEAD', { stdio: 'inherit' })
|
|
19
|
+
|
|
20
|
+
const escClose = closeKeyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
21
|
+
const typesGroup = requireIssue.join('|')
|
|
22
|
+
const closePattern = new RegExp(`^(${typesGroup})(\\(.+\\))?: ${escClose} #(\\d+)`)
|
|
23
|
+
const match = commitMsg.match(closePattern)
|
|
24
|
+
|
|
25
|
+
if (match) {
|
|
26
|
+
const issueNumber = match[3]
|
|
27
|
+
const commitLink = `${repoUrl}/commit/${commitHash}`
|
|
28
|
+
const firstLine = commitMsg.split('\n')[0]
|
|
29
|
+
|
|
30
|
+
console.log(`🔧 Закрываю issue #${issueNumber}...`)
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
execSync(
|
|
34
|
+
`gh issue close "${issueNumber}" --comment "✅ Завершено в коммите: [${commitHash}](${commitLink}) - ${firstLine}"`,
|
|
35
|
+
{ stdio: 'inherit' },
|
|
36
|
+
)
|
|
37
|
+
} catch {
|
|
38
|
+
console.log(`⚠️ Не удалось закрыть issue #${issueNumber}`)
|
|
39
|
+
}
|
|
40
|
+
}
|