@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.
@@ -1,102 +1,124 @@
1
- #!/usr/bin/env node
2
- import { execSync, spawnSync } from 'child_process'
3
- import { platform } from 'os'
4
- import { dirname, join } from 'path'
5
- import { fileURLToPath } from 'url'
6
-
7
- const __dirname = dirname(fileURLToPath(import.meta.url))
8
- const isWin = platform() === 'win32'
9
- const npxCmd = isWin ? 'npx.cmd' : 'npx'
10
-
11
- console.log('🚀 Запуск релиза...\n')
12
-
13
- // 1. Проверка демо
14
- const checkDemo = spawnSync('node', [join(__dirname, 'check-demo-for-release.js')], { stdio: 'inherit' })
15
- if (checkDemo.status !== 0) {
16
- console.error('❌ Проверка демо не прошла')
17
- process.exit(1)
18
- }
19
-
20
- // 2. Получаем текущую версию
21
- const currentVersion = JSON.parse(execSync('npm pkg get version', { encoding: 'utf8' })).replace(/"/g, '')
22
- console.log(`📦 Текущая версия: ${currentVersion}`)
23
-
24
- // 3. Предупреждение для 0.x.x
25
- if (currentVersion.startsWith('0.')) {
26
- console.log('\n⚠️ ВНИМАНИЕ: Версия 0.x.x имеет особое поведение!')
27
- console.log('💡 changelogen для версий < 1.0.0:')
28
- console.log('feat: коммиты → patch bump (0.1.0 → 0.1.1)')
29
- console.log(' • fix: коммиты → patch bump (0.1.0 → 0.1.1)')
30
- console.log(' • BREAKING CHANGE → minor bump (0.1.0 → 0.2.0)')
31
- console.log('')
32
- console.log('📖 Подробнее: https://github.com/conventional-changelog/standard-version/issues/539')
33
- console.log('✅ Рекомендация: используй версии ≥ 1.0.0 для правильного semver')
34
- console.log(' Обнови: npm pkg set version=1.0.0 && git tag v1.0.0\n')
35
- }
36
-
37
- // 4. Анализ коммитов
38
- let lastTag = ''
39
- try {
40
- lastTag = execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim()
41
- console.log(`📌 Последний тег: ${lastTag}`)
42
- } catch (error) {
43
- console.log('📌 Теги не найдены (первый релиз)')
44
- }
45
-
46
- const commitMessages = lastTag
47
- ? execSync(`git log ${lastTag}..HEAD --format=%s`, { encoding: 'utf8' })
48
- : execSync('git log --format=%s -10', { encoding: 'utf8' })
49
-
50
- const commits = commitMessages.split('\n').filter(Boolean)
51
-
52
- const featCount = commits.filter((c) => c.startsWith('feat:')).length
53
- const fixCount = commits.filter((c) => c.startsWith('fix:')).length
54
- const refactorCount = commits.filter((c) => c.startsWith('refactor:')).length
55
- const perfCount = commits.filter((c) => c.startsWith('perf:')).length
56
- const docsCount = commits.filter((c) => c.startsWith('docs:')).length
57
- const buildCount = commits.filter((c) => c.startsWith('build:')).length
58
-
59
- console.log('\n📊 Анализ коммитов:')
60
- if (featCount > 0) console.log(` ✨ feat: ${featCount}`)
61
- if (fixCount > 0) console.log(` 🐛 fix: ${fixCount}`)
62
- if (refactorCount > 0) console.log(` ♻️ refactor: ${refactorCount}`)
63
- if (perfCount > 0) console.log(` ⚡ perf: ${perfCount}`)
64
- if (docsCount > 0) console.log(` 📚 docs: ${docsCount}`)
65
- if (buildCount > 0) console.log(` 🏗️ build: ${buildCount}`)
66
-
67
- // 5. Определяем bump type
68
- const isV0 = currentVersion.startsWith('0.')
69
- let bumpType = 'patch'
70
-
71
- if (isV0) {
72
- // Для 0.x.x используем --major чтобы получить minor bump при feat
73
- bumpType = featCount > 0 ? 'major' : 'patch'
74
- console.log(`\n🔢 Bump type: ${bumpType === 'major' ? 'major (для 0.x.x это даст minor)' : 'patch'}`)
75
- } else {
76
- // Для ≥1.0.0 нормальное поведение
77
- bumpType = featCount > 0 ? 'minor' : 'patch'
78
- console.log(`\n🔢 Bump type: ${bumpType}`)
79
- }
80
-
81
- // 6. Создаём changelog
82
- console.log('\n📝 Создание changelog...')
83
- const changelog = spawnSync(npxCmd, ['changelogen', '--release', `--${bumpType}`], {
84
- stdio: 'inherit',
85
- })
86
-
87
- if (changelog.status !== 0) {
88
- console.error('\n❌ Ошибка создания changelog')
89
- console.log('💡 Попробуй вручную: npx changelogen --release')
90
- process.exit(1)
91
- }
92
-
93
- // 7. Обновляем README
94
- console.log('\n📝 Обновление README...')
95
- const updateReadme = spawnSync('node', [join(__dirname, 'update-readme.js')], { stdio: 'inherit' })
96
- if (updateReadme.status !== 0) {
97
- console.log('⚠️ README не обновлён (возможно нет секции AUTOGENERATED)')
98
- }
99
-
100
- console.log('\n Релиз успешно создан!')
101
- console.log('💡 Теперь можно:')
102
- console.log(' npm run _ push-release # Создать PR и смерджить в main')
1
+ #!/usr/bin/env node
2
+ import { execSync, spawnSync } from 'child_process'
3
+ import { existsSync, readFileSync } from 'fs'
4
+ import { platform } from 'os'
5
+ import { dirname, join } from 'path'
6
+ import { fileURLToPath } from 'url'
7
+ import { loadConfig } from './config.js'
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url))
10
+ const isWin = platform() === 'win32'
11
+ const npxCmd = isWin ? 'npx.cmd' : 'npx'
12
+ const config = await loadConfig()
13
+
14
+ console.log('🚀 Запуск релиза...\n')
15
+
16
+ // 1. Current version
17
+ const currentVersion = JSON.parse(readFileSync('package.json', 'utf8')).version
18
+ if (!currentVersion) {
19
+ console.log('❌ Нет поля version в package.json. Добавь: "version": "X.Y.Z"')
20
+ process.exit(1)
21
+ }
22
+ console.log(`📦 Текущая версия: ${currentVersion}`)
23
+
24
+ // 2. Demo check (configurable)
25
+ if (config.release.requireDemo) {
26
+ const [major, minor, patch] = currentVersion.split('.').map(Number)
27
+ const recentCommits = execSync('git log --oneline -10', { encoding: 'utf8' })
28
+ const nextVersion = recentCommits.includes('feat:')
29
+ ? `v${major}.${minor + 1}.0`
30
+ : `v${major}.${minor}.${patch + 1}`
31
+
32
+ console.log(`📦 Предполагаемая следующая версия: ${nextVersion}`)
33
+
34
+ const { demoDir, demoFormats } = config.release
35
+ const hasDemo = demoFormats.some((fmt) => existsSync(`${demoDir}/${nextVersion}.${fmt}`))
36
+
37
+ if (!hasDemo) {
38
+ console.log(`❌ Релиз ${nextVersion} требует демо!`)
39
+ console.log(`📸 Создай: ${demoDir}/${nextVersion}.${demoFormats[0]}`)
40
+ process.exit(1)
41
+ }
42
+
43
+ console.log(`✅ Демо для ${nextVersion} готово!`)
44
+ } else {
45
+ console.log('⏭️ Проверка демо отключена (release.requireDemo = false)')
46
+ }
47
+
48
+ // 3. Warning for 0.x.x
49
+ if (currentVersion.startsWith('0.')) {
50
+ console.log('\n⚠️ ВНИМАНИЕ: Версия 0.x.x имеет особое поведение!')
51
+ console.log('💡 changelogen для версий < 1.0.0:')
52
+ console.log(' • feat: коммиты → patch bump (0.1.0 0.1.1)')
53
+ console.log(' • fix: коммиты → patch bump (0.1.0 0.1.1)')
54
+ console.log(' • BREAKING CHANGE → minor bump (0.1.0 0.2.0)')
55
+ console.log('')
56
+ console.log('✅ Рекомендация: используй версии ≥ 1.0.0 для правильного semver')
57
+ console.log(' Обнови: npm pkg set version=1.0.0 && git tag v1.0.0\n')
58
+ }
59
+
60
+ // 4. Commit analysis
61
+ let lastTag = ''
62
+ try {
63
+ lastTag = execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim()
64
+ console.log(`📌 Последний тег: ${lastTag}`)
65
+ } catch {
66
+ console.log('📌 Теги не найдены (первый релиз)')
67
+ }
68
+
69
+ const commitLog = lastTag
70
+ ? execSync(`git log ${lastTag}..HEAD --format=%s`, { encoding: 'utf8' })
71
+ : execSync('git log --format=%s -10', { encoding: 'utf8' })
72
+
73
+ const commits = commitLog.split('\n').filter(Boolean)
74
+ const countByType = (prefix) => commits.filter((c) => c.startsWith(`${prefix}:`)).length
75
+
76
+ const featCount = countByType('feat')
77
+ const fixCount = countByType('fix')
78
+ const refactorCount = countByType('refactor')
79
+ const perfCount = countByType('perf')
80
+ const docsCount = countByType('docs')
81
+ const buildCount = countByType('build')
82
+
83
+ console.log('\n📊 Анализ коммитов:')
84
+ if (featCount) console.log(` ✨ feat: ${featCount}`)
85
+ if (fixCount) console.log(` 🐛 fix: ${fixCount}`)
86
+ if (refactorCount) console.log(` ♻️ refactor: ${refactorCount}`)
87
+ if (perfCount) console.log(` ⚡ perf: ${perfCount}`)
88
+ if (docsCount) console.log(` 📚 docs: ${docsCount}`)
89
+ if (buildCount) console.log(` 🏗️ build: ${buildCount}`)
90
+
91
+ // 5. Determine bump type
92
+ const isV0 = currentVersion.startsWith('0.')
93
+ let bumpType
94
+
95
+ if (isV0) {
96
+ bumpType = featCount > 0 ? 'major' : 'patch'
97
+ console.log(`\n🔢 Bump: ${bumpType === 'major' ? 'major (для 0.x.x это даст minor)' : 'patch'}`)
98
+ } else {
99
+ bumpType = featCount > 0 ? 'minor' : 'patch'
100
+ console.log(`\n🔢 Bump: ${bumpType}`)
101
+ }
102
+
103
+ // 6. Changelog
104
+ console.log('\n📝 Создание changelog...')
105
+ const changelog = spawnSync(npxCmd, ['changelogen', '--release', `--${bumpType}`], {
106
+ stdio: 'inherit',
107
+ })
108
+
109
+ if (changelog.status !== 0) {
110
+ console.error('\n❌ Ошибка создания changelog')
111
+ console.log('💡 Попробуй вручную: npx changelogen --release')
112
+ process.exit(1)
113
+ }
114
+
115
+ // 7. Update README
116
+ console.log('\n📝 Обновление README...')
117
+ const updateReadme = spawnSync('node', [join(__dirname, 'update-readme.js')], { stdio: 'inherit' })
118
+ if (updateReadme.status !== 0) {
119
+ console.log('⚠️ README не обновлён (возможно нет секции AUTOGENERATED)')
120
+ }
121
+
122
+ console.log('\n✅ Релиз успешно создан!')
123
+ console.log('💡 Теперь можно:')
124
+ console.log(' npm run _ push-release # Создать PR и смерджить в main')
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'child_process'
3
+ import { platform } from 'os'
4
+
5
+ const jstRepo = 'vv0rkz/js-template'
6
+ const isWin = platform() === 'win32'
7
+ const ghCmd = isWin ? 'gh.exe' : 'gh'
8
+
9
+ const title = process.argv.slice(2).join(' ')
10
+
11
+ console.log(`📝 Создаю issue в ${jstRepo}...`)
12
+
13
+ if (!title) {
14
+ const result = spawnSync(ghCmd, ['issue', 'create', '--repo', jstRepo], { stdio: 'inherit' })
15
+ process.exit(result.status || 0)
16
+ }
17
+
18
+ const result = spawnSync(ghCmd, ['issue', 'create', '--repo', jstRepo, '--title', title], {
19
+ stdio: 'inherit',
20
+ })
21
+
22
+ if (result.status === 0) {
23
+ console.log('\n✅ Issue создан!')
24
+ } else {
25
+ console.error('❌ Ошибка создания issue')
26
+ console.log('\n💡 Убедись что:')
27
+ console.log(' 1. Установлен GitHub CLI: gh --version')
28
+ console.log(' 2. Выполнена авторизация: gh auth login')
29
+ process.exit(1)
30
+ }
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs'
3
+ import { join } from 'path'
4
+ import { loadConfig } from './config.js'
5
+
6
+ const config = await loadConfig()
7
+ const { depUpdater } = config
8
+
9
+ if (!depUpdater) {
10
+ console.log('⏭️ Автообновление зависимостей отключено (depUpdater = false)')
11
+ process.exit(0)
12
+ }
13
+
14
+ if (depUpdater === 'dependabot') {
15
+ const dir = join(process.cwd(), '.github')
16
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
17
+
18
+ const filePath = join(dir, 'dependabot.yml')
19
+ if (existsSync(filePath)) {
20
+ console.log('⏭️ .github/dependabot.yml уже существует')
21
+ process.exit(0)
22
+ }
23
+
24
+ writeFileSync(
25
+ filePath,
26
+ `version: 2
27
+ updates:
28
+ - package-ecosystem: "npm"
29
+ directory: "/"
30
+ schedule:
31
+ interval: "weekly"
32
+ open-pull-requests-limit: 10
33
+ `,
34
+ )
35
+ console.log('✅ Создан .github/dependabot.yml')
36
+ } else if (depUpdater === 'renovate') {
37
+ const filePath = join(process.cwd(), 'renovate.json')
38
+ if (existsSync(filePath)) {
39
+ console.log('⏭️ renovate.json уже существует')
40
+ process.exit(0)
41
+ }
42
+
43
+ writeFileSync(
44
+ filePath,
45
+ JSON.stringify(
46
+ {
47
+ $schema: 'https://docs.renovatebot.com/renovate-schema.json',
48
+ extends: ['config:base'],
49
+ },
50
+ null,
51
+ 2,
52
+ ) + '\n',
53
+ )
54
+ console.log('✅ Создан renovate.json')
55
+ } else {
56
+ console.log(`⚠️ Неизвестный depUpdater: "${depUpdater}"`)
57
+ console.log('💡 Допустимые значения: "dependabot", "renovate", false')
58
+ process.exit(1)
59
+ }
@@ -1,58 +1,53 @@
1
- #!/usr/bin/env node
2
- import { execSync } from 'child_process'
3
-
4
- console.log('🏷️ Настройка GitHub labels...\n')
5
-
6
- const labels = [
7
- { name: 'task', color: '0E8A16', description: 'Новая фича' },
8
- { name: 'bug', color: 'D73A4A', description: 'Баг' },
9
- { name: 'refactor', color: 'FEF2C0', description: 'Рефакторинг/техдолг' },
10
- { name: 'perf', color: '007bff', description: 'Оптимизация производительности' },
11
- ]
12
-
13
- try {
14
- // Проверяем что gh установлен и авторизован
15
- execSync('gh auth status', { stdio: 'ignore' })
16
- } catch (error) {
17
- console.error('❌ GitHub CLI не установлен или не авторизован')
18
- console.log('💡 Установи: https://cli.github.com/')
19
- console.log(' И выполни: gh auth login')
20
- process.exit(1)
21
- }
22
-
23
- // Получаем существующие labels
24
- let existingLabels = []
25
- try {
26
- const output = execSync('gh label list --json name', { encoding: 'utf8' })
27
- existingLabels = JSON.parse(output).map((l) => l.name)
28
- } catch (error) {
29
- console.log('⚠️ Не удалось получить список labels (возможно репозиторий пустой)')
30
- }
31
-
32
- // Создаём недостающие labels
33
- let created = 0
34
- let skipped = 0
35
-
36
- for (const label of labels) {
37
- if (existingLabels.includes(label.name)) {
38
- console.log(` ⏭️ ${label.name} (уже существует)`)
39
- skipped++
40
- continue
41
- }
42
-
43
- try {
44
- execSync(`gh label create "${label.name}" --color "${label.color}" --description "${label.description}"`, {
45
- stdio: 'ignore',
46
- })
47
- console.log(` ✅ ${label.name}`)
48
- created++
49
- } catch (error) {
50
- console.log(` ⚠️ ${label.name} (ошибка создания)`)
51
- }
52
- }
53
-
54
- console.log(`\n📊 Итого: создано ${created}, пропущено ${skipped}`)
55
-
56
- if (created > 0) {
57
- console.log('✅ Labels настроены!')
58
- }
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process'
3
+ import { loadConfig } from './config.js'
4
+
5
+ const config = await loadConfig()
6
+
7
+ console.log('🏷️ Настройка GitHub labels...\n')
8
+
9
+ const labels = config.labels
10
+
11
+ try {
12
+ execSync('gh auth status', { stdio: 'ignore' })
13
+ } catch (error) {
14
+ console.error('❌ GitHub CLI не установлен или не авторизован')
15
+ console.log('💡 Установи: https://cli.github.com/')
16
+ console.log(' И выполни: gh auth login')
17
+ process.exit(1)
18
+ }
19
+
20
+ let existingLabels = []
21
+ try {
22
+ const output = execSync('gh label list --json name', { encoding: 'utf8' })
23
+ existingLabels = JSON.parse(output).map((l) => l.name)
24
+ } catch (error) {
25
+ console.log('⚠️ Не удалось получить список labels (возможно репозиторий пустой)')
26
+ }
27
+
28
+ let created = 0
29
+ let skipped = 0
30
+
31
+ for (const label of labels) {
32
+ if (existingLabels.includes(label.name)) {
33
+ console.log(` ⏭️ ${label.name} (уже существует)`)
34
+ skipped++
35
+ continue
36
+ }
37
+
38
+ try {
39
+ execSync(`gh label create "${label.name}" --color "${label.color}" --description "${label.description}"`, {
40
+ stdio: 'ignore',
41
+ })
42
+ console.log(` ✅ ${label.name}`)
43
+ created++
44
+ } catch (error) {
45
+ console.log(` ⚠️ ${label.name} (ошибка создания)`)
46
+ }
47
+ }
48
+
49
+ console.log(`\n📊 Итого: создано ${created}, пропущено ${skipped}`)
50
+
51
+ if (created > 0) {
52
+ console.log('✅ Labels настроены!')
53
+ }
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'child_process'
2
+ import { execSync, spawnSync } from 'child_process'
3
3
  import { existsSync, readFileSync, writeFileSync } from 'fs'
