@itcase/config 1.6.63 → 1.6.65

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 CHANGED
@@ -17,7 +17,8 @@ npm install --save-dev @itcase/config
17
17
  - `createCommitlintReleaseConfig(SCOPES)` — для релизных веток
18
18
  - `commitlint/commitizenAdapter.js` — адаптер Commitizen (type → scope → subScope → subject)
19
19
 
20
- - **Husky** — husky-hooks
20
+ - **Husky** — общие git-хуки в `husky-hooks/`
21
+ - Логика хуков живёт в пакете; бинарь `itcase-config-husky-hooks` ставится при `npm i`. В репозитории остаются файлы `.husky/<hook>` и локальный `.husky/config`
21
22
 
22
23
  - **Lint-staged** — запуск линтеров для измененных файлов
23
24
  - `lint-staged/index.js` — базовая конфигурация
@@ -68,11 +69,7 @@ export default process.env.RELEASE_BRANCH === 'true'
68
69
  : createCommitlintProjectConfig(SCOPES)
69
70
  ```
70
71
 
71
- 3. Подключите проверку в husky (например, в `.husky/commit-msg`):
72
-
73
- ```bash
74
- npx commitlint --edit "$1"
75
- ```
72
+ 3. Git-хуки подключаются из `husky-hooks/` — см. раздел **Подключение Husky** ниже.
76
73
 
77
74
  4. **(Опционально)** Интерактивное оформление коммитов через Commitizen. Установите:
78
75
 
@@ -84,4 +81,34 @@ npm install --save-dev commitizen @commitlint/cz-commitlint
84
81
 
85
82
  ```json
86
83
  { "path": "@itcase/config/commitlint/commitizenAdapter.js" }
