projectops 4.39.0 → 4.41.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.md +2 -1
- package/package.json +1 -1
- package/src/commands/doctor.js +4 -1
- package/src/commands/full.js +2 -2
- package/src/commands/interactive.js +3 -2
- package/src/commands/version.js +2 -2
- package/src/context.js +1 -0
- package/src/core/copy/simple.js +2 -0
- package/src/core/copy/workflows.js +8 -5
- package/src/core/exclusions.js +1 -1
- package/src/core/migrations/registry.js +2 -1
- package/src/core/options-schema.js +3 -0
- package/src/core/version-yml.js +12 -2
- package/src/i18n/doctor.en.js +1 -0
- package/src/i18n/doctor.ko.js +1 -0
- package/src/index.js +2 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
**Status: actively maintained.** npm `latest` is exactly what is on the `main` branch; work in progress on `develop` is never published. Versions can still ship several times a day. To stay on a version you have tested, run `npx projectops@<version>` ([releases](https://github.com/Cassiiopeia/projectops/releases)).
|
|
10
10
|
|
|
11
11
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
12
|
-
## Latest version : v4.
|
|
12
|
+
## Latest version : v4.40.0 (2026-10-10)
|
|
13
13
|
|
|
14
14
|
[View full version history](CHANGELOG.md)
|
|
15
15
|
|
|
@@ -327,6 +327,7 @@ Browse and search everything on the **[docs site](https://cassiiopeia.github.io/
|
|
|
327
327
|
- [Issues](https://github.com/Cassiiopeia/projectops/issues): bug reports and feature requests
|
|
328
328
|
- Finished issues are marked with the `status: done` label and closed automatically when a release is merged (`close_on_release`).
|
|
329
329
|
- [CONTRIBUTING.md](CONTRIBUTING.md): contribution guide
|
|
330
|
+
- [ARCHITECTURE.md](ARCHITECTURE.md): how the code fits together and how to add a flag, option, provider, language or skill
|
|
330
331
|
- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md): how we expect everyone to behave, and where to report problems
|
|
331
332
|
- [SECURITY.md](SECURITY.md): please report security vulnerabilities privately, not in a public issue
|
|
332
333
|
|
package/package.json
CHANGED
package/src/commands/doctor.js
CHANGED
|
@@ -68,7 +68,10 @@ export function localChecks(cwd = ".") {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
add({ name: t("doctor.integ.name"), purpose: t("doctor.integ.purpose"), status: "OK",
|
|
71
|
-
|
|
71
|
+
// 템플릿 버전이 기록돼 있지 않으면 "vunknown" 이 아니라 사람이 읽는 문구로 — 옛 설치는 version.yml 에 이 값이 없다
|
|
72
|
+
value: existing.templateVersion
|
|
73
|
+
? t("doctor.integ.versions", { tpl: existing.templateVersion, proj: existing.version })
|
|
74
|
+
: t("doctor.integ.versionsNoTemplate", { proj: existing.version }) });
|
|
72
75
|
|
|
73
76
|
const wfDir = join(cwd, PATHS.workflowsDir);
|
|
74
77
|
const files = existsSync(wfDir)
|
package/src/commands/full.js
CHANGED
|
@@ -30,7 +30,7 @@ export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
|
30
30
|
force = true, now, today, templateVersion = "unknown",
|
|
31
31
|
deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false, aiPrSummary = true,
|
|
32
32
|
changelogProvider = "commit", changelogBaseUrl = "", codeReviewCoderabbit = true,
|
|
33
|
-
deployBranch = "", intent = null, semverAuto = true , appRelease = null, labelStyle = "en", closeOnRelease = null, projectsSync = null, language = "en", excludedWorkflows = null } = context;
|
|
33
|
+
deployBranch = "", intent = null, semverAuto = true , appRelease = null, labelStyle = "en", closeOnRelease = null, projectsSync = null, language = "en", excludedWorkflows = null, storeLocales = null } = context;
|
|
34
34
|
|
|
35
35
|
// project_paths 마커 계산 (.sh existing_marker_in_dir 등가 — 대표 마커명)
|
|
36
36
|
const pathMarkers = new Map();
|
|
@@ -53,7 +53,7 @@ export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
|
53
53
|
deployValues,
|
|
54
54
|
updatedBy: resolveUpdatedBy(existsSync(vyFile) ? readFileSync(vyFile, "utf8") : "", targetRoot), // #811
|
|
55
55
|
templateOptions: { templateVersion, deployTarget, publishTargets, includeSecretBackup, aiPrSummary, optionsDate: today,
|
|
56
|
-
changelogProvider, changelogBaseUrl, codeReviewCoderabbit, intent, mode: "full", semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows },
|
|
56
|
+
changelogProvider, changelogBaseUrl, codeReviewCoderabbit, intent, mode: "full", semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales },
|
|
57
57
|
})), { version, versionCode });
|
|
58
58
|
|
|
59
59
|
// 2. README 버전 섹션
|
|
@@ -144,6 +144,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
144
144
|
// 상태 라벨 표기(#776): 저장값 > (신규 en / 기존 ko). 기존 한글 레포에는 영문 전환을 한 번 제안한다(아래).
|
|
145
145
|
const closeOnRelease = existing?.options?.closeOnRelease ?? (existing ? null : true); // #771 묻지 않는다
|
|
146
146
|
const excludedWorkflows = existing?.options?.excludedWorkflows ?? null; // #810 저장값 보존
|
|
147
|
+
const storeLocales = existing?.options?.storeLocales ?? null; // #829 저장값 보존
|
|
147
148
|
// Projects 보드 동기화(#716): 묻지 않는다. 저장값 > 이미 설치돼 있으면 유지 > 신규는 제외 (index.js 와 같은 규칙).
|
|
148
149
|
const projectsSync = existing?.options?.projectsSync
|
|
149
150
|
?? existsSync(join(cwd, ".github/workflows/PROJECT-COMMON-PROJECTS-SYNC-MANAGER.yaml"));
|
|
@@ -330,7 +331,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
330
331
|
const { now, today } = clock || utcNow();
|
|
331
332
|
const ctx = createContext({
|
|
332
333
|
mode, force: true, types, version, versionCode, branch, paths, deployTarget, publishTargets, includeSecretBackup,
|
|
333
|
-
codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch, intent, semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows,
|
|
334
|
+
codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch, intent, semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales,
|
|
334
335
|
repoName, templateVersion, resolvers, envValues, envUseDefaults, now, today,
|
|
335
336
|
// #502 — version 모드가 기존 full 기록을 강등하지 않도록 (full이 우세)
|
|
336
337
|
recordMode: existing?.templateMode === "full" ? "full" : "version",
|
|
@@ -425,7 +426,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
425
426
|
migrationGuidePath = appendGuideEntry(cwd, {
|
|
426
427
|
now, mode, types, repoName,
|
|
427
428
|
templateFrom: existing?.templateVersion || "", templateTo: templateVersion,
|
|
428
|
-
options: { deploy: deployTarget, publish: publishTargets, secretBackup: includeSecretBackup, coderabbit: codeReviewCoderabbit, changelogProvider, intent, semverAuto , appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows },
|
|
429
|
+
options: { deploy: deployTarget, publish: publishTargets, secretBackup: includeSecretBackup, coderabbit: codeReviewCoderabbit, changelogProvider, intent, semverAuto , appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales },
|
|
429
430
|
branches: { defaultBranch: branch, deployBranch, ready: deployBranchReady, created: deployBranchCreated },
|
|
430
431
|
breaking: breakingReport, migrations: migrationsResult, orphans: orphanReport,
|
|
431
432
|
events: trace.events, counters: { skipped: result?.workflows?.skipped ?? 0 },
|
package/src/commands/version.js
CHANGED
|
@@ -17,7 +17,7 @@ export function runVersion(context, tempDir, targetRoot = ".") {
|
|
|
17
17
|
const { version, types = [], paths = new Map(), branch = "main", versionCode = 1,
|
|
18
18
|
now, today, templateVersion = "unknown", deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false,
|
|
19
19
|
changelogProvider = "commit", changelogBaseUrl = "", codeReviewCoderabbit = true,
|
|
20
|
-
deployBranch = "", recordMode = "version", semverAuto = true , appRelease = null, labelStyle = "en", closeOnRelease = null, projectsSync = null, language = null, excludedWorkflows = null } = context;
|
|
20
|
+
deployBranch = "", recordMode = "version", semverAuto = true , appRelease = null, labelStyle = "en", closeOnRelease = null, projectsSync = null, language = null, excludedWorkflows = null, storeLocales = null } = context;
|
|
21
21
|
|
|
22
22
|
const pathMarkers = new Map();
|
|
23
23
|
for (const [t] of paths) pathMarkers.set(t, markerForType(t));
|
|
@@ -34,7 +34,7 @@ export function runVersion(context, tempDir, targetRoot = ".") {
|
|
|
34
34
|
// mode(#502): version 모드가 기존 full 통합 기록을 "version"으로 강등하지 않도록
|
|
35
35
|
// 호출부가 recordMode로 기존 값을 넘긴다 (full이 우세 — 업데이트 재실행 범위 축소 방지).
|
|
36
36
|
templateOptions: { templateVersion, deployTarget, publishTargets, includeSecretBackup, optionsDate: today,
|
|
37
|
-
changelogProvider, changelogBaseUrl, codeReviewCoderabbit, mode: recordMode, semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows },
|
|
37
|
+
changelogProvider, changelogBaseUrl, codeReviewCoderabbit, mode: recordMode, semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales },
|
|
38
38
|
}));
|
|
39
39
|
addVersionSectionToReadme(version, targetRoot);
|
|
40
40
|
copyScripts(tempDir, targetRoot);
|
package/src/context.js
CHANGED
|
@@ -32,6 +32,7 @@ export function createContext(overrides = {}) {
|
|
|
32
32
|
language: null, // 레포 문구 언어 (#769). null=미설정(신규 en / 기존 레포 ko)
|
|
33
33
|
labelStyle: null, // 상태 라벨 표기 (#776). null=미설정(신규 en / 기존 레포 ko)
|
|
34
34
|
excludedWorkflows: null, // 설치하지 않을 워크플로우 파일명 (#810). null=없음
|
|
35
|
+
storeLocales: null, // 스토어 릴리스 노트 언어 (#829). null=미설정 → 현행 동작 유지
|
|
35
36
|
appRelease: null, // 앱 심사 배포 레포인가 (#553). null=미설정(키 기록 안 함)
|
|
36
37
|
templateVersion: "",
|
|
37
38
|
tempDir: "",
|
package/src/core/copy/simple.js
CHANGED
|
@@ -37,6 +37,8 @@ export function copyScripts(tempDir, targetRoot = ".") {
|
|
|
37
37
|
"close_issues_on_release.py",
|
|
38
38
|
// Play Store/TestFlight 배포 모드를 코드 설정(.github/config/store-deploy.json)에서 읽는다 (#767).
|
|
39
39
|
"store_deploy_config.py",
|
|
40
|
+
// 스토어 릴리스 노트를 언어별로 준비 (#829). version.yml 의 store_locales 가 없으면 아무것도 하지 않는다.
|
|
41
|
+
"store_notes.py",
|
|
40
42
|
"changelog_providers/_common.py", "changelog_providers/ladder.py",
|
|
41
43
|
"changelog_providers/commit.py", "changelog_providers/copilot.py",
|
|
42
44
|
"changelog_providers/openai_compatible.py",
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
// 워크플로우 복사
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// 워크플로우 복사 엔진. 구 template_integrator.sh(#458 로 삭제됨)의 copy_workflows 를 포팅한 것이다.
|
|
2
|
+
// 파일 안의 ".sh ... 등가" 표기는 그 삭제된 스크립트를 가리킨다 — 줄 번호는 더 이상 찾을 수 없다.
|
|
3
|
+
//
|
|
4
|
+
// 충돌(기존 파일과 다름)의 처리 결정은 hooks.decisions(Map<파일명, 'skip'|'backup'|'template'>)로 받는다.
|
|
5
|
+
// - 실제 마법사(commands/interactive.js)는 listWorkflowConflicts 로 충돌 목록을 얻어 직접 Map 을 만든다.
|
|
6
|
+
// - copyWorkflowsInteractive 는 onConflict 콜백으로 같은 Map 을 만들어 주는 보조 API 다. 프로덕션 경로는 쓰지 않고
|
|
7
|
+
// 테스트(copy-workflows-conflict.test.js)만 쓴다.
|
|
5
8
|
import { join, basename } from "node:path";
|
|
6
9
|
import { existsSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
7
10
|
import { PATHS } from "../paths.js";
|
|
@@ -378,7 +381,7 @@ function applyDecision(decision, srcDir, workflowsDir, filename, counters, trace
|
|
|
378
381
|
});
|
|
379
382
|
}
|
|
380
383
|
|
|
381
|
-
// 대상 워크플로우 디렉토리에서 changed(충돌) 파일 목록만 뽑는다 — copyWorkflowsInteractive
|
|
384
|
+
// 대상 워크플로우 디렉토리에서 changed(충돌) 파일 목록만 뽑는다 — 마법사(commands/interactive.js)와 copyWorkflowsInteractive 가 쓴다.
|
|
382
385
|
// copyWorkflows 본체와 동일한 classify 기준을 써야 결정 Map이 실제 처리 대상과 1:1로 맞는다.
|
|
383
386
|
export function listWorkflowConflicts(context, tempDir, targetRoot = ".") {
|
|
384
387
|
const { types = [], paths = new Map(), deployTarget = "docker-ssh", repoName = "", resolvers = {}, branch = "", deployBranch = "" } = context;
|
package/src/core/exclusions.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// 템플릿 다운로드 후 제거하는 항목 (복사 제외 목록).
|
|
2
2
|
// ⚠️ CLAUDE.md "템플릿 전용 파일 추가" 규칙의 동기화 지점:
|
|
3
|
-
// template_initializer.
|
|
3
|
+
// template_initializer.py(삭제) / 이 파일(복사 제외)
|
|
4
4
|
// (구 template_integrator.sh/.ps1의 배열은 #458 EOF로 소멸 — 여기가 유일한 복사 제외 지점)
|
|
5
5
|
export const DOCS_TO_REMOVE = [
|
|
6
6
|
"CONTRIBUTING.md",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
// tier - "safe" : 신형이 같은 기능을 대체(순수 리네임). 공존 시 이중 트리거 실해
|
|
12
12
|
// → 확인 1회 후 자동 무해화(.bak) / 삭제
|
|
13
13
|
// "confirm" : 배포 파이프라인일 수 있음(그 레포의 유일한 현역 배포 가능성)
|
|
14
|
-
// →
|
|
14
|
+
// → 기본은 건드리지 않고 완료 요약·doctor 에 목록만 안내한다.
|
|
15
|
+
// --remove-legacy(비대화형) 또는 대화형 확인(기본 아니오)이 있을 때만 .bak 으로 무해화 (#809)
|
|
15
16
|
// "ask" : 사용자 콘텐츠가 담긴 폴더 등 — 손실 없는 이동이지만 사용자 소유물 (#476)
|
|
16
17
|
// → 대화형: 확인 후 이동 / 비대화형: 자동 조치 없이 안내만
|
|
17
18
|
// file - 정확한 파일명 (글롭 금지 — 사용자 커스텀 보호의 핵심)
|
|
@@ -105,6 +105,9 @@ export const VERSION_YML = [
|
|
|
105
105
|
{ path: "metadata.template.options.excluded_workflows", parserKey: "excludedWorkflows", type: "string[]",
|
|
106
106
|
description: "Workflow file names the update must never install. Deleted files are also not restored; a copy is kept in .github/.projectops/incoming/.",
|
|
107
107
|
edit: "by hand" },
|
|
108
|
+
{ path: "metadata.template.options.store_locales", parserKey: "storeLocales", type: "string[]",
|
|
109
|
+
description: "Store release-note languages, first is the default (for example [\"ko-KR\", \"en-US\", \"ja-JP\", \"zh-CN\"]). Missing = one text for every store language, as before. Per-language text comes from store_notes in CHANGELOG.json; a language without text gets the default-language text (the stores reject an empty What's New).",
|
|
110
|
+
edit: "by hand" },
|
|
108
111
|
{ path: "metadata.template.options.code_review.coderabbit", parserKey: "codeReviewCoderabbit", type: "boolean", description: "CodeRabbit config installed.", edit: "wizard" },
|
|
109
112
|
{ path: "metadata.template.options.code_review.ai_summary", parserKey: "aiPrSummary", type: "boolean", default: true, description: "AI PR summary workflow installed.", edit: "wizard (--ai-summary)" },
|
|
110
113
|
{ path: "metadata.template.options.changelog.provider", parserKey: "changelogProvider", type: "enum",
|
package/src/core/version-yml.js
CHANGED
|
@@ -115,6 +115,7 @@ const COMMENTS = {
|
|
|
115
115
|
appRelease: "whether releases go through App Store / Play Store review",
|
|
116
116
|
deploy: "deploy settings remembered by the wizard (non-sensitive, safe to edit by hand)",
|
|
117
117
|
excluded: "workflow files the update must never install (safe to edit by hand)",
|
|
118
|
+
storeLocales: "store release-note languages, first is the default (missing = one text for every store language)",
|
|
118
119
|
},
|
|
119
120
|
ko: {
|
|
120
121
|
versionCode: "app build number",
|
|
@@ -130,6 +131,7 @@ const COMMENTS = {
|
|
|
130
131
|
appRelease: "앱스토어·플레이스토어 심사로 이어지는 배포인가",
|
|
131
132
|
deploy: "마법사가 기억하는 배포 설정 (비민감 / 직접 수정 가능)",
|
|
132
133
|
excluded: "업데이트가 절대 설치하지 않을 워크플로우 파일 (직접 수정 가능)",
|
|
134
|
+
storeLocales: "스토어 릴리스 노트 언어 목록, 첫 항목이 기본 언어 (미기재면 모든 스토어 언어에 같은 문구)",
|
|
133
135
|
},
|
|
134
136
|
};
|
|
135
137
|
const commentsFor = (lang) => COMMENTS[lang === "ko" ? "ko" : "en"];
|
|
@@ -158,7 +160,7 @@ export function resolveUpdatedBy(existingContent = "", cwd = ".") {
|
|
|
158
160
|
export function parseTemplateOptions(content) {
|
|
159
161
|
const out = { deploy: null, publish: null, secretBackup: null,
|
|
160
162
|
changelogProvider: null, changelogBaseUrl: null, codeReviewCoderabbit: null, aiPrSummary: null, projectsSync: null,
|
|
161
|
-
deployBranch: null, intent: null, semverAuto: null, appRelease: null, labelStyle: null, closeOnRelease: null, language: null, excludedWorkflows: null };
|
|
163
|
+
deployBranch: null, intent: null, semverAuto: null, appRelease: null, labelStyle: null, closeOnRelease: null, language: null, excludedWorkflows: null, storeLocales: null };
|
|
162
164
|
// deploy_branch는 metadata 직속(#456) — template.options 밖이라 별도로 스캔한다.
|
|
163
165
|
for (const line of String(content || "").split("\n")) {
|
|
164
166
|
if (line.startsWith("#")) continue;
|
|
@@ -266,6 +268,12 @@ export function parseTemplateOptions(content) {
|
|
|
266
268
|
out.excludedWorkflows = m[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
267
269
|
continue;
|
|
268
270
|
}
|
|
271
|
+
// 스토어 릴리스 노트 언어 (#829) — 인라인 배열, 첫 항목이 기본 언어. 미기재면 현행 동작(한 문구를 모든 언어에).
|
|
272
|
+
m = line.match(/^\s+store_locales:\s*\[([^\]]*)\]/);
|
|
273
|
+
if (m) {
|
|
274
|
+
out.storeLocales = m[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
269
277
|
// 프로젝트 성격(#553) — 앱 심사로 이어지는 레포인가. 워크플로우와 스킬이 같은 값을 본다.
|
|
270
278
|
m = line.match(/^\s+app_release:\s*["']?(true|false)["']?/);
|
|
271
279
|
if (m) {
|
|
@@ -470,7 +478,7 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
470
478
|
if (templateOptions) {
|
|
471
479
|
const { templateVersion = "unknown", deployTarget = "docker-ssh", publishTargets = [], includeSecretBackup = false, optionsDate = today,
|
|
472
480
|
changelogProvider = "commit", changelogBaseUrl = "", codeReviewCoderabbit = true, aiPrSummary = true, intent = null, mode = null,
|
|
473
|
-
semverAuto = true, appRelease = null, labelStyle = null, closeOnRelease = null, projectsSync = null, language = null, excludedWorkflows = null } = templateOptions;
|
|
481
|
+
semverAuto = true, appRelease = null, labelStyle = null, closeOnRelease = null, projectsSync = null, language = null, excludedWorkflows = null, storeLocales = null } = templateOptions;
|
|
474
482
|
const publishJson = `[${publishTargets.map((t) => `"${t}"`).join(",")}]`;
|
|
475
483
|
// intent(프로젝트 성격, #485) — 미지정이면 deploy/publish에서 역추론해 기록 (재통합 시 진입 질문 생략용)
|
|
476
484
|
const intentVal = intent || inferIntent(deployTarget, publishTargets) || "manual";
|
|
@@ -496,6 +504,8 @@ export function buildVersionYml({ version, types = [], paths = new Map(), pathMa
|
|
|
496
504
|
// 레포 문구 언어(#769) — 미지정이면 키를 쓰지 않는다(기존 레포 무변화).
|
|
497
505
|
// 설치 제외 목록(#810) — 전체 재생성이라 다시 쓰지 않으면 사용자가 적은 목록이 사라진다.
|
|
498
506
|
if (excludedWorkflows && excludedWorkflows.length) out += ` excluded_workflows: [${excludedWorkflows.map((f) => `"${f}"`).join(", ")}] # ${C.excluded}\n`;
|
|
507
|
+
// 스토어 릴리스 노트 언어(#829) — 미지정이면 키를 쓰지 않는다. 전체 재생성이라 다시 쓰지 않으면 사용자가 적은 값이 사라진다.
|
|
508
|
+
if (storeLocales && storeLocales.length) out += ` store_locales: [${storeLocales.map((l) => `"${l}"`).join(", ")}] # ${C.storeLocales}\n`;
|
|
499
509
|
if (language) out += ` language: ${language} # ${C.language}\n`;
|
|
500
510
|
if (labelStyle) out += ` label_style: ${labelStyle} # ${C.labelStyle}\n`;
|
|
501
511
|
// 앱 심사 배포 레포 여부(#553) — 미지정이면 키를 쓰지 않는다(기존 레포 무변화).
|
package/src/i18n/doctor.en.js
CHANGED
|
@@ -7,6 +7,7 @@ export default {
|
|
|
7
7
|
"doctor.integ.notIntegrated": "The template is not integrated in this folder.",
|
|
8
8
|
"doctor.integ.howTo": "To integrate: npx projectops",
|
|
9
9
|
"doctor.integ.versions": "Template v{tpl} / Project v{proj}",
|
|
10
|
+
"doctor.integ.versionsNoTemplate": "Template version not recorded / Project v{proj}",
|
|
10
11
|
"doctor.wf.name": "Installed workflows",
|
|
11
12
|
"doctor.wf.purpose": "Automation running in this repository",
|
|
12
13
|
"doctor.count": "{n}",
|
package/src/i18n/doctor.ko.js
CHANGED
|
@@ -7,6 +7,7 @@ export default {
|
|
|
7
7
|
"doctor.integ.notIntegrated": "이 폴더에는 템플릿이 통합되어 있지 않습니다.",
|
|
8
8
|
"doctor.integ.howTo": "통합하려면: npx projectops",
|
|
9
9
|
"doctor.integ.versions": "템플릿 v{tpl} / 프로젝트 v{proj}",
|
|
10
|
+
"doctor.integ.versionsNoTemplate": "템플릿 버전 기록 없음 / 프로젝트 v{proj}",
|
|
10
11
|
"doctor.wf.name": "설치된 워크플로우",
|
|
11
12
|
"doctor.wf.purpose": "이 저장소에서 도는 자동화",
|
|
12
13
|
"doctor.count": "{n}개",
|
package/src/index.js
CHANGED
|
@@ -260,6 +260,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
260
260
|
// (사용자가 version.yml에 직접 쓰거나 스킬이 기록한 값을 그대로 유지).
|
|
261
261
|
appRelease: existing?.options?.appRelease ?? null,
|
|
262
262
|
excludedWorkflows: existing?.options?.excludedWorkflows ?? null, // #810 저장값 보존
|
|
263
|
+
storeLocales: existing?.options?.storeLocales ?? null, // #829 저장값 보존 (전체 재생성이라 안 넘기면 사라진다)
|
|
263
264
|
// 상태 라벨 표기(#776): 플래그 > 저장값 > (신규 en / 기존 ko). 기존 레포의 라벨 이름은 업데이트만으로 바뀌지 않는다.
|
|
264
265
|
// 릴리스 시 완료 이슈 닫기(#771): 저장값 보존, 없으면 신규만 true. 기존 레포는 키를 만들지 않아 현행 유지.
|
|
265
266
|
closeOnRelease: existing?.options?.closeOnRelease ?? (existing ? null : true),
|
|
@@ -361,7 +362,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
361
362
|
migrationGuidePath = appendGuideEntry(cwd, {
|
|
362
363
|
now, mode: opts.mode, types, repoName,
|
|
363
364
|
templateFrom: existing?.templateVersion || "", templateTo: context.templateVersion,
|
|
364
|
-
options: { deploy: deployTarget, publish: publishTargets, secretBackup: context.includeSecretBackup, coderabbit: context.codeReviewCoderabbit, changelogProvider: context.changelogProvider, intent, semverAuto: context.semverAuto , appRelease: context.appRelease, language: context.language, labelStyle: context.labelStyle, closeOnRelease: context.closeOnRelease, projectsSync: context.projectsSync, excludedWorkflows: context.excludedWorkflows },
|
|
365
|
+
options: { deploy: deployTarget, publish: publishTargets, secretBackup: context.includeSecretBackup, coderabbit: context.codeReviewCoderabbit, changelogProvider: context.changelogProvider, intent, semverAuto: context.semverAuto , appRelease: context.appRelease, language: context.language, labelStyle: context.labelStyle, closeOnRelease: context.closeOnRelease, projectsSync: context.projectsSync, excludedWorkflows: context.excludedWorkflows, storeLocales: context.storeLocales },
|
|
365
366
|
branches: { defaultBranch: branch, deployBranch: context.deployBranch || "develop", ready: null, created: null },
|
|
366
367
|
breaking: breakingReport, migrations: migrationsResult, orphans: { cleaned: [], pending: orphanPending },
|
|
367
368
|
events: trace.events, counters: { skipped: result?.workflows?.skipped ?? 0 },
|