projectops 4.2.2 โ 4.2.4
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 +53 -51
- package/package.json +2 -2
- package/src/cli/args.js +7 -0
- package/src/cli/help.js +1 -0
- package/src/commands/full.js +8 -4
- package/src/commands/interactive.js +30 -4
- package/src/commands/version.js +6 -3
- package/src/context.js +5 -0
- package/src/core/copy/coderabbit.js +6 -3
- package/src/core/copy/simple.js +2 -2
- package/src/core/ide/adapters/claude.js +4 -4
- package/src/core/ide/adapters/codex.js +3 -3
- package/src/core/ide/adapters/cursor.js +1 -1
- package/src/core/ide/adapters/gemini.js +1 -1
- package/src/core/ide/adapters/pi-common.js +1 -1
- package/src/core/ide/adapters/pi.js +1 -1
- package/src/core/options-ask.js +82 -1
- package/src/core/version-yml.js +40 -5
- package/src/index.js +6 -0
- package/src/ui/readline-engine.js +19 -5
- package/src/ui/status-cards.js +4 -0
- package/src/ui/summary.js +1 -1
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# ๐ Projectops
|
|
4
4
|
|
|
5
|
-
**GitHub Actions ์๋ํ + Agent AI Skills โ ๊ฐ๋ฐ ์ฌ์ดํด ์ ์ฒด๋ฅผ ์๋ํํ๋ DevOps
|
|
5
|
+
**GitHub Actions ์๋ํ + Agent AI Skills โ ๊ฐ๋ฐ ์ฌ์ดํด ์ ์ฒด๋ฅผ ์๋ํํ๋ DevOps ํ
ํ๋ฆฟ**
|
|
6
6
|
|
|
7
7
|
> ์ด์ ๋ฑ๋ก๋ถํฐ ์ปค๋ฐ, ๋ณด๊ณ ์, ๋ฐฐํฌ๊น์ง. ๊ฐ๋ฐ์๋ ์ฝ๋๋ง ์์ฑํ์ธ์.
|
|
8
8
|
|
|
9
9
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
10
|
-
## ์ต์ ๋ฒ์ : v4.2.
|
|
10
|
+
## ์ต์ ๋ฒ์ : v4.2.3 (2026-07-09)
|
|
11
11
|
|
|
12
12
|
[์ ์ฒด ๋ฒ์ ๊ธฐ๋ก ๋ณด๊ธฐ](CHANGELOG.md)
|
|
13
13
|
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
| ๋ฒ์ ์๋ ๊ด๋ฆฌ, ํ๊ทธ ์ง์ ์์ฑ | ๋ฆด๋ฆฌ์ค(developโmain PR) ์ patch ๋ฒ์ ์๋ ์ฆ๊ฐ + ํ๊ทธ ์์ฑ |
|
|
28
28
|
| ์ฒด์ธ์ง๋ก๊ทธ ์ง์ ์์ฑ (30๋ถ+) | CodeRabbit AI๊ฐ PR๋ง๋ค ์๋ ์์ฑ |
|
|
29
29
|
| CI/CD ์ฒ์๋ถํฐ ์ค์ | ํ๋ก์ ํธ ํ์
๋ณ ์ํฌํ๋ก์ฐ ์ฆ์ ๊ตฌ์ฑ |
|
|
30
|
-
| ์ด์ ๋งค๋ฒ ํ์ ๋ง์ถฐ ์์ฑ (5๋ถ+) | `/
|
|
31
|
-
| ์ปค๋ฐ ๋ฉ์์ง ์ด์ URL ์๋ ๋ณต์ฌ | `/
|
|
32
|
-
| PR ์ค๋ช
/๋ณด๊ณ ์ ์ง์ ์์ฑ | `/
|
|
30
|
+
| ์ด์ ๋งค๋ฒ ํ์ ๋ง์ถฐ ์์ฑ (5๋ถ+) | `/projectops:issue` ํ ๋ฒ์ ํ์ค ํ
ํ๋ฆฟ ์์ฑ |
|
|
31
|
+
| ์ปค๋ฐ ๋ฉ์์ง ์ด์ URL ์๋ ๋ณต์ฌ | `/projectops:commit` ์ด์ ์ปจํ
์คํธ ๊ธฐ๋ฐ ์๋ ์์ฑ |
|
|
32
|
+
| PR ์ค๋ช
/๋ณด๊ณ ์ ์ง์ ์์ฑ | `/projectops:report` git diff ๋ถ์ ํ ์๋ ์์ฑ |
|
|
33
33
|
| ์ฝ๋ ๋ฆฌ๋ทฐยท๋ถ์ ๋งค๋ฒ ํ๋กฌํํธ ์
๋ ฅ | 25์ข
Skills๋ก ์ผ๊ด๋ ๊ฒฐ๊ณผ, ๋งค๋ฒ ์ฌ์
๋ ฅ ๋ถํ์ |
|
|
34
34
|
|
|
35
35
|
---
|
|
@@ -40,23 +40,23 @@ Agent Skills๊ฐ ๊ฐ๋ฐ ์ฌ์ดํด ์ ์ฒด๋ฅผ ์ปค๋ฒํฉ๋๋ค.
|
|
|
40
40
|
|
|
41
41
|
```mermaid
|
|
42
42
|
flowchart TD
|
|
43
|
-
A([์์
์์]) --> B["/
|
|
44
|
-
B --> C["/
|
|
43
|
+
A([์์
์์]) --> B["/projectops:issue\n์ด์ ๋ฑ๋ก + GitHub ์๋ ์์ฑ"]
|
|
44
|
+
B --> C["/projectops:init-worktree\nworktree + ๋ฏผ๊ฐํ์ผ ์๋ ๋ณต์ฌ"]
|
|
45
45
|
C --> D{์์
์ ํ}
|
|
46
46
|
|
|
47
|
-
D -->|์ ๊ธฐ๋ฅ| E1["/
|
|
48
|
-
D -->|๋ฒ๊ทธ| E2["/
|
|
49
|
-
D -->|๋ฆฌํฉํ ๋ง| E3["/
|
|
47
|
+
D -->|์ ๊ธฐ๋ฅ| E1["/projectops:plan\n์ ๋ต ์๋ฆฝ"]
|
|
48
|
+
D -->|๋ฒ๊ทธ| E2["/projectops:troubleshoot\n์์ธ ๋ถ์"]
|
|
49
|
+
D -->|๋ฆฌํฉํ ๋ง| E3["/projectops:refactor-analyze\nSmell ํ์ง"]
|
|
50
50
|
|
|
51
|
-
E1 --> F["/
|
|
51
|
+
E1 --> F["/projectops:implement\n๊ตฌํ"]
|
|
52
52
|
E2 --> F
|
|
53
53
|
E3 --> F
|
|
54
|
-
F --> G["/
|
|
55
|
-
G --> H["/
|
|
56
|
-
H --> I["/
|
|
57
|
-
I --> J["/
|
|
54
|
+
F --> G["/projectops:test\nํ
์คํธ"]
|
|
55
|
+
G --> H["/projectops:review\n์
ํ ๋ฆฌ๋ทฐ"]
|
|
56
|
+
H --> I["/projectops:commit\n์ด์ ์ฐ๋ ์ปค๋ฐ ์๋ ์์ฑ"]
|
|
57
|
+
I --> J["/projectops:report\n๊ตฌํ ๋ณด๊ณ ์ + GitHub ๋๊ธ"]
|
|
58
58
|
J --> K([PR ๋ฑ๋ก])
|
|
59
|
-
K --> L["/
|
|
59
|
+
K --> L["/projectops:changelog-deploy\ndevelop push โ main ๋ฆด๋ฆฌ์ค PR + automerge"]
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
> Skills ์ ์ฒด ๋ชฉ๋ก ๋ฐ ์์ธ ์ฌ์ฉ๋ฒ: **[docs/SKILLS.md](docs/SKILLS.md)**
|
|
@@ -94,7 +94,9 @@ npx projectops
|
|
|
94
94
|
> Node.js 20.12+ ๋ง ์์ผ๋ฉด ๋ณ๋ ์ค์น ์์ด ๋ํํ ๋ง๋ฒ์ฌ๊ฐ ์คํ๋ฉ๋๋ค. ๋น๋ํํ: `npx projectops --mode full --type spring,react --force`
|
|
95
95
|
|
|
96
96
|
<details>
|
|
97
|
-
<summary>๋์ โ ์คํฌ๋ฆฝํธ ์ง์ ์คํ (Node
|
|
97
|
+
<summary>๋์ โ ์คํฌ๋ฆฝํธ ์ง์ ์คํ (Node ์์ด, <b>deprecated</b>)</summary>
|
|
98
|
+
|
|
99
|
+
> โ ๏ธ **`template_integrator.sh` / `.ps1`์ deprecated์
๋๋ค (#458).** ์คํํ๋ฉด `npx projectops`๋ก ์๋ดํ๋ฉฐ, ๋ค์ minor์์ ์ ๊ฑฐ๋ ์ ์์ต๋๋ค. Node 20.12+๋ฅผ ์ธ ์ ์์ผ๋ฉด ์์ `npx projectops`๋ฅผ ์ฌ์ฉํ์ธ์.
|
|
98
100
|
|
|
99
101
|
```bash
|
|
100
102
|
# macOS / Linux
|
|
@@ -113,7 +115,7 @@ $wc=New-Object Net.WebClient;$wc.Encoding=[Text.Encoding]::UTF8;iex $wc.Download
|
|
|
113
115
|
```bash
|
|
114
116
|
# Claude Code
|
|
115
117
|
claude plugin marketplace add Cassiiopeia/projectops
|
|
116
|
-
claude plugin install
|
|
118
|
+
claude plugin install projectops@projectops-marketplace --scope user
|
|
117
119
|
```
|
|
118
120
|
|
|
119
121
|
```bash
|
|
@@ -144,7 +146,7 @@ bash <(curl -fsSL "https://raw.githubusercontent.com/Cassiiopeia/projectops/main
|
|
|
144
146
|
|
|
145
147
|
</details>
|
|
146
148
|
|
|
147
|
-
> Claude Code๋ `/
|
|
149
|
+
> Claude Code๋ `/projectops:` ์๋์์ฑ, Gemini๋ extension, Codex๋ plugin marketplace๋ฅผ ์ฐ์ ์ฌ์ฉํฉ๋๋ค. ์์ธํ ์ค์น ๋ฐฉ์์ [Skills ๊ฐ์ด๋](docs/SKILLS.md)๋ฅผ ํ์ธํ์ธ์.
|
|
148
150
|
|
|
149
151
|
---
|
|
150
152
|
|
|
@@ -169,46 +171,46 @@ bash <(curl -fsSL "https://raw.githubusercontent.com/Cassiiopeia/projectops/main
|
|
|
169
171
|
|
|
170
172
|
| ์คํฌ | ์ฉ๋ |
|
|
171
173
|
|------|------|
|
|
172
|
-
| `/
|
|
173
|
-
| `/
|
|
174
|
-
| `/
|
|
175
|
-
| `/
|
|
176
|
-
| `/
|
|
177
|
-
| `/
|
|
174
|
+
| `/projectops:issue` | ์ค๋ช
ํ ์ค โ GitHub ์ด์ ํ
ํ๋ฆฟ ์๋ ์์ฑ + ๋ฑ๋ก |
|
|
175
|
+
| `/projectops:init-worktree` | Git worktree ์์ฑ + ๋ฏผ๊ฐ ํ์ผ ์๋ ๋ณต์ฌ |
|
|
176
|
+
| `/projectops:commit` | ์ด์ ์ปจํ
์คํธ ๊ธฐ๋ฐ ์ปค๋ฐ ๋ฉ์์ง ์๋ ์์ฑ (superpowers ์ค์) |
|
|
177
|
+
| `/projectops:report` | git diff ๋ถ์ โ ๊ตฌํ ๋ณด๊ณ ์ ์์ฑ + GitHub ๋๊ธ ์๋ ํฌ์คํ
|
|
|
178
|
+
| `/projectops:changelog-deploy` | develop push โ main ๋ฆด๋ฆฌ์ค PR ์์ฑ + ๋ฆด๋ฆฌ์ค ๋
ธํธ ์์ฑ + automerge |
|
|
179
|
+
| `/projectops:github` | GitHub ์ด์/PR/๋๊ธ ๋
๋ฆฝ ์กฐํ ๋ฐ ๊ด๋ฆฌ |
|
|
178
180
|
|
|
179
181
|
### ๐ ๋ถ์ํ (์ฝ๋ ์์ ์์)
|
|
180
182
|
|
|
181
183
|
| ์คํฌ | ์ฉ๋ |
|
|
182
184
|
|------|------|
|
|
183
|
-
| `/
|
|
184
|
-
| `/
|
|
185
|
-
| `/
|
|
186
|
-
| `/
|
|
187
|
-
| `/
|
|
188
|
-
| `/
|
|
185
|
+
| `/projectops:analyze` | ๊ตฌํ ์ ํ์ฌ ์ฝ๋ ์ํ ๋ถ์ ๋ฐ ์ํฅ ๋ฒ์ ํ๊ฐ |
|
|
186
|
+
| `/projectops:plan` | ์๊ตฌ์ฌํญ ๋ช
ํํ + 2๊ฐ์ง ์ด์ ์ ๊ทผ ๋ฐฉ์ ๋น๊ต๋ก ์ ๋ต ์๋ฆฝ |
|
|
187
|
+
| `/projectops:design-analyze` | ์ํคํ
์ฒ/API/DB/UI ์ค๊ณ ๋ถ์ (๊ตฌํ X) |
|
|
188
|
+
| `/projectops:refactor-analyze` | Code Smell ํ์ง + Before/After ๊ธฐ๋ฐ ๋ฆฌํฉํ ๋ง ๊ณํ |
|
|
189
|
+
| `/projectops:review` | ๋ณด์/์ฑ๋ฅ/๋ฒ๊ทธ/ํ์ง 6๊ด์ ๋ฆฌ๋ทฐ, Critical/Major/Minor ๋ถ๋ฅ |
|
|
190
|
+
| `/projectops:troubleshoot` | ๊ฐ์ค-๊ฒ์ฆ ๋ฐฉ์ ๊ทผ๋ณธ ์์ธ ๋ถ์, Quick Fix/Root Fix ์ ์ |
|
|
189
191
|
|
|
190
192
|
### ๐ง ๊ตฌํํ (์ค์ ์ฝ๋ ์์ฑ)
|
|
191
193
|
|
|
192
194
|
| ์คํฌ | ์ฉ๋ |
|
|
193
195
|
|------|------|
|
|
194
|
-
| `/
|
|
195
|
-
| `/
|
|
196
|
-
| `/
|
|
197
|
-
| `/
|
|
198
|
-
| `/
|
|
199
|
-
| `/
|
|
196
|
+
| `/projectops:implement` | ๊ณํ/๋ถ์ ๊ฒฐ๊ณผ ๊ธฐ๋ฐ ์ฝ๋ ๊ตฌํ (๊ธฐ์กด ์คํ์ผ 100% ์ค์) |
|
|
197
|
+
| `/projectops:design` | ์ํคํ
์ฒ/API/DB/UI ์ค๊ณ + ๊ตฌํ๊น์ง |
|
|
198
|
+
| `/projectops:refactor` | Extract Method, DRY ๋ฑ ๋ฆฌํฉํ ๋ง ๊ธฐ๋ฒ ๋จ๊ณ๋ณ ์ ์ฉ |
|
|
199
|
+
| `/projectops:test` | AAA ํจํด ๋จ์/ํตํฉ/E2E ํ
์คํธ ์ฝ๋ ์์ฑ |
|
|
200
|
+
| `/projectops:figma` | Figma CSS โ React/RN/Flutter ๋ฐ์ํ ์ฝ๋ ๋ณํ |
|
|
201
|
+
| `/projectops:build` | ํ๋ก์ ํธ ๋น๋ ์คํ, ์๋ฌ ๋ถ์, ์ต์ ํ ์ ์ |
|
|
200
202
|
|
|
201
203
|
### ๐ ๋ฌธ์/์ฐ์ถ๋ฌผ ์์ฑํ
|
|
202
204
|
|
|
203
205
|
| ์คํฌ | ์ฉ๋ |
|
|
204
206
|
|------|------|
|
|
205
|
-
| `/
|
|
206
|
-
| `/
|
|
207
|
-
| `/
|
|
208
|
-
| `/
|
|
209
|
-
| `/
|
|
210
|
-
| `/
|
|
211
|
-
| `/
|
|
207
|
+
| `/projectops:document` | ์ฝ๋ ์ฃผ์/README/API ๋ฌธ์ ์์ฑ |
|
|
208
|
+
| `/projectops:testcase` | ์ด์ ๋ถ์ โ QA ์ฒดํฌ๋ฆฌ์คํธ ์์ฑ |
|
|
209
|
+
| `/projectops:ppt` | ํธ๋ฌ๋ธ์ํ
/๊ตฌํ ์ฌ๋ก โ 5์น์
๋ฐํ์๋ฃ |
|
|
210
|
+
| `/projectops:spring-test` | Spring Boot ํ
์คํธ ์ํ ์ฝ๋ ์์ฑ |
|
|
211
|
+
| `/projectops:synology-expose` | Synology NAS ์ธ๋ถ ๋๋ฉ์ธ ๋
ธ์ถ ์ค์ ๊ฐ์ด๋ |
|
|
212
|
+
| `/projectops:ssh` | ์๊ฒฉ ์๋ฒ SSH ์ ์ยท๋ช
๋ น ์คํ (AWS EC2, ์๋๋ก์ง NAS, Linux ๋ฑ ๋ฒ์ฉ) |
|
|
213
|
+
| `/projectops:skill-creator` | Skill ์์ฑ/๋ฆฌ๋ทฐ/๊ฐ์ (CREATEยทREVIEWยทIMPROVE 3๋ชจ๋) |
|
|
212
214
|
|
|
213
215
|
---
|
|
214
216
|
|
|
@@ -233,13 +235,13 @@ Issue๋ PR์ ๋๊ธ๋ก ์๋ํ๋ฅผ ์คํํฉ๋๋ค.
|
|
|
233
235
|
|
|
234
236
|
| ๋ช
๋ น์ด | ๊ธฐ๋ฅ | ๋์ |
|
|
235
237
|
|--------|------|------|
|
|
236
|
-
| `@
|
|
237
|
-
| `@
|
|
238
|
-
| `@
|
|
239
|
-
| `@
|
|
240
|
-
| `@
|
|
241
|
-
| `@
|
|
242
|
-
| `@
|
|
238
|
+
| `@projectops server build` | ์์ ์๋ฒ ๋ฐฐํฌ | Spring, Python |
|
|
239
|
+
| `@projectops server destroy` | ์๋ฒ ์ญ์ | Spring, Python |
|
|
240
|
+
| `@projectops server status` | ์๋ฒ ์ํ ํ์ธ | Spring, Python |
|
|
241
|
+
| `@projectops build app` | iOS + Android ๋น๋ | Flutter |
|
|
242
|
+
| `@projectops apk build` | Android๋ง ๋น๋ | Flutter |
|
|
243
|
+
| `@projectops ios build` | iOS๋ง ๋น๋ | Flutter |
|
|
244
|
+
| `@projectops create qa` | QA ์ด์ ์๋ ์์ฑ | ๋ชจ๋ ํ๋ก์ ํธ |
|
|
243
245
|
|
|
244
246
|
> ์์ธ: [PR Preview](docs/PR-PREVIEW.md) | [Flutter ๋น๋](docs/FLUTTER-TEST-BUILD-TRIGGER.md) | [์ด์ ์๋ํ](docs/ISSUE-AUTOMATION.md)
|
|
245
247
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectops",
|
|
3
|
-
"version": "4.2.
|
|
4
|
-
"description": "ProjectOps โ ์์ ์๋ํ GitHub ํ๋ก์ ํธ ๊ด๋ฆฌ ํ
ํ๋ฆฟ ํตํฉ CLI
|
|
3
|
+
"version": "4.2.4",
|
|
4
|
+
"description": "ProjectOps โ ์์ ์๋ํ GitHub ํ๋ก์ ํธ ๊ด๋ฆฌ ํ
ํ๋ฆฟ ํตํฉ CLI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"devops",
|
|
7
7
|
"automation",
|
package/src/cli/args.js
CHANGED
|
@@ -13,6 +13,7 @@ export function parseArgs(argv) {
|
|
|
13
13
|
primaryType: "",
|
|
14
14
|
deployTarget: null, // ๋ฐฐํฌ ์ถ (#439): docker-ssh|vercel|none, null=๋ฏธ์ค์
|
|
15
15
|
publishTargets: null, // publish ์ถ (#439): ํ๊ฒ ๋ฐฐ์ด, null=๋ฏธ์ค์
|
|
16
|
+
deployBranch: "", // ๋ฆด๋ฆฌ์ค PR head ๋ธ๋์น (#456): --deploy-branch, ๋น ๊ฐ=๋ฏธ์ง์
|
|
16
17
|
includeSecretBackup: null,
|
|
17
18
|
pathsCsv: "", // "flutter=app,react=client" ์๋ฌธ (์ ๊ทํ๋ resolve ๋จ๊ณ)
|
|
18
19
|
force: false,
|
|
@@ -72,6 +73,12 @@ export function parseArgs(argv) {
|
|
|
72
73
|
result.publishTargets = targets;
|
|
73
74
|
break;
|
|
74
75
|
}
|
|
76
|
+
case "--deploy-branch": {
|
|
77
|
+
// ๋ฆด๋ฆฌ์ค PR head ๋ธ๋์น (#456). default_branch์ ๋ณ๊ฐ.
|
|
78
|
+
const v = (args.shift() ?? "").trim();
|
|
79
|
+
if (v) result.deployBranch = v;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
75
82
|
// โโ deprecated alias (1 minor ์ ์ง โ #439) โโ
|
|
76
83
|
case "--nexus":
|
|
77
84
|
process.stderr.write("โ ๏ธ --nexus๋ deprecated์
๋๋ค. --publish nexus --deploy none ์ ์ฌ์ฉํ์ธ์.\n");
|
package/src/cli/help.js
CHANGED
|
@@ -15,6 +15,7 @@ export const HELP_TEXT = `projectops โ GitHub ํ๋ก์ ํธ ์๋ํ ํ
ํ๋ฆฟ
|
|
|
15
15
|
--paths "t=p,..." ํ์
๋ณ ํ๋ก์ ํธ ๊ฒฝ๋ก (๋ชจ๋
ธ๋ ํฌ). ์: flutter=app,react=client
|
|
16
16
|
--deploy TARGET ๋ฐฐํฌ ๋ฐฉ์ ํ1: docker-ssh(๊ธฐ๋ณธ) | vercel | none
|
|
17
17
|
--publish CSV publish ํ๊ฒ csv: nexus,npm,github-packages (๊ธฐ๋ณธ: ์์)
|
|
18
|
+
--deploy-branch NAME ๋ฆด๋ฆฌ์ค PR head ๋ธ๋์น (#456, ๊ธฐ๋ณธ: develop). default_branch์ ๋ณ๊ฐ
|
|
18
19
|
--secret-backup / --no-secret-backup Secret ๋ฐฑ์
์ํฌํ๋ก์ฐ ํฌํจ/์ ์ธ
|
|
19
20
|
--nexus / --npm-publish (deprecated โ --publish nexus / --publish npm ์ฌ์ฉ)
|
|
20
21
|
--force ๋ชจ๋ ํ์ธ ์๋ต, ๋น๋ํํ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
|
package/src/commands/full.js
CHANGED
|
@@ -22,7 +22,9 @@ import { ensureGitignore } from "../core/copy/gitignore.js";
|
|
|
22
22
|
export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
23
23
|
const { version, types = [], paths = new Map(), branch = "main", versionCode = 1,
|
|
24
24
|
force = true, now, today, templateVersion = "unknown",
|
|
25
|
-
deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false
|
|
25
|
+
deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false,
|
|
26
|
+
changelogProvider = "github-ai", changelogBaseUrl = "", codeReviewCoderabbit = true,
|
|
27
|
+
deployBranch = "" } = context;
|
|
26
28
|
|
|
27
29
|
// project_paths ๋ง์ปค ๊ณ์ฐ (.sh existing_marker_in_dir ๋ฑ๊ฐ โ ๋ํ ๋ง์ปค๋ช
)
|
|
28
30
|
const pathMarkers = new Map();
|
|
@@ -36,9 +38,10 @@ export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
|
36
38
|
// 1. version.yml ์์ฑ (์ ์ฒด ์ฌ์์ฑ โ metadata โ deploy โ template ์, .sh ์ต์ข
ํ๊ณผ ๋์ผ)
|
|
37
39
|
writeText(join(targetRoot, PATHS.versionFile),
|
|
38
40
|
buildVersionYml({
|
|
39
|
-
version, types, paths, pathMarkers, branch, versionCode, now, today,
|
|
41
|
+
version, types, paths, pathMarkers, branch, deployBranch, versionCode, now, today,
|
|
40
42
|
deployValues,
|
|
41
|
-
templateOptions: { templateVersion, deployTarget, publishTargets, includeSecretBackup, optionsDate: today
|
|
43
|
+
templateOptions: { templateVersion, deployTarget, publishTargets, includeSecretBackup, optionsDate: today,
|
|
44
|
+
changelogProvider, changelogBaseUrl, codeReviewCoderabbit },
|
|
42
45
|
}));
|
|
43
46
|
|
|
44
47
|
// 2. README ๋ฒ์ ์น์
|
|
@@ -56,7 +59,8 @@ export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
|
56
59
|
copyDiscussionTemplates(tempDir, targetRoot);
|
|
57
60
|
|
|
58
61
|
// 8. coderabbit / gitignore / setup guide
|
|
59
|
-
|
|
62
|
+
// CodeRabbit ์ฝ๋๋ฆฌ๋ทฐ ๋ฏธ์ฌ์ฉ ์ ํ(#457)์ด๋ฉด .coderabbit.yaml์ ๋ณต์ฌํ์ง ์๋๋ค.
|
|
63
|
+
copyCoderabbit(tempDir, { force, enabled: codeReviewCoderabbit }, targetRoot);
|
|
60
64
|
ensureGitignore(targetRoot);
|
|
61
65
|
copySetupGuide(tempDir, targetRoot);
|
|
62
66
|
|
|
@@ -83,6 +83,10 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
83
83
|
let deployTarget = existing?.options?.deploy ?? "docker-ssh";
|
|
84
84
|
let publishTargets = existing?.options?.publish ?? [];
|
|
85
85
|
let includeSecretBackup = existing?.options?.secretBackup ?? false;
|
|
86
|
+
let codeReviewCoderabbit = existing?.options?.codeReviewCoderabbit ?? true;
|
|
87
|
+
let changelogProvider = existing?.options?.changelogProvider ?? "github-ai";
|
|
88
|
+
let changelogBaseUrl = existing?.options?.changelogBaseUrl ?? "";
|
|
89
|
+
let deployBranch = existing?.options?.deployBranch ?? "develop"; // #456
|
|
86
90
|
const showOptional = mode === "full" || mode === "workflows";
|
|
87
91
|
const realTty = process.stdout.isTTY === true;
|
|
88
92
|
|
|
@@ -93,17 +97,28 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
93
97
|
if (showOptional) {
|
|
94
98
|
const r = await askAllOptionalWorkflows({
|
|
95
99
|
tempDir, types, targetRoot: cwd,
|
|
96
|
-
current: {
|
|
100
|
+
current: {
|
|
101
|
+
deploy: existing?.options?.deploy ?? null, publish: existing?.options?.publish ?? null, secretBackup: existing?.options?.secretBackup ?? null,
|
|
102
|
+
codeReviewCoderabbit: existing?.options?.codeReviewCoderabbit ?? null,
|
|
103
|
+
changelogProvider: existing?.options?.changelogProvider ?? null,
|
|
104
|
+
changelogBaseUrl: existing?.options?.changelogBaseUrl ?? null,
|
|
105
|
+
deployBranch: existing?.options?.deployBranch ?? null,
|
|
106
|
+
},
|
|
97
107
|
force: false, tty: realTty,
|
|
98
108
|
io: {
|
|
99
109
|
confirm: ({ message, initialValue }) => io.askYesNo(message, initialValue),
|
|
100
110
|
select: io.engineIo?.select,
|
|
101
111
|
multiselect: io.engineIo?.multiselect,
|
|
112
|
+
text: io.engineIo?.text ?? (({ message }) => io.askText?.(message, "")),
|
|
102
113
|
},
|
|
103
114
|
});
|
|
104
115
|
deployTarget = r.deploy;
|
|
105
116
|
publishTargets = r.publish;
|
|
106
117
|
includeSecretBackup = r.secretBackup;
|
|
118
|
+
codeReviewCoderabbit = r.codeReviewCoderabbit;
|
|
119
|
+
changelogProvider = r.changelogProvider;
|
|
120
|
+
changelogBaseUrl = r.changelogBaseUrl;
|
|
121
|
+
deployBranch = r.deployBranch;
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
// ํ์ธ/์์ ๋ฃจํ โ ESC๋ '๋จธ๋ฌด๋ฅด๊ธฐ' (.sh L1877~1881: ๋ช
์์ '์๋์ค'๋ง ์ข
๋ฃ)
|
|
@@ -114,7 +129,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
114
129
|
if (io.analysisCard) {
|
|
115
130
|
io.analysisCard({ mode, modeLabel: modeLabel(mode), types, version, branch, deployTarget, publishTargets, includeSecretBackup, showOptional, paths });
|
|
116
131
|
} else {
|
|
117
|
-
io.note?.(summarize({ mode, types, version, branch, deployTarget, publishTargets, includeSecretBackup, showOptional }), "ํ๋ก์ ํธ ๋ถ์ ๊ฒฐ๊ณผ");
|
|
132
|
+
io.note?.(summarize({ mode, types, version, branch, deployTarget, publishTargets, includeSecretBackup, showOptional, changelogProvider, codeReviewCoderabbit }), "ํ๋ก์ ํธ ๋ถ์ ๊ฒฐ๊ณผ");
|
|
118
133
|
}
|
|
119
134
|
const choice = await io.confirmProjectMenu();
|
|
120
135
|
if (choice === "cancel") { io.cancelMessage?.("์ค์น๋ฅผ ์ทจ์ํ์ต๋๋ค."); return 0; }
|
|
@@ -147,17 +162,25 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
147
162
|
// ๋ฐฐํฌ/publish ์ถ ์ฌ์ง๋ฌธ (#439 โ forceAsk)
|
|
148
163
|
const r = await askAllOptionalWorkflows({
|
|
149
164
|
tempDir, types, targetRoot: cwd,
|
|
150
|
-
current: {
|
|
165
|
+
current: {
|
|
166
|
+
deploy: deployTarget, publish: publishTargets, secretBackup: includeSecretBackup,
|
|
167
|
+
codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch,
|
|
168
|
+
},
|
|
151
169
|
force: false, tty: realTty, forceAsk: true,
|
|
152
170
|
io: {
|
|
153
171
|
confirm: ({ message, initialValue }) => io.askYesNo(message, initialValue),
|
|
154
172
|
select: io.engineIo?.select,
|
|
155
173
|
multiselect: io.engineIo?.multiselect,
|
|
174
|
+
text: io.engineIo?.text ?? (({ message }) => io.askText?.(message, "")),
|
|
156
175
|
},
|
|
157
176
|
});
|
|
158
177
|
deployTarget = r.deploy;
|
|
159
178
|
publishTargets = r.publish;
|
|
160
179
|
includeSecretBackup = r.secretBackup;
|
|
180
|
+
codeReviewCoderabbit = r.codeReviewCoderabbit;
|
|
181
|
+
changelogProvider = r.changelogProvider;
|
|
182
|
+
changelogBaseUrl = r.changelogBaseUrl;
|
|
183
|
+
deployBranch = r.deployBranch;
|
|
161
184
|
} else if (what === "secret") {
|
|
162
185
|
const y = await io.askYesNo("Secret ๋ฐฑ์
์ํฌํ๋ก์ฐ๋ฅผ ํฌํจํ ๊น์?", includeSecretBackup);
|
|
163
186
|
if (!isCancel(y)) includeSecretBackup = y === true;
|
|
@@ -190,6 +213,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
190
213
|
const { now, today } = clock || utcNow();
|
|
191
214
|
const ctx = createContext({
|
|
192
215
|
mode, force: true, types, version, versionCode, branch, paths, deployTarget, publishTargets, includeSecretBackup,
|
|
216
|
+
codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch,
|
|
193
217
|
repoName, templateVersion, resolvers, envValues, envUseDefaults, now, today,
|
|
194
218
|
});
|
|
195
219
|
ctx.templateVersion = templateVersion;
|
|
@@ -240,7 +264,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
240
264
|
}
|
|
241
265
|
}
|
|
242
266
|
|
|
243
|
-
function summarize({ mode, types, version, branch, deployTarget, publishTargets, includeSecretBackup, showOptional }) {
|
|
267
|
+
function summarize({ mode, types, version, branch, deployTarget, publishTargets, includeSecretBackup, showOptional, changelogProvider, codeReviewCoderabbit }) {
|
|
244
268
|
const lines = [
|
|
245
269
|
`ํตํฉ ๋ชจ๋ : ${modeLabel(mode)}`,
|
|
246
270
|
`ํ๋ก์ ํธ ํ์
: ${types.join(", ")}${types.length > 1 ? " (๋ฉํฐ)" : ""}`,
|
|
@@ -251,6 +275,8 @@ function summarize({ mode, types, version, branch, deployTarget, publishTargets,
|
|
|
251
275
|
lines.push(`๋ฐฐํฌ ๋ฐฉ์ : ${deployTarget || "docker-ssh"}`);
|
|
252
276
|
lines.push(`Publish : ${(publishTargets ?? []).join(",") || "์์"}`);
|
|
253
277
|
lines.push(`Secret ๋ฐฑ์
: ${includeSecretBackup ? "ํฌํจ" : "์ ์ธ"}`);
|
|
278
|
+
lines.push(`Changelog : ${changelogProvider || "github-ai"}`);
|
|
279
|
+
lines.push(`CodeRabbit ๋ฆฌ๋ทฐ : ${codeReviewCoderabbit ? "์ฌ์ฉ" : "๋ฏธ์ฌ์ฉ"}`);
|
|
254
280
|
}
|
|
255
281
|
return lines.join("\n");
|
|
256
282
|
}
|
package/src/commands/version.js
CHANGED
|
@@ -12,15 +12,18 @@ import { ensureGitignore } from "../core/copy/gitignore.js";
|
|
|
12
12
|
|
|
13
13
|
export function runVersion(context, tempDir, targetRoot = ".") {
|
|
14
14
|
const { version, types = [], paths = new Map(), branch = "main", versionCode = 1,
|
|
15
|
-
now, today, templateVersion = "unknown", deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false
|
|
15
|
+
now, today, templateVersion = "unknown", deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false,
|
|
16
|
+
changelogProvider = "github-ai", changelogBaseUrl = "", codeReviewCoderabbit = true,
|
|
17
|
+
deployBranch = "" } = context;
|
|
16
18
|
|
|
17
19
|
const pathMarkers = new Map();
|
|
18
20
|
for (const [t] of paths) pathMarkers.set(t, markerForType(t));
|
|
19
21
|
|
|
20
22
|
writeText(join(targetRoot, PATHS.versionFile),
|
|
21
23
|
buildVersionYml({
|
|
22
|
-
version, types, paths, pathMarkers, branch, versionCode, now, today,
|
|
23
|
-
templateOptions: { templateVersion, deployTarget, publishTargets, includeSecretBackup, optionsDate: today
|
|
24
|
+
version, types, paths, pathMarkers, branch, deployBranch, versionCode, now, today,
|
|
25
|
+
templateOptions: { templateVersion, deployTarget, publishTargets, includeSecretBackup, optionsDate: today,
|
|
26
|
+
changelogProvider, changelogBaseUrl, codeReviewCoderabbit },
|
|
24
27
|
}));
|
|
25
28
|
addVersionSectionToReadme(version, targetRoot);
|
|
26
29
|
copyScripts(tempDir, targetRoot);
|
package/src/context.js
CHANGED
|
@@ -19,6 +19,11 @@ export function createContext(overrides = {}) {
|
|
|
19
19
|
deployTarget: null, // 'docker-ssh'(๊ธฐ๋ณธ) | 'vercel' | 'none'
|
|
20
20
|
publishTargets: null, // ['nexus','npm','github-packages'] ๋ถ๋ถ์งํฉ
|
|
21
21
|
includeSecretBackup: null,
|
|
22
|
+
// changelog provider ์ถ (#455 โ null=๋ฏธ์ค์ )
|
|
23
|
+
changelogProvider: null, // 'github-ai'(๊ธฐ๋ณธ) | 'coderabbit' | 'openai' | 'gemini' | 'claude' | 'ollama' | 'commit'
|
|
24
|
+
changelogBaseUrl: null, // ollama์ผ ๋๋ง ๊ฐ
|
|
25
|
+
codeReviewCoderabbit: null,
|
|
26
|
+
deployBranch: "", // ๋ฆด๋ฆฌ์ค PR head ๋ธ๋์น (#456). ๋น ๊ฐ=metadata.deploy_branch ๋ฏธ์ถ๋ ฅ
|
|
22
27
|
templateVersion: "",
|
|
23
28
|
tempDir: "",
|
|
24
29
|
deployValues: new Map(), // "type.KEY" -> value
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { exists, copyFileSync } from "../fsutil.js";
|
|
5
5
|
|
|
6
|
-
// ๋ฐํ: 'skip-no-src' | 'copied-new' | 'overwritten-backup' | 'skip-non-tty'
|
|
7
|
-
// opts: { force, tty } โ SP2-B ๊ฒ์ฆ์ force:true ๊ฒฝ๋ก.
|
|
8
|
-
|
|
6
|
+
// ๋ฐํ: 'skip-disabled' | 'skip-no-src' | 'copied-new' | 'overwritten-backup' | 'skip-non-tty'
|
|
7
|
+
// opts: { force, tty, enabled } โ SP2-B ๊ฒ์ฆ์ force:true ๊ฒฝ๋ก.
|
|
8
|
+
// enabled=false๋ฉด(#457 CodeRabbit ๋ฏธ์ฌ์ฉ ์ ํ) ํ์ผ์ ์์ ๋ณต์ฌํ์ง ์๋๋ค.
|
|
9
|
+
// enabled ๋ฏธ์ง์ (undefined)์ด๋ฉด ๊ธฐ์กด ๋์(๋ณต์ฌ) ์ ์ง โ ๊ตฌ ํธ์ถ๋ถ ํ์ํธํ.
|
|
10
|
+
export function copyCoderabbit(tempDir, { force = false, tty = false, enabled } = {}, targetRoot = ".") {
|
|
11
|
+
if (enabled === false) return "skip-disabled";
|
|
9
12
|
const src = join(tempDir, ".coderabbit.yaml");
|
|
10
13
|
if (!exists(src)) return "skip-no-src";
|
|
11
14
|
const dst = join(targetRoot, ".coderabbit.yaml");
|
package/src/core/copy/simple.js
CHANGED
|
@@ -50,8 +50,8 @@ export function copyDiscussionTemplates(tempDir, targetRoot = ".") {
|
|
|
50
50
|
return true;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
export const SETUP_GUIDE_NAME = "
|
|
53
|
+
// PROJECTOPS-SETUP-GUIDE.md ๋ฃจํธ๋ก ๋ฎ์ด์ฐ๊ธฐ. ์์ผ๋ฉด ์คํต.
|
|
54
|
+
export const SETUP_GUIDE_NAME = "PROJECTOPS-SETUP-GUIDE.md";
|
|
55
55
|
export function copySetupGuide(tempDir, targetRoot = ".") {
|
|
56
56
|
const src = join(tempDir, SETUP_GUIDE_NAME);
|
|
57
57
|
if (!exists(src)) return false;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Claude Code ์ด๋ํฐ (.sh _manage_claude_section / _do_claude_plugin_install /
|
|
2
2
|
// _remove_claude_section / _remove_claude_plugin_data ๋ฑ๊ฐ).
|
|
3
|
-
// ๋ง์ผํ๋ ์ด์ค Cassiiopeia/projectops, ํ๋ฌ๊ทธ์ธ
|
|
3
|
+
// ๋ง์ผํ๋ ์ด์ค Cassiiopeia/projectops, ํ๋ฌ๊ทธ์ธ projectops@projectops-marketplace.
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { existsSync, readdirSync, mkdirSync, cpSync, rmSync } from "node:fs";
|
|
6
6
|
import { compareCacheName } from "../util.js";
|
|
7
7
|
|
|
8
8
|
const MARKETPLACE = "Cassiiopeia/projectops";
|
|
9
|
-
const PLUGIN = "
|
|
9
|
+
const PLUGIN = "projectops@projectops-marketplace";
|
|
10
10
|
|
|
11
11
|
function detect(io) {
|
|
12
12
|
if (!io.which("claude")) return { installed: false, version: null, cliMissing: true, note: "CLI ์์" };
|
|
@@ -17,7 +17,7 @@ function detect(io) {
|
|
|
17
17
|
const list = Array.isArray(arr) ? arr : (arr.plugins || []);
|
|
18
18
|
for (const p of list) {
|
|
19
19
|
const name = String(p.name || p.id || "");
|
|
20
|
-
if (name.includes("
|
|
20
|
+
if (name.includes("projectops@") || name === "projectops") { scope = p.scope || ""; version = p.version || null; break; }
|
|
21
21
|
}
|
|
22
22
|
} catch { /* ํ์ฑ ์คํจ โ ๋ฏธ์ค์น ์ทจ๊ธ */ }
|
|
23
23
|
return { installed: !!scope, version, cliMissing: false, scope };
|
|
@@ -42,7 +42,7 @@ function install(io, scope = "user") {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function update(io, scope) {
|
|
45
|
-
const cacheRoot = join(io.home(), ".claude/plugins/cache/
|
|
45
|
+
const cacheRoot = join(io.home(), ".claude/plugins/cache/projectops-marketplace/projectops");
|
|
46
46
|
const oldCache = latestCacheDir(cacheRoot);
|
|
47
47
|
io.log("ํ๋ฌ๊ทธ์ธ ์
๋ฐ์ดํธ ์ค...");
|
|
48
48
|
const up = io.run("claude", ["plugin", "update", PLUGIN, "--scope", scope]);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Codex CLI ์ด๋ํฐ (.sh _manage_codex_skills / _do_codex_marketplace_register /
|
|
2
2
|
// _remove_codex_section ๋ฑ๊ฐ).
|
|
3
|
-
// marketplace ๋ฑ๋ก/์
๊ทธ๋ ์ด๋๊ฐ ์ฃผ ๊ฒฝ๋ก. native ~/.agents/skills/
|
|
3
|
+
// marketplace ๋ฑ๋ก/์
๊ทธ๋ ์ด๋๊ฐ ์ฃผ ๊ฒฝ๋ก. native ~/.agents/skills/projectops ์ฌ๋งํฌ๋ ๊ฐ์งยท์ ๊ฑฐ์ ์ฌ์ฉ.
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { existsSync, lstatSync, rmSync } from "node:fs";
|
|
6
6
|
|
|
7
7
|
const MARKETPLACE = "Cassiiopeia/projectops";
|
|
8
|
-
const PLUGIN = "
|
|
8
|
+
const PLUGIN = "projectops";
|
|
9
9
|
|
|
10
|
-
function nativeTarget(io) { return join(io.home(), ".agents/skills/
|
|
10
|
+
function nativeTarget(io) { return join(io.home(), ".agents/skills/projectops"); }
|
|
11
11
|
|
|
12
12
|
function detect(io) {
|
|
13
13
|
const tgt = nativeTarget(io);
|
|
@@ -62,7 +62,7 @@ function writeMeta(io, destDir, templateVersion) {
|
|
|
62
62
|
let installedAt = now;
|
|
63
63
|
if (existsSync(file)) { try { installedAt = JSON.parse(readFileSync(file, "utf8")).installedAt || now; } catch { /* ๋ฌดํด */ } }
|
|
64
64
|
const meta = {
|
|
65
|
-
name: "
|
|
65
|
+
name: "projectops", version, scope: "user",
|
|
66
66
|
source: "https://github.com/Cassiiopeia/projectops",
|
|
67
67
|
installPath: destDir, installedAt, lastUpdated: now,
|
|
68
68
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Gemini CLI ์ด๋ํฐ (.sh _manage_gemini_extension / _remove_gemini_section ๋ฑ๊ฐ).
|
|
2
2
|
// extension install/update/uninstall. ๋ง์ผํ๋ ์ด์ค ๋์ git URL ํ์ฅ.
|
|
3
|
-
const EXT = "
|
|
3
|
+
const EXT = "projectops";
|
|
4
4
|
const URL = "https://github.com/Cassiiopeia/projectops";
|
|
5
5
|
|
|
6
6
|
function detect(io) {
|
|
@@ -11,7 +11,7 @@ export function piInstalled(io) {
|
|
|
11
11
|
if (!io.which("pi")) return false;
|
|
12
12
|
const r = io.run("pi", ["list"]); // pi๋ ์ผ๋ถ ์ถ๋ ฅ์ stderr๋ก ๋ณด๋ โ ๋ ๋ค ๊ฒ์ฌ
|
|
13
13
|
const out = (r.stdout || "") + (r.stderr || "");
|
|
14
|
-
return /SUH-DEVOPS-TEMPLATE|projectops
|
|
14
|
+
return /SUH-DEVOPS-TEMPLATE|projectops/i.test(out);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// pi ํด๋ก ๊ฒฝ๋ก(harness loader๊ฐ ์ฌ๋ ๊ณณ). ๋ ํฌ๋ช
๋ณ๊ฒฝ ์ ๊ตฌ ๊ฒฝ๋ก ํ์ํธํ.
|
|
@@ -18,7 +18,7 @@ function apply(io) {
|
|
|
18
18
|
}
|
|
19
19
|
if (piInstalled(io)) {
|
|
20
20
|
io.log(" PI ํจํค์ง ์ค์น / ์
๋ฐ์ดํธ ์๋ฃ");
|
|
21
|
-
io.log(" โ 'pi' ์ฌ์คํ ํ 'pi list' ๋ก ํ์ธ, ์ฑํ
์ฐฝ์์ /
|
|
21
|
+
io.log(" โ 'pi' ์ฌ์คํ ํ 'pi list' ๋ก ํ์ธ, ์ฑํ
์ฐฝ์์ /projectops:analyze ๋ฑ ํธ์ถ");
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
24
|
io.log(` PI ์ค์น/์
๋ฐ์ดํธ ์คํจ โ ์๋: pi install ${PI_PACKAGE_URL}`);
|
package/src/core/options-ask.js
CHANGED
|
@@ -17,6 +17,8 @@ export { parseTemplateOptions };
|
|
|
17
17
|
|
|
18
18
|
export const DEPLOY_TARGETS = ["docker-ssh", "vercel", "none"];
|
|
19
19
|
export const PUBLISH_TARGETS = ["nexus", "npm", "github-packages"];
|
|
20
|
+
// #455 โ changelog ์์ฑ๊ธฐ provider. github-ai๊ฐ ๊ธฐ๋ณธ(์ค์ ์ ๋ก). openai/gemini/claude/ollama๋ openai ํธํ ํ ๊ฐ๋.
|
|
21
|
+
export const CHANGELOG_PROVIDERS = ["github-ai", "coderabbit", "openai", "gemini", "claude", "ollama", "commit"];
|
|
20
22
|
|
|
21
23
|
const isCancel = (v) => typeof v === "symbol";
|
|
22
24
|
|
|
@@ -62,6 +64,10 @@ export async function askAllOptionalWorkflows({
|
|
|
62
64
|
let deploy = current.deploy ?? null;
|
|
63
65
|
let publish = current.publish ?? null;
|
|
64
66
|
let secretBackup = current.secretBackup ?? null;
|
|
67
|
+
let codeReviewCoderabbit = current.codeReviewCoderabbit ?? null;
|
|
68
|
+
let changelogProvider = current.changelogProvider ?? null;
|
|
69
|
+
let changelogBaseUrl = current.changelogBaseUrl ?? null;
|
|
70
|
+
let deployBranch = current.deployBranch ?? null; // #456 ๋ฆด๋ฆฌ์ค PR head ๋ธ๋์น
|
|
65
71
|
|
|
66
72
|
// basic ๋จ๋
ํ์
์ ์๋ฒ ๋ฐฐํฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ publish๋ ๊ฐ๋
์ ์ฑ๋ฆฝํ์ง ์๋๋ค.
|
|
67
73
|
// ๋ฐฐํฌ/publish ์ง๋ฌธ์ ๊ฑด๋๋ฐ๊ณ noneยท[]๋ก ์กฐ์ฉํ ํ์ ํ๋ค (ํ์
๋ณ๊ฒฝ ์ ์ฌ์ง๋ฌธ๋จ).
|
|
@@ -86,6 +92,12 @@ export async function askAllOptionalWorkflows({
|
|
|
86
92
|
secretBackup = saved.secretBackup;
|
|
87
93
|
say(`Secret ๋ฐฑ์
์ต์
: version.yml ์ ์ฅ๊ฐ(${secretBackup}) ์ ์ง โ ์ฌ์ง๋ฌธ ์๋ต`);
|
|
88
94
|
}
|
|
95
|
+
// #455 changelog/code_review ์ ์ฅ๊ฐ ์ฌ์ฌ์ฉ
|
|
96
|
+
if (codeReviewCoderabbit === null && saved.codeReviewCoderabbit !== null) codeReviewCoderabbit = saved.codeReviewCoderabbit;
|
|
97
|
+
if (changelogProvider === null && saved.changelogProvider !== null) changelogProvider = saved.changelogProvider;
|
|
98
|
+
if (changelogBaseUrl === null && saved.changelogBaseUrl !== null) changelogBaseUrl = saved.changelogBaseUrl;
|
|
99
|
+
// #456 deploy_branch ์ ์ฅ๊ฐ ์ฌ์ฌ์ฉ
|
|
100
|
+
if (deployBranch === null && saved.deployBranch != null) deployBranch = saved.deployBranch;
|
|
89
101
|
}
|
|
90
102
|
}
|
|
91
103
|
|
|
@@ -140,6 +152,69 @@ export async function askAllOptionalWorkflows({
|
|
|
140
152
|
}
|
|
141
153
|
}
|
|
142
154
|
|
|
155
|
+
// โโ code_review: CodeRabbit AI ์ฝ๋ ๋ฆฌ๋ทฐ (changelog์ ๋ฌด๊ด โ #455) โโ
|
|
156
|
+
if (forceAsk || codeReviewCoderabbit === null) {
|
|
157
|
+
if (force || !tty || typeof io.confirm !== "function") {
|
|
158
|
+
codeReviewCoderabbit = codeReviewCoderabbit ?? false;
|
|
159
|
+
} else {
|
|
160
|
+
say("");
|
|
161
|
+
say("๐ค CodeRabbit AI ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ์ธ๊น์? (PR ์ฌ๋ฆด ๋ ์ฝ๋ ๋ฆฌ๋ทฐ ๋๊ธ์ ๋ต๋๋ค)");
|
|
162
|
+
const ans = await io.confirm({ message: "CodeRabbit AI ์ฝ๋ ๋ฆฌ๋ทฐ ์ฌ์ฉ", initialValue: false });
|
|
163
|
+
codeReviewCoderabbit = (ans === true && !isCancel(ans));
|
|
164
|
+
say(`CodeRabbit ์ฝ๋ ๋ฆฌ๋ทฐ: ${codeReviewCoderabbit ? "์ฌ์ฉ" : "๋ฏธ์ฌ์ฉ"}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// โโ changelog: ๋ฆด๋ฆฌ์ค ๋
ธํธ ์์ฑ๊ธฐ (๊ธฐ๋ณธ ์ปค์ = github-ai โ #455) โโ
|
|
169
|
+
if (forceAsk || changelogProvider === null) {
|
|
170
|
+
if (force || !tty || typeof io.select !== "function") {
|
|
171
|
+
changelogProvider = changelogProvider ?? "github-ai";
|
|
172
|
+
} else {
|
|
173
|
+
say("");
|
|
174
|
+
say("๐ ๋ฆด๋ฆฌ์ค ๋
ธํธ(changelog)๋ ๋ญ๋ก ๋ง๋ค๊น์?");
|
|
175
|
+
say(" GitHub AI๋ ์ค์ ์์ด ๋ฐ๋ก ๋ฉ๋๋ค. ๋๋จธ์ง๋ ๋์ค์ GitHub Secret ๋ฑ๋ก์ด ํ์ํ ์ ์์ด์.");
|
|
176
|
+
const ans = await io.select({
|
|
177
|
+
message: "changelog ์์ฑ๊ธฐ๋ฅผ ์ ํํ์ธ์",
|
|
178
|
+
options: [
|
|
179
|
+
{ value: "github-ai", label: "GitHub AI (์ถ์ฒ ยท ์ค์ ๋ถํ์)" },
|
|
180
|
+
{ value: "coderabbit", label: "CodeRabbit" },
|
|
181
|
+
{ value: "openai", label: "OpenAI ํธํ API (ํค ๋ฑ๋ก ํ์)" },
|
|
182
|
+
{ value: "commit", label: "์ปค๋ฐ ๋ถ์๋ง (AI ์์)" },
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
changelogProvider = (!isCancel(ans) && CHANGELOG_PROVIDERS.includes(ans)) ? ans : (changelogProvider ?? "github-ai");
|
|
186
|
+
say(`changelog ์์ฑ๊ธฐ: ${changelogProvider}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ollama ์ ํ ์์๋ง base_url ์ง๋ฌธ (๋๋จธ์ง provider๋ preset base_url ์๋ โ #455)
|
|
191
|
+
if (changelogProvider === "ollama" && (forceAsk || changelogBaseUrl === null || changelogBaseUrl === "")) {
|
|
192
|
+
if (force || !tty || typeof io.text !== "function") {
|
|
193
|
+
changelogBaseUrl = changelogBaseUrl ?? "";
|
|
194
|
+
} else {
|
|
195
|
+
const ans = await io.text({ message: "Ollama ์๋ฒ base_url (์: https://ai.suhsaechan.kr/v1)" });
|
|
196
|
+
changelogBaseUrl = (typeof ans === "string" && !isCancel(ans)) ? ans.trim() : "";
|
|
197
|
+
say(`Ollama base_url: ${changelogBaseUrl || "(๋ฏธ์ง์ )"}`);
|
|
198
|
+
}
|
|
199
|
+
} else if (changelogBaseUrl === null) {
|
|
200
|
+
changelogBaseUrl = "";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// โโ deploy_branch: ๋ฆด๋ฆฌ์ค PR์ head ๋ธ๋์น (#456 โ default_branch์ ๋ณ๊ฐ) โโ
|
|
204
|
+
// ๋๋ถ๋ถ developโmain ๋ฆด๋ฆฌ์ค ๊ตฌ์กฐ๋ผ ๊ธฐ๋ณธ๊ฐ develop. ๋ค๋ฅธ head๋ฅผ ์ฐ๋ ๋ ํฌ๋ฅผ ์ํด ๋ฌผ์ด๋ณธ๋ค.
|
|
205
|
+
if (forceAsk || deployBranch === null) {
|
|
206
|
+
if (force || !tty || typeof io.text !== "function") {
|
|
207
|
+
deployBranch = deployBranch ?? "develop";
|
|
208
|
+
} else {
|
|
209
|
+
say("");
|
|
210
|
+
say("๐ฟ ๋ฆด๋ฆฌ์ค ๋ฐฐํฌ ๋ธ๋์น(๋ฆด๋ฆฌ์ค PR์ head)๋ ๋ฌด์์ธ๊ฐ์?");
|
|
211
|
+
say(" developโmain ๋ฆด๋ฆฌ์ค ๊ตฌ์กฐ๋ฉด develop ๊ทธ๋๋ก ๋์ธ์. ๋ฐฐํฌ ๋ธ๋์น๊ฐ ๋ฐ๋ก๋ฉด ๊ทธ ์ด๋ฆ์ ์ ์ด์ฃผ์ธ์.");
|
|
212
|
+
const ans = await io.text({ message: "๋ฐฐํฌ ๋ธ๋์น", initialValue: deployBranch ?? "develop" });
|
|
213
|
+
deployBranch = (typeof ans === "string" && !isCancel(ans) && ans.trim()) ? ans.trim() : (deployBranch ?? "develop");
|
|
214
|
+
say(`๋ฐฐํฌ ๋ธ๋์น: ${deployBranch}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
143
218
|
// โโ โฃ Secret ๋ฐฑ์
: ๊ณตํต ํด๋ (๋ฐฐํฌ์ถ ์๋ โ ๊ธฐ์กด ํด๋ ์ง๋ฌธ ์ ์ง) โโ
|
|
144
219
|
const real = join(tempDir, PATHS.workflowsDir, PATHS.projectTypesDir);
|
|
145
220
|
const ptDir = existsSync(real) ? real : join(tempDir, PATHS.projectTypesDir);
|
|
@@ -149,5 +224,11 @@ export async function askAllOptionalWorkflows({
|
|
|
149
224
|
current: secretBackup, force, tty, io, forceAsk, say,
|
|
150
225
|
});
|
|
151
226
|
|
|
152
|
-
return {
|
|
227
|
+
return {
|
|
228
|
+
deploy: deploy ?? "docker-ssh", publish: publish ?? [], secretBackup: secretBackup === true,
|
|
229
|
+
codeReviewCoderabbit: codeReviewCoderabbit === true,
|
|
230
|
+
changelogProvider: changelogProvider ?? "github-ai",
|
|
231
|
+
changelogBaseUrl: changelogBaseUrl ?? "",
|
|
232
|
+
deployBranch: deployBranch ?? "develop",
|
|
233
|
+
};
|
|
153
234
|
}
|
package/src/core/version-yml.js
CHANGED
|
@@ -32,7 +32,7 @@ const HEADER = `# ==============================================================
|
|
|
32
32
|
# ์ฐ๊ด๋ ์ํฌํ๋ก์ฐ:
|
|
33
33
|
# - .github/workflows/PROJECT-VERSION-CONTROL.yaml
|
|
34
34
|
# - .github/workflows/PROJECT-README-VERSION-UPDATE.yaml
|
|
35
|
-
# - .github/workflows/PROJECT-
|
|
35
|
+
# - .github/workflows/PROJECT-RELEASE-CHANGELOG.yaml
|
|
36
36
|
#
|
|
37
37
|
# ์ฃผ์์ฌํญ:
|
|
38
38
|
# - project_types๋ ์ต์ด ์ค์ ํ ๋ณ๊ฒฝํ์ง ๋ง์ธ์
|
|
@@ -49,17 +49,40 @@ const HEADER = `# ==============================================================
|
|
|
49
49
|
// ๊ตฌ synology ํค ๋ฑ ๋ค๋ฅธ ํค๋ ์ด๋ ๋ถ๊ธฐ์๋ ์ ๊ฑธ๋ ค ์์ฐํ ๋ฌด์๋๋ค.
|
|
50
50
|
// (options-ask.js๊ฐ ์ด ํจ์๋ฅผ importํ๋ค โ ์ํ ๋ฐฉ์ง ์ํด ์ฌ๊ธฐ(version-yml)์ ์ ์.)
|
|
51
51
|
export function parseTemplateOptions(content) {
|
|
52
|
-
const out = { deploy: null, publish: null, secretBackup: null
|
|
52
|
+
const out = { deploy: null, publish: null, secretBackup: null,
|
|
53
|
+
changelogProvider: null, changelogBaseUrl: null, codeReviewCoderabbit: null,
|
|
54
|
+
deployBranch: null };
|
|
55
|
+
// deploy_branch๋ metadata ์ง์(#456) โ template.options ๋ฐ์ด๋ผ ๋ณ๋๋ก ์ค์บํ๋ค.
|
|
56
|
+
for (const line of String(content || "").split("\n")) {
|
|
57
|
+
if (line.startsWith("#")) continue;
|
|
58
|
+
const m = line.match(/^\s+deploy_branch:\s*"([^"]*)"/);
|
|
59
|
+
if (m) { out.deployBranch = m[1]; break; }
|
|
60
|
+
}
|
|
53
61
|
let legacyNexus = null;
|
|
54
62
|
let legacyNpm = null;
|
|
55
63
|
// ๊ฐ ์ ๊ทํ: ๋ฐ์ดํ ์ ๊ฑฐ + ํธ๋ฆผ (.sh tr -d '"' | tr -d "'" | xargs ๋ฑ๊ฐ)
|
|
56
64
|
const strip = (s) => String(s).replace(/["']/g, "").trim();
|
|
57
65
|
let inTemplate = false;
|
|
58
66
|
let inOptions = false;
|
|
67
|
+
let inCodeReview = false;
|
|
68
|
+
let inChangelog = false;
|
|
59
69
|
for (const line of String(content || "").split("\n")) {
|
|
60
70
|
if (/^\s*template:/.test(line)) { inTemplate = true; continue; }
|
|
61
71
|
if (inTemplate && /^\s+options:/.test(line)) { inOptions = true; continue; }
|
|
62
72
|
if (inTemplate && inOptions) {
|
|
73
|
+
// ์ค์ฒฉ ๋ธ๋ก ํค๋ ๊ฐ์ง (options ๋ฐ ํ ๋จ๊ณ) โ code_review / changelog (#455)
|
|
74
|
+
if (/^\s+code_review:\s*$/.test(line)) { inCodeReview = true; inChangelog = false; continue; }
|
|
75
|
+
if (/^\s+changelog:\s*$/.test(line)) { inChangelog = true; inCodeReview = false; continue; }
|
|
76
|
+
if (inCodeReview) {
|
|
77
|
+
const cm = line.match(/^\s+coderabbit:\s*(.+)/);
|
|
78
|
+
if (cm) { const v = strip(cm[1]); if (v === "true") out.codeReviewCoderabbit = true; if (v === "false") out.codeReviewCoderabbit = false; continue; }
|
|
79
|
+
}
|
|
80
|
+
if (inChangelog) {
|
|
81
|
+
const pm = line.match(/^\s+provider:\s*(.+)/);
|
|
82
|
+
if (pm) { out.changelogProvider = strip(pm[1]); continue; }
|
|
83
|
+
const bm = line.match(/^\s+base_url:\s*(.*)/);
|
|
84
|
+
if (bm) { out.changelogBaseUrl = strip(bm[1]); continue; }
|
|
85
|
+
}
|
|
63
86
|
let m = line.match(/^\s+deploy:\s*(.+)/);
|
|
64
87
|
if (m) {
|
|
65
88
|
const v = strip(m[1]);
|
|
@@ -152,9 +175,12 @@ export function parseExisting(content) {
|
|
|
152
175
|
if (/^\S/.test(l)) break;
|
|
153
176
|
}
|
|
154
177
|
}
|
|
178
|
+
// ๋ธ๋์น ์ ๋ณด (#456) โ default_branch(๋ ํฌ ๊ธฐ๋ณธ)์ deploy_branch(๋ฆด๋ฆฌ์ค PR head)๋ ๋ณ๊ฐ.
|
|
179
|
+
const defaultBranch = line(/^\s*default_branch:\s*"([^"]*)"/);
|
|
180
|
+
const deployBranch = line(/^\s*deploy_branch:\s*"([^"]*)"/);
|
|
155
181
|
// ์ ํ ์ํฌํ๋ก์ฐ ์ต์
(metadata.template.options โ nexus/secret_backup)
|
|
156
182
|
const options = parseTemplateOptions(text);
|
|
157
|
-
return { version, versionCode, types, paths, templateVersion, options };
|
|
183
|
+
return { version, versionCode, types, paths, templateVersion, options, defaultBranch, deployBranch };
|
|
158
184
|
}
|
|
159
185
|
|
|
160
186
|
// version.yml ์ ์ฒด ์์ฑ (.sh create_version_yml + save_template_options ์ ๊ท ์ผ์ด์ค ๋ฑ๊ฐ).
|
|
@@ -164,7 +190,7 @@ export function parseExisting(content) {
|
|
|
164
190
|
// today = "YYYY-MM-DD" (UTC)
|
|
165
191
|
// pathMarkers = Map<type, markerFilename> (project_paths ์ฃผ์์ฉ)
|
|
166
192
|
// templateOptions = { templateVersion, deployTarget, publishTargets, includeSecretBackup, optionsDate } (template ๋ธ๋ก)
|
|
167
|
-
export function buildVersionYml({ version, types = [], paths = new Map(), pathMarkers = new Map(), branch = "main", versionCode = 1, now, today, templateOptions = null, deployValues = new Map() }) {
|
|
193
|
+
export function buildVersionYml({ version, types = [], paths = new Map(), pathMarkers = new Map(), branch = "main", deployBranch = "", versionCode = 1, now, today, templateOptions = null, deployValues = new Map() }) {
|
|
168
194
|
const typesJson = types.length ? `[${types.map((t) => `"${t}"`).join(",")}]` : `["basic"]`;
|
|
169
195
|
|
|
170
196
|
let out = HEADER + "\n";
|
|
@@ -186,6 +212,9 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
186
212
|
out += ` last_updated: "${now}"\n`;
|
|
187
213
|
out += ` last_updated_by: "template_integrator"\n`;
|
|
188
214
|
out += ` default_branch: "${branch}"\n`;
|
|
215
|
+
// deploy_branch: ๋ฆด๋ฆฌ์ค PR์ head ๋ธ๋์น(#456). default_branch(๋ ํฌ ๊ธฐ๋ณธ)์ ๋ณ๊ฐ ๊ฐ๋
.
|
|
216
|
+
// ์ง์ ๋ ๊ฒฝ์ฐ์๋ง ์ถ๋ ฅ โ ๋ฏธ์ง์ ์ด๋ฉด ์คํฌ์ด develop๋ก ํด๋ฐฑ(ํ์ํธํ).
|
|
217
|
+
if (deployBranch) out += ` deploy_branch: "${deployBranch}"\n`;
|
|
189
218
|
out += ` integrated_from: "projectops"\n`;
|
|
190
219
|
out += ` integration_date: "${today}"\n`;
|
|
191
220
|
|
|
@@ -203,7 +232,8 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
203
232
|
|
|
204
233
|
// template ์ต์
๋ธ๋ก (.sh save_template_options ์ ๊ท ์ถ๊ฐ ์ผ์ด์ค). templateOptions ์ง์ ์.
|
|
205
234
|
if (templateOptions) {
|
|
206
|
-
const { templateVersion = "unknown", deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false, optionsDate = today
|
|
235
|
+
const { templateVersion = "unknown", deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false, optionsDate = today,
|
|
236
|
+
changelogProvider = "github-ai", changelogBaseUrl = "", codeReviewCoderabbit = true } = templateOptions;
|
|
207
237
|
const publishJson = `[${publishTargets.map((t) => `"${t}"`).join(",")}]`;
|
|
208
238
|
out += ` template:\n`;
|
|
209
239
|
out += ` source: "projectops"\n`;
|
|
@@ -214,6 +244,11 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
214
244
|
out += ` deploy: "${deployTarget}"\n`;
|
|
215
245
|
out += ` publish: ${publishJson}\n`;
|
|
216
246
|
out += ` secret_backup: ${includeSecretBackup}\n`;
|
|
247
|
+
out += ` code_review:\n`;
|
|
248
|
+
out += ` coderabbit: ${codeReviewCoderabbit}\n`;
|
|
249
|
+
out += ` changelog:\n`;
|
|
250
|
+
out += ` provider: "${changelogProvider}"\n`;
|
|
251
|
+
out += ` base_url: "${changelogBaseUrl}"\n`;
|
|
217
252
|
}
|
|
218
253
|
return out;
|
|
219
254
|
}
|
package/src/index.js
CHANGED
|
@@ -109,6 +109,12 @@ export async function run(argv, { cwd = process.cwd(), source = { type: "git" },
|
|
|
109
109
|
deployTarget: opts.deployTarget ?? existing?.options?.deploy ?? "docker-ssh",
|
|
110
110
|
publishTargets: opts.publishTargets ?? existing?.options?.publish ?? [],
|
|
111
111
|
includeSecretBackup: opts.includeSecretBackup ?? existing?.options?.secretBackup ?? false,
|
|
112
|
+
// ๋ฆด๋ฆฌ์ค ๋ฐฐํฌ ๋ธ๋์น(#456): CLI ํ๋๊ทธ โ version.yml ์ ์ฅ๊ฐ โ ๋น ๊ฐ(๋ฏธ์ถ๋ ฅ, ์คํฌ์ด develop ํด๋ฐฑ)
|
|
113
|
+
deployBranch: opts.deployBranch || existing?.options?.deployBranch || "",
|
|
114
|
+
// changelog/code_review ์ถ(#455): ๋น๋ํํ์ ์ ์ฅ๊ฐ โ ๊ธฐ๋ณธ๊ฐ. null์ด ํ๋ฌ provider:"null"๋ก ๊ธฐ๋ก๋๋ ๋ฒ๊ทธ ์์ .
|
|
115
|
+
changelogProvider: existing?.options?.changelogProvider ?? "github-ai",
|
|
116
|
+
changelogBaseUrl: existing?.options?.changelogBaseUrl ?? "",
|
|
117
|
+
codeReviewCoderabbit: existing?.options?.codeReviewCoderabbit ?? false,
|
|
112
118
|
repoName,
|
|
113
119
|
// ์ค resolver 4์ข
(.sh resolve_token ๋ฑ๊ฐ โ spring-app-yml ์คํ
์ ๊ฑฐ)
|
|
114
120
|
resolvers: makeResolvers(cwd, repoName, paths),
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// ๊ณ์ฝ: ์ทจ์(ESC/Ctrl+C)๋ CANCEL ์ฌ๋ณผ ๋ฐํ. ๊ฐ ํจ์ async.
|
|
7
7
|
import { emitKeypressEvents } from "node:readline";
|
|
8
8
|
import { stdin, stdout } from "node:process";
|
|
9
|
+
import { visualWidth } from "./ansi.js";
|
|
9
10
|
|
|
10
11
|
export const CANCEL = Symbol("cancel");
|
|
11
12
|
|
|
@@ -28,18 +29,31 @@ const S_BAR = paint("โ", c.gray);
|
|
|
28
29
|
const S_Q = paint("โ", c.cyan);
|
|
29
30
|
const S_DONE = paint("โ", c.green);
|
|
30
31
|
|
|
32
|
+
// ํ ๋
ผ๋ฆฌ ๋ผ์ธ์ด ํฐ๋ฏธ๋ ํญ์์ ์ฐจ์งํ๋ ๋ฌผ๋ฆฌ ์ค ์ (wrap ๊ณ ๋ ค).
|
|
33
|
+
// WHY: ๊ธด ๋ผ๋ฒจ(ํ๊ธ 2์นธ ํฌํจ)์ด ํฐ๋ฏธ๋ ํญ์ ๋์ผ๋ฉด ํฐ๋ฏธ๋์ด ์๋ ์ค๋ฐ๊ฟํด ๋ฌผ๋ฆฌ์ ์ผ๋ก 2์ค+๊ฐ ๋๋ค.
|
|
34
|
+
// ์ปค์ ๋๊ฐ๊ธฐ(ESC[nA)๋ ๋ฌผ๋ฆฌ ์ค ๊ธฐ์ค์ด๋ฏ๋ก, ๋
ผ๋ฆฌ ์ค ์๋ง ์ธ๋ฉด wrap๋ ๋งํผ ๋ ์ฌ๋ผ๊ฐ
|
|
35
|
+
// ์ด์ ์ถ๋ ฅ์ด ์ ์ง์์ง๊ณ ๋งค ๋ ๋๋ง๋ค ํ๋ฉด์ด ์๋๋ก ๋ฐ๋ฆฐ๋ค. (์ค์ธก: Windows PowerShell ํญ 80)
|
|
36
|
+
export function physicalRows(line, cols) {
|
|
37
|
+
const w = visualWidth(line); // ANSI ์ํ์ค ์ ์ธ + CJK 2์นธ
|
|
38
|
+
if (cols <= 0) return 1;
|
|
39
|
+
// ๋น ์ค๋ ์ต์ 1ํ. ํญ์ ์ ์๋ฐฐ๋ก ๋ฑ ๋จ์ด์ง๋ฉด wrap ์ ๋๋ฏ๋ก ๊ทธ๋๋ก(80์นธ ๋ผ์ธ@80ํญ = 1ํ).
|
|
40
|
+
if (w === 0) return 1;
|
|
41
|
+
return Math.max(1, Math.ceil(w / cols));
|
|
42
|
+
}
|
|
43
|
+
|
|
31
44
|
// ์ฌ๋ฌ ์ค ์ง์ด ๋ค ์ปค์๋ฅผ ๋ธ๋ก ์์์ผ๋ก ๋๋๋ฆฌ๋ ๋ ๋๋ฌ.
|
|
32
|
-
//
|
|
45
|
+
// ์ง์ ๋ ๋์ "๋ฌผ๋ฆฌ ์ค ์"๋งํผ ์๋ก ์ฌ๋ผ๊ฐ ์ง์ฐ๊ณ ์๋ก ๊ทธ๋ฆฐ๋ค (wrap ์์ ).
|
|
33
46
|
function makeRenderer() {
|
|
34
|
-
let
|
|
47
|
+
let prevRows = 0; // ์ง์ ๋ ๋๊ฐ ์ค์ ๋ก ์ฐจ์งํ ๋ฌผ๋ฆฌ ์ค ์
|
|
35
48
|
return {
|
|
36
49
|
render(lines) {
|
|
37
|
-
if (
|
|
50
|
+
if (prevRows > 0) stdout.write(`${ESC}${prevRows}A`); // ๋ฌผ๋ฆฌ ์ค ๊ธฐ์ค์ผ๋ก ์๋ก
|
|
38
51
|
stdout.write(`${ESC}0J`); // ์ปค์ ์๋ ์ ๋ถ ์ง์ฐ๊ธฐ
|
|
39
52
|
stdout.write(lines.join("\n") + "\n");
|
|
40
|
-
|
|
53
|
+
const cols = stdout.columns || 80; // ํฐ๋ฏธ๋ ํญ (ํ์ดํ ๋ฑ์ผ๋ก ์์ผ๋ฉด 80 ๊ฐ์ )
|
|
54
|
+
prevRows = lines.reduce((sum, l) => sum + physicalRows(l, cols), 0);
|
|
41
55
|
},
|
|
42
|
-
reset() {
|
|
56
|
+
reset() { prevRows = 0; },
|
|
43
57
|
};
|
|
44
58
|
}
|
|
45
59
|
|
package/src/ui/status-cards.js
CHANGED
|
@@ -80,11 +80,15 @@ export function printIdeStatus(statuses, out = (s) => process.stdout.write(s)) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
// ์ธต5 โ ์ ๊ท ํตํฉ vs ์
๋ฐ์ดํธ ํ๋ณ ๋ผ์ธ (Breaking ๋ฐ์ค๋ breaking-check.js)
|
|
83
|
+
// WHY ํ์ : version.yml์ metadata.template.version(์ด์ ํตํฉ ํ์ )์ด ์์ผ๋ฉด ์
๋ฐ์ดํธ, ์์ผ๋ฉด ์ ๊ท.
|
|
84
|
+
// ์ฌ์ฉ์๊ฐ "๋ฌด์จ ๊ธฐ์ค์ผ๋ก ์ ๊ท/์
๋ฐ์ดํธ์ธ์ง"๋ฅผ ๋ฐ๋ก ์๋๋ก ํ์ ๊ทผ๊ฑฐ๋ฅผ ํ๋ฉด์ ๋ฐํ๋ค.
|
|
83
85
|
export function printInstallKind({ currentTemplateVersion = "", templateVersion = "" }, out = (s) => process.stdout.write(s)) {
|
|
84
86
|
if (currentTemplateVersion) {
|
|
85
87
|
out(`${GUT} โป๏ธ ${paint("์
๋ฐ์ดํธ", A.bold)} โ ํ
ํ๋ฆฟ ${paint(`v${currentTemplateVersion}`, A.dim)} โ ${paint(`v${templateVersion}`, A.green)}\n`);
|
|
88
|
+
out(`${GUT} ${paint("version.yml์ ์ด์ ํตํฉ ๊ธฐ๋ก์ด ์์ด ์
๋ฐ์ดํธ๋ก ์งํํฉ๋๋ค", A.dim)}\n`);
|
|
86
89
|
} else {
|
|
87
90
|
out(`${GUT} ๐ ${paint("์ ๊ท ํตํฉ", A.bold)} โ ์ด ํ๋ก์ ํธ์ ์ฒ์ ์ค์นํฉ๋๋ค (ํ
ํ๋ฆฟ ${paint(`v${templateVersion}`, A.green)})\n`);
|
|
91
|
+
out(`${GUT} ${paint("version.yml์ ์ด์ ํตํฉ ๊ธฐ๋ก์ด ์์ด ์ ๊ท๋ก ๋ด
๋๋ค", A.dim)}\n`);
|
|
88
92
|
}
|
|
89
93
|
out(`${GUT}\n`);
|
|
90
94
|
}
|
package/src/ui/summary.js
CHANGED
|
@@ -168,6 +168,6 @@ export function printSummary(ctx, targetRoot = ".") {
|
|
|
168
168
|
err(SEPARATOR);
|
|
169
169
|
err("");
|
|
170
170
|
err(`${CYAN}๐ ์์ธํ ์ค์ ๋ฐฉ๋ฒ์ ๋ค์ ํ์ผ์ ์ฐธ๊ณ ํ์ธ์:${NC}`);
|
|
171
|
-
err(" โ
|
|
171
|
+
err(" โ PROJECTOPS-SETUP-GUIDE.md");
|
|
172
172
|
err("");
|
|
173
173
|
}
|