87
- ```
84
+ ```
85
+
86
+ ## Подключение Husky
87
+
88
+ В репозитории остаются файлы, на которые опираются git и husky. Логика лежит в `@itcase/config/husky-hooks`. При установке пакета npm кладёт бинарь `itcase-config-husky-hooks` в `node_modules/.bin` — husky уже добавляет этот путь в `PATH`.
89
+
90
+ При `npm i` пакет сам копирует `.husky/_run.sh` (git его не вызывает). Добавьте в `.gitignore`:
91
+
92
+ ```gitignore
93
+ .husky/_run.sh
94
+ ```
95
+
96
+ Репы без обёрток скрипт не трогает. `npm i --ignore-scripts` файл не создаст.
97
+
98
+ В каждом git-hook оставьте обёртку:
99
+
100
+ ```sh
101
+ #!/usr/bin/env sh
102
+ . "$(dirname "$0")/_run.sh"
103
+ ```
104
+
105
+ Нужны: `pre-commit`, `commit-msg`, `prepare-commit-msg`, `pre-push`, `post-merge`, `post-commit`. `set-release-message` тоже можно обернуть — его вызывает `post-merge`.
106
+
107
+ Локальный `.husky/config` не оборачивайте. Образец: `husky-hooks/config.example`.
108
+
109
+ ```bash
110
+ PROTECTED_BRANCHES="master"
111
+ RELEASE_BRANCHES="master"
112
+ ```
113
+
114
+ `prepare` в `package.json` как обычно: `"prepare": "husky"`.
@@ -0,0 +1,57 @@
1
+ # Shared helpers for @itcase/config husky-hooks.
2
+ # HUSKY_DIR — consumer .husky (set by the repo wrapper).
3
+
4
+ PACKAGE_HOOKS="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+
6
+ # Только локальный .husky/config потребителя, не example из пакета
7
+ if [ -n "${HUSKY_DIR:-}" ] && [ -f "${HUSKY_DIR}/config" ]; then
8
+ # shellcheck disable=SC1091
9
+ . "${HUSKY_DIR}/config"
10
+ fi
11
+
12
+ itcase_is_ci() {
13
+ [ -n "${GIT_WORKFLOW:-}" ] || [ -n "${CI:-}" ] || [ -n "${GITHUB_ACTIONS:-}" ]
14
+ }
15
+
16
+ itcase_current_branch() {
17
+ git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''
18
+ }
19
+
20
+ itcase_is_release_branch() {
21
+ local branch="$1"
22
+ local release_branch
23
+
24
+ if [ -z "${RELEASE_BRANCHES:-}" ]; then
25
+ [ "$branch" = "master" ]
26
+ return $?
27
+ fi
28
+
29
+ for release_branch in $RELEASE_BRANCHES; do
30
+ if [ "$branch" = "$release_branch" ]; then
31
+ return 0
32
+ fi
33
+ done
34
+
35
+ return 1
36
+ }
37
+
38
+ itcase_export_release_branch_env() {
39
+ if itcase_is_release_branch "$(itcase_current_branch)"; then
40
+ export RELEASE_BRANCH=true
41
+ fi
42
+ }
43
+
44
+ itcase_conflict_marker_files() {
45
+ git grep -lI --untracked --exclude-standard \
46
+ -e '^<<<<<<< ' -e '^=======$' -e '^>>>>>>> ' \
47
+ -- . ':!node_modules' ':!dist' 2>/dev/null
48
+ }
49
+
50
+ itcase_run_lint_staged() {
51
+ if [ -f "./node_modules/lint-staged/bin/lint-staged.js" ]; then
52
+ node ./node_modules/lint-staged/bin/lint-staged.js
53
+ return $?
54
+ fi
55
+
56
+ npx lint-staged
57
+ }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env sh
2
+ # Shared launcher for @itcase/config husky hooks. Git does not run this file.
3
+ export HUSKY_DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
4
+ HOOK_NAME="$(basename "$0")"
5
+
6
+ if command -v itcase-config-husky-hooks >/dev/null 2>&1; then
7
+ exec itcase-config-husky-hooks "$HOOK_NAME" "$@"
8
+ fi
9
+
10
+ ROOT="$(CDPATH= cd -- "$HUSKY_DIR/.." && pwd)"
11
+ for candidate in \
12
+ "$ROOT/husky-hooks/run.js" \
13
+ "$ROOT/node_modules/@itcase/config/husky-hooks/run.js" \
14
+ "$ROOT/../itcase-config/husky-hooks/run.js"
15
+ do
16
+ if [ -f "$candidate" ]; then
17
+ exec node "$candidate" "$HOOK_NAME" "$@"
18
+ fi
19
+ done
20
+
21
+ echo "❌ itcase-config-husky-hooks not found. Install @itcase/config." >&2
22
+ exit 1
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck disable=SC1091
4
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_lib.sh"
5
+
6
+ # Пропускаем в CI окружении
7
+ if itcase_is_ci; then
8
+ exit 0
9
+ fi
10
+
11
+ commit_msg=$(cat "$1")
12
+
13
+ # Если сообщение коммита пустое или содержит только пробелы/комментарии, отменяем коммит
14
+ clean_msg=$(echo "$commit_msg" | sed '/^#/d; s/^[[:space:]]*//; s/[[:space:]]*$//')
15
+ [ -z "$clean_msg" ] && echo "Aborting commit due to empty commit message." && exit 1
16
+
17
+ # Пропускаем специальные коммиты
18
+ echo "$commit_msg" | grep -qE "^release: publishing new version|^Merge (branch|commit|remote-tracking)|^(Merge pull request|Squash and merge|Rebase and merge)|^Revert |^\(cherry picked from commit" && exit 0
19
+
20
+ # Устанавливаем переменную окружения для выбора конфига commitlint
21
+ itcase_export_release_branch_env
22
+
23
+ npx commitlint --edit "$1"
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Пример локального .husky/config — скопируйте в репозиторий и поправьте ветки.
4
+ # Пакет этот файл не подключает.
5
+
6
+ # Защищенные ветки - в них можно делать только merge коммиты (push после merge)
7
+ PROTECTED_BRANCHES="master"
8
+
9
+ # Ветки релизов - для них используется специальная конфигурация
10
+ RELEASE_BRANCHES="master"
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copies husky-hooks/_run.sh into the project .husky/ on install.
4
+ * Skips repos that do not use the wrappers.
5
+ */
6
+ import fs from 'node:fs'
7
+ import path from 'node:path'
8
+
9
+ const GIT_HOOKS = [
10
+ 'pre-commit',
11
+ 'commit-msg',
12
+ 'prepare-commit-msg',
13
+ 'pre-push',
14
+ 'post-merge',
15
+ 'post-commit',
16
+ 'set-release-message',
17
+ ]
18
+
19
+ function projectRoot() {
20
+ const initCwd = process.env.INIT_CWD
21
+ if (
22
+ initCwd &&
23
+ path.isAbsolute(initCwd) &&
24
+ !initCwd.includes(`${path.sep}node_modules${path.sep}`)
25
+ ) {
26
+ return initCwd
27
+ }
28
+
29
+ const cwd = process.cwd()
30
+ const marker = `${path.sep}node_modules${path.sep}@itcase${path.sep}config`
31
+ const index = cwd.lastIndexOf(marker)
32
+ if (index !== -1) {
33
+ return cwd.slice(0, index)
34
+ }
35
+
36
+ return cwd
37
+ }
38
+
39
+ function usesItcaseHooks(huskyDir) {
40
+ if (fs.existsSync(path.join(huskyDir, '_run.sh'))) {
41
+ return true
42
+ }
43
+
44
+ return GIT_HOOKS.some((name) => {
45
+ try {
46
+ const text = fs.readFileSync(path.join(huskyDir, name), 'utf8')
47
+ return text.includes('_run.sh')
48
+ } catch {
49
+ return false
50
+ }
51
+ })
52
+ }
53
+
54
+ const root = projectRoot()
55
+ const huskyDir = path.join(root, '.husky')
56
+ if (!fs.existsSync(huskyDir) || !usesItcaseHooks(huskyDir)) {
57
+ process.exit(0)
58
+ }
59
+
60
+ const src = path.join(import.meta.dirname, '_run.sh')
61
+ const dest = path.join(huskyDir, '_run.sh')
62
+ fs.copyFileSync(src, dest)
63
+ try {
64
+ fs.chmodSync(dest, 0o755)
65
+ } catch {
66
+ // Windows may not support chmod; Git Bash still runs the file via sh.
67
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Коммит уже создан: защиту веток и lint-staged делает pre-commit.
4
+ exit 0
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck disable=SC1091
4
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_lib.sh"
5
+
6
+ # Ранний выход для различных условий
7
+ itcase_is_ci && exit 0
8
+
9
+ branch="$(itcase_current_branch)"
10
+ itcase_is_release_branch "$branch" || exit 0
11
+
12
+ commit_msg=$(git log -1 --pretty=%B 2>/dev/null || echo '')
13
+ [ -z "$commit_msg" ] && exit 0
14
+
15
+ # Проверка формата коммита (major/minor/patch или release: publishing)
16
+ echo "$commit_msg" | grep -qE "^(major|minor|patch)(\([^)]+\))?:|^release: publishing" && exit 0
17
+
18
+ # Проверка наличия коммита в origin
19
+ git branch -r --contains HEAD 2>/dev/null | grep -q "origin/" && exit 0
20
+
21
+ # Проверка на автоматические merge коммиты
22
+ echo "$commit_msg" | grep -qE "^Merge (remote-tracking|made by|branch .* of (http|https|git@))" && exit 0
23
+
24
+ # Проверка merge parent в origin
25
+ if git rev-parse --verify HEAD^2 >/dev/null 2>&1; then
26
+ merge_parent=$(git rev-parse HEAD^2 2>/dev/null)
27
+ git branch -r --contains "$merge_parent" 2>/dev/null | grep -q . && exit 0
28
+ fi
29
+
30
+ # Если дошли сюда, нужно установить release message
31
+ export RELEASE_BRANCH=true
32
+
33
+ # Проверка на merge коммит или невалидный commitlint
34
+ if echo "$commit_msg" | grep -qE "^Merge (branch|commit|remote-tracking)|^(Merge pull request|Squash and merge|Rebase and merge)" || \
35
+ ! echo "$commit_msg" | npx commitlint --no-edit 2>/dev/null; then
36
+ bash "${PACKAGE_HOOKS}/set-release-message"
37
+ exit $?
38
+ fi
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck disable=SC1091
4
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_lib.sh"
5
+
6
+ # Пропускаем в CI окружении
7
+ if itcase_is_ci; then
8
+ exit 0
9
+ fi
10
+
11
+ BRANCH="$(itcase_current_branch)"
12
+
13
+ # Проверяем, является ли текущая ветка защищенной
14
+ if [ -n "${PROTECTED_BRANCHES:-}" ]; then
15
+ for protected_branch in $PROTECTED_BRANCHES; do
16
+ if [ "$BRANCH" = "$protected_branch" ]; then
17
+ # Если это merge операция - разрешаем
18
+ if [ -f "$(git rev-parse --git-dir)/MERGE_HEAD" ]; then
19
+ break
20
+ fi
21
+
22
+ echo "❌ Прямые коммиты в ветку '$BRANCH' запрещены!"
23
+ echo " Разрешены только merge коммиты из других веток."
24
+ echo " Создайте новую ветку и сделайте merge через pull request."
25
+ exit 1
26
+ fi
27
+ done
28
+ fi
29
+
30
+ # Проверка на конфликты слияния
31
+ if conflict_files="$(itcase_conflict_marker_files)"; then
32
+ echo 'Fix conflicts'
33
+ printf '%s\n' "$conflict_files" | sed 's/^/ /'
34
+ exit 1
35
+ fi
36
+
37
+ itcase_run_lint_staged
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck disable=SC1091
4
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_lib.sh"
5
+
6
+ if itcase_is_ci; then
7
+ exit 0
8
+ fi
9
+
10
+ branch="$(itcase_current_branch)"
11
+
12
+ if ! itcase_is_release_branch "$branch"; then
13
+ exit 0
14
+ fi
15
+
16
+ commit_msg=$(git log --format=%B -n 1 2>/dev/null || echo '')
17
+
18
+ if [ -z "$commit_msg" ]; then
19
+ exit 0
20
+ fi
21
+
22
+ if echo "$commit_msg" | grep -qE "^(major|minor|patch).*:"; then
23
+ exit 0
24
+ fi
25
+
26
+ # Пропускаем мерж-коммиты
27
+ if echo "$commit_msg" | grep -qE "^Merge (branch|commit|remote-tracking)|^(Merge pull request|Squash and merge|Rebase and merge)"; then
28
+ exit 0
29
+ fi
30
+
31
+ echo "$commit_msg" | npx commitlint
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # shellcheck disable=SC1091
4
+ . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_lib.sh"
5
+
6
+ # Пропускаем в CI окружении
7
+ if itcase_is_ci; then
8
+ exit 0
9
+ fi
10
+
11
+ if [ -n "$2" ] && [ "$2" != "message" ]; then
12
+ exit 0
13
+ fi
14
+
15
+ if [ "$2" = "merge" ] || [ "$2" = "squash" ] || [ "$2" = "commit" ]; then
16
+ exit 0
17
+ fi
18
+
19
+ # Проверяем, является ли текущая ветка веткой релиза
20
+ itcase_export_release_branch_env
21
+
22
+ if [ -f "$1" ]; then
23
+ commit_msg=$(cat "$1" 2>/dev/null | sed '/^#/d' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
24
+ if [ -n "$commit_msg" ]; then
25
+ exit 0
26
+ fi
27
+ fi
28
+
29
+ # Импортируем утилиты для настройки TTY
30
+ if [ -f "${PACKAGE_HOOKS}/setup_tty.sh" ]; then
31
+ # shellcheck disable=SC1091
32
+ . "${PACKAGE_HOOKS}/setup_tty.sh"
33
+ # Настраиваем TTY для интерактивного ввода
34
+ setup_tty
35
+ fi
36
+
37
+ if ! npx cz --hook; then
38
+ if [ -f "$1" ]; then
39
+ > "$1"
40
+ fi
41
+ exit 1
42
+ fi
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process'
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+
6
+ const ALLOWED_HOOKS = new Set([
7
+ 'pre-commit',
8
+ 'commit-msg',
9
+ 'prepare-commit-msg',
10
+ 'pre-push',
11
+ 'post-merge',
12
+ 'post-commit',
13
+ 'set-release-message',
14
+ ])
15
+
16
+ const hookName = process.argv[2]
17
+ const hookArgs = process.argv.slice(3)
18
+
19
+ if (!hookName || !ALLOWED_HOOKS.has(hookName)) {
20
+ process.stderr.write(
21
+ 'Usage: itcase-config-husky-hooks <hook-name> [args...]\n',
22
+ )
23
+ process.exit(1)
24
+ }
25
+
26
+ const hookScript = path.join(import.meta.dirname, hookName)
27
+
28
+ if (!fs.existsSync(hookScript)) {
29
+ process.stderr.write(
30
+ `❌ Husky hook '${hookName}' not found in @itcase/config.\n`,
31
+ )
32
+ process.stderr.write(` Expected: ${hookScript}\n`)
33
+ process.exit(1)
34
+ }
35
+
36
+ function resolveBash() {
37
+ if (process.platform !== 'win32') {
38
+ return 'bash'
39
+ }
40
+
41
+ const candidates = [
42
+ process.env.PROGRAMFILES &&
43
+ path.join(process.env.PROGRAMFILES, 'Git', 'bin', 'bash.exe'),
44
+ process.env['PROGRAMFILES(X86)'] &&
45
+ path.join(process.env['PROGRAMFILES(X86)'], 'Git', 'bin', 'bash.exe'),
46
+ 'C:\\Program Files\\Git\\bin\\bash.exe',
47
+ ].filter(Boolean)
48
+
49
+ for (const candidate of candidates) {
50
+ if (fs.existsSync(candidate)) {
51
+ return candidate
52
+ }
53
+ }
54
+
55
+ return 'bash'
56
+ }
57
+
58
+ const result = spawnSync(resolveBash(), [hookScript, ...hookArgs], {
59
+ env: { ...process.env, HUSKY_DIR: process.env.HUSKY_DIR || '' },
60
+ stdio: 'inherit',
61
+ })
62
+
63
+ if (result.error) {
64
+ process.stderr.write(
65
+ `❌ Failed to run bash for husky hook '${hookName}': ${result.error.message}\n`,
66
+ )
67
+ process.exit(1)
68
+ }
69
+
70
+ process.exit(result.status ?? 1)
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env bash
2
+
3
+ PACKAGE_HOOKS="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+
5
+ # Импортируем утилиты для настройки TTY
6
+ if [ -f "${PACKAGE_HOOKS}/setup_tty.sh" ]; then
7
+ # shellcheck disable=SC1091
8
+ . "${PACKAGE_HOOKS}/setup_tty.sh"
9
+ # Настраиваем TTY для интерактивного ввода
10
+ setup_tty
11
+ fi
12
+
13
+ # Опциональный параметр - хеш коммита для обновления (если не указан, обновляем HEAD)
14
+ TARGET_COMMIT="${1:-HEAD}"
15
+
16
+ clear_stale_merge_state() {
17
+ local git_dir="$1"
18
+ rm -f "${git_dir}/MERGE_HEAD" \
19
+ "${git_dir}/MERGE_MSG" \
20
+ "${git_dir}/MERGE_MODE" \
21
+ "${git_dir}/MERGE_RR" \
22
+ "${git_dir}/AUTO_MERGE" 2>/dev/null
23
+ }
24
+
25
+ # release_type: major | minor | patch
26
+ format_release_message() {
27
+ local release_type="$1"
28
+ local commit_message="$2"
29
+
30
+ if echo "$commit_message" | grep -qE "^${release_type}(\([^)]+\))?:"; then
31
+ printf '%s\n' "$commit_message"
32
+ return 0
33
+ fi
34
+
35
+ if [ -n "$RELEASE_BRANCH" ] || echo "$commit_message" | grep -qE '^Merge '; then
36
+ local subject
37
+ subject=$(printf '%s' "$commit_message" | head -n 1)
38
+ printf '%s(other): %s\n' "$release_type" "$subject"
39
+ return 0
40
+ fi
41
+
42
+ if echo "$commit_message" | grep -qE '^[^:]+:'; then
43
+ local before_colon after_colon
44
+ before_colon=$(echo "$commit_message" | sed 's/:.*$//')
45
+ after_colon=$(echo "$commit_message" | sed 's/^[^:]*: *//')
46
+ printf '%s(%s): %s\n' "$release_type" "$before_colon" "$after_colon"
47
+ return 0
48
+ fi
49
+
50
+ printf '%s(other): %s\n' "$release_type" "$commit_message"
51
+ }
52
+
53
+ update_message() {
54
+ local release_type="$1"
55
+ local commit_message
56
+ local git_dir
57
+ local new_message
58
+
59
+ commit_message=$(git log --format=%B -n 1 "$TARGET_COMMIT" 2>/dev/null) || return 1
60
+ [ -z "$commit_message" ] && return 1
61
+
62
+ if echo "$commit_message" | grep -qE "^${release_type}(\([^)]+\))?:"; then
63
+ return 0
64
+ fi
65
+
66
+ git_dir=$(git rev-parse --git-dir 2>/dev/null) || return 1
67
+ new_message=$(format_release_message "$release_type" "$commit_message") || return 1
68
+
69
+ # Если обновляем не HEAD, используем rebase
70
+ if [ "$TARGET_COMMIT" != "HEAD" ]; then
71
+ # Проверяем, не находится ли репозиторий в процессе rebase (не проверяем MERGE_HEAD, т.к. в post-merge merge уже завершен)
72
+ if [ -f "${git_dir}/rebase-merge" ] || [ -f "${git_dir}/rebase-apply" ]; then
73
+ echo "Error: Repository is in the middle of a rebase. Cannot update commit message."
74
+ return 1
75
+ fi
76
+
77
+ local commit_hash parent temp_sequence_script temp_editor_script message_file short_hash
78
+
79
+ commit_hash=$(git rev-parse "$TARGET_COMMIT" 2>/dev/null) || return 1
80
+ parent=$(git rev-parse "${commit_hash}^" 2>/dev/null) || return 1
81
+
82
+ message_file=$(mktemp)
83
+ printf '%s' "$new_message" > "$message_file"
84
+
85
+ temp_sequence_script=$(mktemp)
86
+ # Используем полный хеш для надежности
87
+ short_hash=$(git rev-parse --short "$commit_hash" 2>/dev/null || echo "${commit_hash:0:7}")
88
+ echo "sed -i.bak '1s/^pick ${short_hash}/reword ${short_hash}/'" > "$temp_sequence_script"
89
+ chmod +x "$temp_sequence_script"
90
+
91
+ # Создаем временный скрипт для редактирования сообщения коммита
92
+ temp_editor_script=$(mktemp)
93
+ cat > "$temp_editor_script" <<EOF
94
+ #!/usr/bin/env bash
95
+ # GIT_EDITOR получает путь к файлу с сообщением коммита как аргумент
96
+ cp "$message_file" "\$1"
97
+ EOF
98
+ chmod +x "$temp_editor_script"
99
+
100
+ export GIT_SEQUENCE_EDITOR="$temp_sequence_script"
101
+ export GIT_EDITOR="$temp_editor_script"
102
+
103
+ if ! GIT_WORKFLOW=1 git rebase -i "$parent" >/dev/null 2>&1; then
104
+ git rebase --abort >/dev/null 2>&1
105
+ rm -f "$temp_sequence_script" "$temp_editor_script" "$message_file"
106
+ return 1
107
+ fi
108
+
109
+ rm -f "$temp_sequence_script" "$temp_editor_script" "$message_file"
110
+ return 0
111
+ fi
112
+
113
+ if [ -f "${git_dir}/rebase-merge" ] || [ -f "${git_dir}/rebase-apply" ]; then
114
+ return 1
115
+ fi
116
+
117
+ # post-merge: merge-коммит уже создан, MERGE_HEAD ещё не удалён git'ом
118
+ if [ -n "$RELEASE_BRANCH" ] && [ -f "${git_dir}/MERGE_HEAD" ]; then
119
+ clear_stale_merge_state "$git_dir"
120
+ elif [ -f "${git_dir}/MERGE_HEAD" ]; then
121
+ return 1
122
+ fi
123
+
124
+ git rev-parse --verify HEAD >/dev/null 2>&1 || return 1
125
+
126
+ git diff --cached --quiet 2>&1 || git reset HEAD >/dev/null 2>&1
127
+
128
+ if ! GIT_EDITOR=true GIT_WORKFLOW=1 git commit --amend --no-verify -m "$new_message" >/dev/null 2>&1; then
129
+ echo "Error: Could not amend commit"
130
+ return 1
131
+ fi
132
+
133
+ return 0
134
+ }
135
+
136
+ PS3='Select release type: '
137
+
138
+ MAJOR='⭐️ MAJOR version when you make incompatible API changes'
139
+ MINOR='🐞 MINOR version when you add functionality in a backwards compatible manner'
140
+ PATCH='✅ PATCH version when you make backwards compatible bug fixes'
141
+ CANCEL='Cancel'
142
+
143
+ options=("${MAJOR}" "${MINOR}" "${PATCH}" "${CANCEL}")
144
+ select opt in "${options[@]}"
145
+ do
146
+ case $opt in
147
+ $MAJOR)
148
+ update_message 'major' && exit 0 || exit 1
149
+ ;;
150
+ $MINOR)
151
+ update_message 'minor' && exit 0 || exit 1
152
+ ;;
153
+ $PATCH)
154
+ update_message 'patch' && exit 0 || exit 1
155
+ ;;
156
+ $CANCEL)
157
+ exit 1
158
+ ;;
159
+ *) echo "invalid option $REPLY";;
160
+ esac
161
+ done
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Утилиты для настройки TTY в хуках husky
4
+
5
+ # Проверяет существование команды
6
+ command_exists() {
7
+ command -v "$1" >/dev/null 2>&1
8
+ }
9
+
10
+ # Настраивает TTY для интерактивного ввода
11
+ # Автоматически выходит из скрипта, если не интерактивный режим
12
+ setup_tty() {
13
+ if ! test -t 1; then
14
+ exit 0
15
+ fi
16
+
17
+ if command_exists winpty && test -t 1; then
18
+ exec < /dev/tty
19
+ elif test -t 1 && [ -c /dev/tty ] 2>/dev/null; then
20
+ exec < /dev/tty
21
+ fi
22
+ }
@@ -10,7 +10,7 @@ export default {
10
10
  // Проверяем, есть ли package.json в корне репозитория
11
11
  const rootPackageJson = resolve(process.cwd(), 'package.json')
12
12
  if (existsSync(rootPackageJson) && files.includes(rootPackageJson)) {
13
- return ['npm install --package-lock-only', 'git add package-lock.json']
13
+ return 'npm install --package-lock-only'
14
14
  }
15
15
  return 'true' // Пропускаем, если не найден
16
16
  },
