projectops 4.1.1 → 4.1.3
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 +1 -1
- package/package.json +1 -1
- package/src/cli/args.js +3 -0
- package/src/cli/help.js +1 -0
- package/src/commands/full.js +2 -2
- package/src/commands/interactive.js +11 -5
- package/src/commands/version.js +2 -2
- package/src/context.js +1 -0
- package/src/core/copy/simple.js +7 -2
- package/src/core/copy/workflows.js +19 -2
- package/src/core/options-ask.js +16 -3
- package/src/core/version-yml.js +11 -3
- package/src/index.js +1 -0
- package/src/ui/prompts.js +1 -0
- package/src/ui/status-cards.js +2 -1
package/README.md
CHANGED
package/package.json
CHANGED
package/src/cli/args.js
CHANGED
|
@@ -10,6 +10,7 @@ export function parseArgs(argv) {
|
|
|
10
10
|
primaryType: "",
|
|
11
11
|
includeNexus: null, // null=미설정
|
|
12
12
|
includeSecretBackup: null,
|
|
13
|
+
includeNpmPublish: null,
|
|
13
14
|
pathsCsv: "", // "flutter=app,react=client" 원문 (정규화는 resolve 단계)
|
|
14
15
|
force: false,
|
|
15
16
|
help: false,
|
|
@@ -50,6 +51,8 @@ export function parseArgs(argv) {
|
|
|
50
51
|
case "--no-nexus": result.includeNexus = false; break;
|
|
51
52
|
case "--secret-backup": result.includeSecretBackup = true; break;
|
|
52
53
|
case "--no-secret-backup": result.includeSecretBackup = false; break;
|
|
54
|
+
case "--npm-publish": result.includeNpmPublish = true; break;
|
|
55
|
+
case "--no-npm-publish": result.includeNpmPublish = false; break;
|
|
53
56
|
case "--paths": result.pathsCsv = args.shift() ?? ""; break;
|
|
54
57
|
case "-h": case "--help": result.help = true; break;
|
|
55
58
|
default:
|
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
|
--nexus / --no-nexus Nexus 라이브러리 publish 워크플로우 포함/제외
|
|
17
17
|
--secret-backup / --no-secret-backup Secret 백업 워크플로우 포함/제외
|
|
18
|
+
--npm-publish / --no-npm-publish npm 패키지 publish 워크플로우 포함/제외
|
|
18
19
|
--force 모든 확인 생략, 비대화형 기본값 사용
|
|
19
20
|
-v, --version projectops 버전 출력
|
|
20
21
|
-h, --help 이 도움말 표시
|
package/src/commands/full.js
CHANGED
|
@@ -22,7 +22,7 @@ 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
|
-
includeNexus = false, includeSecretBackup = false } = context;
|
|
25
|
+
includeNexus = false, includeSecretBackup = false, includeNpmPublish = false } = context;
|
|
26
26
|
|
|
27
27
|
// project_paths 마커 계산 (.sh existing_marker_in_dir 등가 — 대표 마커명)
|
|
28
28
|
const pathMarkers = new Map();
|
|
@@ -38,7 +38,7 @@ export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
|
38
38
|
buildVersionYml({
|
|
39
39
|
version, types, paths, pathMarkers, branch, versionCode, now, today,
|
|
40
40
|
deployValues,
|
|
41
|
-
templateOptions: { templateVersion, includeNexus, includeSecretBackup, optionsDate: today },
|
|
41
|
+
templateOptions: { templateVersion, includeNexus, includeSecretBackup, includeNpmPublish, optionsDate: today },
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
44
|
// 2. README 버전 섹션
|
|
@@ -82,6 +82,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
82
82
|
// 선택 워크플로우 초기값: version.yml 저장 옵션 (.sh read_template_options L2361 등가)
|
|
83
83
|
let includeNexus = existing?.options?.nexus ?? false;
|
|
84
84
|
let includeSecretBackup = existing?.options?.secretBackup ?? false;
|
|
85
|
+
let includeNpmPublish = existing?.options?.npmPublish ?? false;
|
|
85
86
|
const showOptional = mode === "full" || mode === "workflows";
|
|
86
87
|
const realTty = process.stdout.isTTY === true;
|
|
87
88
|
|
|
@@ -92,12 +93,13 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
92
93
|
if (showOptional) {
|
|
93
94
|
const r = await askAllOptionalWorkflows({
|
|
94
95
|
tempDir, types, targetRoot: cwd,
|
|
95
|
-
current: { nexus: existing?.options?.nexus ?? null, secretBackup: existing?.options?.secretBackup ?? null },
|
|
96
|
+
current: { nexus: existing?.options?.nexus ?? null, secretBackup: existing?.options?.secretBackup ?? null, npmPublish: existing?.options?.npmPublish ?? null },
|
|
96
97
|
force: false, tty: realTty,
|
|
97
98
|
io: { confirm: ({ message, initialValue }) => io.askYesNo(message, initialValue) },
|
|
98
99
|
});
|
|
99
100
|
includeNexus = r.nexus;
|
|
100
101
|
includeSecretBackup = r.secretBackup;
|
|
102
|
+
includeNpmPublish = r.npmPublish;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
// 확인/수정 루프 — ESC는 '머무르기' (.sh L1877~1881: 명시적 '아니오'만 종료)
|
|
@@ -106,9 +108,9 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
106
108
|
while (!confirmed) {
|
|
107
109
|
// 층3 — 프로젝트 분석 개요 카드 (#446). 스텁엔 없음 → note 폴백.
|
|
108
110
|
if (io.analysisCard) {
|
|
109
|
-
io.analysisCard({ mode, modeLabel: modeLabel(mode), types, version, branch, includeNexus, includeSecretBackup, showOptional, paths });
|
|
111
|
+
io.analysisCard({ mode, modeLabel: modeLabel(mode), types, version, branch, includeNexus, includeSecretBackup, includeNpmPublish, showOptional, paths });
|
|
110
112
|
} else {
|
|
111
|
-
io.note?.(summarize({ mode, types, version, branch, includeNexus, includeSecretBackup, showOptional }), "프로젝트 분석 결과");
|
|
113
|
+
io.note?.(summarize({ mode, types, version, branch, includeNexus, includeSecretBackup, includeNpmPublish, showOptional }), "프로젝트 분석 결과");
|
|
112
114
|
}
|
|
113
115
|
const choice = await io.confirmProjectMenu();
|
|
114
116
|
if (choice === "cancel") { io.cancelMessage?.("설치를 취소했습니다."); return 0; }
|
|
@@ -143,6 +145,9 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
143
145
|
} else if (what === "secret") {
|
|
144
146
|
const y = await io.askYesNo("Secret 백업 워크플로우를 포함할까요?", includeSecretBackup);
|
|
145
147
|
if (!isCancel(y)) includeSecretBackup = y === true;
|
|
148
|
+
} else if (what === "npm-publish") {
|
|
149
|
+
const y = await io.askYesNo("npm publish 워크플로우를 포함할까요?", includeNpmPublish);
|
|
150
|
+
if (!isCancel(y)) includeNpmPublish = y === true;
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
153
|
}
|
|
@@ -171,7 +176,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
171
176
|
|
|
172
177
|
const { now, today } = clock || utcNow();
|
|
173
178
|
const ctx = createContext({
|
|
174
|
-
mode, force: true, types, version, versionCode, branch, paths, includeNexus, includeSecretBackup,
|
|
179
|
+
mode, force: true, types, version, versionCode, branch, paths, includeNexus, includeSecretBackup, includeNpmPublish,
|
|
175
180
|
repoName, templateVersion, resolvers, envValues, envUseDefaults, now, today,
|
|
176
181
|
});
|
|
177
182
|
ctx.templateVersion = templateVersion;
|
|
@@ -222,7 +227,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
222
227
|
}
|
|
223
228
|
}
|
|
224
229
|
|
|
225
|
-
function summarize({ mode, types, version, branch, includeNexus, includeSecretBackup, showOptional }) {
|
|
230
|
+
function summarize({ mode, types, version, branch, includeNexus, includeSecretBackup, includeNpmPublish, showOptional }) {
|
|
226
231
|
const lines = [
|
|
227
232
|
`통합 모드 : ${modeLabel(mode)}`,
|
|
228
233
|
`프로젝트 타입 : ${types.join(", ")}${types.length > 1 ? " (멀티)" : ""}`,
|
|
@@ -232,6 +237,7 @@ function summarize({ mode, types, version, branch, includeNexus, includeSecretBa
|
|
|
232
237
|
if (showOptional) {
|
|
233
238
|
lines.push(`Nexus publish : ${includeNexus ? "포함" : "제외"}`);
|
|
234
239
|
lines.push(`Secret 백업 : ${includeSecretBackup ? "포함" : "제외"}`);
|
|
240
|
+
lines.push(`npm publish : ${includeNpmPublish ? "포함" : "제외"}`);
|
|
235
241
|
}
|
|
236
242
|
return lines.join("\n");
|
|
237
243
|
}
|
package/src/commands/version.js
CHANGED
|
@@ -12,7 +12,7 @@ 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", includeNexus = false, includeSecretBackup = false } = context;
|
|
15
|
+
now, today, templateVersion = "unknown", includeNexus = false, includeSecretBackup = false, includeNpmPublish = false } = context;
|
|
16
16
|
|
|
17
17
|
const pathMarkers = new Map();
|
|
18
18
|
for (const [t] of paths) pathMarkers.set(t, markerForType(t));
|
|
@@ -20,7 +20,7 @@ export function runVersion(context, tempDir, targetRoot = ".") {
|
|
|
20
20
|
writeText(join(targetRoot, PATHS.versionFile),
|
|
21
21
|
buildVersionYml({
|
|
22
22
|
version, types, paths, pathMarkers, branch, versionCode, now, today,
|
|
23
|
-
templateOptions: { templateVersion, includeNexus, includeSecretBackup, optionsDate: today },
|
|
23
|
+
templateOptions: { templateVersion, includeNexus, includeSecretBackup, includeNpmPublish, optionsDate: today },
|
|
24
24
|
}));
|
|
25
25
|
addVersionSectionToReadme(version, targetRoot);
|
|
26
26
|
copyScripts(tempDir, targetRoot);
|
package/src/context.js
CHANGED
|
@@ -16,6 +16,7 @@ export function createContext(overrides = {}) {
|
|
|
16
16
|
branch: "",
|
|
17
17
|
paths: new Map(), // type -> path
|
|
18
18
|
includeNexus: null, // null=미설정, true/false=명시
|
|
19
|
+
includeNpmPublish: null, // null=미설정, true/false=명시 (node/npm-publish/)
|
|
19
20
|
includeSecretBackup: null,
|
|
20
21
|
templateVersion: "",
|
|
21
22
|
tempDir: "",
|
package/src/core/copy/simple.js
CHANGED
|
@@ -5,9 +5,14 @@ import { chmodSync } from "node:fs";
|
|
|
5
5
|
import { PATHS } from "../paths.js";
|
|
6
6
|
import { exists, copyFileSync, copyDirSync } from "../fsutil.js";
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// 버전관리/릴리스노트 스크립트만 무조건 덮어쓰기 + chmod +x.
|
|
9
|
+
// version_manager는 .sh(위임 shim) + .py(실 로직) 한 쌍 — 둘 다 복사해야 동작 (#448).
|
|
9
10
|
export function copyScripts(tempDir, targetRoot = ".") {
|
|
10
|
-
const scripts = [
|
|
11
|
+
const scripts = [
|
|
12
|
+
"version_manager.sh", "version_manager.py",
|
|
13
|
+
"changelog_manager.py",
|
|
14
|
+
"truncate_release_notes.sh", "truncate_release_notes.py",
|
|
15
|
+
];
|
|
11
16
|
let copied = 0;
|
|
12
17
|
for (const s of scripts) {
|
|
13
18
|
const src = join(tempDir, PATHS.scriptsDir, s);
|
|
@@ -150,7 +150,7 @@ export async function copyWorkflowsInteractive(context, tempDir, targetRoot = ".
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters) {
|
|
153
|
-
const { includeNexus, force = false, paths = new Map(), repoName = "", resolvers = {}, envOptsFor, collectAsks = null, decisions = new Map() } = ctx;
|
|
153
|
+
const { includeNexus, includeNpmPublish = false, force = false, paths = new Map(), repoName = "", resolvers = {}, envOptsFor, collectAsks = null, decisions = new Map() } = ctx;
|
|
154
154
|
const typeDir = join(projectTypesDir, type);
|
|
155
155
|
const envOpts = envOptsFor(type);
|
|
156
156
|
let unchangedNames = [];
|
|
@@ -195,8 +195,25 @@ function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// npm-publish (opt-in — 현재 node/npm-publish/만 존재)
|
|
199
|
+
const npmPublishDir = join(typeDir, "npm-publish");
|
|
200
|
+
if (exists(npmPublishDir) && includeNpmPublish) {
|
|
201
|
+
for (const filename of listYamlFiles(npmPublishDir)) {
|
|
202
|
+
const src = join(npmPublishDir, filename);
|
|
203
|
+
const dst = join(workflowsDir, filename);
|
|
204
|
+
if (existsSync(dst) && isUnchanged(readFileSync(src, "utf8"), readFileSync(dst, "utf8"), envOpts)) {
|
|
205
|
+
counters.skipped++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (existsSync(dst)) renameSync(dst, dst + ".bak");
|
|
209
|
+
copyFileSync(src, dst);
|
|
210
|
+
counters.optionalCopied++;
|
|
211
|
+
counters.copied++;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
198
215
|
// env 치환 — 이 타입의 원본 디렉토리들에서 복사돼 존재하고 unchanged 아닌 파일만
|
|
199
|
-
for (const srcDir of [typeDir, serverDeployDir, nexusDir]) {
|
|
216
|
+
for (const srcDir of [typeDir, serverDeployDir, nexusDir, npmPublishDir]) {
|
|
200
217
|
if (!exists(srcDir)) continue;
|
|
201
218
|
for (const filename of listYamlFiles(srcDir)) {
|
|
202
219
|
const target = join(workflowsDir, filename);
|
package/src/core/options-ask.js
CHANGED
|
@@ -49,8 +49,8 @@ async function askOptionalWorkflow({ dir, icon, short, desc, current, force, tty
|
|
|
49
49
|
// 모든 opt-in 워크플로우를 순서대로 질문 (.sh ask_all_optional_workflows 등가).
|
|
50
50
|
// tempDir: 템플릿 다운로드 루트 — project-types는 {tempDir}/.github/workflows/project-types
|
|
51
51
|
// (copyWorkflows와 동일 규약. 테스트 픽스처용으로 {tempDir}/project-types 도 허용.)
|
|
52
|
-
// current: { nexus: bool|null, secretBackup: bool|null } — CLI(--nexus 등) 명시값
|
|
53
|
-
// 반환: { nexus: bool, secretBackup: bool } (미결정 null은 false로 확정)
|
|
52
|
+
// current: { nexus: bool|null, secretBackup: bool|null, npmPublish: bool|null } — CLI(--nexus 등) 명시값
|
|
53
|
+
// 반환: { nexus: bool, secretBackup: bool, npmPublish: bool } (미결정 null은 false로 확정)
|
|
54
54
|
export async function askAllOptionalWorkflows({
|
|
55
55
|
tempDir, types = [], current = {}, targetRoot = ".",
|
|
56
56
|
force = false, tty = true, io = {}, forceAsk = false,
|
|
@@ -58,6 +58,7 @@ export async function askAllOptionalWorkflows({
|
|
|
58
58
|
const say = io.log || ((m) => process.stderr.write(`${m}\n`));
|
|
59
59
|
let nexus = current.nexus ?? null;
|
|
60
60
|
let secretBackup = current.secretBackup ?? null;
|
|
61
|
+
let npmPublish = current.npmPublish ?? null;
|
|
61
62
|
|
|
62
63
|
// ① --force-ask가 아니면 version.yml 저장값을 먼저 읽어 재질문을 건너뛴다 (.sh L2715~2717).
|
|
63
64
|
// CLI 명시값(current)이 이미 있으면 그쪽이 우선 — 저장값은 빈 자리만 채운다.
|
|
@@ -73,6 +74,10 @@ export async function askAllOptionalWorkflows({
|
|
|
73
74
|
secretBackup = saved.secretBackup;
|
|
74
75
|
say(`Secret 백업 옵션: version.yml 저장값(${secretBackup}) 유지 — 재질문 생략`);
|
|
75
76
|
}
|
|
77
|
+
if (npmPublish === null && saved.npmPublish !== null) {
|
|
78
|
+
npmPublish = saved.npmPublish;
|
|
79
|
+
say(`npm publish 옵션: version.yml 저장값(${npmPublish}) 유지 — 재질문 생략`);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -88,6 +93,14 @@ export async function askAllOptionalWorkflows({
|
|
|
88
93
|
current: nexus, force, tty, io, forceAsk, say,
|
|
89
94
|
});
|
|
90
95
|
}
|
|
96
|
+
// ②-2 npm publish: 각 타입의 npm-publish/ 폴더 (현재 node만 존재)
|
|
97
|
+
for (const t of types) {
|
|
98
|
+
npmPublish = await askOptionalWorkflow({
|
|
99
|
+
dir: join(ptDir, t, "npm-publish"), icon: "📦", short: "npm 패키지 publish",
|
|
100
|
+
desc: "패키지를 공개 npmjs 레지스트리에 자동 배포하는 워크플로우입니다. npm 라이브러리/CLI 프로젝트에만 필요합니다 (NPM_TOKEN secret 필요).",
|
|
101
|
+
current: npmPublish, force, tty, io, forceAsk, say,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
91
104
|
// ③ Secret 백업: 공통 폴더 (.sh L2726~2729)
|
|
92
105
|
secretBackup = await askOptionalWorkflow({
|
|
93
106
|
dir: join(ptDir, "common", "secret-backup"), icon: "🔐", short: "Secret 서버 백업",
|
|
@@ -96,5 +109,5 @@ export async function askAllOptionalWorkflows({
|
|
|
96
109
|
});
|
|
97
110
|
|
|
98
111
|
// ④ 미결정(null)은 false로 확정 — .sh에서 빈 INCLUDE_* 가 이후 false 취급되는 것과 동일
|
|
99
|
-
return { nexus: nexus === true, secretBackup: secretBackup === true };
|
|
112
|
+
return { nexus: nexus === true, secretBackup: secretBackup === true, npmPublish: npmPublish === true };
|
|
100
113
|
}
|
package/src/core/version-yml.js
CHANGED
|
@@ -41,11 +41,11 @@ const HEADER = `# ==============================================================
|
|
|
41
41
|
`;
|
|
42
42
|
|
|
43
43
|
// metadata.template.options 상태머신 파싱 (.sh read_template_options L2361~2416 등가).
|
|
44
|
-
// 반환: { nexus: bool|null, secretBackup: bool|null } — null=미기재.
|
|
44
|
+
// 반환: { nexus: bool|null, secretBackup: bool|null, npmPublish: bool|null } — null=미기재.
|
|
45
45
|
// 구 synology 키 등 다른 키는 어느 분기에도 안 걸려 자연히 무시된다.
|
|
46
46
|
// (options-ask.js가 이 함수를 import한다 — 순환 방지 위해 여기(version-yml)에 정의.)
|
|
47
47
|
export function parseTemplateOptions(content) {
|
|
48
|
-
const out = { nexus: null, secretBackup: null };
|
|
48
|
+
const out = { nexus: null, secretBackup: null, npmPublish: null };
|
|
49
49
|
// 값 정규화: 따옴표 제거 + 트림 (.sh tr -d '"' | tr -d "'" | xargs 등가)
|
|
50
50
|
const strip = (s) => String(s).replace(/["']/g, "").trim();
|
|
51
51
|
let inTemplate = false;
|
|
@@ -68,6 +68,13 @@ export function parseTemplateOptions(content) {
|
|
|
68
68
|
if (v === "false") out.secretBackup = false;
|
|
69
69
|
continue;
|
|
70
70
|
}
|
|
71
|
+
m = line.match(/^\s+npm_publish:\s*(.+)/);
|
|
72
|
+
if (m) {
|
|
73
|
+
const v = strip(m[1]);
|
|
74
|
+
if (v === "true") out.npmPublish = true;
|
|
75
|
+
if (v === "false") out.npmPublish = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
71
78
|
// 들여쓰기 0~4칸의 다른 키 → options 섹션 종료 (.sh L2404~2408)
|
|
72
79
|
if (/^\s{0,4}[a-z_]+:/.test(line)) { inOptions = false; inTemplate = false; }
|
|
73
80
|
}
|
|
@@ -170,7 +177,7 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
170
177
|
|
|
171
178
|
// template 옵션 블록 (.sh save_template_options 신규 추가 케이스). templateOptions 지정 시.
|
|
172
179
|
if (templateOptions) {
|
|
173
|
-
const { templateVersion = "unknown", includeNexus = false, includeSecretBackup = false, optionsDate = today } = templateOptions;
|
|
180
|
+
const { templateVersion = "unknown", includeNexus = false, includeSecretBackup = false, includeNpmPublish = false, optionsDate = today } = templateOptions;
|
|
174
181
|
out += ` template:\n`;
|
|
175
182
|
out += ` source: "projectops"\n`;
|
|
176
183
|
out += ` version: "${templateVersion}"\n`;
|
|
@@ -179,6 +186,7 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
179
186
|
out += ` options:\n`;
|
|
180
187
|
out += ` nexus: ${includeNexus}\n`;
|
|
181
188
|
out += ` secret_backup: ${includeSecretBackup}\n`;
|
|
189
|
+
out += ` npm_publish: ${includeNpmPublish}\n`;
|
|
182
190
|
}
|
|
183
191
|
return out;
|
|
184
192
|
}
|
package/src/index.js
CHANGED
|
@@ -108,6 +108,7 @@ export async function run(argv, { cwd = process.cwd(), source = { type: "git" },
|
|
|
108
108
|
// 옵션 워크플로우: CLI 플래그 최우선 → version.yml 저장 옵션(.sh read_template_options 등가) → false
|
|
109
109
|
includeNexus: opts.includeNexus ?? existing?.options?.nexus ?? false,
|
|
110
110
|
includeSecretBackup: opts.includeSecretBackup ?? existing?.options?.secretBackup ?? false,
|
|
111
|
+
includeNpmPublish: opts.includeNpmPublish ?? existing?.options?.npmPublish ?? false,
|
|
111
112
|
repoName,
|
|
112
113
|
// 실 resolver 4종 (.sh resolve_token 등가 — spring-app-yml 스텁 제거)
|
|
113
114
|
resolvers: makeResolvers(cwd, repoName, paths),
|
package/src/ui/prompts.js
CHANGED
|
@@ -41,6 +41,7 @@ export async function editMenu({ showOptional = false } = {}) {
|
|
|
41
41
|
if (showOptional) {
|
|
42
42
|
options.push({ value: "nexus", label: "Nexus publish 포함 여부" });
|
|
43
43
|
options.push({ value: "secret", label: "Secret 백업 포함 여부" });
|
|
44
|
+
options.push({ value: "npm-publish", label: "npm publish 포함 여부" });
|
|
44
45
|
}
|
|
45
46
|
options.push({ value: "done", label: "모두 맞음, 계속" });
|
|
46
47
|
return engine.select({ message: "어떤 항목을 수정할까요?", options });
|
package/src/ui/status-cards.js
CHANGED
|
@@ -25,7 +25,7 @@ export function printDetectionLog({ types = [], version = "", branch = "" }, out
|
|
|
25
25
|
|
|
26
26
|
// 층3 — 프로젝트 분석 개요 카드 (.ps1 Print-ProjectAnalysis 등가+)
|
|
27
27
|
export function printAnalysisCard({ mode = "", modeLabel = "", types = [], version = "", branch = "",
|
|
28
|
-
includeNexus = null, includeSecretBackup = null, paths = new Map(), showOptional = false },
|
|
28
|
+
includeNexus = null, includeSecretBackup = null, includeNpmPublish = null, paths = new Map(), showOptional = false },
|
|
29
29
|
out = (s) => process.stdout.write(s)) {
|
|
30
30
|
out(`${HEAD} ${paint("프로젝트 분석 결과", A.bold)}\n`);
|
|
31
31
|
const row = (icon, label, value) => out(`${GUT} ${icon} ${label.padEnd(10)} ${value}\n`);
|
|
@@ -36,6 +36,7 @@ export function printAnalysisCard({ mode = "", modeLabel = "", types = [], versi
|
|
|
36
36
|
if (showOptional) {
|
|
37
37
|
row("📦", "Nexus", includeNexus === true ? paint("포함", A.green) : paint("제외", A.dim));
|
|
38
38
|
row("🔐", "Secret백업", includeSecretBackup === true ? paint("포함", A.green) : paint("제외", A.dim));
|
|
39
|
+
row("📦", "npm publish", includeNpmPublish === true ? paint("포함", A.green) : paint("제외", A.dim));
|
|
39
40
|
}
|
|
40
41
|
// 모노레포 경로 — 루트가 아닌 항목이 하나라도 있으면 표시
|
|
41
42
|
const nonRoot = [...paths.entries()].filter(([, p]) => p && p !== ".");
|