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 +4 -6
- package/package.json +1 -1
- package/src/commands/interactive.js +25 -32
- package/src/core/copy/workflows.js +29 -60
- package/src/core/options-ask.js +7 -3
- package/src/core/orphan-workflows.js +7 -22
- package/src/core/resolve-options.js +91 -0
- package/src/core/workflow-groups.js +44 -0
- package/src/index.js +19 -70
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.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 |
|
|
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
|
|
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["
|
|
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
|
@@ -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 {
|
|
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
|
|
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
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
let
|
|
132
|
-
let
|
|
133
|
-
let
|
|
134
|
-
let
|
|
135
|
-
let
|
|
136
|
-
let
|
|
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 =
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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:
|
|
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/
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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 (
|
|
239
|
-
|
|
240
|
-
|
|
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:
|
|
259
|
+
trace?.event("copy", "skipped-unchanged", filename, { group: group.copyLabel });
|
|
270
260
|
continue;
|
|
271
261
|
}
|
|
272
|
-
if (skipNew(
|
|
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:
|
|
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,
|
|
415
|
-
if (exists(serverDeployDir) && (deployTarget
|
|
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,
|
|
468
|
-
if (exists(serverDeployDir) && (deployTarget
|
|
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` }); }
|
package/src/core/options-ask.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 ?? "
|
|
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,
|
|
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
|
-
//
|
|
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
|
-
|
|
37
|
-
|
|
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:
|
|
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 {
|
|
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
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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,
|