projectops 4.45.0 โ†’ 4.47.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 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.44.2 (2026-10-10)
12
+ ## Latest version : v4.45.1 (2026-10-10)
13
13
 
14
14
  [View full version history](CHANGELOG.md)
15
15
 
@@ -49,7 +49,7 @@ This project automates your development workflow on two axes.
49
49
  | Format every issue by hand | `/pro-github` writes and files an issue from the standard template in one step |
50
50
  | Copy the issue URL into commit messages | `/pro-commit` completes the message from the issue context |
51
51
  | Write PR descriptions and reports by hand | `/pro-report` analyzes the git diff and generates one |
52
- | Re-type prompts for every code review or analysis | 17 Skills give consistent results without re-typing |
52
+ | Re-type prompts for every code review or analysis | 15 Skills give consistent results without re-typing |
53
53
 
54
54
  ---
55
55
 
@@ -125,14 +125,12 @@ Open a PR from the development branch to `main` and it writes the release notes,
125
125
  | Skill | Purpose |
126
126
  |------|------|
127
127
  | `/pro-review` | Review from 6 angles (security, performance, bugs, quality...), classified Critical/Major/Minor |
128
- | `/pro-note` | When stuck, search past notes; save what you found out as an actionable note |
129
128
 
130
129
  ### ๐Ÿ”ง Implementation (writes real code)
131
130
 
132
131
  | Skill | Purpose |
133
132
  |------|------|
134
- | `/pro-figma-verify` | Move a Figma design into code, then compare the result with the design pixel by pixel |
135
- | `/pro-build` | Run the project build, analyze errors, suggest optimizations |
133
+ | `/pro-figma` | Move a Figma design into code, then compare the result with the design pixel by pixel |
136
134
  | `/pro-launch` | Launch, drive, and capture apps, web, and servers: fixed status bar, width changes, response mocking (empty list, failures), render from code |
137
135
  | `/pro-design-brief` | A design request for a screen you built before the mockup exists: state captures, alternatives, copy candidates, and must-keep rules as a board for the designer |
138
136
  | `/pro-oss-consult` | Diagnose a GitHub repo as an open-source project: type detection, 6-axis scoring, maturity stage, and edits to About, topics, labels, and files only after approval. One repo or a whole account |
@@ -165,7 +163,7 @@ flowchart TD
165
163
  C --> D{"Type of work"}
166
164
 
167
165
  D -->|New feature / design / refactor| E1["superpowers:brainstorming<br/>Decide what and why"]
168
- D -->|Bug / outage| E2["/pro-note<br/>Search past notes + investigate"]
166
+ D -->|Bug / outage| E2["superpowers:systematic-debugging<br/>Find the root cause"]
169
167
 
170
168
  E1 --> P["superpowers:writing-plans<br/>Implementation plan"]
