project-auto-wizard 0.16.1 → 0.17.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/LICENSE +1 -0
- package/README.ja.md +5 -1
- package/README.ko.md +5 -1
- package/README.md +6 -2
- package/README.zh-CN.md +5 -1
- package/package.json +1 -1
- package/payload/scripts/messages.py +44 -6
- package/payload/scripts/version_manager.py +76 -3
- package/payload/version.yml.template +5 -4
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +7 -7
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +111 -57
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +10 -1
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +13 -3
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +10 -1
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +2 -2
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +10 -10
- package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +10 -10
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +14 -12
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +35 -18
- package/payload/workflows/go/PROJECT-GO-CI.yaml +3 -1
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +81 -61
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +3 -1
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +81 -61
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +3 -2
- package/payload/workflows/spring/PROJECT-SPRING-CI.yml +6 -5
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +86 -68
- package/src/commands/doctor.js +14 -24
- package/src/commands/dry-run.js +5 -3
- package/src/commands/full.js +6 -6
- package/src/commands/interactive.js +16 -3
- package/src/commands/status.js +9 -2
- package/src/core/copy/gitignore.js +3 -3
- package/src/core/copy/readme.js +11 -10
- package/src/core/detect-fs.js +9 -4
- package/src/core/options.js +36 -0
- package/src/core/version-yml.js +43 -13
- package/src/i18n/catalog/en/cli.js +1 -0
- package/src/i18n/catalog/en/commands.js +4 -1
- package/src/i18n/catalog/en/core.js +1 -0
- package/src/i18n/catalog/en/core2.js +2 -0
- package/src/i18n/catalog/en/ui.js +5 -0
- package/src/i18n/catalog/ko/cli.js +1 -0
- package/src/i18n/catalog/ko/commands.js +4 -1
- package/src/i18n/catalog/ko/core.js +1 -0
- package/src/i18n/catalog/ko/core2.js +2 -0
- package/src/i18n/catalog/ko/ui.js +5 -0
- package/src/index.js +25 -7
- package/src/ui/prompts.js +11 -1
- package/src/ui/summary.js +15 -6
package/src/index.js
CHANGED
|
@@ -8,13 +8,13 @@ import { readFileSync, existsSync } from "node:fs";
|
|
|
8
8
|
import { createInterface } from "node:readline/promises";
|
|
9
9
|
import { parseArgs, parsePathsCsv, CliError, TYPE_CLI_FLAGS } from "./cli/args.js";
|
|
10
10
|
import { helpText } from "./cli/help.js";
|
|
11
|
-
import { resolveLanguage, setLanguage, t, DEFAULT_LANGUAGE, LANG_ENV_VAR, normalizeLanguage } from "./i18n/index.js";
|
|
11
|
+
import { resolveLanguage, setLanguage, t, DEFAULT_LANGUAGE, LANG_ENV_VAR, SUPPORTED_LANGUAGES, normalizeLanguage } from "./i18n/index.js";
|
|
12
12
|
import { fallbackStyleTypes } from "./core/deploy-style.js";
|
|
13
13
|
import { hooksFor, mergeHookResults } from "./core/types.js";
|
|
14
14
|
import { PATHS } from "./core/paths.js";
|
|
15
15
|
import { resolvePayloadRoot, assertPayload, readTemplateVersion } from "./core/assets.js";
|
|
16
16
|
import { detectTypes, detectDefaultBranch, detectRepoName, makeResolvers, detectMarkers } from "./core/detect-fs.js";
|
|
17
|
-
import { parseExisting, droppedPathLines } from "./core/version-yml.js";
|
|
17
|
+
import { parseExisting, droppedPathLines, invalidOptionLines } from "./core/version-yml.js";
|
|
18
18
|
import { resolveReleaseOptions } from "./core/release-options.js";
|
|
19
19
|
import { explicitFromContext, optionContextFields } from "./core/options.js";
|
|
20
20
|
import { runBreakingCheck } from "./core/breaking-check.js";
|
|
@@ -100,16 +100,31 @@ async function runInner(argv, {
|
|
|
100
100
|
// Language: --lang -> env var -> saved version.yml value -> en. Resolved after --help/--version
|
|
101
101
|
// so an invalid env var cannot block the help output.
|
|
102
102
|
let language;
|
|
103
|
+
// Language written to version.yml: the environment variable only decides this run's output, so it must not
|
|
104
|
+
// overwrite a saved choice. Only --lang (or a first install / an unusable saved value) sets the stored language.
|
|
105
|
+
let storedLanguage;
|
|
106
|
+
// Interactive mode asks for the language itself when nothing says which one to use.
|
|
107
|
+
let askLanguage = false;
|
|
103
108
|
try {
|
|
104
109
|
const savedVy = join(cwd, "version.yml");
|
|
105
|
-
const
|
|
110
|
+
const savedParsed = existsSync(savedVy) ? parseExisting(readFileSync(savedVy, "utf8")) : null;
|
|
111
|
+
const saved = savedParsed?.language ?? null;
|
|
106
112
|
language = resolveLanguage({ flag: opts.lang, env: process.env[LANG_ENV_VAR], saved });
|
|
113
|
+
storedLanguage = normalizeLanguage(opts.lang) ? language : (saved ?? language);
|
|
107
114
|
// An existing install without a saved language now falls back to English: say so once, since
|
|
108
|
-
// its workflow messages switch from Korean on the next update.
|
|
115
|
+
// its workflow messages switch from Korean on the next update. A hand-edited unknown value also
|
|
116
|
+
// falls back to English, but it was never the old default, so it gets its own wording.
|
|
109
117
|
const existingWithoutLanguage = existsSync(savedVy) && !saved;
|
|
110
118
|
const unspecified = !normalizeLanguage(opts.lang) && !normalizeLanguage(process.env[LANG_ENV_VAR]);
|
|
119
|
+
askLanguage = opts.mode === "interactive" && unspecified && !saved;
|
|
111
120
|
if (existingWithoutLanguage && unspecified && ["full", "interactive"].includes(opts.mode)) {
|
|
112
|
-
|
|
121
|
+
const unknown = savedParsed?.languageUnsupported;
|
|
122
|
+
if (unknown) {
|
|
123
|
+
console.error(t("cli.lang.unsupportedSavedNotice", { value: unknown, supported: SUPPORTED_LANGUAGES.join(", ") }, DEFAULT_LANGUAGE));
|
|
124
|
+
} else if (!askLanguage) {
|
|
125
|
+
// When the user is asked instead, there is no silent fallback left to explain.
|
|
126
|
+
console.error(t("cli.lang.defaultNotice", {}, DEFAULT_LANGUAGE));
|
|
127
|
+
}
|
|
113
128
|
}
|
|
114
129
|
} catch (e) {
|
|
115
130
|
if (e instanceof CliError) { console.error(e.message); return 1; }
|
|
@@ -150,7 +165,7 @@ async function runInner(argv, {
|
|
|
150
165
|
// The log file is created on the first write, so viewing only status/doctor from the menu leaves nothing behind.
|
|
151
166
|
startLog("install");
|
|
152
167
|
return await runInteractive(
|
|
153
|
-
{ ...optionContextFields(opts), language },
|
|
168
|
+
{ ...optionContextFields(opts), language: askLanguage ? null : storedLanguage, askLanguage },
|
|
154
169
|
{ cwd, payloadRoot: payload, clock },
|
|
155
170
|
);
|
|
156
171
|
}
|
|
@@ -319,6 +334,8 @@ async function runInner(argv, {
|
|
|
319
334
|
|
|
320
335
|
// Warn against the final folders (an explicit --paths may keep the other one). The dry-run preview prints its own notice.
|
|
321
336
|
if (!opts.dryRun) for (const line of droppedPathLines(existing?.droppedPaths, paths)) console.error(`⚠️ ${line}`);
|
|
337
|
+
// An option value that could not be read is rewritten as false by this run - say so instead of doing it silently.
|
|
338
|
+
for (const line of invalidOptionLines(existing?.invalidOptions)) console.error(`⚠️ ${line}`);
|
|
322
339
|
|
|
323
340
|
// version: existing version.yml first (SSoT; prevents overwriting on re-run) -> CLI value -> file detection.
|
|
324
341
|
// Non-interactive, so the fallback notice uses the CLI wording (--project-version) as is.
|
|
@@ -374,7 +391,7 @@ async function runInner(argv, {
|
|
|
374
391
|
repoName,
|
|
375
392
|
// resolvers that compute @wizard ask/auto token values
|
|
376
393
|
resolvers: makeResolvers(cwd, repoName, paths, typeOptions),
|
|
377
|
-
now, today, language,
|
|
394
|
+
now, today, language: storedLanguage,
|
|
378
395
|
// Extra context for the install log; does not change the install itself.
|
|
379
396
|
markers: detectMarkers(cwd, types), detectWarnings,
|
|
380
397
|
});
|
|
@@ -406,6 +423,7 @@ async function runInner(argv, {
|
|
|
406
423
|
// Completion summary (also printed in CLI mode)
|
|
407
424
|
printSummary({
|
|
408
425
|
mode: opts.mode, types, version, versionCode, branches, developMissing,
|
|
426
|
+
releaseAutomerge: context.includeReleaseAutomerge !== false,
|
|
409
427
|
copiedFiles: result?.workflows?.copiedFiles ?? [],
|
|
410
428
|
autoUpdated: result?.workflows?.autoUpdated ?? [],
|
|
411
429
|
gitignoreUpdated: result?.gitignoreUpdated === true,
|
package/src/ui/prompts.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// On ESC each function returns the CANCEL symbol -> the caller interprets it as default/stay.
|
|
4
4
|
// Ctrl+C / Ctrl+D make the engine reject with PromptAbortError -> run() catches it and exits with code 130.
|
|
5
5
|
import * as engine from "./readline-engine.js";
|
|
6
|
-
import { t } from "../i18n/index.js";
|
|
6
|
+
import { t, SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from "../i18n/index.js";
|
|
7
7
|
import { DEPLOY_STYLES, NO_DEPLOY_STYLE } from "../core/deploy-style.js";
|
|
8
8
|
import { TYPE_IDS, typeInfo } from "../core/types.js";
|
|
9
9
|
import { askableOptions } from "../core/options.js";
|
|
@@ -11,6 +11,16 @@ import { ENV_MODES, DEFAULT_ENV_MODE, STORE_PLATFORMS, DEPLOY_MODES, DEFAULT_DEP
|
|
|
11
11
|
|
|
12
12
|
export const CANCEL = engine.CANCEL;
|
|
13
13
|
|
|
14
|
+
// Language selection - asked before any language is known, so the message and every label are rendered
|
|
15
|
+
// once per supported language instead of in the active one. Returns the language code. CANCEL on cancel.
|
|
16
|
+
export async function selectLanguage() {
|
|
17
|
+
return engine.select({
|
|
18
|
+
message: SUPPORTED_LANGUAGES.map((lang) => t("ui.prompts.language.message", {}, lang)).join(" / "),
|
|
19
|
+
options: SUPPORTED_LANGUAGES.map((lang) => ({ value: lang, label: t("ui.prompts.language.label", {}, lang) })),
|
|
20
|
+
initialIndex: Math.max(0, SUPPORTED_LANGUAGES.indexOf(DEFAULT_LANGUAGE)),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
// Mode selection - localized labels, returns the internal key. CANCEL on cancel.
|
|
15
25
|
// again=true is a re-entry after running a read-only mode (status/doctor) and returning to the menu -
|
|
16
26
|
// asking "what should we install?" again would feel odd, so the wording changes.
|
package/src/ui/summary.js
CHANGED
|
@@ -13,6 +13,8 @@ export function printSummary(ctx) {
|
|
|
13
13
|
const { mode, types = [], version = "", versionCode = null, copiedFiles = [], autoUpdated = [], branches = null, gitignoreUpdated = false,
|
|
14
14
|
// pr-flow but the develop branch could not be created on the remote - the layout line alone looks ready, so warn separately.
|
|
15
15
|
developMissing = false,
|
|
16
|
+
// false when version.yml turns the release PR automerge off: the summary must not describe automerge or its PAT then.
|
|
17
|
+
releaseAutomerge = true,
|
|
16
18
|
// Post-install verification and record
|
|
17
19
|
answers = [], unresolved = [], secrets = new Map(), optionalSecrets = new Map(), logPath = "", legacyMdLogs = false, cleanup = null,
|
|
18
20
|
// Flutter store deployment - app files created/kept and cleanup of deselected stores
|
|
@@ -49,7 +51,7 @@ export function printSummary(ctx) {
|
|
|
49
51
|
if (branches.mode === "trunk-based") {
|
|
50
52
|
err(t("ui.summary.branches.trunk", { main: branches.main }));
|
|
51
53
|
} else {
|
|
52
|
-
err(t("ui.summary.branches.prFlow", { develop: branches.develop, main: branches.main }));
|
|
54
|
+
err(t(releaseAutomerge ? "ui.summary.branches.prFlow" : "ui.summary.branches.prFlowManual", { develop: branches.develop, main: branches.main }));
|
|
53
55
|
if (developMissing) {
|
|
54
56
|
err(t("ui.summary.branches.developMissing", { develop: branches.develop }));
|
|
55
57
|
err(t("ui.summary.branches.developHint", { main: branches.main, develop: branches.develop }));
|
|
@@ -208,11 +210,18 @@ export function printSummary(ctx) {
|
|
|
208
210
|
err("");
|
|
209
211
|
}
|
|
210
212
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
// With automerge off (pr-flow) nothing merges the release PR on the workflow's behalf, so the PAT guidance does not apply.
|
|
214
|
+
// In trunk-based mode there is no release PR, and the PAT block is kept as before.
|
|
215
|
+
if (releaseAutomerge || !branches || branches.mode === "trunk-based") {
|
|
216
|
+
err(` ${num()} ${t("ui.summary.todo.pat")}`);
|
|
217
|
+
err(t("ui.summary.todo.patPath"));
|
|
218
|
+
err(t("ui.summary.todo.patName"));
|
|
219
|
+
err(t("ui.summary.todo.patAccount"));
|
|
220
|
+
err(t("ui.summary.todo.patOptional"));
|
|
221
|
+
} else {
|
|
222
|
+
err(` ${num()} ${t("ui.summary.todo.manualMerge")}`);
|
|
223
|
+
err(t("ui.summary.todo.manualMergeHow"));
|
|
224
|
+
}
|
|
216
225
|
err("");
|
|
217
226
|
// Installed workflows declare the permissions they need themselves - report it by the same rule as the doctor guidance.
|
|
218
227
|
err(` ${num()} ${t("ui.summary.todo.permissions")}`);
|