projectops 4.78.1 → 4.79.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 +1 -1
- package/package.json +1 -1
- package/src/commands/interactive.js +5 -0
- package/src/core/copy/workflows.js +1 -20
- package/src/core/migration-guide.js +6 -0
- package/src/core/options-schema.js +1 -1
- package/src/core/store-locales-hint.js +89 -0
- package/src/i18n/summary.en.js +2 -0
- package/src/i18n/summary.ko.js +2 -0
- package/src/index.js +5 -0
- package/src/ui/summary.js +7 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
**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)).
|
|
12
12
|
|
|
13
13
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
14
|
-
## Latest version : v4.78.
|
|
14
|
+
## Latest version : v4.78.1 (2026-10-29)
|
|
15
15
|
|
|
16
16
|
[View full version history](CHANGELOG.md)
|
|
17
17
|
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// 새 시각 층(banner/detectionLog/analysisCard/ideStatus/installKind/summary)과 저수준 엔진(engineIo)은
|
|
4
4
|
// io의 "옵셔널 멤버" — 스텁이 생략하면 해당 층만 건너뛰고 실행 계약은 동일하다.
|
|
5
5
|
import { resolveOptions } from "../core/resolve-options.js";
|
|
6
|
+
import { detectStoreLocalesHint } from "../core/store-locales-hint.js";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
9
|
import { PATHS } from "../core/paths.js";
|
|
@@ -290,6 +291,8 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
290
291
|
// @wizard env 계획 질문 (full/workflows만).
|
|
291
292
|
// 업데이트 모드는 생략 — 신규 파일은 기본값, 교체 파일은 아래 carryover가 기존 실값을 이월한다.
|
|
292
293
|
const resolvers = makeResolvers(cwd, repoName, paths);
|
|
294
|
+
// 다국어 스토어인데 store_locales 미설정이면 안내만 한다 (#843) — 복사 전 상태로 판정
|
|
295
|
+
const storeLocalesHint = detectStoreLocalesHint({ root: cwd, existing, paths, trace });
|
|
293
296
|
let envValues = new Map(), envUseDefaults = true;
|
|
294
297
|
if (showOptional && !updateRun) {
|
|
295
298
|
const plan = await promptEnvPlan({
|
|
@@ -425,6 +428,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
425
428
|
now, mode, types, repoName,
|
|
426
429
|
templateFrom: existing?.templateVersion || "", templateTo: templateVersion,
|
|
427
430
|
options: { deploy: deployTarget, publish: publishTargets, secretBackup: includeSecretBackup, coderabbit: codeReviewCoderabbit, changelogProvider, intent, semverAuto , appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales, storeLocalesIos, storeLocalesPlay },
|
|
431
|
+
storeLocalesHint, // #843 체크리스트 한 항목
|
|
428
432
|
branches: { defaultBranch: branch, deployBranch, ready: deployBranchReady, created: deployBranchCreated },
|
|
429
433
|
breaking: breakingReport, migrations: migrationsResult, orphans: orphanReport,
|
|
430
434
|
events: trace.events, counters: { skipped: result?.workflows?.skipped ?? 0 },
|
|
@@ -441,6 +445,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
441
445
|
legacyLeftover: migrationsResult?.confirmPending ?? [], // #809 신·구 세대 동시 실행 안내
|
|
442
446
|
notInstalled: result?.workflows?.notInstalled ?? [], // #810 깔지 않은 워크플로우 안내
|
|
443
447
|
semverAutoNewlyOn,
|
|
448
|
+
storeLocalesHint, // #843 다국어 스토어 안내
|
|
444
449
|
verification: result?.verification, // #549 설치 검증 결과
|
|
445
450
|
aiPrSummary, codeReviewCoderabbit, // #569 — 고른 것만 안내
|
|
446
451
|
logDir: files ? MIGRATION_DIR : null, // #561 기록 위치 안내
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 충돌(기존 파일과 다름)의 처리 결정은 hooks.decisions(Map<파일명, 'skip'|'backup'|'template'>)로 받는다.
|
|
4
4
|
// - 실제 마법사(commands/interactive.js)는 listWorkflowConflicts 로 충돌 목록을 얻어 직접 Map 을 만든다.
|
|
5
|
-
// - copyWorkflowsInteractive 는 onConflict 콜백으로 같은 Map 을 만들어 주는 보조 API 다. 프로덕션 경로는 쓰지 않고
|
|
6
|
-
// 테스트(copy-workflows-conflict.test.js)만 쓴다.
|
|
7
5
|
import { join, basename } from "node:path";
|
|
8
6
|
import { existsSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
9
7
|
import { PATHS } from "../paths.js";
|
|
@@ -346,7 +344,7 @@ function applyDecision(decision, srcDir, workflowsDir, filename, counters, trace
|
|
|
346
344
|
});
|
|
347
345
|
}
|
|
348
346
|
|
|
349
|
-
// 대상 워크플로우 디렉토리에서 changed(충돌) 파일 목록만 뽑는다 — 마법사(commands/interactive.js)
|
|
347
|
+
// 대상 워크플로우 디렉토리에서 changed(충돌) 파일 목록만 뽑는다 — 마법사(commands/interactive.js)가 쓴다.
|
|
350
348
|
// copyWorkflows 본체와 동일한 classify 기준을 써야 결정 Map이 실제 처리 대상과 1:1로 맞는다.
|
|
351
349
|
export function listWorkflowConflicts(context, tempDir, targetRoot = ".") {
|
|
352
350
|
const { types = [], paths = new Map(), deployTarget = "docker-ssh", repoName = "", resolvers = {}, branch = "", deployBranch = "" } = context;
|
|
@@ -383,23 +381,6 @@ export function listWorkflowConflicts(context, tempDir, targetRoot = ".") {
|
|
|
383
381
|
}
|
|
384
382
|
return conflicts;
|
|
385
383
|
}
|
|
386
|
-
|
|
387
|
-
// 대화형 진입점 (async) — 충돌마다 onConflict(filename, type)를 await해 결정 Map을 만든 뒤
|
|
388
|
-
// 동기 엔진에 위임한다. WHY 분리: copyWorkflows를 async로 바꾸면 await 없이 호출하는
|
|
389
|
-
// 기존 호출부(runFull/runWorkflows)가 깨진다 — 시그니처 무변경 원칙.
|
|
390
|
-
// onConflict 반환값: 'template' | 'skip' | 'backup' (그 외/미지정 → 'skip').
|
|
391
|
-
export async function copyWorkflowsInteractive(context, tempDir, targetRoot = ".", { onConflict } = {}) {
|
|
392
|
-
const decisions = new Map();
|
|
393
|
-
if (typeof onConflict === "function") {
|
|
394
|
-
for (const { filename, type } of listWorkflowConflicts(context, tempDir, targetRoot)) {
|
|
395
|
-
if (decisions.has(filename)) continue; // 파일명은 PROJECT-{TYPE}- prefix로 타입 간 유일
|
|
396
|
-
decisions.set(filename, await onConflict(filename, type));
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return copyWorkflows(context, tempDir, targetRoot, { decisions });
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
384
|
function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters) {
|
|
404
385
|
const { deployTarget = "docker-ssh", publishTargets = [], force = false, paths = new Map(), repoName = "", resolvers = {}, envOptsFor, collectAsks = null, decisions = new Map(), trace = null, baseline = null } = ctx;
|
|
405
386
|
const typeDir = join(projectTypesDir, type);
|
|
@@ -130,6 +130,12 @@ export function renderGuideEntry(report) {
|
|
|
130
130
|
for (const [k, v] of kv) checklist.push(` - ${t} \`${k}\` = \`${v}\``);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
+
// #843 — 다국어 스토어인데 store_locales 미설정. 안내만 (키는 쓰지 않는다)
|
|
134
|
+
const storeHint = r.storeLocalesHint ?? null;
|
|
135
|
+
if (storeHint) {
|
|
136
|
+
checklist.push(`- [ ] **Decide whether to turn on per-language store release notes**: ${storeHint.locales.length} store languages found (${storeHint.locales.map((l) => `\`${l}\``).join(", ")}) but \`store_locales\` is not set, so every language gets the same text. To use per-language text from \`store_notes\` in CHANGELOG.json, add \`metadata.template.options.store_locales: ["ko-KR", "en-US", ...]\` to version.yml (first = default language). Found in:`);
|
|
137
|
+
for (const s of storeHint.signals) checklist.push(` - \`${s.path}\` (${s.source}: ${s.locales.join(", ")})`);
|
|
138
|
+
}
|
|
133
139
|
const devBranch = r.branches?.deployBranch ?? "develop";
|
|
134
140
|
const devBranchResult = r.branches?.result ?? null; // #715 --create-development-branch 결과
|
|
135
141
|
if (devBranchResult?.status === "created") {
|
|
@@ -283,7 +283,7 @@ export const KEY_DETAILS = {
|
|
|
283
283
|
"metadata.template.options.projects_sync": { when: "Edit via --projects-sync.", effect: "Whether the Projects status sync workflow is installed.", omitted: "false for new installs; kept if installed." },
|
|
284
284
|
"metadata.template.options.app_release": { when: "Set true by hand for repos whose releases go through App Store / Play Store review.", effect: "The deploy skill (pro-changelog-deploy) treats releases as going through store review: release notes get a review warning banner and are checked more strictly, and the release PR always waits for manual approval instead of automerging.", omitted: "Not set. Never enabled automatically.", caution: "Do not set it on repos that do not ship to a store." },
|
|
285
285
|
"metadata.template.options.excluded_workflows": { when: "Add a file name to stop the update from ever installing that template workflow.", effect: "Listed files are skipped on install and update. Files you deleted yourself are also not restored; a copy of the template is kept in .github/.projectops/incoming/.", omitted: "[]", example: 'excluded_workflows: ["PROJECT-COMMON-TEMPLATE-UTIL-VERSION-SYNC.yml"]' },
|
|
286
|
-
"metadata.template.options.store_locales": { when: "Set it when the store listing has several languages and release notes must not be one Korean text copied into all of them.", effect: "Turns on per-language store release notes (Play Console 'Release notes', App Store 'What's New'). The first entry is the default language. Each other language uses the text in store_notes[<language>] of that version in CHANGELOG.json; a language without text gets the default-language text, because the stores reject an empty What's New.", omitted: "Off: the same text is used for every store language, as before.", example: 'store_locales: ["ko-KR", "en-US", "ja-JP", "zh-CN"]' },
|
|
286
|
+
"metadata.template.options.store_locales": { when: "Set it when the store listing has several languages and release notes must not be one Korean text copied into all of them. Likely needed when the repo already has DELIVER_LOCALES with 2+ languages in a workflow or Fastfile, 2+ android/fastlane/metadata/android/<lang> folders, or 2+ release-notes/<lang> folders; an update (npx projectops) then prints a notice on the completion screen and a checklist item in the migration guide, without writing this key.", effect: "Turns on per-language store release notes (Play Console 'Release notes', App Store 'What's New'). The first entry is the default language. Each other language uses the text in store_notes[<language>] of that version in CHANGELOG.json; a language without text gets the default-language text, because the stores reject an empty What's New.", omitted: "Off: the same text is used for every store language, as before (also when DELIVER_LOCALES lists several languages: they all get that one text).", example: 'store_locales: ["ko-KR", "en-US", "ja-JP", "zh-CN"]' },
|
|
287
287
|
"metadata.template.options.store_locales_ios": { when: "Set it only when the App Store listing has a different set of languages than Google Play.", effect: "The App Store uses this list instead of store_locales (same rules: first entry is the default language, a language without text gets the default-language text). Needed because fastlane deliver ACTIVATES every language folder the app version does not have, so a language the App Store listing lacks would be added to the app.", omitted: "The App Store uses store_locales.", example: 'store_locales_ios: ["ko-KR", "en-US"]' },
|
|
288
288
|
"metadata.template.options.store_locales_play": { when: "Set it only when Google Play has a different set of languages than the App Store.", effect: "Google Play uses this list instead of store_locales (same rules).", omitted: "Google Play uses store_locales.", example: 'store_locales_play: ["ko-KR", "en-US", "ja-JP"]' },
|
|
289
289
|
"metadata.template.options.code_review.coderabbit": { when: "Edit via --coderabbit / --no-coderabbit.", effect: "Whether .coderabbit.yaml is installed by --mode full. PR review bot only; unrelated to release notes.", omitted: "false" },
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// 스토어 다국어 설정 감지 (#843): store_locales 없이 여러 언어에 한 문구를 올리는 레포를 찾아 알리기만 한다.
|
|
2
|
+
// 동작·version.yml 은 바꾸지 않는다. 결과는 완료 화면 한 줄 + 마이그레이션 가이드 체크리스트 한 항목.
|
|
3
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
4
|
+
import { join, relative } from "node:path";
|
|
5
|
+
|
|
6
|
+
// 언어 폴더 이름 모양 (ko, ko-KR, en-US, zh-Hans)
|
|
7
|
+
const LOCALE_NAME = /^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$/;
|
|
8
|
+
// DELIVER_LOCALES 대입 (yaml `KEY: "a,b"`, 셸 `KEY=a,b`, Ruby `ENV["KEY"] = "a,b"`). `||` 기본값은 대입이 아니라 제외
|
|
9
|
+
const DELIVER_LOCALES_ASSIGN = /DELIVER_LOCALES["']?\]?\s*[:=]\s*["']?([A-Za-z0-9_, -]+)/g;
|
|
10
|
+
const MINIMUM_LOCALE_COUNT = 2;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 다국어 스토어 신호 3종 수집: DELIVER_LOCALES 2개 이상, Play metadata 언어 폴더, release-notes 언어 폴더
|
|
14
|
+
* storeLocales 가 이미 있거나(null 아님) 신호가 없으면 null
|
|
15
|
+
*/
|
|
16
|
+
export function detectMultiLocaleStore({ root, flutterPath = ".", storeLocales = null }) {
|
|
17
|
+
if (storeLocales != null) return null;
|
|
18
|
+
const appRoot = join(root, flutterPath || ".");
|
|
19
|
+
const signals = [
|
|
20
|
+
...findDeliverLocales(root, appRoot),
|
|
21
|
+
...findLocaleDirectories(root, join(appRoot, "android/fastlane/metadata/android"), "play-metadata"),
|
|
22
|
+
...findLocaleDirectories(root, join(appRoot, "release-notes"), "release-notes"),
|
|
23
|
+
];
|
|
24
|
+
// 루트와 앱 폴더가 다를 때만 루트 release-notes 도 본다 (같으면 중복)
|
|
25
|
+
if (appRoot !== join(root, ".")) signals.push(...findLocaleDirectories(root, join(root, "release-notes"), "release-notes"));
|
|
26
|
+
if (signals.length === 0) return null;
|
|
27
|
+
const locales = [...new Set(signals.flatMap((s) => s.locales))];
|
|
28
|
+
return { signals, locales };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 워크플로우(.yml/.yaml) + Fastfile(ios·android·루트) 의 DELIVER_LOCALES
|
|
32
|
+
function findDeliverLocales(root, appRoot) {
|
|
33
|
+
const candidates = [];
|
|
34
|
+
const workflowsDir = join(root, ".github/workflows");
|
|
35
|
+
if (existsSync(workflowsDir)) {
|
|
36
|
+
for (const name of readdirSync(workflowsDir)) {
|
|
37
|
+
if (/\.ya?ml$/.test(name)) candidates.push(join(workflowsDir, name));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const sub of ["ios/fastlane/Fastfile", "android/fastlane/Fastfile", "fastlane/Fastfile"]) {
|
|
41
|
+
candidates.push(join(appRoot, sub));
|
|
42
|
+
}
|
|
43
|
+
const found = [];
|
|
44
|
+
for (const file of new Set(candidates)) {
|
|
45
|
+
if (!isFile(file)) continue;
|
|
46
|
+
const text = readFileSync(file, "utf8");
|
|
47
|
+
for (const match of text.matchAll(DELIVER_LOCALES_ASSIGN)) {
|
|
48
|
+
const locales = match[1].split(",").map((s) => s.trim()).filter((s) => LOCALE_NAME.test(s));
|
|
49
|
+
if (locales.length >= MINIMUM_LOCALE_COUNT) {
|
|
50
|
+
found.push({ source: "deliver-locales", path: toRelative(root, file), locales });
|
|
51
|
+
break; // 파일당 1건이면 충분
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// <dir>/<lang>/ 폴더가 2개 이상이면 신호 1건
|
|
59
|
+
function findLocaleDirectories(root, dir, source) {
|
|
60
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];
|
|
61
|
+
const locales = readdirSync(dir)
|
|
62
|
+
.filter((name) => LOCALE_NAME.test(name) && statSync(join(dir, name)).isDirectory())
|
|
63
|
+
.sort();
|
|
64
|
+
return locales.length >= MINIMUM_LOCALE_COUNT ? [{ source, path: toRelative(root, dir), locales }] : [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isFile(path) {
|
|
68
|
+
return existsSync(path) && statSync(path).isFile();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 화면·가이드 표기는 OS 와 무관하게 / 구분자
|
|
72
|
+
function toRelative(root, path) {
|
|
73
|
+
return relative(root, path).split("\\").join("/");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 진입점(비대화형·대화형) 공통: 기존 레포 + storeLocales 미설정일 때만 감지 > trace 기록 > 결과 반환
|
|
78
|
+
* 신규 설치는 null (알릴 대상은 이미 다국어로 운영 중인 레포)
|
|
79
|
+
*/
|
|
80
|
+
export function detectStoreLocalesHint({ root, existing, paths, trace = null }) {
|
|
81
|
+
if (!existing) return null;
|
|
82
|
+
const hint = detectMultiLocaleStore({
|
|
83
|
+
root, flutterPath: paths?.get?.("flutter") || ".", storeLocales: existing.options?.storeLocales ?? null,
|
|
84
|
+
});
|
|
85
|
+
trace?.event?.("detect", "store-locales-hint", hint ? "multi-locale" : "none", hint
|
|
86
|
+
? { locales: hint.locales, signals: hint.signals.map((s) => `${s.source}:${s.path}`) }
|
|
87
|
+
: { storeLocalesSet: existing.options?.storeLocales != null });
|
|
88
|
+
return hint;
|
|
89
|
+
}
|
package/src/i18n/summary.en.js
CHANGED
|
@@ -26,6 +26,8 @@ export default {
|
|
|
26
26
|
"done.semverTitle": "Automatic version bump (semver_auto) is now ON for this repo",
|
|
27
27
|
"done.semverBody": "The next release picks the bump from commit titles: 'feat:' raises the minor version, 'type!:' the major one, anything else the patch.",
|
|
28
28
|
"done.semverOff": "To keep patch-only bumps, set metadata.template.options.semver_auto: false in version.yml.",
|
|
29
|
+
"done.storeLocalesTitle": "Several store languages found ({locales}) but store_locales is not set: every language gets the same release note",
|
|
30
|
+
"done.storeLocalesHint": "For per-language notes add metadata.template.options.store_locales: [\"ko-KR\", \"en-US\", ...] to version.yml (first = default). Nothing was changed.",
|
|
29
31
|
"done.notInstalledTitle": "{count} template workflow(s) were not installed",
|
|
30
32
|
"done.notInstalled.excluded": "listed in excluded_workflows",
|
|
31
33
|
"done.notInstalled.deleted-by-user": "you deleted it earlier",
|
package/src/i18n/summary.ko.js
CHANGED
|
@@ -26,6 +26,8 @@ export default {
|
|
|
26
26
|
"done.semverTitle": "이 레포의 자동 버전 승격(semver_auto)이 켜졌습니다",
|
|
27
27
|
"done.semverBody": "다음 릴리스부터 커밋 제목으로 올리는 폭을 정합니다: 'feat:'는 minor, 'type!:'는 major, 그 외는 patch.",
|
|
28
28
|
"done.semverOff": "patch만 올리고 싶으면 version.yml 의 metadata.template.options.semver_auto: false 로 두세요.",
|
|
29
|
+
"done.storeLocalesTitle": "스토어 언어가 여러 개({locales})인데 store_locales 가 없어 모든 언어에 같은 릴리스 노트가 올라갑니다",
|
|
30
|
+
"done.storeLocalesHint": "언어별 문구를 쓰려면 version.yml 에 metadata.template.options.store_locales: [\"ko-KR\", \"en-US\", ...] 를 넣으세요 (첫 항목이 기본 언어). 아무것도 바꾸지 않았습니다.",
|
|
29
31
|
"done.notInstalledTitle": "템플릿 워크플로우 {count}개를 설치하지 않았습니다",
|
|
30
32
|
"done.notInstalled.excluded": "excluded_workflows 에 적혀 있음",
|
|
31
33
|
"done.notInstalled.deleted-by-user": "예전에 직접 지운 파일",
|
package/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { createRunTrace, MIGRATION_DIR } from "./core/run-trace.js";
|
|
|
21
21
|
import { appendGuideEntry } from "./core/migration-guide.js";
|
|
22
22
|
import { resolveProjectPaths, markerForType } from "./core/paths-resolve.js";
|
|
23
23
|
import { resolveOptions } from "./core/resolve-options.js";
|
|
24
|
+
import { detectStoreLocalesHint } from "./core/store-locales-hint.js";
|
|
24
25
|
import { runDevelopmentBranchStep } from "./core/development-branch.js";
|
|
25
26
|
import { printBannerCompact } from "./ui/banner.js";
|
|
26
27
|
import { printSummary } from "./ui/summary.js";
|
|
@@ -203,6 +204,8 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
203
204
|
});
|
|
204
205
|
const { intent, deployTarget, publishTargets } = resolved;
|
|
205
206
|
const { before: beforeCleanup, applicable } = sources;
|
|
207
|
+
// 다국어 스토어인데 store_locales 미설정이면 안내만 한다 (#843) — 복사 전 상태로 판정
|
|
208
|
+
const storeLocalesHint = detectStoreLocalesHint({ root: cwd, existing, paths, trace });
|
|
206
209
|
|
|
207
210
|
// 축 확정 근거 (#561) — "왜 이 값인가"가 가장 헷갈리는 자리다.
|
|
208
211
|
// CLI 플래그 / 저장값 / intent 유도 / 타입 적용성 정리 중 무엇이 이겼는지 남긴다.
|
|
@@ -327,6 +330,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
327
330
|
now, mode: opts.mode, types, repoName,
|
|
328
331
|
templateFrom: existing?.templateVersion || "", templateTo: context.templateVersion,
|
|
329
332
|
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, storeLocalesIos: context.storeLocalesIos, storeLocalesPlay: context.storeLocalesPlay },
|
|
333
|
+
storeLocalesHint, // #843 체크리스트 한 항목
|
|
330
334
|
branches: { defaultBranch: branch, deployBranch: context.deployBranch || "develop", ready: isDevelopmentBranchReady,
|
|
331
335
|
created: developmentBranchResult ? developmentBranchResult.status === "created" : null, result: developmentBranchResult },
|
|
332
336
|
breaking: breakingReport, migrations: migrationsResult, orphans: { cleaned: [], pending: orphanPending },
|
|
@@ -346,6 +350,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
|
|
|
346
350
|
legacyLeftover: migrationsResult?.confirmPending ?? [], // #809 신·구 세대 동시 실행 안내
|
|
347
351
|
notInstalled: result?.workflows?.notInstalled ?? [], // #810 깔지 않은 워크플로우 안내
|
|
348
352
|
semverAutoNewlyOn: sources.semverAutoNewlyOn && context.semverAuto === true, // 업데이트가 켠 사실 안내
|
|
353
|
+
storeLocalesHint, // #843 다국어 스토어 안내
|
|
349
354
|
verification: result?.verification, // #549 설치 후 검증 결과 (full/workflows 모드에서만 존재)
|
|
350
355
|
// #569 — 고른 것만 안내하려면 선택값이 필요하다
|
|
351
356
|
aiPrSummary: context.aiPrSummary, codeReviewCoderabbit: context.codeReviewCoderabbit,
|
package/src/ui/summary.js
CHANGED
|
@@ -128,6 +128,13 @@ export function printSummary(ctx, targetRoot = ".", write = null) {
|
|
|
128
128
|
err(` ${t("done.semverOff")}`);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// Several store languages but no store_locales (#843): every language still gets the same text. Notice only.
|
|
132
|
+
if (ctx?.storeLocalesHint) {
|
|
133
|
+
err("");
|
|
134
|
+
err(` ${CYAN}ℹ️ ${t("done.storeLocalesTitle", { locales: ctx.storeLocalesHint.locales.join(", ") })}${NC}`);
|
|
135
|
+
err(` ${t("done.storeLocalesHint")}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
131
138
|
// Workflows left out on purpose (#810): deleted by the user, listed in excluded_workflows, or needing a branch this repo lacks.
|
|
132
139
|
const skippedNew = ctx?.notInstalled ?? [];
|
|
133
140
|
if (skippedNew.length > 0) {
|