@@ -18,7 +18,7 @@ export default {
18
18
  // Проверяем, есть ли package.json в корне репозитория
19
19
  const rootPackageJson = resolve(process.cwd(), 'package.json')
20
20
  if (existsSync(rootPackageJson) && files.includes(rootPackageJson)) {
21
- return ['npm install --package-lock-only', 'git add package-lock.json']
21
+ return 'npm install --package-lock-only'
22
22
  }
23
23
  return 'true' // Пропускаем, если не найден
24
24
  },
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@itcase/config",
3
- "version": "1.6.63",
3
+ "version": "1.6.65",
4
4
  "author": "ITCase",
5
5
  "description": "ITCase Config",
6
6
  "engines": {
7
7
  "node": ">=22.0.0"
8
8
  },
9
+ "bin": {
10
+ "itcase-config-husky-hooks": "./husky-hooks/run.js"
11
+ },
9
12
  "scripts": {
10
13
  "prepare": "husky",
14
+ "postinstall": "node ./husky-hooks/install-launcher.js",
11
15
  "semantic-release": "semantic-release"
12
16
  },
13
17
  "type": "module",
@@ -19,6 +23,7 @@
19
23
  },
20
24
  "files": [
21
25
  "commitlint",
26
+ "husky-hooks",
22
27
  "lint-staged",
23
28
  "lint-staged-ts",
24
29
  "semantic-release",
@@ -31,15 +36,15 @@
31
36
  "registry": "https://registry.npmjs.org/"
32
37
  },
