@gracefullight/validate-branch 0.1.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/README.en.md +423 -0
- package/README.md +423 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +141 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +92 -0
- package/package.json +60 -0
package/README.en.md
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# @gracefullight/validate-branch
|
|
2
|
+
|
|
3
|
+
> Git branch name validation tool with custom regexp support
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@gracefullight/validate-branch)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
[한국어](./README.md) | **English**
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Flexible Regular Expression Pattern** - Use default pattern or custom regular expression
|
|
13
|
+
- **Current Branch Detection** - Automatically detect current branch name from Git repository
|
|
14
|
+
- **Config File Support** - Load patterns from `branch.config.{ts,js,mjs}` files
|
|
15
|
+
- **CLI Tool** - Use directly from command line
|
|
16
|
+
- **Detailed Error Messages** - Clear error descriptions on validation failure
|
|
17
|
+
- **Full TypeScript Support** - Complete TypeScript type definitions
|
|
18
|
+
|
|
19
|
+
## Default Pattern
|
|
20
|
+
|
|
21
|
+
By default, the following branch names are allowed:
|
|
22
|
+
|
|
23
|
+
- `main` / `master` - Main branches
|
|
24
|
+
- `develop` / `stage` - Development/staging branches
|
|
25
|
+
- `feature/.*` - Feature branches (e.g., `feature/login`, `feature/user-auth`)
|
|
26
|
+
- `fix/.*` - Bug fix branches (e.g., `fix/memory-leak`, `fix/typo`)
|
|
27
|
+
- `hotfix/.*` - Hotfix branches (e.g., `hotfix/critical-bug`)
|
|
28
|
+
- `release/.*` - Release branches (e.g., `release/v1.0.0`)
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Using pnpm
|
|
34
|
+
pnpm add @gracefullight/validate-branch
|
|
35
|
+
|
|
36
|
+
# Using npm
|
|
37
|
+
npm install @gracefullight/validate-branch
|
|
38
|
+
|
|
39
|
+
# Using yarn
|
|
40
|
+
yarn add @gracefullight/validate-branch
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### CLI Usage
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Validate current branch
|
|
49
|
+
npx validate-branch
|
|
50
|
+
|
|
51
|
+
# Validate specific branch
|
|
52
|
+
npx validate-branch --test feature/new-feature
|
|
53
|
+
|
|
54
|
+
# Use custom regular expression
|
|
55
|
+
npx validate-branch --regexp "^(main|develop|feature/.+)$"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Programmatic Usage
|
|
59
|
+
|
|
60
|
+
#### Validate with Default Pattern
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { validateBranchName, validateWithDetails } from "@gracefullight/validate-branch";
|
|
64
|
+
|
|
65
|
+
// Simple boolean return
|
|
66
|
+
const isValid = validateBranchName("feature/new-component");
|
|
67
|
+
console.log(isValid); // true
|
|
68
|
+
|
|
69
|
+
// Detailed result return
|
|
70
|
+
const result = validateWithDetails("feature/new-component");
|
|
71
|
+
console.log(result);
|
|
72
|
+
// { valid: true, branchName: "feature/new-component" }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Validate with Custom Regexp
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { validateBranchName } from "@gracefullight/validate-branch";
|
|
79
|
+
|
|
80
|
+
const customPattern = "^(main|develop|story/.+|bugfix/.+)$";
|
|
81
|
+
const isValid = validateBranchName("story/US-123-add-user", { customRegexp: customPattern });
|
|
82
|
+
console.log(isValid); // true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Get Current Branch Name
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { getCurrentBranchName } from "@gracefullight/validate-branch";
|
|
89
|
+
|
|
90
|
+
const currentBranch = await getCurrentBranchName();
|
|
91
|
+
console.log(currentBranch); // "feature/new-component" or null
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Load Config File
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { loadConfig } from "@gracefullight/validate-branch";
|
|
98
|
+
|
|
99
|
+
const config = await loadConfig();
|
|
100
|
+
console.log(config);
|
|
101
|
+
// {
|
|
102
|
+
// pattern: "^(main|develop|feature/.+)$",
|
|
103
|
+
// description: "Project branch naming rules"
|
|
104
|
+
// }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Configuration File
|
|
108
|
+
|
|
109
|
+
Create a `branch.config.ts` or `branch.config.js` file in your project root to define custom patterns.
|
|
110
|
+
|
|
111
|
+
### TypeScript Config (`branch.config.ts`)
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import type { Config } from "@gracefullight/validate-branch";
|
|
115
|
+
|
|
116
|
+
const config: Config = {
|
|
117
|
+
pattern: "^(main|develop|feature/.+|fix/.+|refactor/.+)$",
|
|
118
|
+
description: "Project branch naming rules",
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default config;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### JavaScript Config (`branch.config.js`)
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
module.exports = {
|
|
128
|
+
pattern: "^(main|develop|feature/.+|fix/.+)$",
|
|
129
|
+
description: "Project branch naming rules",
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API Reference
|
|
134
|
+
|
|
135
|
+
### `validateBranchName(branchName, options?)`
|
|
136
|
+
|
|
137
|
+
Check if a branch name is valid.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface ValidateBranchNameOptions {
|
|
141
|
+
customRegexp?: string;
|
|
142
|
+
preset?: "gitflow" | "jira";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validateBranchName(
|
|
146
|
+
branchName: string,
|
|
147
|
+
options?: ValidateBranchNameOptions
|
|
148
|
+
): boolean
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Parameters:**
|
|
152
|
+
- `branchName`: Branch name to validate
|
|
153
|
+
- `options`: Optional, validation options object
|
|
154
|
+
- `customRegexp`: Custom regular expression string
|
|
155
|
+
- `preset`: Preset pattern (`gitflow` or `jira`, default: `gitflow`)
|
|
156
|
+
|
|
157
|
+
**Returns:** Validity status
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### `validateWithDetails(branchName, options?)`
|
|
162
|
+
|
|
163
|
+
Return detailed validation result.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
function validateWithDetails(
|
|
167
|
+
branchName: string,
|
|
168
|
+
options?: ValidateBranchNameOptions
|
|
169
|
+
): ValidationResult
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Returns:**
|
|
173
|
+
```typescript
|
|
174
|
+
{
|
|
175
|
+
valid: boolean;
|
|
176
|
+
branchName: string;
|
|
177
|
+
error?: string; // Error message on failure
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Example:**
|
|
182
|
+
```typescript
|
|
183
|
+
const result = validateWithDetails("invalid-branch");
|
|
184
|
+
// {
|
|
185
|
+
// valid: false,
|
|
186
|
+
// branchName: "invalid-branch",
|
|
187
|
+
// error: 'Branch name "invalid-branch" does not match pattern: /^(main|master|...)$/'
|
|
188
|
+
// }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### `getCurrentBranchName()`
|
|
194
|
+
|
|
195
|
+
Get current Git branch name.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
function getCurrentBranchName(): Promise<string | null>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Returns:** Current branch name, `null` if not in a Git repository
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### `loadConfig()`
|
|
206
|
+
|
|
207
|
+
Load project configuration file.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
function loadConfig(): Promise<Config | null>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Config file search order:**
|
|
214
|
+
1. `branch.config.ts`
|
|
215
|
+
2. `branch.config.mts`
|
|
216
|
+
3. `branch.config.js`
|
|
217
|
+
4. `branch.config.cjs`
|
|
218
|
+
5. `branch.config.mjs`
|
|
219
|
+
|
|
220
|
+
**Returns:** Configuration object, `null` if no config file exists
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Type Definitions
|
|
225
|
+
|
|
226
|
+
### `Config`
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
interface Config {
|
|
230
|
+
pattern?: string; // Single regex pattern
|
|
231
|
+
patterns?: string[]; // Multiple regex patterns (planned)
|
|
232
|
+
description?: string; // Config description
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### `ValidationResult`
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
interface ValidationResult {
|
|
240
|
+
valid: boolean;
|
|
241
|
+
branchName: string;
|
|
242
|
+
error?: string;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Examples
|
|
247
|
+
|
|
248
|
+
### Use with Git Hooks
|
|
249
|
+
|
|
250
|
+
#### With Husky in `package.json`
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"husky": {
|
|
255
|
+
"hooks": {
|
|
256
|
+
"pre-commit": "validate-branch"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Direct Git Hooks
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
#!/bin/sh
|
|
266
|
+
# .git/hooks/pre-commit
|
|
267
|
+
|
|
268
|
+
npx validate-branch
|
|
269
|
+
if [ $? -ne 0 ]; then
|
|
270
|
+
echo "Branch name does not follow naming conventions."
|
|
271
|
+
exit 1
|
|
272
|
+
fi
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Use with GitHub Actions
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
name: Branch Validation
|
|
279
|
+
|
|
280
|
+
on:
|
|
281
|
+
pull_request:
|
|
282
|
+
types: [opened, synchronize]
|
|
283
|
+
|
|
284
|
+
jobs:
|
|
285
|
+
validate-branch:
|
|
286
|
+
runs-on: ubuntu-latest
|
|
287
|
+
steps:
|
|
288
|
+
- uses: actions/checkout@v4
|
|
289
|
+
|
|
290
|
+
- name: Setup Node.js
|
|
291
|
+
uses: actions/setup-node@v4
|
|
292
|
+
with:
|
|
293
|
+
node-version: "20"
|
|
294
|
+
|
|
295
|
+
- name: Install validate-branch
|
|
296
|
+
run: npm install -g @gracefullight/validate-branch
|
|
297
|
+
|
|
298
|
+
- name: Validate branch name
|
|
299
|
+
run: validate-branch --test ${{ github.head_ref }}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Use in CI/CD Pipeline
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
# .github/workflows/ci.yml
|
|
306
|
+
|
|
307
|
+
jobs:
|
|
308
|
+
test:
|
|
309
|
+
runs-on: ubuntu-latest
|
|
310
|
+
steps:
|
|
311
|
+
- name: Checkout
|
|
312
|
+
uses: actions/checkout@v4
|
|
313
|
+
|
|
314
|
+
- name: Validate branch name
|
|
315
|
+
run: npx validate-branch
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Use with ESLint Plugin
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// .eslintrc.js
|
|
322
|
+
const { execSync } = require("child_process");
|
|
323
|
+
|
|
324
|
+
module.exports = {
|
|
325
|
+
// ... other config
|
|
326
|
+
rules: {
|
|
327
|
+
"custom/validate-branch": {
|
|
328
|
+
meta: {
|
|
329
|
+
type: "suggestion",
|
|
330
|
+
docs: {
|
|
331
|
+
description: "Validate git branch name",
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
create(context) {
|
|
335
|
+
return {
|
|
336
|
+
Program() {
|
|
337
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
338
|
+
encoding: "utf-8",
|
|
339
|
+
}).trim();
|
|
340
|
+
|
|
341
|
+
const { validateBranchName } = require("@gracefullight/validate-branch");
|
|
342
|
+
|
|
343
|
+
if (!validateBranchName(branch)) {
|
|
344
|
+
context.report({
|
|
345
|
+
node: {},
|
|
346
|
+
message: `Invalid branch name: ${branch}`,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Development
|
|
358
|
+
|
|
359
|
+
### Setup
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
# Clone repository
|
|
363
|
+
git clone https://github.com/gracefullight/saju.git
|
|
364
|
+
cd packages/validate-branch
|
|
365
|
+
|
|
366
|
+
# Install dependencies
|
|
367
|
+
pnpm install
|
|
368
|
+
|
|
369
|
+
# Build
|
|
370
|
+
pnpm build
|
|
371
|
+
|
|
372
|
+
# Test
|
|
373
|
+
pnpm test
|
|
374
|
+
|
|
375
|
+
# Lint
|
|
376
|
+
pnpm lint
|
|
377
|
+
|
|
378
|
+
# Format
|
|
379
|
+
pnpm lint:fix
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Test CLI Locally
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# After building, test CLI locally
|
|
386
|
+
node bin/validate-branch.js --test feature/new-feature
|
|
387
|
+
|
|
388
|
+
# Test with custom pattern
|
|
389
|
+
node bin/validate-branch.js --test invalid-name --regexp "^(main|develop)$"
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Contributing
|
|
393
|
+
|
|
394
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
395
|
+
|
|
396
|
+
1. Fork repository
|
|
397
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
398
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
399
|
+
4. Push to branch (`git push origin feature/amazing-feature`)
|
|
400
|
+
5. Open a Pull Request
|
|
401
|
+
|
|
402
|
+
### Guidelines
|
|
403
|
+
|
|
404
|
+
- Write tests for new features
|
|
405
|
+
- Maintain or improve code coverage
|
|
406
|
+
- Follow existing code style (enforced by Biome)
|
|
407
|
+
- Update documentation as needed
|
|
408
|
+
|
|
409
|
+
## License
|
|
410
|
+
|
|
411
|
+
MIT © [gracefullight](https://github.com/gracefullight)
|
|
412
|
+
|
|
413
|
+
## Related Projects
|
|
414
|
+
|
|
415
|
+
- [isomorphic-git](https://isomorphic-git.org/) - Git implementation library
|
|
416
|
+
- [Commander.js](https://commander.js/) - Node.js CLI framework
|
|
417
|
+
- [Chalk](https://chalk.js.org/) - Terminal string styling
|
|
418
|
+
|
|
419
|
+
## Support
|
|
420
|
+
|
|
421
|
+
- [Documentation](https://github.com/gracefullight/saju#readme)
|
|
422
|
+
- [Issue Tracker](https://github.com/gracefullight/saju/issues)
|
|
423
|
+
- [Discussions](https://github.com/gracefullight/saju/discussions)
|
package/README.md
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# @gracefullight/validate-branch
|
|
2
|
+
|
|
3
|
+
> 커스텀 정규식을 지원하는 Git 브랜치 이름 유효성 검사 도구
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@gracefullight/validate-branch)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
**한국어** | [English](./README.en.md)
|
|
9
|
+
|
|
10
|
+
## 주요 기능
|
|
11
|
+
|
|
12
|
+
- **유연한 정규식 패턴** - 기본 패턴 또는 커스텀 정규식 사용
|
|
13
|
+
- **현재 브랜치 감지** - Git 저장소에서 현재 브랜치 이름 자동 감지
|
|
14
|
+
- **설정 파일 지원** - `branch.config.{ts,js,mjs}` 파일에서 패턴 로드
|
|
15
|
+
- **CLI 도구** - 명령줄에서 직접 사용 가능
|
|
16
|
+
- **상세한 오류 메시지** - 실패 시 명확한 오류 설명 제공
|
|
17
|
+
- **타입스크립트 지원** - 완전한 TypeScript 타입 정의
|
|
18
|
+
|
|
19
|
+
## 기본 패턴
|
|
20
|
+
|
|
21
|
+
기본적으로 다음 브랜치 이름이 허용됩니다:
|
|
22
|
+
|
|
23
|
+
- `main` / `master` - 메인 브랜치
|
|
24
|
+
- `develop` / `stage` - 개발/스테이지 브랜치
|
|
25
|
+
- `feature/.*` - 기능 브랜치 (예: `feature/login`, `feature/user-auth`)
|
|
26
|
+
- `fix/.*` - 버그 수정 브랜치 (예: `fix/memory-leak`, `fix/typo`)
|
|
27
|
+
- `hotfix/.*` - 핫픽스 브랜치 (예: `hotfix/critical-bug`)
|
|
28
|
+
- `release/.*` - 릴리스 브랜치 (예: `release/v1.0.0`)
|
|
29
|
+
|
|
30
|
+
## 설치
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# pnpm 사용
|
|
34
|
+
pnpm add @gracefullight/validate-branch
|
|
35
|
+
|
|
36
|
+
# npm 사용
|
|
37
|
+
npm install @gracefullight/validate-branch
|
|
38
|
+
|
|
39
|
+
# yarn 사용
|
|
40
|
+
yarn add @gracefullight/validate-branch
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 사용법
|
|
44
|
+
|
|
45
|
+
### CLI 사용
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 현재 브랜치 검증
|
|
49
|
+
npx validate-branch
|
|
50
|
+
|
|
51
|
+
# 특정 브랜치 검증
|
|
52
|
+
npx validate-branch --test feature/new-feature
|
|
53
|
+
|
|
54
|
+
# 커스텀 정규식 사용
|
|
55
|
+
npx validate-branch --regexp "^(main|develop|feature/.+)$"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 프로그래밍 방식 사용
|
|
59
|
+
|
|
60
|
+
#### 기본 패턴으로 검증
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { validateBranchName, validateWithDetails } from "@gracefullight/validate-branch";
|
|
64
|
+
|
|
65
|
+
// 간단한 boolean 반환
|
|
66
|
+
const isValid = validateBranchName("feature/new-component");
|
|
67
|
+
console.log(isValid); // true
|
|
68
|
+
|
|
69
|
+
// 상세한 결과 반환
|
|
70
|
+
const result = validateWithDetails("feature/new-component");
|
|
71
|
+
console.log(result);
|
|
72
|
+
// { valid: true, branchName: "feature/new-component" }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### 커스텀 정규식 사용
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { validateBranchName } from "@gracefullight/validate-branch";
|
|
79
|
+
|
|
80
|
+
const customPattern = "^(main|develop|story/.+|bugfix/.+)$";
|
|
81
|
+
const isValid = validateBranchName("story/US-123-add-user", { customRegexp: customPattern });
|
|
82
|
+
console.log(isValid); // true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### 현재 브랜치 이름 가져오기
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { getCurrentBranchName } from "@gracefullight/validate-branch";
|
|
89
|
+
|
|
90
|
+
const currentBranch = await getCurrentBranchName();
|
|
91
|
+
console.log(currentBranch); // "feature/new-component" 또는 null
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### 설정 파일 로드
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { loadConfig } from "@gracefullight/validate-branch";
|
|
98
|
+
|
|
99
|
+
const config = await loadConfig();
|
|
100
|
+
console.log(config);
|
|
101
|
+
// {
|
|
102
|
+
// pattern: "^(main|develop|feature/.+)$",
|
|
103
|
+
// description: "프로젝트 브랜치 네이밍 규칙"
|
|
104
|
+
// }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 설정 파일
|
|
108
|
+
|
|
109
|
+
프로젝트 루트에 `branch.config.ts` 또는 `branch.config.js` 파일을 생성하여 커스텀 패턴을 정의하세요.
|
|
110
|
+
|
|
111
|
+
### TypeScript 설정 (`branch.config.ts`)
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import type { Config } from "@gracefullight/validate-branch";
|
|
115
|
+
|
|
116
|
+
const config: Config = {
|
|
117
|
+
pattern: "^(main|develop|feature/.+|fix/.+|refactor/.+)$",
|
|
118
|
+
description: "프로젝트 브랜치 네이밍 규칙",
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default config;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### JavaScript 설정 (`branch.config.js`)
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
module.exports = {
|
|
128
|
+
pattern: "^(main|develop|feature/.+|fix/.+)$",
|
|
129
|
+
description: "프로젝트 브랜치 네이밍 규칙",
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API 레퍼런스
|
|
134
|
+
|
|
135
|
+
### `validateBranchName(branchName, options?)`
|
|
136
|
+
|
|
137
|
+
브랜치 이름이 유효한지 확인합니다.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface ValidateBranchNameOptions {
|
|
141
|
+
customRegexp?: string;
|
|
142
|
+
preset?: "gitflow" | "jira";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validateBranchName(
|
|
146
|
+
branchName: string,
|
|
147
|
+
options?: ValidateBranchNameOptions
|
|
148
|
+
): boolean
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**매개변수:**
|
|
152
|
+
- `branchName`: 검증할 브랜치 이름
|
|
153
|
+
- `options`: 선택사항, 검증 옵션 객체
|
|
154
|
+
- `customRegexp`: 커스텀 정규식 문자열
|
|
155
|
+
- `preset`: 프리셋 패턴 (`gitflow` 또는 `jira`, 기본값: `gitflow`)
|
|
156
|
+
|
|
157
|
+
**반환값:** 유효 여부
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### `validateWithDetails(branchName, options?)`
|
|
162
|
+
|
|
163
|
+
상세한 검증 결과를 반환합니다.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
function validateWithDetails(
|
|
167
|
+
branchName: string,
|
|
168
|
+
options?: ValidateBranchNameOptions
|
|
169
|
+
): ValidationResult
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**반환값:**
|
|
173
|
+
```typescript
|
|
174
|
+
{
|
|
175
|
+
valid: boolean;
|
|
176
|
+
branchName: string;
|
|
177
|
+
error?: string; // 실패 시 오류 메시지
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**예시:**
|
|
182
|
+
```typescript
|
|
183
|
+
const result = validateWithDetails("invalid-branch");
|
|
184
|
+
// {
|
|
185
|
+
// valid: false,
|
|
186
|
+
// branchName: "invalid-branch",
|
|
187
|
+
// error: 'Branch name "invalid-branch" does not match pattern: /^(main|master|...)$/'
|
|
188
|
+
// }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### `getCurrentBranchName()`
|
|
194
|
+
|
|
195
|
+
현재 Git 브랜치 이름을 가져옵니다.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
function getCurrentBranchName(): Promise<string | null>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**반환값:** 현재 브랜치 이름, Git 저장소가 아닌 경우 `null`
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### `loadConfig()`
|
|
206
|
+
|
|
207
|
+
프로젝트 설정 파일을 로드합니다.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
function loadConfig(): Promise<Config | null>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**설정 파일 탐색 순서:**
|
|
214
|
+
1. `branch.config.ts`
|
|
215
|
+
2. `branch.config.mts`
|
|
216
|
+
3. `branch.config.js`
|
|
217
|
+
4. `branch.config.cjs`
|
|
218
|
+
5. `branch.config.mjs`
|
|
219
|
+
|
|
220
|
+
**반환값:** 설정 객체 또는 설정 파일이 없는 경우 `null`
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 타입 정의
|
|
225
|
+
|
|
226
|
+
### `Config`
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
interface Config {
|
|
230
|
+
pattern?: string; // 단일 정규식 패턴
|
|
231
|
+
patterns?: string[]; // 다중 정규식 패턴 (예정)
|
|
232
|
+
description?: string; // 설정 설명
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### `ValidationResult`
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
interface ValidationResult {
|
|
240
|
+
valid: boolean;
|
|
241
|
+
branchName: string;
|
|
242
|
+
error?: string;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 예제
|
|
247
|
+
|
|
248
|
+
### Git Hooks와 함께 사용
|
|
249
|
+
|
|
250
|
+
#### `package.json`에서 husky 설정
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"husky": {
|
|
255
|
+
"hooks": {
|
|
256
|
+
"pre-commit": "validate-branch"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### 직접 Git Hooks 사용
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
#!/bin/sh
|
|
266
|
+
# .git/hooks/pre-commit
|
|
267
|
+
|
|
268
|
+
npx validate-branch
|
|
269
|
+
if [ $? -ne 0 ]; then
|
|
270
|
+
echo "브랜치 이름이 규칙에 맞지 않습니다."
|
|
271
|
+
exit 1
|
|
272
|
+
fi
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### GitHub Actions와 함께 사용
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
name: Branch Validation
|
|
279
|
+
|
|
280
|
+
on:
|
|
281
|
+
pull_request:
|
|
282
|
+
types: [opened, synchronize]
|
|
283
|
+
|
|
284
|
+
jobs:
|
|
285
|
+
validate-branch:
|
|
286
|
+
runs-on: ubuntu-latest
|
|
287
|
+
steps:
|
|
288
|
+
- uses: actions/checkout@v4
|
|
289
|
+
|
|
290
|
+
- name: Setup Node.js
|
|
291
|
+
uses: actions/setup-node@v4
|
|
292
|
+
with:
|
|
293
|
+
node-version: "20"
|
|
294
|
+
|
|
295
|
+
- name: Install validate-branch
|
|
296
|
+
run: npm install -g @gracefullight/validate-branch
|
|
297
|
+
|
|
298
|
+
- name: Validate branch name
|
|
299
|
+
run: validate-branch --test ${{ github.head_ref }}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### CI/CD 파이프라인에서 사용
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
# .github/workflows/ci.yml
|
|
306
|
+
|
|
307
|
+
jobs:
|
|
308
|
+
test:
|
|
309
|
+
runs-on: ubuntu-latest
|
|
310
|
+
steps:
|
|
311
|
+
- name: Checkout
|
|
312
|
+
uses: actions/checkout@v4
|
|
313
|
+
|
|
314
|
+
- name: Validate branch name
|
|
315
|
+
run: npx validate-branch
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### ESLint 플러그인과 함께 사용
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// .eslintrc.js
|
|
322
|
+
const { execSync } = require("child_process");
|
|
323
|
+
|
|
324
|
+
module.exports = {
|
|
325
|
+
// ... 기본 설정
|
|
326
|
+
rules: {
|
|
327
|
+
"custom/validate-branch": {
|
|
328
|
+
meta: {
|
|
329
|
+
type: "suggestion",
|
|
330
|
+
docs: {
|
|
331
|
+
description: "Validate git branch name",
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
create(context) {
|
|
335
|
+
return {
|
|
336
|
+
Program() {
|
|
337
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
338
|
+
encoding: "utf-8",
|
|
339
|
+
}).trim();
|
|
340
|
+
|
|
341
|
+
const { validateBranchName } = require("@gracefullight/validate-branch");
|
|
342
|
+
|
|
343
|
+
if (!validateBranchName(branch)) {
|
|
344
|
+
context.report({
|
|
345
|
+
node: {},
|
|
346
|
+
message: `Invalid branch name: ${branch}`,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## 개발
|
|
358
|
+
|
|
359
|
+
### 설정
|
|
360
|
+
|
|
361
|
+
```bash
|
|
362
|
+
# 저장소 클론
|
|
363
|
+
git clone https://github.com/gracefullight/saju.git
|
|
364
|
+
cd packages/validate-branch
|
|
365
|
+
|
|
366
|
+
# 의존성 설치
|
|
367
|
+
pnpm install
|
|
368
|
+
|
|
369
|
+
# 빌드
|
|
370
|
+
pnpm build
|
|
371
|
+
|
|
372
|
+
# 테스트
|
|
373
|
+
pnpm test
|
|
374
|
+
|
|
375
|
+
# 린트
|
|
376
|
+
pnpm lint
|
|
377
|
+
|
|
378
|
+
# 포맷
|
|
379
|
+
pnpm lint:fix
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 로컬에서 CLI 테스트
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# 빌드 후 로컬에서 CLI 테스트
|
|
386
|
+
node bin/validate-branch.js --test feature/new-feature
|
|
387
|
+
|
|
388
|
+
# 커스텀 패턴 테스트
|
|
389
|
+
node bin/validate-branch.js --test invalid-name --regexp "^(main|develop)$"
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## 기여하기
|
|
393
|
+
|
|
394
|
+
기여를 환영합니다! Pull Request를 자유롭게 제출해주세요.
|
|
395
|
+
|
|
396
|
+
1. 저장소 포크
|
|
397
|
+
2. feature 브랜치 생성 (`git checkout -b feature/amazing-feature`)
|
|
398
|
+
3. 변경사항 커밋 (`git commit -m 'Add some amazing feature'`)
|
|
399
|
+
4. 브랜치에 푸시 (`git push origin feature/amazing-feature`)
|
|
400
|
+
5. Pull Request 오픈
|
|
401
|
+
|
|
402
|
+
### 가이드라인
|
|
403
|
+
|
|
404
|
+
- 새 기능에 대한 테스트 작성
|
|
405
|
+
- 코드 커버리지 유지 또는 개선
|
|
406
|
+
- 기존 코드 스타일 따르기 (Biome으로 강제)
|
|
407
|
+
- 필요에 따라 문서 업데이트
|
|
408
|
+
|
|
409
|
+
## 라이선스
|
|
410
|
+
|
|
411
|
+
MIT © [gracefullight](https://github.com/gracefullight)
|
|
412
|
+
|
|
413
|
+
## 관련 프로젝트
|
|
414
|
+
|
|
415
|
+
- [isomorphic-git](https://isomorphic-git.org/) - Git 구현 라이브러리
|
|
416
|
+
- [Commander.js](https://commander.js/) - Node.js CLI 프레임워크
|
|
417
|
+
- [Chalk](https://chalk.js.org/) - 터미널 문자열 스타일링
|
|
418
|
+
|
|
419
|
+
## 지원
|
|
420
|
+
|
|
421
|
+
- [문서](https://github.com/gracefullight/saju#readme)
|
|
422
|
+
- [이슈 트래커](https://github.com/gracefullight/saju/issues)
|
|
423
|
+
- [토론](https://github.com/gracefullight/saju/discussions)
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command, Option } from "commander";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
|
|
7
|
+
// src/validate-branch-name.ts
|
|
8
|
+
import * as git from "isomorphic-git";
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import { cwd } from "process";
|
|
11
|
+
var GITFLOW_PATTERN = /^(main|master|develop|stage|feature\/[A-Za-z0-9_-]+|fix\/[A-Za-z0-9_-]+|hotfix\/[A-Za-z0-9_-]+|release\/[A-Za-z0-9_.-]+)$/;
|
|
12
|
+
var JIRA_PATTERN = /^(main|master|develop|stage|[A-Z]+-[0-9]+)$/;
|
|
13
|
+
async function getCurrentBranchName() {
|
|
14
|
+
try {
|
|
15
|
+
const branch = await git.currentBranch({
|
|
16
|
+
fs,
|
|
17
|
+
dir: cwd(),
|
|
18
|
+
fullname: false
|
|
19
|
+
});
|
|
20
|
+
return branch ?? null;
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function validateWithDetails(branchName, options) {
|
|
26
|
+
const { customRegexp, preset = "gitflow" } = options ?? {};
|
|
27
|
+
if (customRegexp) {
|
|
28
|
+
const pattern2 = new RegExp(customRegexp);
|
|
29
|
+
if (pattern2.test(branchName)) {
|
|
30
|
+
return { valid: true, branchName };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
valid: false,
|
|
34
|
+
branchName,
|
|
35
|
+
error: `Branch name "${branchName}" does not match pattern: ${pattern2.source}`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const pattern = preset === "jira" ? JIRA_PATTERN : GITFLOW_PATTERN;
|
|
39
|
+
if (pattern.test(branchName)) {
|
|
40
|
+
return { valid: true, branchName };
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
valid: false,
|
|
44
|
+
branchName,
|
|
45
|
+
error: `Branch name "${branchName}" does not match pattern: ${pattern.source}`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/load-config.ts
|
|
50
|
+
import { readFile } from "fs/promises";
|
|
51
|
+
import { resolve } from "path";
|
|
52
|
+
import { cwd as cwd2 } from "process";
|
|
53
|
+
var CONFIG_FILES = [
|
|
54
|
+
"branch.config.ts",
|
|
55
|
+
"branch.config.mts",
|
|
56
|
+
"branch.config.js",
|
|
57
|
+
"branch.config.cjs",
|
|
58
|
+
"branch.config.mjs"
|
|
59
|
+
];
|
|
60
|
+
async function loadConfig() {
|
|
61
|
+
for (const file of CONFIG_FILES) {
|
|
62
|
+
const configPath = resolve(cwd2(), file);
|
|
63
|
+
try {
|
|
64
|
+
await readFile(configPath, "utf-8");
|
|
65
|
+
if (file.endsWith(".ts") || file.endsWith(".mts")) {
|
|
66
|
+
const { default: config } = await import(`file://${configPath}?ts=${Date.now()}`);
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
if (file.endsWith(".mjs")) {
|
|
70
|
+
const { default: config } = await import(`file://${configPath}?ts=${Date.now()}`);
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
if (file.endsWith(".js") || file.endsWith(".cjs")) {
|
|
74
|
+
const { default: config } = await import(configPath);
|
|
75
|
+
return config;
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error.code !== "ENOENT") {
|
|
79
|
+
console.error(`Error loading config file ${file}:`, error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/cli.ts
|
|
87
|
+
var SUCCESS_CODE = 0;
|
|
88
|
+
var FAILED_CODE = 1;
|
|
89
|
+
var program = new Command();
|
|
90
|
+
program.name("validate-branch").description("Git branch name validation tool").version("0.1.0", "-v, --version");
|
|
91
|
+
program.addOption(
|
|
92
|
+
new Option("-t, --test <branch>", "Test target branch name (uses current branch by default)")
|
|
93
|
+
);
|
|
94
|
+
program.addOption(
|
|
95
|
+
new Option("-r, --regexp <regexp>", "Custom regular expression to test branch name")
|
|
96
|
+
);
|
|
97
|
+
program.addOption(
|
|
98
|
+
new Option("-p, --preset <preset>", "Use preset pattern (gitflow or jira)").choices([
|
|
99
|
+
"gitflow",
|
|
100
|
+
"jira"
|
|
101
|
+
])
|
|
102
|
+
);
|
|
103
|
+
async function main() {
|
|
104
|
+
program.parse(process.argv);
|
|
105
|
+
const options = program.opts();
|
|
106
|
+
const branch = options.test || await getCurrentBranchName();
|
|
107
|
+
if (!branch) {
|
|
108
|
+
console.error(chalk.red.bold("Error: Not a git repository\n"));
|
|
109
|
+
process.exit(FAILED_CODE);
|
|
110
|
+
}
|
|
111
|
+
const config = await loadConfig();
|
|
112
|
+
const customRegexp = options.regexp || config?.pattern;
|
|
113
|
+
const preset = options.preset || config?.preset || "gitflow";
|
|
114
|
+
console.log(chalk.cyan.bold(`
|
|
115
|
+
Validating branch: ${chalk.yellow(branch)}
|
|
116
|
+
`));
|
|
117
|
+
console.log(chalk.gray(`Preset: ${preset}
|
|
118
|
+
`));
|
|
119
|
+
if (config?.description) {
|
|
120
|
+
console.log(chalk.gray(`Config: ${config.description}
|
|
121
|
+
`));
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const result = validateWithDetails(branch, { customRegexp, preset });
|
|
125
|
+
if (result.valid) {
|
|
126
|
+
console.log(chalk.green.bold("\u2713 Valid branch name\n"));
|
|
127
|
+
process.exit(SUCCESS_CODE);
|
|
128
|
+
} else {
|
|
129
|
+
console.error(chalk.red.bold("\u2717 Invalid branch name\n"));
|
|
130
|
+
console.error(chalk.yellow(result.error));
|
|
131
|
+
process.exit(FAILED_CODE);
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
135
|
+
console.error(chalk.red.bold(`
|
|
136
|
+
Error: ${errorMessage}
|
|
137
|
+
`));
|
|
138
|
+
process.exit(FAILED_CODE);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
main();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type Preset = "gitflow" | "jira";
|
|
2
|
+
interface ValidateBranchNameOptions {
|
|
3
|
+
customRegexp?: string;
|
|
4
|
+
preset?: Preset;
|
|
5
|
+
}
|
|
6
|
+
declare function validateBranchName(branchName: string, options?: ValidateBranchNameOptions): boolean;
|
|
7
|
+
declare function getCurrentBranchName(): Promise<string | null>;
|
|
8
|
+
interface ValidationResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
branchName: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function validateWithDetails(branchName: string, options?: ValidateBranchNameOptions): ValidationResult;
|
|
14
|
+
|
|
15
|
+
interface Config {
|
|
16
|
+
pattern?: string;
|
|
17
|
+
patterns?: string[];
|
|
18
|
+
preset?: Preset;
|
|
19
|
+
description?: string;
|
|
20
|
+
}
|
|
21
|
+
declare function loadConfig(): Promise<Config | null>;
|
|
22
|
+
|
|
23
|
+
export { type Config, type Preset, type ValidateBranchNameOptions, type ValidationResult, getCurrentBranchName, loadConfig, validateBranchName, validateWithDetails };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/validate-branch-name.ts
|
|
2
|
+
import * as git from "isomorphic-git";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import { cwd } from "process";
|
|
5
|
+
var GITFLOW_PATTERN = /^(main|master|develop|stage|feature\/[A-Za-z0-9_-]+|fix\/[A-Za-z0-9_-]+|hotfix\/[A-Za-z0-9_-]+|release\/[A-Za-z0-9_.-]+)$/;
|
|
6
|
+
var JIRA_PATTERN = /^(main|master|develop|stage|[A-Z]+-[0-9]+)$/;
|
|
7
|
+
function validateBranchName(branchName, options) {
|
|
8
|
+
const { customRegexp, preset = "gitflow" } = options ?? {};
|
|
9
|
+
if (customRegexp) {
|
|
10
|
+
return new RegExp(customRegexp).test(branchName);
|
|
11
|
+
}
|
|
12
|
+
const pattern = preset === "jira" ? JIRA_PATTERN : GITFLOW_PATTERN;
|
|
13
|
+
return pattern.test(branchName);
|
|
14
|
+
}
|
|
15
|
+
async function getCurrentBranchName() {
|
|
16
|
+
try {
|
|
17
|
+
const branch = await git.currentBranch({
|
|
18
|
+
fs,
|
|
19
|
+
dir: cwd(),
|
|
20
|
+
fullname: false
|
|
21
|
+
});
|
|
22
|
+
return branch ?? null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function validateWithDetails(branchName, options) {
|
|
28
|
+
const { customRegexp, preset = "gitflow" } = options ?? {};
|
|
29
|
+
if (customRegexp) {
|
|
30
|
+
const pattern2 = new RegExp(customRegexp);
|
|
31
|
+
if (pattern2.test(branchName)) {
|
|
32
|
+
return { valid: true, branchName };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
valid: false,
|
|
36
|
+
branchName,
|
|
37
|
+
error: `Branch name "${branchName}" does not match pattern: ${pattern2.source}`
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const pattern = preset === "jira" ? JIRA_PATTERN : GITFLOW_PATTERN;
|
|
41
|
+
if (pattern.test(branchName)) {
|
|
42
|
+
return { valid: true, branchName };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
valid: false,
|
|
46
|
+
branchName,
|
|
47
|
+
error: `Branch name "${branchName}" does not match pattern: ${pattern.source}`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/load-config.ts
|
|
52
|
+
import { readFile } from "fs/promises";
|
|
53
|
+
import { resolve } from "path";
|
|
54
|
+
import { cwd as cwd2 } from "process";
|
|
55
|
+
var CONFIG_FILES = [
|
|
56
|
+
"branch.config.ts",
|
|
57
|
+
"branch.config.mts",
|
|
58
|
+
"branch.config.js",
|
|
59
|
+
"branch.config.cjs",
|
|
60
|
+
"branch.config.mjs"
|
|
61
|
+
];
|
|
62
|
+
async function loadConfig() {
|
|
63
|
+
for (const file of CONFIG_FILES) {
|
|
64
|
+
const configPath = resolve(cwd2(), file);
|
|
65
|
+
try {
|
|
66
|
+
await readFile(configPath, "utf-8");
|
|
67
|
+
if (file.endsWith(".ts") || file.endsWith(".mts")) {
|
|
68
|
+
const { default: config } = await import(`file://${configPath}?ts=${Date.now()}`);
|
|
69
|
+
return config;
|
|
70
|
+
}
|
|
71
|
+
if (file.endsWith(".mjs")) {
|
|
72
|
+
const { default: config } = await import(`file://${configPath}?ts=${Date.now()}`);
|
|
73
|
+
return config;
|
|
74
|
+
}
|
|
75
|
+
if (file.endsWith(".js") || file.endsWith(".cjs")) {
|
|
76
|
+
const { default: config } = await import(configPath);
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error.code !== "ENOENT") {
|
|
81
|
+
console.error(`Error loading config file ${file}:`, error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
getCurrentBranchName,
|
|
89
|
+
loadConfig,
|
|
90
|
+
validateBranchName,
|
|
91
|
+
validateWithDetails
|
|
92
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gracefullight/validate-branch",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Git branch name validation tool with custom regexp support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"validate-branch": "./dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"git",
|
|
24
|
+
"branch",
|
|
25
|
+
"validation",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "Eunkwang Shin",
|
|
30
|
+
"email": "gracefullight.dev@gmail.com"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "http://github.com/gracefullight/workspace/packages/validate-branch"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^24.10.4",
|
|
42
|
+
"tsup": "^8.5.1",
|
|
43
|
+
"tsx": "^4.19.2",
|
|
44
|
+
"typescript": "^5.7.3",
|
|
45
|
+
"vitest": "^4.0.7"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"chalk": "^5.4.1",
|
|
49
|
+
"commander": "^12.1.0",
|
|
50
|
+
"isomorphic-git": "^1.30.1"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"dev": "tsx src/cli.ts",
|
|
55
|
+
"test": "vitest",
|
|
56
|
+
"lint": "biome check src",
|
|
57
|
+
"lint:fix": "biome check --write src",
|
|
58
|
+
"format": "biome format --write src"
|
|
59
|
+
}
|
|
60
|
+
}
|