171
169
  P --> F["superpowers:executing-plans<br/>Run the plan"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectops",
3
- "version": "4.45.0",
3
+ "version": "4.47.0",
4
4
  "description": "ProjectOps: installer CLI for a fully automated GitHub project management template (versioning, CI/CD, release notes, agent skills)",
5
5
  "keywords": [
6
6
  "devops",
@@ -2,7 +2,7 @@
2
2
  // io ์ฃผ์ž…์œผ๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ. ์‹ค์ œ ์‹คํ–‰์€ src/ui/prompts.js ํ•จ์ˆ˜๋ฅผ io๋กœ ๋„˜๊ธด๋‹ค.
3
3
  // ์ƒˆ ์‹œ๊ฐ ์ธต(banner/detectionLog/analysisCard/ideStatus/installKind/summary)๊ณผ ์ €์ˆ˜์ค€ ์—”์ง„(engineIo)์€
4
4
  // io์˜ "์˜ต์…”๋„ ๋ฉค๋ฒ„" โ€” ์Šคํ…์ด ์ƒ๋žตํ•˜๋ฉด ํ•ด๋‹น ์ธต๋งŒ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์‹คํ–‰ ๊ณ„์•ฝ์€ ๋™์ผํ•˜๋‹ค.
5
- import { DEFAULT_CODE_REVIEW_CODERABBIT } from "../core/constants.js";
5
+ import { resolveOptions } from "../core/resolve-options.js";
6
6
  import { join } from "node:path";
7
7
  import { existsSync, readFileSync } from "node:fs";
8
8
  import { PATHS } from "../core/paths.js";
@@ -14,7 +14,7 @@ import { runBreakingCheck } from "../core/breaking-check.js";
14
14
  import { runMigrations } from "../core/migrations/index.js";
15
15
  import { detectOrphanWorkflows, applyOrphanCleanup } from "../core/orphan-workflows.js";
16
16
  import { resolveProjectPaths, filterExcludedTypes } from "../core/paths-resolve.js";
17
- import { askAllOptionalWorkflows, OPTION_AXES, applicableTargets, migrateProvider } from "../core/options-ask.js";
17
+ import { askAllOptionalWorkflows, OPTION_AXES, applicableTargets } from "../core/options-ask.js";
18
18
  import { createRunTrace, MIGRATION_DIR } from "../core/run-trace.js";
19
19
  import { appendGuideEntry } from "../core/migration-guide.js";
20
20
  import { promptEnvPlan } from "../ui/env-plan.js";
@@ -125,35 +125,26 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
125
125
  let branch = detectDefaultBranch(cwd);
126
126
  const repoName = detectRepoName(cwd);
127
127
  const versionCode = existing?.versionCode ?? 1; // ๊ธฐ์กด ๋นŒ๋“œ๋ฒˆํ˜ธ ๋ณด์กด
128
- // ๋ฐฐํฌ/publish ์ถ• ์ดˆ๊ธฐ๊ฐ’(#439): version.yml ์ €์žฅ ์˜ต์…˜ (๊ตฌ ํ‚ค ์ž๋™ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํฌํ•จ)
129
- let deployTarget = existing?.options?.deploy ?? "docker-ssh";
130
- let publishTargets = existing?.options?.publish ?? [];
131
- let includeSecretBackup = existing?.options?.secretBackup ?? false;
132
- let aiPrSummary = existing?.options?.aiPrSummary ?? null; // null = ์•„์ง ์•ˆ ๋ฌผ์Œ (#566)
133
- let codeReviewCoderabbit = existing?.options?.codeReviewCoderabbit ?? DEFAULT_CODE_REVIEW_CODERABBIT;
134
- let changelogProvider = migrateProvider(existing?.options?.changelogProvider) ?? "commit";
135
- let changelogBaseUrl = existing?.options?.changelogBaseUrl ?? "";
136
- let deployBranch = existing?.options?.deployBranch ?? "develop"; // #456
128
+ // ์˜ต์…˜ ์ดˆ๊ธฐ๊ฐ’์€ ๋น„๋Œ€ํ™”ํ˜•๊ณผ ๊ฐ™์€ resolve-options.js ์—์„œ ๋ฐ›๋Š”๋‹ค (#851).
129
+ // ์งˆ๋ฌธ์„ ๊ฑด๋„ˆ๋›ฐ๋Š” ๋ชจ๋“œ(version ๋“ฑ)๋„ ์ด ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ๊ธฐ๋ก๋˜๋ฏ€๋กœ ํƒ€์ž… ์ ์šฉ์„ฑ ์ •๋ฆฌ๊นŒ์ง€ ๊ฑฐ์นœ ๊ฐ’์ด์–ด์•ผ ํ•œ๋‹ค.
130
+ const { values: init, sources: initSources } = resolveOptions({ existing, types, cwd });
131
+ let deployTarget = init.deployTarget;
132
+ let publishTargets = init.publishTargets;
133
+ let includeSecretBackup = init.includeSecretBackup;
134
+ let aiPrSummary = init.aiPrSummary;
135
+ let codeReviewCoderabbit = init.codeReviewCoderabbit;
136
+ let changelogProvider = init.changelogProvider;
137
+ let changelogBaseUrl = init.changelogBaseUrl;
138
+ let deployBranch = init.deployBranch; // #456 โ€” ๋น„์–ด ์žˆ์œผ๋ฉด ๊ธฐ๋กํ•˜์ง€ ์•Š๋Š”๋‹ค. ์งˆ๋ฌธ์—์„œ ๊ณ ๋ฅธ ๊ฐ’๋งŒ ๋‚จ๊ธด๋‹ค
137
139
  let deployBranchReady = null; // #490 โ€” ์ด๋ฒˆ ์‹คํ–‰์—์„œ ๊ฐœ๋ฐœ ๋ธŒ๋žœ์น˜ ์กด์žฌ/์ƒ์„ฑ์ด ํ™•์ธ๋๋Š”์ง€
138
140
  let deployBranchCreated = null; // #493 โ€” ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ์ง์ ‘ ์ƒ์„ฑํ–ˆ๋Š”์ง€ (๊ฐ€์ด๋“œ ๊ธฐ๋ก์šฉ)
139
- let intent = existing?.options?.intent ?? null; // #485 ํ”„๋กœ์ ํŠธ ์„ฑ๊ฒฉ
140
- // semver ์ž๋™ ์Šน๊ฒฉ(#546) โ€” ์งˆ๋ฌธํ•˜์ง€ ์•Š๋Š”๋‹ค(#485 ์งˆ๋ฌธ ๋ถ€๋‹ด ์ถ•์†Œ ๋ฐฉํ–ฅ ์œ ์ง€).
141
- // ์ €์žฅ๊ฐ’(๋ช…์‹œ์  false ํฌํ•จ)์€ ๋ณด์กดํ•˜๊ณ , ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ๊ธฐ์กด ๋ ˆํฌ๋„ ON. ์ผœ์ง„ ์‚ฌ์‹ค์€ ์™„๋ฃŒ ํ™”๋ฉด์ด ์•Œ๋ฆฐ๋‹ค.
142
- const semverAuto = existing?.options?.semverAuto ?? true;
143
- const semverAutoNewlyOn = !!existing && existing?.options?.semverAuto == null;
144
- const appRelease = existing?.options?.appRelease ?? null; // #553 ์ €์žฅ๊ฐ’ ๋ณด์กด (๋ฌป์ง€ ์•Š์Œ)
145
- // ์ƒํƒœ ๋ผ๋ฒจ ํ‘œ๊ธฐ(#776): ์ €์žฅ๊ฐ’ > (์‹ ๊ทœ en / ๊ธฐ์กด ko). ๊ธฐ์กด ํ•œ๊ธ€ ๋ ˆํฌ์—๋Š” ์˜๋ฌธ ์ „ํ™˜์„ ํ•œ ๋ฒˆ ์ œ์•ˆํ•œ๋‹ค(์•„๋ž˜).
146
- const closeOnRelease = existing?.options?.closeOnRelease ?? (existing ? null : true); // #771 ๋ฌป์ง€ ์•Š๋Š”๋‹ค
147
- const excludedWorkflows = existing?.options?.excludedWorkflows ?? null; // #810 ์ €์žฅ๊ฐ’ ๋ณด์กด
148
- const storeLocales = existing?.options?.storeLocales ?? null; // #829 ์ €์žฅ๊ฐ’ ๋ณด์กด
149
- const storeLocalesIos = existing?.options?.storeLocalesIos ?? null;
150
- const storeLocalesPlay = existing?.options?.storeLocalesPlay ?? null;
151
- // Projects ๋ณด๋“œ ๋™๊ธฐํ™”(#716): ๋ฌป์ง€ ์•Š๋Š”๋‹ค. ์ €์žฅ๊ฐ’ > ์ด๋ฏธ ์„ค์น˜๋ผ ์žˆ์œผ๋ฉด ์œ ์ง€ > ์‹ ๊ทœ๋Š” ์ œ์™ธ (index.js ์™€ ๊ฐ™์€ ๊ทœ์น™).
152
- const projectsSync = existing?.options?.projectsSync
153
- ?? existsSync(join(cwd, ".github/workflows/PROJECT-COMMON-PROJECTS-SYNC-MANAGER.yaml"));
154
- // ๋ ˆํฌ ๋ฌธ๊ตฌ ์–ธ์–ด(#769): ์ €์žฅ๊ฐ’ > (์‹ ๊ทœ en / ๊ธฐ์กด ko). ๊ธฐ์กด ๋ ˆํฌ์—๋Š” ์˜๋ฌธ ์ „ํ™˜์„ ํ•œ ๋ฒˆ๋งŒ ์ œ์•ˆํ•œ๋‹ค.
155
- let language = resolveRepoLanguage({ flag: null, stored: existing?.options?.language, existing: !!existing });
156
- let labelStyle = resolveLabelStyle({ flag: null, stored: existing?.options?.labelStyle, existing: !!existing });
141
+ let intent = init.intent; // #485 ํ”„๋กœ์ ํŠธ ์„ฑ๊ฒฉ
142
+ // ๋ฌป์ง€ ์•Š๋Š” ๊ฐ’ โ€” ์ €์žฅ๊ฐ’ ๋ณด์กด ๊ทœ์น™์€ resolve-options.js ์— ์žˆ๋‹ค
143
+ const { semverAuto, appRelease, closeOnRelease, excludedWorkflows, storeLocales, storeLocalesIos, storeLocalesPlay, projectsSync } = init;
144
+ const semverAutoNewlyOn = initSources.semverAutoNewlyOn;
145
+ // ๋ ˆํฌ ๋ฌธ๊ตฌ ์–ธ์–ด(#769)ยท๋ผ๋ฒจ ํ‘œ๊ธฐ(#776): ๊ธฐ์กด ๋ ˆํฌ์—๋Š” ์˜๋ฌธ ์ „ํ™˜์„ ํ•œ ๋ฒˆ๋งŒ ์ œ์•ˆํ•œ๋‹ค(์•„๋ž˜).
146
+ let language = init.language;
147
+ let labelStyle = init.labelStyle;
157
148
  const showOptional = mode === "full" || mode === "workflows";
158
149
  const realTty = process.stdout.isTTY === true;
159
150
  // ๊ธฐ์กด ํ•œ๊ธ€ ๋ผ๋ฒจ ๋ ˆํฌ์—๋Š” ์˜๋ฌธ ํ‘œ์ค€ ์ „ํ™˜์„ ํ•œ ๋ฒˆ๋งŒ ์ œ์•ˆํ•œ๋‹ค(#776). ๊ฑฐ์ ˆํ•˜๋ฉด ํ•œ๊ธ€์„ ์ €์žฅํ•ด ๋‹ค์‹œ ๋ฌป์ง€ ์•Š๋Š”๋‹ค.
@@ -194,6 +185,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
194
185
  publishTargets = r.publish;
195
186
  includeSecretBackup = r.secretBackup;
196
187
  codeReviewCoderabbit = r.codeReviewCoderabbit;
188
+ aiPrSummary = r.aiPrSummary; // #851 โ€” ๋น ์ ธ ์žˆ์–ด PR ์š”์•ฝ ์„ ํƒ์ด ๋ฒ„๋ ค์กŒ๋‹ค
197
189
  changelogProvider = r.changelogProvider;
198
190
  changelogBaseUrl = r.changelogBaseUrl;
199
191
  deployBranch = r.deployBranch;
@@ -256,7 +248,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
256
248
  tempDir, types, targetRoot: cwd, defaultBranch: branch,
257
249
  current: {
258
250
  deploy: deployTarget, publish: publishTargets, secretBackup: includeSecretBackup,
259
- codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch, intent,
251
+ codeReviewCoderabbit, aiPrSummary, changelogProvider, changelogBaseUrl, deployBranch, intent,
260
252
  },
261
253
  force: false, tty: realTty, forceAsk: true, scope: [what],
262
254
  io: {
@@ -270,6 +262,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
270
262
  publishTargets = r.publish;
271
263
  includeSecretBackup = r.secretBackup;
272
264
  codeReviewCoderabbit = r.codeReviewCoderabbit;
265
+ aiPrSummary = r.aiPrSummary; // #851
273
266
  changelogProvider = r.changelogProvider;
274
267
  changelogBaseUrl = r.changelogBaseUrl;
275
268
  deployBranch = r.deployBranch;
@@ -334,10 +327,10 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
334
327
  const { now, today } = clock || utcNow();
335
328
  const ctx = createContext({
336
329
  mode, force: true, types, version, versionCode, branch, paths, deployTarget, publishTargets, includeSecretBackup,
337
- codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch, intent, semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales, storeLocalesIos, storeLocalesPlay,
330
+ aiPrSummary, codeReviewCoderabbit, changelogProvider, changelogBaseUrl, deployBranch, intent, semverAuto, appRelease, labelStyle, closeOnRelease, projectsSync, language, excludedWorkflows, storeLocales, storeLocalesIos, storeLocalesPlay,
338
331
  repoName, templateVersion, resolvers, envValues, envUseDefaults, now, today,
339
332
  // #502 โ€” version ๋ชจ๋“œ๊ฐ€ ๊ธฐ์กด full ๊ธฐ๋ก์„ ๊ฐ•๋“ฑํ•˜์ง€ ์•Š๋„๋ก (full์ด ์šฐ์„ธ)
340
- recordMode: existing?.templateMode === "full" ? "full" : "version",
333
+ recordMode: init.recordMode,
341
334
  });
342
335
  ctx.templateVersion = templateVersion;
343
336
 
@@ -15,6 +15,7 @@ import { substituteBranches } from "../branch-sub.js";
15
15
  import { saveIncoming, lineDiffCounts } from "../incoming.js";
16
16
  import { branchStatus } from "../git-branch.js";
17
17
  import { PUBLISH_TARGETS } from "../constants.js";
18
+ import { commonOptionalGroups, serverDeployEnabled, SERVER_DEPLOY_DIR } from "../workflow-groups.js";
18
19
 
19
20
  // on: ๋ธ”๋ก์˜ ๋ธŒ๋žœ์น˜ ํ•„ํ„ฐ๊ฐ€ ์ „๋ถ€ develop ๋ฟ์ธ๊ฐ€ (#810).
20
21
  // ๊ฐœ๋ฐœ ๋ธŒ๋žœ์น˜๊ฐ€ ์—†๋Š” ๋ ˆํฌ์— ๊น”๋ฉด ์˜์˜ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๊ฐ€๋ฆฐ๋‹ค.
@@ -229,78 +230,46 @@ export function copyWorkflows(context, tempDir, targetRoot = ".", hooks = {}) {
229
230
  if (asks.size) deployValues.set(type, asks);
230
231
  }
231
232
 
232
- // (4.5) common/deploy/<target> โ€” ํƒ€์ž… ๋น„์ข…์† ๋ฐฐํฌ ํƒ€๊ฒŸ (vercel ๋“ฑ, #439)
233
- const commonDeployDir = join(commonDir, "deploy", deployTarget || "docker-ssh");
234
- if (exists(commonDeployDir)) {
235
- for (const filename of listYamlFiles(commonDeployDir)) {
236
- const src = join(commonDeployDir, filename);
233
+ // (4.5~5) common/ ์•„๋ž˜ ์„ ํƒํ˜• ํด๋” โ€” deploy/<target>, pr-summary, projects-sync, secret-backup.
234
+ // ์–ด๋А ํด๋”๋ฅผ ์ผœ๋Š”์ง€๋Š” workflow-groups.js ํ•œ ๊ณณ์ด ์ •ํ•œ๋‹ค (#851). ๊ณ ์•„ ๊ฐ์ง€๋„ ๊ฐ™์€ ํ‘œ๋ฅผ ์“ด๋‹ค.
235
+ // bak-replace: ํ…œํ”Œ๋ฆฟ๊ณผ ๊ฐ™์œผ๋ฉด ๊ฑด๋„ˆ๋›ฐ๊ณ , ๋‹ค๋ฅด๋ฉด .bak ์œผ๋กœ ๋‚จ๊ธฐ๊ณ  ๊ต์ฒดํ•œ๋‹ค.
236
+ // new-only : ์ด๋ฏธ ์žˆ์œผ๋ฉด ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. ์ƒˆ๋กœ ๊น” ๋•Œ๋งŒ ๋ ˆํฌ ์ด๋ฆ„์„ ์ฑ„์šด๋‹ค (#729 โ€” ๊ณ ์ •๊ฐ’์ด๋ฉด
237
+ // ๋ชจ๋“  ํ”„๋กœ์ ํŠธ๊ฐ€ ์„œ๋ฒ„์˜ ๊ฐ™์€ ํด๋”์— Secret ์„ ๋ฎ์–ด์“ด๋‹ค).
238
+ for (const group of commonOptionalGroups(commonDir, { includeSecretBackup, aiPrSummary, projectsSync, deployTarget })) {
239
+ const groupDir = join(commonDir, group.dir);
240
+ if (!group.enabled || !exists(groupDir)) continue;
241
+ for (const filename of listYamlFiles(groupDir)) {
242
+ const src = join(groupDir, filename);
237
243
  const dst = join(workflowsDir, filename);
238
- if (existsSync(dst) && isUnchanged(readFileSync(src, "utf8"), readFileSync(dst, "utf8"), envOptsFor("common"))) {
239
- counters.skipped++;
240
- trace?.event("copy", "skipped-unchanged", filename, { group: "common-deploy" });
244
+ if (group.policy === "new-only") {
245
+ if (existsSync(dst)) continue;
246
+ if (skipNew(groupDir, filename, group.copyLabel)) continue;
247
+ copyFileSync(src, dst);
248
+ const raw = readFileSync(dst, "utf8");
249
+ const filled = resolveGlobalTokens(raw, repoName || "my-project");
250
+ if (filled !== raw) writeFileSync(dst, filled);
251
+ counters.optionalCopied++;
252
+ counters.copied++;
253
+ counters.copiedFiles.push(filename);
254
+ trace?.event("copy", "copied", filename, { group: group.copyLabel });
241
255
  continue;
242
256
  }
243
- if (skipNew(commonDeployDir, filename, "common-deploy")) continue;
244
- const backedUp = existsSync(dst);
245
- if (backedUp) renameSync(dst, dst + ".bak");
246
- copyFileSync(src, dst);
247
- counters.optionalCopied++;
248
- counters.copied++;
249
- counters.copiedFiles.push(filename);
250
- trace?.event("copy", backedUp ? "replaced-bak" : "copied", filename, { group: "common-deploy" });
251
- if (backedUp) counters.replacedBak.push(filename); // #673
252
- }
253
- }
254
-
255
- // (4.7) common/pr-summary โ€” AI ๋ณ€๊ฒฝ ์š”์•ฝ (#566). ์„ ํƒํ–ˆ์„ ๋•Œ๋งŒ ๋ณต์‚ฌํ•œ๋‹ค.
256
- // ์ข…์ „์—๋Š” common ๋ณธ์ฒด์— ์žˆ์–ด ๋ฌด์กฐ๊ฑด ๋ณต์‚ฌ๋œ ๋’ค ๋Ÿฐํƒ€์ž„์— ์Šค์Šค๋กœ ๋น ์กŒ๊ณ , ๊ทธ ํŒ๋‹จ์ด
257
- // ".coderabbit.yaml ์กด์žฌ"๋ผ ํŒŒ์ผ๋งŒ ์žˆ๊ณ  ์•ฑ์ด ์—†๋Š” ์ €์žฅ์†Œ์—์„œ๋Š” ์•„๋ฌด๋„ ์š”์•ฝํ•˜์ง€ ์•Š์•˜๋‹ค.
258
- // (4.8) common/projects-sync โ€” Projects ๋ณด๋“œ ๋™๊ธฐํ™” (#716). ์ด๊ฒƒ๋„ ์„ ํƒํ–ˆ์„ ๋•Œ๋งŒ ๋ณต์‚ฌํ•œ๋‹ค.
259
- // Secret(_GITHUB_PAT_TOKEN)๊ณผ PROJECT_URL ๋ณ€์ˆ˜๋ฅผ ๋“ฑ๋กํ•ด์•ผ ๋™์ž‘ํ•˜๋Š”๋ฐ, Projects ๋ฅผ ์•ˆ ์“ฐ๋Š” ๋ ˆํฌ๊ฐ€
260
- // ๋งŽ์•„ ์„ค์น˜๋งŒ ๋˜๊ณ  ์„ค์ •๋˜์ง€ ์•Š์€ ์ฑ„ ๋‚จ์•˜๋‹ค. ๊ฐ™์€ ํด๋” ๋ฐฉ์‹์ด๋ผ ๊ฒŒ์ดํŠธ์™€ ๊ณ ์•„ ๊ฐ์ง€(orphan-workflows.js)๋ฅผ ํ•จ๊ป˜ ๋‘”๋‹ค.
261
- for (const [groupName, groupEnabled] of [["pr-summary", aiPrSummary], ["projects-sync", projectsSync]]) {
262
- const prSummaryDir = join(commonDir, groupName);
263
- if (exists(prSummaryDir) && groupEnabled) {
264
- for (const filename of listYamlFiles(prSummaryDir)) {
265
- const src = join(prSummaryDir, filename);
266
- const dst = join(workflowsDir, filename);
267
257
  if (existsSync(dst) && isUnchanged(readFileSync(src, "utf8"), readFileSync(dst, "utf8"), envOptsFor("common"))) {
268
258
  counters.skipped++;
269
- trace?.event("copy", "skipped-unchanged", filename, { group: groupName });
259
+ trace?.event("copy", "skipped-unchanged", filename, { group: group.copyLabel });
270
260
  continue;
271
261
  }
272
- if (skipNew(prSummaryDir, filename, groupName)) continue;
262
+ if (skipNew(groupDir, filename, group.copyLabel)) continue;
273
263
  const backedUp = existsSync(dst);
274
264
  if (backedUp) renameSync(dst, dst + ".bak");
275
265
  copyFileSync(src, dst);
276
266
  counters.optionalCopied++;
277
267
  counters.copied++;
278
268
  counters.copiedFiles.push(filename);
279
- trace?.event("copy", backedUp ? "replaced-bak" : "copied", filename, { group: groupName });
269
+ trace?.event("copy", backedUp ? "replaced-bak" : "copied", filename, { group: group.copyLabel });
280
270
  if (backedUp) counters.replacedBak.push(filename); // #673
281
271
  }
282
272
  }
283
- }
284
-
285
- // (5) common/secret-backup โ€” ์žˆ์œผ๋ฉด ๋ฌด์กฐ๊ฑด ์Šคํ‚ต/์‹ ๊ทœ๋งŒ ๋ณต์‚ฌ
286
- const secretDir = join(commonDir, "secret-backup");
287
- if (exists(secretDir) && includeSecretBackup) {
288
- for (const filename of listYamlFiles(secretDir)) {
289
- const dst = join(workflowsDir, filename);
290
- if (existsSync(dst)) continue; // ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด ์Šคํ‚ต
291
- if (skipNew(secretDir, filename, "secret-backup")) continue;
292
- copyFileSync(join(secretDir, filename), dst);
293
- // PROJECT_NAME ์ด ๊ณ ์ •๊ฐ’์ด๋ฉด ๋ชจ๋“  ํ”„๋กœ์ ํŠธ๊ฐ€ ์„œ๋ฒ„์˜ ๊ฐ™์€ ํด๋”์— Secret ์„ ๋ฎ์–ด์“ด๋‹ค (#729) โ€”
294
- // ๋‹ค๋ฅธ ๋ฐฐํฌ ์›Œํฌํ”Œ๋กœ์ฒ˜๋Ÿผ ๋ ˆํฌ ์ด๋ฆ„์œผ๋กœ ์ฑ„์šด๋‹ค. ์ด๋ฆ„์„ ์•Œ ์ˆ˜ ์—†์œผ๋ฉด ๊ธฐ์กด ๊ธฐ๋ณธ๊ฐ’์„ ์“ด๋‹ค.
295
- const raw = readFileSync(dst, "utf8");
296
- const filled = resolveGlobalTokens(raw, repoName || "my-project");
297
- if (filled !== raw) writeFileSync(dst, filled);
298
- counters.optionalCopied++;
299
- counters.copied++;
300
- counters.copiedFiles.push(filename);
301
- trace?.event("copy", "copied", filename, { group: "secret-backup" });
302
- }
303
- }
304
273
 
305
274
  // (6) ๋ธŒ๋žœ์น˜ ์น˜ํ™˜ post-pass (#477) โ€” ํ‘œ์ค€๊ณผ ๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜ ์ „๋žต์ผ ๋•Œ๋งŒ ๋ณต์‚ฌ๋œ ํŒŒ์ผ์— ์ ์šฉ.
306
275
  // isUnchanged ๊ฐ€์ƒ ๋น„๊ต์—๋„ ๊ฐ™์€ branches๊ฐ€ ๋“ค์–ด๊ฐ€๋ฏ€๋กœ ๋‹ค์Œ ์—…๋ฐ์ดํŠธ์—์„œ ์žฌ๋ณต์‚ฌ churn์ด ์—†๋‹ค.
@@ -411,8 +380,8 @@ export function listWorkflowConflicts(context, tempDir, targetRoot = ".") {
411
380
  if (exists(typeDir)) {
412
381
  for (const f of classify(typeDir, workflowsDir, envOpts, baseline).changed) conflicts.push({ filename: f, type });
413
382
  }
414
- const serverDeployDir = join(typeDir, "server-deploy");
415
- if (exists(serverDeployDir) && (deployTarget || "docker-ssh") === "docker-ssh") {
383
+ const serverDeployDir = join(typeDir, SERVER_DEPLOY_DIR);
384
+ if (exists(serverDeployDir) && serverDeployEnabled(deployTarget)) {
416
385
  for (const f of classify(serverDeployDir, workflowsDir, envOpts, baseline).changed) conflicts.push({ filename: f, type });
417
386
  }
418
387
  }
@@ -464,8 +433,8 @@ function copyWorkflowsForType(type, projectTypesDir, workflowsDir, ctx, counters
464
433
  }
465
434
 
466
435
  // server-deploy โ€” deploy=docker-ssh์ผ ๋•Œ๋งŒ ํฌํ•จ (#439)
467
- const serverDeployDir = join(typeDir, "server-deploy");
468
- if (exists(serverDeployDir) && (deployTarget || "docker-ssh") === "docker-ssh") {
436
+ const serverDeployDir = join(typeDir, SERVER_DEPLOY_DIR);
437
+ if (exists(serverDeployDir) && serverDeployEnabled(deployTarget)) {
469
438
  const { newFiles, unchanged, changed, upstream } = classify(serverDeployDir, workflowsDir, envOpts, baseline);
470
439
  for (const f of unchanged) { counters.skipped++; trace?.event("copy", "skipped-unchanged", f, { group: `${type}/server-deploy` }); }
471
440
  for (const f of newFiles) { if (counters.skipNew(serverDeployDir, f, `${type}/server-deploy`)) continue; copyFileSync(join(serverDeployDir, f), join(workflowsDir, f)); counters.copied++; counters.copiedFiles.push(f); trace?.event("copy", "copied", f, { group: `${type}/server-deploy` }); }
@@ -234,7 +234,10 @@ export async function askAllOptionalWorkflows({
234
234
  if (askIntent) {
235
235
  if (force || !tty || typeof io.select !== "function") {
236
236
  // ๋น„๋Œ€ํ™”ํ˜•: CLI intent ๋ฏธ์ง€์ •์ด๋ฉด deploy/publish์—์„œ ์—ญ์ถ”๋ก , ๊ทธ๋ž˜๋„ ์—†์œผ๋ฉด manual(๊ธฐ์กด ๋™์ž‘)
237
- intent = intent ?? inferIntent(deploy, publish) ?? "manual";
237
+ // ์ถ•์ด ์•„์ง ์•ˆ ์ •ํ•ด์กŒ์œผ๋ฉด ๋น„๋Œ€ํ™”ํ˜•(resolve-options.js)๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธ ์ถ•์œผ๋กœ ์ถ”๋ก ํ•œ๋‹ค (#851).
238
+ // ์˜ˆ์ „์—๋Š” null ๋กœ ์ถ”๋ก ํ•ด "manual" ์ด ๊ธฐ๋ก๋˜์–ด ๊ฐ™์€ ๋ ˆํฌ์—์„œ ๋‘ ๊ฒฝ๋กœ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐˆ๋ ธ๋‹ค.
239
+ const defaultDeploy = applicable.deploy.includes("docker-ssh") ? "docker-ssh" : "none";
240
+ intent = intent ?? inferIntent(deploy ?? defaultDeploy, publish ?? []) ?? "manual";
238
241
  } else {
239
242
  say("");
240
243
  say(msg("options.intent.intro"));
@@ -394,7 +397,8 @@ export async function askAllOptionalWorkflows({
394
397
  // "๋ฐฐํฌ ๋ธŒ๋žœ์น˜"๊ฐ€ ์•„๋‹ˆ๋‹ค โ€” ๋ฐฐํฌ๊ฐ€ ๋„๋Š” ๊ณณ์€ ๊ธฐ๋ณธ ๋ธŒ๋žœ์น˜(default) ์ชฝ ๊ฐœ๋…. #482 ์ฐธ์กฐ.
395
398
  if (ask("release-branch") || deployBranch === null) {
396
399
  if (force || !tty || typeof io.text !== "function") {
397
- deployBranch = deployBranch ?? "develop";
400
+ // ๋ฌป์ง€ ๋ชปํ–ˆ์œผ๋ฉด ๊ฐ’์„ ์ง€์–ด๋‚ด์ง€ ์•Š๋Š”๋‹ค (#851) โ€” ๋น„์–ด ์žˆ์œผ๋ฉด ๊ธฐ๋กํ•˜์ง€ ์•Š๊ณ  ์Šคํ‚ฌ์ด develop ์œผ๋กœ ํด๋ฐฑํ•œ๋‹ค.
401
+ deployBranch = deployBranch ?? "";
398
402
  } else {
399
403
  const base = defaultBranch || "main"; // git์œผ๋กœ ๊ฐ์ง€๋œ ๊ธฐ๋ณธ ๋ธŒ๋žœ์น˜ (#481 ๋™์  ์•ˆ๋‚ด)
400
404
  say("");
@@ -431,7 +435,7 @@ export async function askAllOptionalWorkflows({
431
435
  aiPrSummary: aiPrSummary !== false,
432
436
  changelogProvider: changelogProvider ?? "commit",
433
437
  changelogBaseUrl: changelogBaseUrl ?? "",
434
- deployBranch: deployBranch ?? "develop",
438
+ deployBranch: deployBranch ?? "",
435
439
  deployBranchReady, // #490 โ€” true=์กด์žฌ/์ƒ์„ฑ ํ™•์ธ๋จ, false=๊ฑฐ์ ˆ/์‹คํŒจ, null=ํ™•์ธ ์•ˆ ํ•จ
436
440
  deployBranchCreated, // #493 โ€” true=์ด๋ฒˆ ์‹คํ–‰์—์„œ ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ์ƒ์„ฑ, null=ํ™•์ธ ์•ˆ ํ•จ
437
441
 
@@ -6,11 +6,12 @@ import { existsSync, renameSync, rmSync, readdirSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { PATHS } from "./paths.js";
8
8
  import { exists, listYamlFiles } from "./fsutil.js";
9
+ import { commonOptionalGroups, SERVER_DEPLOY_DIR } from "./workflow-groups.js";
9
10
 
10
11
  // ํ…œํ”Œ๋ฆฟ์˜ project-types/<type>/ ์ธ๋ฒคํ† ๋ฆฌ โ€” ์งํ•˜์œ„ + server-deploy/ + publish/*/ (copy ์—”์ง„๊ณผ ๋™์ผ ๋ฒ”์œ„)
11
12
  function typeInventory(projectTypesDir, type) {
12
13
  const typeDir = join(projectTypesDir, type);
13
- const dirs = [typeDir, join(typeDir, "server-deploy")];
14
+ const dirs = [typeDir, join(typeDir, SERVER_DEPLOY_DIR)];
14
15
  const pubRoot = join(typeDir, "publish");
15
16
  if (exists(pubRoot)) {
16
17
  for (const e of readdirSync(pubRoot, { withFileTypes: true })) {
@@ -28,30 +29,14 @@ function typeInventory(projectTypesDir, type) {
28
29
  // common/ ์•„๋ž˜ "์„ ํƒํ–ˆ์„ ๋•Œ๋งŒ ๋ณต์‚ฌ๋˜๋Š”" ํด๋”๋“ค (#566).
29
30
  // ์ผœ๋ฉด ๋ณต์‚ฌ ์—”์ง„์ด ๋„ฃ์–ด์ฃผ์ง€๋งŒ, ๋„๋ฉด ์ด๋ฏธ ์žˆ๋˜ ํŒŒ์ผ์ด ๊ทธ๋Œ€๋กœ ๋‚จ์•„ ๊ณ„์† ์‹คํ–‰๋๋‹ค.
30
31
  // ์ถ•์„ ๋ˆ ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ๊ทธ๊ฒŒ ๊ณง "๊ป๋Š”๋ฐ ์™œ ๋„๋‚˜"๊ฐ€ ๋œ๋‹ค โ€” ์—ฌ๊ธฐ์„œ ๊ณ ์•„๋กœ ์žก๋Š”๋‹ค.
31
- // deploy๋Š” ํƒ€๊ฒŸ๋ณ„ ํด๋”๋ผ ์„ ํƒ๋œ ํƒ€๊ฒŸ๋งŒ ์‚ด๋ฆฌ๊ณ  ๋‚˜๋จธ์ง€๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์‚ผ๋Š”๋‹ค.
32
+ // ์ผœ์ง/๊บผ์ง์€ ๋ณต์‚ฌ ์—”์ง„๊ณผ ๊ฐ™์€ ํ‘œ(workflow-groups.js)์—์„œ ์ฝ๋Š”๋‹ค (#851).
32
33
  function commonOptionalOrphans(commonDir, opts) {
33
- const { includeSecretBackup = false, aiPrSummary = true, projectsSync = true, deployTarget = "docker-ssh" } = opts;
34
34
  const out = [];
35
-
36
- const gated = [
37
- ["secret-backup", includeSecretBackup, "secret-backup"],
38
- ["pr-summary", aiPrSummary, "pr-summary"],
39
- ["projects-sync", projectsSync, "projects-sync"],
40
- ];
41
- for (const [dir, enabled, label] of gated) {
42
- if (enabled) continue;
43
- const d = join(commonDir, dir);
35
+ for (const group of commonOptionalGroups(commonDir, opts)) {
36
+ if (group.enabled) continue;
37
+ const d = join(commonDir, group.dir);
44
38
  if (!exists(d)) continue;
45
- for (const f of listYamlFiles(d)) out.push({ filename: f, type: label });
46
- }
47
-
48
- // deploy/<target> โ€” ์„ ํƒ๋œ ํƒ€๊ฒŸ ์™ธ ํด๋”๋Š” ์ „๋ถ€ ๊ณ ์•„ ํ›„๋ณด
49
- const deployRoot = join(commonDir, "deploy");
50
- if (exists(deployRoot)) {
51
- for (const e of readdirSync(deployRoot, { withFileTypes: true })) {
52
- if (!e.isDirectory() || e.name === deployTarget) continue;
53
- for (const f of listYamlFiles(join(deployRoot, e.name))) out.push({ filename: f, type: `deploy/${e.name}` });
54
- }
39
+ for (const f of listYamlFiles(d)) out.push({ filename: f, type: group.orphanLabel });
55
40
  }
56
41
  return out;
57
42
  }
@@ -0,0 +1,91 @@
1
+ // ์„ค์น˜ ์˜ต์…˜ ๊ฐ’์„ ์ •ํ•˜๋Š” ๋‹จ ํ•œ ๊ณณ (#851).
2
+ //
3
+ // ์˜ˆ์ „์—๋Š” ๋น„๋Œ€ํ™”ํ˜•(src/index.js)๊ณผ ๋Œ€ํ™”ํ˜•(src/commands/interactive.js)์ด ๊ฐ™์€ ์˜ต์…˜์„
4
+ // ๊ฐ์ž ์ •ํ–ˆ๋‹ค. ๊ทธ ์‚ฌ์ด ๊ทœ์น™์ด ๊ฐˆ๋ผ์ ธ ๋Œ€ํ™”ํ˜•์—์„œ PR ์š”์•ฝ ์„ ํƒ์ด ๋ฒ„๋ ค์ง€๊ณ (aiPrSummary),
5
+ // ์งˆ๋ฌธ์„ ๊ฑด๋„ˆ๋›ฐ๋Š” version ๋ชจ๋“œ๊ฐ€ ๋ชจ๋ฐ”์ผ ๋‹จ๋… ๋ ˆํฌ์— deploy: docker-ssh ๋ฅผ ๋‚จ๊ฒผ๋‹ค.
6
+ // ๊ทœ์น™์€ ๋ชจ๋“  ์˜ต์…˜์— ๊ฐ™๋‹ค: CLI ํ”Œ๋ž˜๊ทธ > version.yml ์ €์žฅ๊ฐ’ > ๊ธฐ๋ณธ๊ฐ’.
7
+ // ๋Œ€ํ™”ํ˜•์€ ์ด ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์“ฐ๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ตํ•œ ๊ฒƒ๋งŒ ๊ทธ ์œ„์— ๋ฎ์–ด์“ด๋‹ค.
8
+ import { existsSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { applicableTargets, migrateProvider } from "./options-ask.js";
11
+ import { DEFAULT_CODE_REVIEW_CODERABBIT } from "./constants.js";
12
+ import { resolveLabelStyle } from "./label-style.js";
13
+ import { resolveRepoLanguage } from "./repo-language.js";
14
+
15
+ // ์ด๋ฏธ ์„ค์น˜๋ผ ์žˆ์œผ๋ฉด Projects ๋™๊ธฐํ™”๋ฅผ ์ผœ์ง„ ๊ฒƒ์œผ๋กœ ๋ณด๋Š” ๊ธฐ์ค€ ํŒŒ์ผ (#716)
16
+ export const PROJECTS_SYNC_WORKFLOW = ".github/workflows/PROJECT-COMMON-PROJECTS-SYNC-MANAGER.yaml";
17
+
18
+ // ํƒ€์ž…์— ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š” ์ถ• ๊ฐ’์„ ์ •๋ฆฌํ•œ๋‹ค (#498). ๋ชจ๋ฐ”์ผ ์•ฑยทbasic ๋‹จ๋…์€ ๋‘ ์ถ•์ด ๋ชจ๋‘ ๋น„์–ด
19
+ // intent ๋„ none ์œผ๋กœ ํ™•์ •๋œ๋‹ค. ์งˆ๋ฌธ์„ ๊ฑฐ์นœ ๊ฐ’์—๋„ ๊ฐ™์€ ๊ทœ์น™์„ ๋‹ค์‹œ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋”ฐ๋กœ ๋‘”๋‹ค.
20
+ export function normalizeAxes({ deploy, publish, intent, types }) {
21
+ const applicable = applicableTargets(types);
22
+ const outDeploy = deploy !== "none" && !applicable.deploy.includes(deploy) ? "none" : deploy;
23
+ const outPublish = publish.filter((t) => applicable.publish.includes(t));
24
+ const noAxes = types.length > 0 && applicable.deploy.length === 0 && applicable.publish.length === 0;
25
+ return { deploy: outDeploy, publish: outPublish, intent: noAxes ? "none" : intent, applicable };
26
+ }
27
+
28
+ // flags: parseArgs ๊ฒฐ๊ณผ ์ค‘ ์˜ต์…˜ ๊ด€๋ จ ๊ฐ’ (์—†์œผ๋ฉด {} โ€” ๋Œ€ํ™”ํ˜• ์ดˆ๊ธฐ๊ฐ’)
29
+ // existing: parseExisting ๊ฒฐ๊ณผ ๋˜๋Š” null
30
+ // ๋ฐ˜ํ™˜: { values, sources } โ€” sources ๋Š” trace ๊ฐ€ "์™œ ์ด ๊ฐ’์ธ๊ฐ€"๋ฅผ ๋‚จ๊ธธ ๊ทผ๊ฑฐ๋‹ค (#561).
31
+ export function resolveOptions({ flags = {}, existing = null, types = [], cwd = "." }) {
32
+ const stored = existing?.options ?? {};
33
+ const applicable = applicableTargets(types);
34
+
35
+ // ํ”„๋กœ์ ํŠธ ์„ฑ๊ฒฉ(#485): CLI --intent โ†’ ์ €์žฅ๊ฐ’. deploy/publish ์œ ๋„์˜ ๊ธฐ์ค€.
36
+ let intent = flags.intent ?? stored.intent ?? null;
37
+ // ๋ฐฐํฌ/publish ์ถ•(#439): ํ”Œ๋ž˜๊ทธ โ†’ ์ €์žฅ๊ฐ’ โ†’ ๊ธฐ๋ณธ๊ฐ’(์ ์šฉ ๊ฐ€๋Šฅํ•  ๋•Œ๋งŒ docker-ssh โ€” #498).
38
+ let deploy = flags.deployTarget ?? stored.deploy
39
+ ?? (applicable.deploy.includes("docker-ssh") ? "docker-ssh" : "none");
40
+ let publish = flags.publishTargets ?? stored.publish ?? [];
41
+ // --intent ๋ฅผ ์คฌ๊ณ  ํ•ด๋‹น ์ถ• ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์œผ๋ฉด intent ๊ฐ€ ์ถ•์„ ์œ ๋„ํ•œ๋‹ค (#485 ๋น„๋Œ€ํ™”ํ˜•)
42
+ if (flags.intent != null) {
43
+ if ((intent === "library" || intent === "none") && flags.deployTarget == null) deploy = "none";
44
+ if ((intent === "app" || intent === "none") && flags.publishTargets == null) publish = [];
45
+ }
46
+ const before = { deploy, publish: [...publish] };
47
+ ({ deploy, publish, intent } = normalizeAxes({ deploy, publish, intent, types }));
48
+
49
+ const values = {
50
+ intent, deployTarget: deploy, publishTargets: publish,
51
+ includeSecretBackup: flags.includeSecretBackup ?? stored.secretBackup ?? false,
52
+ // #566 โ€” ์„ค์ • ์—†์ด ๋ฐ”๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ ๊ธฐ๋ณธ ์ผœ์ง
53
+ aiPrSummary: flags.aiPrSummary ?? stored.aiPrSummary ?? true,
54
+ // #716 โ€” ์„ค์ •(Secret, PROJECT_URL) ์—†๋Š” ์‹ ๊ทœ ๋ ˆํฌ์— ๊น”๋ฉด ์•Œ๋ฆผ๋งŒ ์Œ“์ธ๋‹ค. ์ด๋ฏธ ๊น”๋ฆฐ ๋ ˆํฌ๋Š” ์œ ์ง€.
55
+ projectsSync: flags.projectsSync ?? stored.projectsSync ?? existsSync(join(cwd, PROJECTS_SYNC_WORKFLOW)),
56
+ // #502 โ€” version ๋ชจ๋“œ๊ฐ€ ๊ธฐ์กด full ํ†ตํ•ฉ ๊ธฐ๋ก์„ ๊ฐ•๋“ฑํ•˜์ง€ ์•Š๊ฒŒ (full ์ด ์šฐ์„ธ)
57
+ recordMode: existing?.templateMode === "full" ? "full" : "version",
58
+ // #456 โ€” ๋น„์–ด ์žˆ์œผ๋ฉด version.yml ์— ์“ฐ์ง€ ์•Š๋Š”๋‹ค (์Šคํ‚ฌ์ด develop ์œผ๋กœ ํด๋ฐฑ). ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๋ฅด๊ฑฐ๋‚˜ ํ”Œ๋ž˜๊ทธ๋กœ ์ค€ ๊ฒƒ๋งŒ ๊ธฐ๋ก.
59
+ deployBranch: flags.deployBranch || stored.deployBranch || "",
60
+ // #455 โ€” null ์ด ํ˜๋Ÿฌ provider:"null" ๋กœ ๊ธฐ๋ก๋˜๋˜ ๋ฒ„๊ทธ ๋ฐฉ์ง€
61
+ changelogProvider: migrateProvider(stored.changelogProvider) ?? "commit",
62
+ changelogBaseUrl: stored.changelogBaseUrl ?? "",
63
+ codeReviewCoderabbit: flags.codeReviewCoderabbit ?? stored.codeReviewCoderabbit ?? DEFAULT_CODE_REVIEW_CODERABBIT,
64
+ // #546, #814 โ€” ๋ช…์‹œ์  false ๋Š” ์กด์ค‘, ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์‹ ๊ทœยท๊ธฐ์กด ๋ชจ๋‘ ์ผ ๋‹ค
65
+ semverAuto: stored.semverAuto ?? true,
66
+ // ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ๋ฌป์ง€ ์•Š๋Š” ๊ฐ’์€ ์ €์žฅ๊ฐ’๋งŒ ๋ณด์กดํ•œ๋‹ค (์ „์ฒด ์žฌ์ƒ์„ฑ์ด๋ผ ์•ˆ ๋„˜๊ธฐ๋ฉด ์‚ฌ๋ผ์ง„๋‹ค)
67
+ appRelease: stored.appRelease ?? null, // #553
68
+ excludedWorkflows: stored.excludedWorkflows ?? null, // #810
69
+ storeLocales: stored.storeLocales ?? null, // #829
70
+ storeLocalesIos: stored.storeLocalesIos ?? null,
71
+ storeLocalesPlay: stored.storeLocalesPlay ?? null,
72
+ // #771 โ€” ์‹ ๊ทœ๋งŒ ์ผ ๋‹ค. ๊ธฐ์กด ๋ ˆํฌ๋Š” ํ‚ค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•„ ํ˜„ํ–‰ ์œ ์ง€
73
+ closeOnRelease: stored.closeOnRelease ?? (existing ? null : true),
74
+ // #769, #776 โ€” ์‹ ๊ทœ en / ๊ธฐ์กด ko. ์—…๋ฐ์ดํŠธ๋งŒ์œผ๋กœ ๊ธฐ์กด ๋ ˆํฌ ๋ฌธ๊ตฌยท๋ผ๋ฒจ์ด ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค
75
+ language: resolveRepoLanguage({ flag: flags.language ?? null, stored: stored.language, existing: !!existing }),
76
+ labelStyle: resolveLabelStyle({ flag: flags.labelStyle ?? null, stored: stored.labelStyle, existing: !!existing }),
77
+ };
78
+
79
+ const from = (flagVal, storedVal, flagName) =>
80
+ flagVal != null ? `cli-flag(${flagName})` : (storedVal ? "version.yml(stored value)" : "default");
81
+ const sources = {
82
+ intent: flags.intent != null ? "cli-flag(--intent)"
83
+ : (stored.intent ? "version.yml(stored value)" : "not given, inferred from deploy/publish"),
84
+ deploy: from(flags.deployTarget, stored.deploy, "--deploy"),
85
+ publish: from(flags.publishTargets, stored.publish, "--publish"),
86
+ before, applicable,
87
+ // ๊ธฐ์กด ๋ ˆํฌ์—์„œ semver_auto ๊ฐ€ ์ด๋ฒˆ์— ์ฒ˜์Œ ์ผœ์กŒ๋Š”์ง€ โ€” ์™„๋ฃŒ ํ™”๋ฉด์ด ์•Œ๋ฆฐ๋‹ค
88
+ semverAutoNewlyOn: !!existing && stored.semverAuto == null,
89
+ };
90
+ return { values, sources };
91
+ }
@@ -0,0 +1,44 @@
1
+ // ์„ ํƒํ˜• ์›Œํฌํ”Œ๋กœ ๊ทธ๋ฃน์˜ ์ผœ์ง/๊บผ์ง์„ ์ •ํ•˜๋Š” ๋‹จ ํ•œ ๊ณณ (#851).
2
+ //
3
+ // ์˜ˆ์ „์—๋Š” ๋ณต์‚ฌ(copy/workflows.js), ์ถฉ๋Œ ๋ชฉ๋ก(listWorkflowConflicts), ๊ณ ์•„ ๊ฐ์ง€(orphan-workflows.js)๊ฐ€
4
+ // ๊ฐ™์€ ์กฐ๊ฑด์„ ๊ฐ์ž ๋“ค๊ณ  ์žˆ์—ˆ๋‹ค. ํ•œ์ชฝ๋งŒ ๊ณ ์น˜๋ฉด "์ผฐ๋Š”๋ฐ ์•ˆ ๊น”๋ฆผ" ๋˜๋Š” "๊ป๋Š”๋ฐ ๊ณ„์† ๋Ž"์ด ๋œ๋‹ค (#567).
5
+ // ์ƒˆ ์กฐ๊ฑด๋ถ€ ํด๋”๋ฅผ ๋งŒ๋“ค๋ฉด ์—ฌ๊ธฐ์— ํ•œ ์ค„์„ ๋„ฃ๋Š”๋‹ค. ์„ธ ๊ณณ์ด ํ•จ๊ป˜ ๋”ฐ๋ผ์˜จ๋‹ค.
6
+ import { readdirSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { exists } from "./fsutil.js";
9
+
10
+ // ์„œ๋ฒ„ ๋ฐฐํฌ(<type>/server-deploy/)๋Š” docker-ssh ์ผ ๋•Œ๋งŒ ๊น๋‹ค (#439)
11
+ export const SERVER_DEPLOY_DIR = "server-deploy";
12
+ export function serverDeployEnabled(deployTarget) {
13
+ return (deployTarget || "docker-ssh") === "docker-ssh";
14
+ }
15
+
16
+ // common/ ์•„๋ž˜ ์„ ํƒํ˜• ํด๋” ๋ชฉ๋ก. ์ˆœ์„œ๊ฐ€ ๋ณต์‚ฌ ์ˆœ์„œ๋‹ค (๊ธฐ๋กยท์š”์•ฝ์— ๋‚˜์˜ค๋Š” ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค).
17
+ // policy: "bak-replace" โ€” ํ…œํ”Œ๋ฆฟ๊ณผ ๋‹ค๋ฅด๋ฉด .bak ์œผ๋กœ ๋‚จ๊ธฐ๊ณ  ๊ต์ฒด
18
+ // "new-only" โ€” ์ด๋ฏธ ์žˆ์œผ๋ฉด ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค (secret-backup: ์„œ๋ฒ„ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ์ณ ์“ด๋‹ค)
19
+ // copyLabel / orphanLabel โ€” trace ๊ทธ๋ฃน ์ด๋ฆ„๊ณผ ๊ณ ์•„ ์•ˆ๋‚ด ์ด๋ฆ„. ๊ธฐ์กด ๊ธฐ๋ก๊ณผ ๊ฐ™๊ฒŒ ๋‘”๋‹ค.
20
+ export function commonOptionalGroups(commonDir, opts = {}) {
21
+ const { includeSecretBackup = false, aiPrSummary = true, projectsSync = true, deployTarget = "docker-ssh" } = opts;
22
+ const selectedDeploy = deployTarget || "docker-ssh";
23
+ const groups = [];
24
+
25
+ // deploy/<target> โ€” ํƒ€์ž… ๋น„์ข…์† ๋ฐฐํฌ ํƒ€๊ฒŸ(vercel ๋“ฑ). ๊ณ ๋ฅธ ํƒ€๊ฒŸ ํด๋”๋งŒ ์ผ ๋‹ค.
26
+ const deployRoot = join(commonDir, "deploy");
27
+ const deployNames = exists(deployRoot)
28
+ ? readdirSync(deployRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name)
29
+ : [];
30
+ if (!deployNames.includes(selectedDeploy)) deployNames.push(selectedDeploy); // ํด๋”๊ฐ€ ์—†์œผ๋ฉด ์•„๋ž˜์—์„œ exists ๋กœ ๊ฑธ๋Ÿฌ์ง„๋‹ค
31
+ for (const name of deployNames) {
32
+ groups.push({ dir: join("deploy", name), enabled: name === selectedDeploy, policy: "bak-replace",
33
+ copyLabel: "common-deploy", orphanLabel: `deploy/${name}` });
34
+ }
35
+ groups.push(
36
+ // #566 โ€” AI ๋ณ€๊ฒฝ ์š”์•ฝ. ์„ค์ • ์—†์ด ๋ฐ”๋กœ ๋™์ž‘
37
+ { dir: "pr-summary", enabled: aiPrSummary === true, policy: "bak-replace", copyLabel: "pr-summary", orphanLabel: "pr-summary" },
38
+ // #716 โ€” Projects ๋ณด๋“œ ๋™๊ธฐํ™”. Secretยท๋ณ€์ˆ˜๋ฅผ ๋“ฑ๋กํ•ด์•ผ ๋™์ž‘ํ•ด ์‹ ๊ทœ๋Š” ๊ธฐ๋ณธ ๊บผ์ง
39
+ { dir: "projects-sync", enabled: projectsSync === true, policy: "bak-replace", copyLabel: "projects-sync", orphanLabel: "projects-sync" },
40
+ // Secret ์„œ๋ฒ„ ๋ฐฑ์—…
41
+ { dir: "secret-backup", enabled: includeSecretBackup === true, policy: "new-only", copyLabel: "secret-backup", orphanLabel: "secret-backup" },
42
+ );
43
+ return groups;
44
+ }
package/src/index.js CHANGED
@@ -19,16 +19,13 @@ import { detectOrphanWorkflows } from "./core/orphan-workflows.js";
19
19
  import { createRunTrace, MIGRATION_DIR } from "./core/run-trace.js";
20
20
  import { appendGuideEntry } from "./core/migration-guide.js";
21
21
  import { resolveProjectPaths, markerForType } from "./core/paths-resolve.js";
22
- import { applicableTargets, migrateProvider } from "./core/options-ask.js";
23
- import { DEFAULT_CODE_REVIEW_CODERABBIT } from "./core/constants.js";
22
+ import { resolveOptions } from "./core/resolve-options.js";
24
23
  import { printBannerCompact } from "./ui/banner.js";
25
24
  import { printSummary } from "./ui/summary.js";
26
25
  import { runFull } from "./commands/full.js";
27
26
  import { runVersion } from "./commands/version.js";
28
27
  import { runWorkflows } from "./commands/workflows.js";
29
28
  import { runIssues } from "./commands/issues.js";
30
- import { resolveLabelStyle } from "./core/label-style.js";
31
- import { resolveRepoLanguage } from "./core/repo-language.js";
32
29
  import { runInteractive } from "./commands/interactive.js";
33
30
  import { runSkills } from "./commands/skills.js";
34
31
 
@@ -191,86 +188,38 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
191
188
  const { now, today } = clock || utcNow();
192
189
  const tempDir = join(cwd, PATHS.tempDir);
193
190
 
194
- // ํ”„๋กœ์ ํŠธ ์„ฑ๊ฒฉ(#485): CLI --intent โ†’ version.yml ์ €์žฅ๊ฐ’. deploy/publish ์œ ๋„์˜ ๊ธฐ์ค€.
195
- let intent = opts.intent ?? existing?.options?.intent ?? null;
196
- // ๋ฐฐํฌ/publish ์ถ•(#439): CLI ํ”Œ๋ž˜๊ทธ ์ตœ์šฐ์„  โ†’ ์ €์žฅ๊ฐ’ โ†’ ๊ธฐ๋ณธ๊ฐ’(์ ์šฉ ๊ฐ€๋Šฅํ•  ๋•Œ๋งŒ docker-ssh โ€” #498).
197
- // ๋‹จ --intent๊ฐ€ ๋ช…์‹œ๋๊ณ  ํ•ด๋‹น ์ถ• ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์œผ๋ฉด intent๊ฐ€ ์œ ๋„ํ•œ๋‹ค (#485 ๋น„๋Œ€ํ™”ํ˜•):
198
- // library/none์ด๋ฉด deploy=none, app/none์ด๋ฉด publish=[].
199
- const applicable = applicableTargets(types);
200
- let deployTarget = opts.deployTarget ?? existing?.options?.deploy
201
- ?? (applicable.deploy.includes("docker-ssh") ? "docker-ssh" : "none");
202
- let publishTargets = opts.publishTargets ?? existing?.options?.publish ?? [];
203
- if (opts.intent != null) {
204
- if ((intent === "library" || intent === "none") && opts.deployTarget == null) deployTarget = "none";
205
- if ((intent === "app" || intent === "none") && opts.publishTargets == null) publishTargets = [];
206
- }
207
- // ์ ์šฉ ๋ถˆ๊ฐ€ ํƒ€๊ฒŸ ์กฐ์šฉํ•œ ์ •๋ฆฌ (#498) โ€” ๋Œ€ํ™”ํ˜•๊ณผ ๋™์ผ ๊ทœ์น™. ํƒ€์ž…์— ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š” ์ถ• ๊ฐ’์€
208
- // ๋ณต์‚ฌ ๊ฒฐ๊ณผ๊ฐ€ ๋™์ผํ•˜๋ฏ€๋กœ ๊ฒฝ๊ณ  ์—†์ด none/๊ต์ง‘ํ•ฉ์œผ๋กœ ์ •๋ฆฌํ•œ๋‹ค (๋ชจ๋ฐ”์ผ ์•ฑ/basic ๋‹จ๋… ๋“ฑ).
209
- const beforeCleanup = { deploy: deployTarget, publish: [...publishTargets] };
210
- if (deployTarget !== "none" && !applicable.deploy.includes(deployTarget)) deployTarget = "none";
211
- publishTargets = publishTargets.filter((t) => applicable.publish.includes(t));
212
- if (types.length > 0 && applicable.deploy.length === 0 && applicable.publish.length === 0) intent = "none";
191
+ // ์˜ต์…˜ ๊ฐ’์€ resolve-options.js ํ•œ ๊ณณ์—์„œ ์ •ํ•œ๋‹ค (#851) โ€” ๋Œ€ํ™”ํ˜•๊ณผ ๊ฐ™์€ ๊ทœ์น™.
192
+ const { values: resolved, sources } = resolveOptions({
193
+ flags: {
194
+ intent: opts.intent, deployTarget: opts.deployTarget, publishTargets: opts.publishTargets,
195
+ includeSecretBackup: opts.includeSecretBackup, aiPrSummary: opts.aiPrSummary, projectsSync: opts.projectsSync,
196
+ deployBranch: opts.deployBranch, codeReviewCoderabbit: opts.codeReviewCoderabbit,
197
+ language: opts.language, labelStyle: opts.labelStyle,
198
+ },
199
+ existing, types, cwd,
200
+ });
201
+ const { intent, deployTarget, publishTargets } = resolved;
202
+ const { before: beforeCleanup, applicable } = sources;
213
203
 
214
204
  // ์ถ• ํ™•์ • ๊ทผ๊ฑฐ (#561) โ€” "์™œ ์ด ๊ฐ’์ธ๊ฐ€"๊ฐ€ ๊ฐ€์žฅ ํ—ท๊ฐˆ๋ฆฌ๋Š” ์ž๋ฆฌ๋‹ค.
215
205
  // CLI ํ”Œ๋ž˜๊ทธ / ์ €์žฅ๊ฐ’ / intent ์œ ๋„ / ํƒ€์ž… ์ ์šฉ์„ฑ ์ •๋ฆฌ ์ค‘ ๋ฌด์—‡์ด ์ด๊ฒผ๋Š”์ง€ ๋‚จ๊ธด๋‹ค.
216
- trace.event("resolve", "intent", String(intent ?? "(unset)"), {
217
- source: opts.intent != null ? "cli-flag(--intent)"
218
- : (existing?.options?.intent ? "version.yml(stored value)" : "not given, inferred from deploy/publish"),
219
- });
206
+ trace.event("resolve", "intent", String(intent ?? "(unset)"), { source: sources.intent });
220
207
  trace.event("resolve", "deploy", deployTarget, {
221
- source: opts.deployTarget != null ? "cli-flag(--deploy)"
222
- : (existing?.options?.deploy ? "version.yml(stored value)" : "default"),
208
+ source: sources.deploy,
223
209
  applicableForTypes: applicable.deploy,
224
210
  adjusted: beforeCleanup.deploy !== deployTarget
225
211
  ? `${beforeCleanup.deploy} โ†’ ${deployTarget} (not applicable to the selected types)` : null,
226
212
  });
227
213
  trace.event("resolve", "publish", publishTargets.join(",") || "(none)", {
228
- source: opts.publishTargets != null ? "cli-flag(--publish)"
229
- : (existing?.options?.publish ? "version.yml(stored value)" : "default"),
214
+ source: sources.publish,
230
215
  applicableForTypes: applicable.publish,
231
216
  adjusted: beforeCleanup.publish.join(",") !== publishTargets.join(",")
232
217
  ? `${beforeCleanup.publish.join(",") || "(none)"} โ†’ ${publishTargets.join(",") || "(none)"} (cleaned up, not applicable)` : null,
233
218
  });
234
219
 
235
220
  const context = createContext({
236
- mode: opts.mode, force: true, types, version, versionCode, branch,
237
- paths,
238
- deployTarget,
239
- publishTargets,
240
- includeSecretBackup: opts.includeSecretBackup ?? existing?.options?.secretBackup ?? false,
241
- // #566 โ€” CLI ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด ์ €์žฅ๊ฐ’ ๋ณด์กด, ๊ทธ๊ฒƒ๋„ ์—†์œผ๋ฉด true(์„ค์ • ์—†์ด ๋ฐ”๋กœ ๋™์ž‘).
242
- aiPrSummary: opts.aiPrSummary ?? existing?.options?.aiPrSummary ?? true,
243
- // Projects ๋ณด๋“œ ๋™๊ธฐํ™”(#716): CLI > ์ €์žฅ๊ฐ’ > ์ด๋ฏธ ์„ค์น˜๋ผ ์žˆ์œผ๋ฉด ์œ ์ง€ > ์‹ ๊ทœ๋Š” ์ œ์™ธ.
244
- // ์„ค์ •(Secret, PROJECT_URL)์ด ์—†๋Š” ๋ ˆํฌ์— ์„ค์น˜๋˜๋ฉด ๋ผ๋ฒจ์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์•Œ๋ฆผ๋งŒ ์Œ“์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
245
- // ์ด๋ฏธ ์„ค์น˜๋œ ๋ ˆํฌ๊ฐ€ ๊ฐฑ์‹ ๋งŒ์œผ๋กœ ์—ฐ๋™์„ ์žƒ์ง€ ์•Š๋„๋ก ํŒŒ์ผ์ด ์žˆ์œผ๋ฉด ์ผœ์ง„ ๊ฒƒ์œผ๋กœ ๋ณธ๋‹ค.
246
- projectsSync: opts.projectsSync ?? existing?.options?.projectsSync
247
- ?? existsSync(join(cwd, ".github/workflows/PROJECT-COMMON-PROJECTS-SYNC-MANAGER.yaml")),
248
- // #502 โ€” version ๋ชจ๋“œ๊ฐ€ ๊ธฐ์กด full ํ†ตํ•ฉ ๊ธฐ๋ก(mode)์„ ๊ฐ•๋“ฑํ•˜์ง€ ์•Š๋„๋ก (full์ด ์šฐ์„ธ)
249
- recordMode: existing?.templateMode === "full" ? "full" : "version",
250
- // ๋ฆด๋ฆฌ์Šค ๋ฐฐํฌ ๋ธŒ๋žœ์น˜(#456): CLI ํ”Œ๋ž˜๊ทธ โ†’ version.yml ์ €์žฅ๊ฐ’ โ†’ ๋นˆ ๊ฐ’(๋ฏธ์ถœ๋ ฅ, ์Šคํ‚ฌ์ด develop ํด๋ฐฑ)
251
- deployBranch: opts.deployBranch || existing?.options?.deployBranch || "",
252
- intent,
253
- // changelog/code_review ์ถ•(#455): ๋น„๋Œ€ํ™”ํ˜•์€ ์ €์žฅ๊ฐ’ โ†’ ๊ธฐ๋ณธ๊ฐ’. null์ด ํ˜๋Ÿฌ provider:"null"๋กœ ๊ธฐ๋ก๋˜๋˜ ๋ฒ„๊ทธ ์ˆ˜์ •.
254
- changelogProvider: migrateProvider(existing?.options?.changelogProvider) ?? "commit",
255
- changelogBaseUrl: existing?.options?.changelogBaseUrl ?? "",
256
- // ํ”Œ๋ž˜๊ทธ > ์ €์žฅ๊ฐ’ > ๊ธฐ๋ณธ(๊บผ์ง). ์“ฐ๊ฒ ๋‹ค๊ณ  ๋ช…์‹œํ•˜๋ฉด ์ผœ์ง€๊ณ , ์•„๋ฌด ๋ง์ด ์—†์„ ๋•Œ๋งŒ ๊ธฐ๋ณธ๊ฐ’์ด ์ ์šฉ๋œ๋‹ค.
257
- codeReviewCoderabbit: opts.codeReviewCoderabbit ?? existing?.options?.codeReviewCoderabbit ?? DEFAULT_CODE_REVIEW_CODERABBIT,
258
- // semver ์ž๋™ ์Šน๊ฒฉ(#546, #814): ์ €์žฅ๊ฐ’(๋ช…์‹œ์  false ํฌํ•จ)์€ ์กด์ค‘ํ•˜๊ณ , ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ์‹ ๊ทœยท๊ธฐ์กด ๋ชจ๋‘ ์ผ ๋‹ค.
259
- // ๊ธฐ์กด ๋ ˆํฌ์—์„œ ์ฒ˜์Œ ์ผœ์งˆ ๋•Œ๋Š” ๋ฒ„์ „์ด ์˜ˆ๊ณ  ์—†์ด ์˜ค๋ฅด์ง€ ์•Š๊ฒŒ ์™„๋ฃŒ ํ™”๋ฉด์ด ์•Œ๋ฆฐ๋‹ค (semverAutoNewlyOn).
260
- semverAuto: existing?.options?.semverAuto ?? true,
261
- // ์•ฑ ์‹ฌ์‚ฌ ๋ฐฐํฌ ๋ ˆํฌ ์—ฌ๋ถ€(#553): ์ €์žฅ๊ฐ’๋งŒ ๋ณด์กดํ•œ๋‹ค. ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ๋ฌป์ง€ ์•Š์œผ๋ฏ€๋กœ ์ƒˆ๋กœ ์ผœ์ง€ ์•Š๋Š”๋‹ค
262
- // (์‚ฌ์šฉ์ž๊ฐ€ version.yml์— ์ง์ ‘ ์“ฐ๊ฑฐ๋‚˜ ์Šคํ‚ฌ์ด ๊ธฐ๋กํ•œ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€).
263
- appRelease: existing?.options?.appRelease ?? null,
264
- excludedWorkflows: existing?.options?.excludedWorkflows ?? null, // #810 ์ €์žฅ๊ฐ’ ๋ณด์กด
265
- storeLocales: existing?.options?.storeLocales ?? null, // #829 ์ €์žฅ๊ฐ’ ๋ณด์กด (์ „์ฒด ์žฌ์ƒ์„ฑ์ด๋ผ ์•ˆ ๋„˜๊ธฐ๋ฉด ์‚ฌ๋ผ์ง„๋‹ค)
266
- storeLocalesIos: existing?.options?.storeLocalesIos ?? null,
267
- storeLocalesPlay: existing?.options?.storeLocalesPlay ?? null,
268
- // ์ƒํƒœ ๋ผ๋ฒจ ํ‘œ๊ธฐ(#776): ํ”Œ๋ž˜๊ทธ > ์ €์žฅ๊ฐ’ > (์‹ ๊ทœ en / ๊ธฐ์กด ko). ๊ธฐ์กด ๋ ˆํฌ์˜ ๋ผ๋ฒจ ์ด๋ฆ„์€ ์—…๋ฐ์ดํŠธ๋งŒ์œผ๋กœ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.
269
- // ๋ฆด๋ฆฌ์Šค ์‹œ ์™„๋ฃŒ ์ด์Šˆ ๋‹ซ๊ธฐ(#771): ์ €์žฅ๊ฐ’ ๋ณด์กด, ์—†์œผ๋ฉด ์‹ ๊ทœ๋งŒ true. ๊ธฐ์กด ๋ ˆํฌ๋Š” ํ‚ค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•„ ํ˜„ํ–‰ ์œ ์ง€.
270
- closeOnRelease: existing?.options?.closeOnRelease ?? (existing ? null : true),
271
- // ๋ ˆํฌ ๋ฌธ๊ตฌ ์–ธ์–ด(#769): ํ”Œ๋ž˜๊ทธ > ์ €์žฅ๊ฐ’ > (์‹ ๊ทœ en / ๊ธฐ์กด ko). ๊ธฐ์กด ๋ ˆํฌ์˜ ํ…œํ”Œ๋ฆฟ ์–ธ์–ด๋Š” ์—…๋ฐ์ดํŠธ๋งŒ์œผ๋กœ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.
272
- language: resolveRepoLanguage({ flag: opts.language, stored: existing?.options?.language, existing: !!existing }),
273
- labelStyle: resolveLabelStyle({ flag: opts.labelStyle, stored: existing?.options?.labelStyle, existing: !!existing }),
221
+ mode: opts.mode, force: true, types, version, versionCode, branch, paths,
222
+ ...resolved,
274
223
  repoName,
275
224
  // ์‹ค resolver 4์ข… (.sh resolve_token ๋“ฑ๊ฐ€ โ€” spring-app-yml ์Šคํ… ์ œ๊ฑฐ)
276
225
  resolvers: makeResolvers(cwd, repoName, paths),
@@ -382,7 +331,7 @@ async function runCore(argv, { cwd = process.cwd(), source = { type: "git" }, cl
382
331
  replacedBak: result?.workflows?.replacedBak ?? [], // #673 ๊ธฐ์ค€์  ์—†์ด ๊ต์ฒดํ•œ ํŒŒ์ผ ์•ˆ๋‚ด
383
332
  legacyLeftover: migrationsResult?.confirmPending ?? [], // #809 ์‹ ยท๊ตฌ ์„ธ๋Œ€ ๋™์‹œ ์‹คํ–‰ ์•ˆ๋‚ด
384
333
  notInstalled: result?.workflows?.notInstalled ?? [], // #810 ๊น”์ง€ ์•Š์€ ์›Œํฌํ”Œ๋กœ์šฐ ์•ˆ๋‚ด
385
- semverAutoNewlyOn: !!existing && existing?.options?.semverAuto == null && context.semverAuto === true, // ์—…๋ฐ์ดํŠธ๊ฐ€ ์ผ  ์‚ฌ์‹ค ์•ˆ๋‚ด
334
+ semverAutoNewlyOn: sources.semverAutoNewlyOn && context.semverAuto === true, // ์—…๋ฐ์ดํŠธ๊ฐ€ ์ผ  ์‚ฌ์‹ค ์•ˆ๋‚ด
386
335
  verification: result?.verification, // #549 ์„ค์น˜ ํ›„ ๊ฒ€์ฆ ๊ฒฐ๊ณผ (full/workflows ๋ชจ๋“œ์—์„œ๋งŒ ์กด์žฌ)
387
336
  // #569 โ€” ๊ณ ๋ฅธ ๊ฒƒ๋งŒ ์•ˆ๋‚ดํ•˜๋ ค๋ฉด ์„ ํƒ๊ฐ’์ด ํ•„์š”ํ•˜๋‹ค
388
337
  aiPrSummary: context.aiPrSummary, codeReviewCoderabbit: context.codeReviewCoderabbit,