4
+ import { loadConfig } from './config.js'
5
+
6
+ const config = await loadConfig()
7
+ const demoDir = config.release.demoDir
4
8
 
5
9
  console.log('🎨 Обновляю README релизами с демо...')
6
10
 
@@ -30,21 +34,49 @@ try {
30
34
  console.log('⚠️ Не удалось определить URL репозитория')
31
35
  }
32
36
 
37
+ /**
38
+ * Пытается извлечь первый кадр GIF в PNG через ffmpeg.
39
+ * Возвращает true если PNG был создан, false если ffmpeg недоступен или ошибка.
40
+ */
41
+ function tryGeneratePng(gifPath, pngPath) {
42
+ const result = spawnSync('ffmpeg', ['-i', gifPath, '-frames:v', '1', pngPath, '-y'], {
43
+ encoding: 'utf8',
44
+ })
45
+ if (result.status === 0 && existsSync(pngPath)) {
46
+ console.log(`🖼️ PNG превью создан: ${pngPath}`)
47
+ return true
48
+ }
49
+ return false
50
+ }
51
+
33
52
  // Парсим changelog - ТОЛЬКО версии с демо
34
53
  const versionBlocks = changelog.split('## v').slice(1)
35
54
  let prettyChangelog = '## 📋 История версий\n\n'
