project-auto-wizard 0.9.0 → 0.9.1
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 +5 -5
- package/package.json +1 -1
- package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
- package/payload/version.yml.template +1 -1
- package/src/cli/args.js +2 -2
- package/src/cli/help.js +1 -1
- package/src/core/copy/workflows.js +21 -9
- package/src/core/deploy-style.js +9 -1
- package/src/ui/env-plan.js +14 -4
- package/src/ui/prompts.js +10 -4
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npx project-auto-wizard
|
|
|
17
17
|
[](package.json)
|
|
18
18
|
|
|
19
19
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
20
|
-
## 최신 버전 : v0.9.0 (2026-
|
|
20
|
+
## 최신 버전 : v0.9.0 (2026-09-18)
|
|
21
21
|
|
|
22
22
|
[전체 버전 기록 보기](CHANGELOG.md)
|
|
23
23
|
|
|
@@ -67,10 +67,10 @@ flutter.APP_ARTIFACT_NAME:
|
|
|
67
67
|
`spring`/`flutter`는 아래처럼 단일 CI 이상으로 깊게 구성되어 있습니다:
|
|
68
68
|
|
|
69
69
|
- **flutter**: Android(Firebase/Playstore/Selfhosted/TestAPK 배포), iOS(TestFlight/Test-TestFlight), CI, Lab 트리거까지 8종
|
|
70
|
-
- **spring**: 서버 배포 1종(단일 서버 / 무중단 Nginx / 무중단 Traefik 중 택1) + PR 프리뷰 + 라이브러리 publish 2종(Nexus·GitHub Packages, `--nexus` opt-in)
|
|
71
|
-
- 서버 배포 워크플로우는 **서로 대체재**라 하나만 설치합니다. 대화형에서 고르면 그것만 깔리고 **`push` 트리거까지 켜진 채로** 설치됩니다. 비대화형은 `--deploy-style simple|nginx|traefik` (기본: `simple`).
|
|
70
|
+
- **spring**: 서버 배포 1종(단일 서버 / 무중단 Nginx / 무중단 Traefik / 배포 안 함 중 택1) + PR 프리뷰 + 라이브러리 publish 2종(Nexus·GitHub Packages, `--nexus` opt-in)
|
|
71
|
+
- 서버 배포 워크플로우는 **서로 대체재**라 하나만 설치합니다. 대화형에서 고르면 그것만 깔리고 **`push` 트리거까지 켜진 채로** 설치됩니다. 비대화형은 `--deploy-style simple|nginx|traefik|none` (기본: `simple`).
|
|
72
72
|
- 고른 방식은 `version.yml`에 기록되므로 다시 실행해도 묻지 않습니다. 방식을 바꾸면 **이전 워크플로우를 마법사가 정리합니다** — 손대지 않은 파일은 삭제하고, 수정한 파일은 `.bak`으로 옮겨 내용을 보존합니다. 남겨두면 배포가 두 번 돕니다.
|
|
73
|
-
- PR 프리뷰는 배포 방식과 무관한 별개 축이라 선택과 관계없이 함께
|
|
73
|
+
- PR 프리뷰는 배포 방식과 무관한 별개 축이라 선택과 관계없이 함께 설치됩니다 (단, `none`을 고르면 PR 프리뷰도 함께 제외됩니다 — 서버 배포 자체를 하지 않는 프로젝트를 위한 선택지입니다).
|
|
74
74
|
- **react/next**: CI와 CI+CD 분리 구성
|
|
75
75
|
- **python**: CI / PR 프리뷰 / SimpleCICD
|
|
76
76
|
- **go**: CI(Dockerfile 불필요, go test/vet/build/lint) / PR 프리뷰 / SimpleCICD(Dockerfile 있는 프로젝트만 해당)
|
|
@@ -153,7 +153,7 @@ npx project-auto-wizard [옵션]
|
|
|
153
153
|
--paths "t=p,..." 모노레포 타입별 경로
|
|
154
154
|
--main-branch B 릴리스 브랜치 (기본: 감지된 default branch)
|
|
155
155
|
--develop-branch B 개발 브랜치 (기본: develop)
|
|
156
|
-
--deploy-style S 서버 배포 방식: simple | nginx | traefik (기본: simple)
|
|
156
|
+
--deploy-style S 서버 배포 방식: simple | nginx | traefik | none (기본: simple)
|
|
157
157
|
--nexus 라이브러리 publish 워크플로우 포함 (Nexus + GitHub Packages)
|
|
158
158
|
--secret-backup Secret 서버 백업 워크플로우 포함
|
|
159
159
|
--semver-auto 커밋 타입 기반 자동 major/minor/patch 승격 (기본: 사용함, --no-semver-auto로 끔)
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -47,5 +47,5 @@ metadata:
|
|
|
47
47
|
nexus: {{OPT_NEXUS}}
|
|
48
48
|
secret_backup: {{OPT_SECRET_BACKUP}}
|
|
49
49
|
semver_auto: {{OPT_SEMVER_AUTO}}
|
|
50
|
-
deploy_style: "{{OPT_DEPLOY_STYLE}}" # simple | nginx | traefik (서버 배포 워크플로우)
|
|
50
|
+
deploy_style: "{{OPT_DEPLOY_STYLE}}" # simple | nginx | traefik | none (서버 배포 워크플로우)
|
|
51
51
|
{{DEPLOY}}
|
package/src/cli/args.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// CLI 인자 파싱 (.sh top-level while-case 등가) — template_integrator.sh 842~920.
|
|
2
2
|
import { VALID_TYPES, VALID_MODES } from "../context.js";
|
|
3
|
-
import { DEPLOY_STYLES, isDeployStyle } from "../core/deploy-style.js";
|
|
3
|
+
import { DEPLOY_STYLES, isDeployStyle, NO_DEPLOY_STYLE } from "../core/deploy-style.js";
|
|
4
4
|
|
|
5
5
|
// argv(process.argv.slice(2)) → 파싱 결과. 오류 시 throw(호출부에서 exit 1).
|
|
6
6
|
export function parseArgs(argv) {
|
|
@@ -80,7 +80,7 @@ export function parseArgs(argv) {
|
|
|
80
80
|
case "--deploy-style": {
|
|
81
81
|
const v = args.shift();
|
|
82
82
|
if (!isDeployStyle(v)) {
|
|
83
|
-
throw new CliError(`--deploy-style 값이 올바르지 않습니다: ${v ?? "(없음)"} (${DEPLOY_STYLES.map((s) => s.value).join(" | ")})`);
|
|
83
|
+
throw new CliError(`--deploy-style 값이 올바르지 않습니다: ${v ?? "(없음)"} (${[...DEPLOY_STYLES.map((s) => s.value), NO_DEPLOY_STYLE].join(" | ")})`);
|
|
84
84
|
}
|
|
85
85
|
result.deployStyle = v; break;
|
|
86
86
|
}
|
package/src/cli/help.js
CHANGED
|
@@ -16,7 +16,7 @@ export const HELP_TEXT = `project-auto-wizard — One command DevOps: GitHub-nat
|
|
|
16
16
|
--paths "t=p,..." 타입별 프로젝트 경로 (모노레포). 예: flutter=app,react=client
|
|
17
17
|
--main-branch B 릴리스 브랜치 (기본: 감지된 default branch)
|
|
18
18
|
--develop-branch B 개발 브랜치 (기본: develop). 릴리스 브랜치와 같으면 trunk-based 모드
|
|
19
|
-
--deploy-style STYLE 서버 배포 방식: simple | nginx | traefik (기본: simple)
|
|
19
|
+
--deploy-style STYLE 서버 배포 방식: simple | nginx | traefik | none (기본: simple)
|
|
20
20
|
--nexus / --no-nexus 라이브러리 publish 워크플로우 포함/제외 (Nexus + GitHub Packages)
|
|
21
21
|
--secret-backup / --no-secret-backup Secret 백업 워크플로우 포함/제외
|
|
22
22
|
--semver-auto / --no-semver-auto 커밋 타입 기반 자동 major/minor/patch 승격 (기본: 사용함)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// 대화형 3지선(기존 파일 충돌)은 copyWorkflowsInteractive(async)가 결정 Map을 만들어
|
|
4
4
|
// 동기 엔진(copyWorkflows)에 hooks.decisions로 전달한다 — 기존 시그니처·force 동작 무변경.
|
|
5
5
|
import { join, basename } from "node:path";
|
|
6
|
-
import { deployFilter, isDeployWorkflow, activateDeployTrigger, DEFAULT_DEPLOY_STYLE } from "../deploy-style.js";
|
|
6
|
+
import { deployFilter, isDeployWorkflow, activateDeployTrigger, DEFAULT_DEPLOY_STYLE, NO_DEPLOY_STYLE } from "../deploy-style.js";
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
8
8
|
import { PATHS, PAYLOAD } from "../paths.js";
|
|
9
9
|
import { exists, writeText, listYamlFiles } from "../fsutil.js";
|
|
@@ -296,9 +296,13 @@ export function surveyWorkflows(context, payloadRoot, targetRoot = ".") {
|
|
|
296
296
|
for (const type of types) {
|
|
297
297
|
const envOpts = { type, projectPath: paths.get(type) || ".", repoName, resolvers };
|
|
298
298
|
const typeDir = join(projectTypesDir, type);
|
|
299
|
-
if (exists(typeDir))
|
|
299
|
+
if (exists(typeDir)) {
|
|
300
|
+
collect(typeDir, envOpts, type, () => false, deployStyle === NO_DEPLOY_STYLE ? keepDeploy : null);
|
|
301
|
+
}
|
|
300
302
|
const serverDeployDir = join(typeDir, "server-deploy");
|
|
301
|
-
if (exists(serverDeployDir) && !includeNexus
|
|
303
|
+
if (exists(serverDeployDir) && !includeNexus && deployStyle !== NO_DEPLOY_STYLE) {
|
|
304
|
+
collect(serverDeployDir, envOpts, type, () => false, keepDeploy);
|
|
305
|
+
}
|
|
302
306
|
}
|
|
303
307
|
return { conflicts, removed };
|
|
304
308
|
}
|
|
@@ -333,17 +337,20 @@ function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters
|
|
|
333
337
|
// 치환을 다시 걸면 사용자 수정본을 덮어쓰게 된다.
|
|
334
338
|
const untouched = [];
|
|
335
339
|
|
|
336
|
-
// 타입별 워크플로우 (직하위)
|
|
340
|
+
// 타입별 워크플로우 (직하위). go/python처럼 CD 워크플로우가 server-deploy 없이 타입 루트에
|
|
341
|
+
// 바로 있는 타입도 있다 — "배포 안 함"일 때는 타입 루트에서도 CD 파일(SIMPLE-CICD 등)을
|
|
342
|
+
// 걸러야 한다. simple/nginx/traefik은 오늘과 동일하게 필터 없이 전부 복사한다.
|
|
337
343
|
if (exists(typeDir)) {
|
|
338
|
-
const
|
|
344
|
+
const typeRootFilter = deployStyle === NO_DEPLOY_STYLE ? keepDeploy : undefined;
|
|
345
|
+
const c = processDir(typeDir, workflowsDir, envOpts, dirCtx, counters, typeRootFilter);
|
|
339
346
|
untouched.push(...c.unchanged, ...c.localOnly);
|
|
340
347
|
}
|
|
341
348
|
|
|
342
349
|
// server-deploy
|
|
343
350
|
const serverDeployDir = join(typeDir, "server-deploy");
|
|
344
351
|
if (exists(serverDeployDir)) {
|
|
345
|
-
if (includeNexus) {
|
|
346
|
-
// Nexus 프로젝트 → 폴더째 제외 (복사 안 함)
|
|
352
|
+
if (includeNexus || deployStyle === NO_DEPLOY_STYLE) {
|
|
353
|
+
// Nexus 프로젝트 또는 "배포 안 함" → 폴더째 제외 (복사 안 함)
|
|
347
354
|
} else {
|
|
348
355
|
const c = processDir(serverDeployDir, workflowsDir, envOpts, dirCtx, counters, keepDeploy);
|
|
349
356
|
untouched.push(...c.unchanged, ...c.localOnly);
|
|
@@ -363,9 +370,11 @@ function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters
|
|
|
363
370
|
// env 치환 — 이 타입의 원본 디렉토리들에서 복사돼 존재하고, 손대지 않기로 한 것이 아닌 파일만
|
|
364
371
|
for (const srcDir of [typeDir, serverDeployDir, nexusDir]) {
|
|
365
372
|
if (!exists(srcDir)) continue;
|
|
373
|
+
if (srcDir === serverDeployDir && (includeNexus || deployStyle === NO_DEPLOY_STYLE)) continue; // 폴더째 제외
|
|
366
374
|
for (const filename of listYamlFiles(srcDir)) {
|
|
367
375
|
const target = join(workflowsDir, filename);
|
|
368
376
|
if (srcDir === serverDeployDir && !keepDeploy(filename)) continue; // 안 고른 배포 방식
|
|
377
|
+
if (srcDir === typeDir && deployStyle === NO_DEPLOY_STYLE && !keepDeploy(filename)) continue; // 타입 루트 CD도 배제
|
|
369
378
|
if (!existsSync(target)) continue; // 건너뛴 파일 제외
|
|
370
379
|
if (untouched.includes(filename)) continue; // unchanged/localOnly 제외
|
|
371
380
|
configureEnv(target, { ...envOpts, collectAsks }); // env 계획 values/useDefaults 포함
|
|
@@ -417,10 +426,13 @@ export function planWorkflows(context, payloadRoot, targetRoot = ".") {
|
|
|
417
426
|
for (const type of types) {
|
|
418
427
|
const envOpts = { type, projectPath: paths.get(type) || ".", repoName, resolvers };
|
|
419
428
|
const typeDir = join(projectTypesDir, type);
|
|
420
|
-
if (exists(typeDir))
|
|
429
|
+
if (exists(typeDir)) {
|
|
430
|
+
const typeRootFilter = deployStyle === NO_DEPLOY_STYLE ? deployFilter(deployStyle) : null;
|
|
431
|
+
merge(classify(typeDir, workflowsDir, envOpts, srcText, baseline, typeRootFilter), type);
|
|
432
|
+
}
|
|
421
433
|
|
|
422
434
|
const serverDeployDir = join(typeDir, "server-deploy");
|
|
423
|
-
if (exists(serverDeployDir) && !includeNexus) {
|
|
435
|
+
if (exists(serverDeployDir) && !includeNexus && deployStyle !== NO_DEPLOY_STYLE) {
|
|
424
436
|
merge(classify(serverDeployDir, workflowsDir, envOpts, srcText, baseline, deployFilter(deployStyle)), type);
|
|
425
437
|
}
|
|
426
438
|
|
package/src/core/deploy-style.js
CHANGED
|
@@ -14,7 +14,13 @@ export const DEPLOY_STYLES = [
|
|
|
14
14
|
|
|
15
15
|
export const DEFAULT_DEPLOY_STYLE = "simple";
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// 서버 배포 자체를 하지 않는 프로젝트(프론트엔드 전용, 라이브러리 등)를 위한 값.
|
|
18
|
+
// DEPLOY_STYLES에는 넣지 않는다 — isDeployWorkflow/suffixOf가 이 배열을 순회하는데, 빈 접미사를
|
|
19
|
+
// 돌려주면 endsWith("")가 항상 참이라 모든 파일이 CD로 오판되어 cleanupOtherDeployWorkflows가
|
|
20
|
+
// 설치된 워크플로우 전체를 지우는 회귀가 생긴다 (이슈 #126).
|
|
21
|
+
export const NO_DEPLOY_STYLE = "none";
|
|
22
|
+
|
|
23
|
+
export const isDeployStyle = (v) => v === NO_DEPLOY_STYLE || DEPLOY_STYLES.some((s) => s.value === v);
|
|
18
24
|
|
|
19
25
|
// 이 파일이 CD 본체인가 (= 택1 대상인가). PR 프리뷰는 배포 방식과 직교하는 축이라 제외한다.
|
|
20
26
|
export const isDeployWorkflow = (filename) => DEPLOY_STYLES.some((s) => filename.endsWith(s.suffix));
|
|
@@ -25,7 +31,9 @@ const suffixOf = (style) =>
|
|
|
25
31
|
(DEPLOY_STYLES.find((s) => s.value === style) ?? DEPLOY_STYLES.find((s) => s.value === DEFAULT_DEPLOY_STYLE)).suffix;
|
|
26
32
|
|
|
27
33
|
// 파일 필터 — 고른 방식의 CD만 통과. CD가 아닌 파일(PR 프리뷰·common 등)은 항상 통과.
|
|
34
|
+
// "none"은 CD를 하나도 설치하지 않으므로 접미사 매칭 없이 CD 파일 전부를 거른다.
|
|
28
35
|
export function deployFilter(style) {
|
|
36
|
+
if (style === NO_DEPLOY_STYLE) return (filename) => !isDeployWorkflow(filename);
|
|
29
37
|
const suffix = suffixOf(style);
|
|
30
38
|
return (filename) => !isDeployWorkflow(filename) || filename.endsWith(suffix);
|
|
31
39
|
}
|
package/src/ui/env-plan.js
CHANGED
|
@@ -10,7 +10,7 @@ import { PAYLOAD } from "../core/paths.js";
|
|
|
10
10
|
import { exists, listYamlFiles } from "../core/fsutil.js";
|
|
11
11
|
import { parseWizardLine, resolveToken, replaceProjectTokens } from "../core/wizard-env.js";
|
|
12
12
|
import { loadWizardPrompts, wfField, workflowDisplayName } from "../core/wizard-labels.js";
|
|
13
|
-
import { deployFilter } from "../core/deploy-style.js";
|
|
13
|
+
import { deployFilter, NO_DEPLOY_STYLE } from "../core/deploy-style.js";
|
|
14
14
|
import * as engine from "./readline-engine.js";
|
|
15
15
|
|
|
16
16
|
const CANCEL = engine.CANCEL;
|
|
@@ -58,9 +58,19 @@ export function collectAsks(payloadRoot, types = [], opts = {}) {
|
|
|
58
58
|
for (const type of types) {
|
|
59
59
|
const typeDir = join(baseDir, type);
|
|
60
60
|
if (!exists(typeDir)) continue;
|
|
61
|
-
// 복사 엔진과 동일한 폴더 구성: 타입 직하위 + (nexus
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
// 복사 엔진과 동일한 폴더 구성: 타입 직하위 + (nexus면) nexus + (그 외엔, "배포 안 함"이
|
|
62
|
+
// 아닐 때만) server-deploy. server-deploy가 있는 타입은 "none"일 때 그 폴더(PR 프리뷰
|
|
63
|
+
// 포함) 자체를 스캔에서 뺀다.
|
|
64
|
+
// go/python처럼 CD가 server-deploy 없이 타입 루트에 바로 있는 타입은, "배포 안 함"일 때
|
|
65
|
+
// 타입 루트 스캔에도 keepDeploy를 걸어야 CD 전용 ask 키(예: DEPLOY_PORT)가 걸러진다.
|
|
66
|
+
// PR 프리뷰 자체의 ask 키(SSH_AUTH_METHOD 등)는 이 필터로는 걸러지지 않는다 — PR 프리뷰는
|
|
67
|
+
// 배포 방식과 무관하게 항상 설치되는 별도 축이라 의도된 잔여 범위다.
|
|
68
|
+
units.push([type, typeDir, deployStyle === NO_DEPLOY_STYLE ? keepDeploy : null]);
|
|
69
|
+
if (includeNexus) {
|
|
70
|
+
units.push([type, join(typeDir, "nexus"), null]);
|
|
71
|
+
} else if (deployStyle !== NO_DEPLOY_STYLE) {
|
|
72
|
+
units.push([type, join(typeDir, "server-deploy"), keepDeploy]);
|
|
73
|
+
}
|
|
64
74
|
}
|
|
65
75
|
if (includeSecretBackup) units.push(["common", join(baseDir, "common", "secret-backup"), null]);
|
|
66
76
|
|
package/src/ui/prompts.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// node:readline 기반 자체 엔진 사용 (@clack/prompts 는 Windows TTY에서 Enter가 멈추는 버그로 제거).
|
|
3
3
|
// 취소(ESC/Ctrl+C)는 각 함수가 CANCEL 심볼을 반환 → 호출부가 정상 종료(exit 0) 처리.
|
|
4
4
|
import * as engine from "./readline-engine.js";
|
|
5
|
-
import { DEPLOY_STYLES } from "../core/deploy-style.js";
|
|
5
|
+
import { DEPLOY_STYLES, NO_DEPLOY_STYLE } from "../core/deploy-style.js";
|
|
6
6
|
|
|
7
7
|
export const CANCEL = engine.CANCEL;
|
|
8
8
|
|
|
@@ -82,18 +82,24 @@ export async function confirmTypes({ types = [], markers = null } = {}) {
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// 배포 방식 선택 (이슈 #80). 서버 배포 CD 워크플로우는 서로 대체재라 하나만 쓴다.
|
|
85
|
+
// 배포 방식 선택 (이슈 #80, #126). 서버 배포 CD 워크플로우는 서로 대체재라 하나만 쓴다.
|
|
86
86
|
// 고른 것만 설치하고 push 트리거까지 켜준다 — 종전에는 넷을 다 깔고 SIMPLE만 켜져 있어,
|
|
87
87
|
// 무중단을 원한 사람은 설치 후 YAML을 직접 고쳐야 했다.
|
|
88
|
+
// "서버 배포 안 함"은 server-deploy 폴더 자체(PR 프리뷰 포함)를 제외한다 — 서버 배포를
|
|
89
|
+
// 하지 않는 프로젝트(프론트엔드 전용, 라이브러리 등)를 위한 선택지다.
|
|
88
90
|
export async function selectDeployStyle() {
|
|
89
91
|
engine.note(
|
|
90
92
|
"서버 배포 워크플로우는 서로 대체재입니다 (Nginx와 Traefik을 동시에 쓰지 않습니다).\n" +
|
|
91
|
-
"고른 방식만 설치하고 자동 실행(push 트리거)까지 켭니다. PR 프리뷰는 선택과 무관하게 함께
|
|
93
|
+
"고른 방식만 설치하고 자동 실행(push 트리거)까지 켭니다. PR 프리뷰는 선택과 무관하게 함께 설치됩니다\n" +
|
|
94
|
+
"(단, server-deploy 폴더가 있는 타입(spring)은 \"서버 배포 안 함\"을 고르면 PR 프리뷰도 함께 제외됩니다).",
|
|
92
95
|
"배포 방식",
|
|
93
96
|
);
|
|
94
97
|
return engine.select({
|
|
95
98
|
message: "서버 배포는 어떤 방식으로 할까요?",
|
|
96
|
-
options:
|
|
99
|
+
options: [
|
|
100
|
+
...DEPLOY_STYLES.map((s) => ({ value: s.value, label: s.label })),
|
|
101
|
+
{ value: NO_DEPLOY_STYLE, label: "서버 배포 안 함 — CD 워크플로우/배포 설정을 생성하지 않음" },
|
|
102
|
+
],
|
|
97
103
|
});
|
|
98
104
|
}
|
|
99
105
|
|