33
38
  "dependencies": {
34
- "@commitlint/cli": "^21.2.1",
35
- "@commitlint/config-conventional": "^21.2.0",
36
- "@commitlint/cz-commitlint": "^21.2.0",
39
+ "@commitlint/cli": "^21.2.2",
40
+ "@commitlint/config-conventional": "^21.2.2",
41
+ "@commitlint/cz-commitlint": "^21.2.2",
37
42
  "@lehoczky/postcss-fluid": "^1.0.3",
38
- "@lerna-lite/cli": "^5.4.2",
39
- "@lerna-lite/exec": "^5.4.2",
40
- "@lerna-lite/list": "^5.4.2",
41
- "@lerna-lite/run": "^5.4.2",
42
- "@lerna-lite/watch": "^5.4.2",
43
+ "@lerna-lite/cli": "^5.5.0",
44
+ "@lerna-lite/exec": "^5.5.0",
45
+ "@lerna-lite/list": "^5.5.0",
46
+ "@lerna-lite/run": "^5.5.0",
47
+ "@lerna-lite/watch": "^5.5.0",
43
48
  "@rollup/plugin-alias": "^6.0.0",
44
49
  "@rollup/plugin-babel": "^7.1.0",
45
50
  "@rollup/plugin-commonjs": "^29.0.3",
@@ -55,9 +60,9 @@
55
60
  "autoprefixer": "^10.5.4",
56
61
  "chokidar-cli": "^3.0.0",
57
62
  "commitizen": "^4.3.2",
58
- "conventional-changelog-conventionalcommits": "^10.2.1",
59
- "cssnano": "^8.0.4",
60
- "cssnano-preset-default": "^8.0.4",
63
+ "conventional-changelog-conventionalcommits": "^10.3.0",
64
+ "cssnano": "^8.0.5",
65
+ "cssnano-preset-default": "^8.0.5",
61
66
  "inquirer": "^14.0.2",
62
67
  "postcss": "^8.5.26",
63
68
  "postcss-aspect-ratio-polyfill": "^2.0.0",
@@ -67,13 +72,13 @@
67
72
  "postcss-combine-duplicated-selectors": "^10.0.3",
68
73
  "postcss-convert-unit": "^0.1.3",
69
74
  "postcss-dark-theme-class": "^2.0.0",
70
- "postcss-discard-duplicates": "^8.0.2",
75
+ "postcss-discard-duplicates": "^8.0.3",
71
76
  "postcss-each": "^1.1.0",
72
77
  "postcss-easings": "^4.0.0",
73
78
  "postcss-extend-rule": "^4.0.0",
74
79
  "postcss-flexbugs-fixes": "^5.0.2",
75
80
  "postcss-hexrgba": "^2.1.0",
76
- "postcss-import": "^16.1.1",
81
+ "postcss-import": "^16.2.0",
77
82
  "postcss-import-ext-glob": "^2.1.1",
78
83
  "postcss-loader": "^8.2.1",
79
84
  "postcss-merge-at-rules": "^2.0.1",
@@ -97,7 +102,7 @@
97
102
  "rollup-plugin-peer-deps-external": "^2.2.4",
98
103
  "rollup-preserve-directives": "^1.1.3",
99
104
  "semantic-release": "^25.0.9",
100
- "semantic-release-lerna": "^3.6.0"
105
+ "semantic-release-lerna": "^3.6.1"
101
106
  },
102
107
  "devDependencies": {
103
108
  "@itcase/lint": "^1.1.121",
@@ -106,7 +111,7 @@
106
111
  "inquirer": "^14.0.2",
107
112
  "lint-staged": "^17.3.0",
108
113
  "prettier": "^3.9.6",
109
- "storybook": "^10.5.7",
114
+ "storybook": "^10.5.8",
110
115
  "stylelint": "^17.14.1",
111
116
  "typescript": "^6.0.2"
112
117
  }