36
55
  const processedVersions = new Set()
37
56
 
38
57
  versionBlocks.forEach((versionBlock) => {
39
- const versionMatch = versionBlock.match(/^(\d+\.\d+\.\d+)/)
58
+ // Поддержка заголовков вида "v1.4.0...v2.0.0" (changelogen диапазон) и обычных "v2.0.0"
59
+ const rangeMatch = versionBlock.match(/^\d+\.\d+\.\d+\.\.\.v(\d+\.\d+\.\d+)/)
60
+ const simpleMatch = versionBlock.match(/^(\d+\.\d+\.\d+)/)
61
+ const versionMatch = rangeMatch || simpleMatch
40
62
  if (!versionMatch) return
41
63
 
42
64
  const version = `v${versionMatch[1]}`
43
65
  if (processedVersions.has(version)) return
44
66
  processedVersions.add(version)
45
67
 
46
- // ПРОВЕРЯЕМ ДЕМО - если нет демо, пропускаем
47
- const hasDemo = existsSync(`docs/${version}.gif`) || existsSync(`docs/${version}.png`)
68
+ const gifPath = `${demoDir}/${version}.gif`
69
+ const pngPath = `${demoDir}/${version}.png`
70
+
71
+ const hasGif = existsSync(gifPath)
72
+ let hasPng = existsSync(pngPath)
73
+
74
+ // Если есть GIF но нет PNG — пытаемся сгенерировать PNG через ffmpeg
75
+ if (hasGif && !hasPng) {
76
+ hasPng = tryGeneratePng(gifPath, pngPath)
77
+ }
78
+
79
+ const hasDemo = hasGif || hasPng
48
80
  if (!hasDemo) {
49
81
  console.log(`⏭️ Пропускаем ${version} - нет демо`)
50
82
  return
@@ -94,11 +126,16 @@ versionBlocks.forEach((versionBlock) => {
94
126
  // Форматируем версию
95
127
  prettyChangelog += `### 🟢 ${version}\n\n`
96
128
 
97
- // Добавляем демо (гарантированно есть)
98
- if (existsSync(`docs/${version}.gif`)) {
99
- prettyChangelog += `**Демо работы** \n<img src="docs/${version}.gif" width="400" />\n\n`
129
+ // Добавляем демо
130
+ // Если есть GIF + PNG → кликабельный превью (быстрая загрузка + анимация по клику)
131
+ // Если только PNG обычный img
132
+ // Если только GIF → обычный img с GIF
133
+ if (hasGif && hasPng) {
134
+ prettyChangelog += `**Демо работы** \n<a href="${gifPath}" title="Нажми чтобы увидеть анимацию"><img src="${pngPath}" width="400" /></a>\n\n`
135
+ } else if (hasPng) {
136
+ prettyChangelog += `**Демо работы** \n<img src="${pngPath}" width="400" />\n\n`
100
137
  } else {
101
- prettyChangelog += `**Демо работы** \n<img src="docs/${version}.png" width="400" />\n\n`
138
+ prettyChangelog += `**Демо работы** \n<img src="${gifPath}" width="400" />\n\n`
102
139
  }
103
140
 
104
141
  // Добавляем функционал
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'
3
+ import { spawnSync } from 'child_process'
4
+ import { dirname, join } from 'path'
5
+ import { fileURLToPath } from 'url'
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url))
8
+ const templateDir = join(__dirname, '../templates')
9
+ const targetDir = process.cwd()
10
+
11
+ console.log('⬆️ JST Upgrade — обновление конфигов и хуков\n')
12
+
13
+ // 1. jst.config.js — только если нет (не перезаписываем пользовательские настройки)
14
+ const configSrc = join(templateDir, 'jst.config.js')
15
+ const configDest = join(targetDir, 'jst.config.js')
16
+
17
+ if (!existsSync(configDest)) {
18
+ copyFileSync(configSrc, configDest)
19
+ console.log('✅ jst.config.js создан (настрой под свой проект)')
20
+ } else {
21
+ console.log('⏭️ jst.config.js уже существует (не перезаписан)')
22
+ }
23
+
24
+ // 2. Husky hooks — всегда обновляем (это тонкие обёртки, кастомизация в jst.config.js)
25
+ console.log('\n🐶 Обновление husky хуков...')
26
+ const huskyDir = join(targetDir, '.husky')
27
+ const huskyTemplateDir = join(templateDir, '.husky')
28
+
29
+ if (!existsSync(huskyDir)) mkdirSync(huskyDir, { recursive: true })
30
+
31
+ if (existsSync(huskyTemplateDir)) {
32
+ const copyDir = (src, dest) => {
33
+ if (!existsSync(dest)) mkdirSync(dest, { recursive: true })
34
+ for (const entry of readdirSync(src)) {
35
+ const srcPath = join(src, entry)
36
+ const destPath = join(dest, entry)
37
+ if (statSync(srcPath).isDirectory()) {
38
+ copyDir(srcPath, destPath)
39
+ } else {
40
+ copyFileSync(srcPath, destPath)
41
+ }
42
+ }
43
+ }
44
+ copyDir(huskyTemplateDir, huskyDir)
45
+ console.log('✅ Хуки обновлены')
46
+ }
47
+
48
+ // 3. commitlint + changelog — обновляем (кастомизация теперь через jst.config.js)
49
+ const configs = ['commitlint.config.js', 'changelog.config.js']
50
+
51
+ for (const name of configs) {
52
+ const src = join(templateDir, name)
53
+ const dest = join(targetDir, name)
54
+ if (existsSync(src)) {
55
+ copyFileSync(src, dest)
56
+ console.log(`✅ ${name} обновлён`)
57
+ }
58
+ }
59
+
60
+ // 4. Применяем конфиг (labels + deps)
61
+ console.log('\n⚙️ Применение конфига...')
62
+
63
+ try {
64
+ spawnSync('node', [join(__dirname, 'setup-labels.js')], { stdio: 'inherit' })
65
+ } catch {
66
+ console.log('⏭️ Labels пропущены (нет gh)')
67
+ }
68
+
69
+ spawnSync('node', [join(__dirname, 'setup-deps.js')], { stdio: 'inherit' })
70
+
71
+ console.log('\n✅ Upgrade завершён!')
72
+ console.log('💡 Проверь jst.config.js и настрой под свой проект')
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process'
3
+ import { loadConfig, compileBranchRegex } from './config.js'
4
+
5
+ const config = await loadConfig()
6
+ const { main: mainBranch, patterns } = config.branch
7
+
8
+ const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim()
9
+
10
+ if (currentBranch === mainBranch) {
11
+ console.log(`✅ Разрешённая ветка: ${currentBranch}`)
12
+ process.exit(0)
13
+ }
14
+
15
+ const branchRegex = compileBranchRegex(config.branch)
16
+
17
+ if (!branchRegex.test(currentBranch)) {
18
+ console.log(`❌ Неправильный формат ветки: ${currentBranch}`)
19
+ console.log('')
20
+
21
+ if (patterns && patterns.length) {
22
+ console.log('✅ Допустимые форматы:')
23
+ for (const p of patterns) {
24
+ console.log(` ${p}`)
25
+ }
26
+ } else {
27
+ console.log(`✅ Паттерн: ${config.branch.pattern}`)
28
+ }
29
+
30
+ process.exit(1)
31
+ }
32
+
33
+ console.log(`✅ Формат ветки правильный: ${currentBranch}`)