projectops 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/commands/doctor.js +35 -0
- package/src/commands/interactive.js +1 -0
- package/src/core/options-ask.js +9 -5
- package/src/core/verify.js +1 -1
- package/src/index.js +2 -0
- package/src/ui/summary.js +27 -3
package/README.md
CHANGED
package/package.json
CHANGED
package/src/commands/doctor.js
CHANGED
|
@@ -100,6 +100,27 @@ export function localChecks(cwd = ".") {
|
|
|
100
100
|
] : null,
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
+
// AI 요약 키 (#569) — "등록했는데 되는 건가?"를 확인할 수단이 없었다.
|
|
104
|
+
// 로컬에서는 저장소 Secret을 읽을 수 없으므로, 어떤 이름을 쓰면 되는지와
|
|
105
|
+
// 등록하지 않아도 무방하다는 사실을 알려준다. 실제 등록 여부는 원격 점검이 본다.
|
|
106
|
+
const AI_KEY_NAMES = ["GEMINI_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GROQ_API_KEY", "MISTRAL_API_KEY"];
|
|
107
|
+
const hasSummaryWf = files.some((f) => /AI-PR-SUMMARY|RELEASE-CHANGELOG/.test(f));
|
|
108
|
+
if (hasSummaryWf) {
|
|
109
|
+
add({
|
|
110
|
+
name: "AI 요약 키", purpose: "릴리스 노트를 AI로 다듬을지 (선택)",
|
|
111
|
+
status: "INFO", value: "선택 사항",
|
|
112
|
+
detail: [
|
|
113
|
+
"등록하지 않아도 릴리스 노트는 나옵니다 — 커밋 내용을 분석해 만듭니다.",
|
|
114
|
+
"AI가 다듬은 문장을 원하면 아래 중 하나를 저장소 Secret에 등록하세요.",
|
|
115
|
+
" GEMINI_API_KEY 무료 · https://aistudio.google.com/apikey",
|
|
116
|
+
" GROQ_API_KEY 무료",
|
|
117
|
+
" MISTRAL_API_KEY 무료",
|
|
118
|
+
" OPENAI_API_KEY / ANTHROPIC_API_KEY 유료",
|
|
119
|
+
"등록한 것이 자동으로 쓰입니다. 별도 설정은 필요 없습니다.",
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
103
124
|
// 워크플로우 파일의 permissions 선언 점검 (#558).
|
|
104
125
|
// 저장소 설정(Settings > Actions)은 "요청할 수 있는 최대 범위"이고, 워크플로우의
|
|
105
126
|
// permissions 블록은 "실제로 요청한 범위"다. 둘은 다른 층이라 저장소 설정이 정상이어도
|
|
@@ -190,6 +211,20 @@ export async function remoteChecks(slug, token, requiredSecrets = []) {
|
|
|
190
211
|
detail: ["토큰에 Secret 조회 권한이 없어 확인하지 못했습니다."] });
|
|
191
212
|
} else {
|
|
192
213
|
const have = new Set((sec.data?.secrets || []).map((s) => s.name));
|
|
214
|
+
|
|
215
|
+
// AI 요약 키가 실제로 등록돼 있는지 (#569) — 있으면 어느 서비스인지까지 보여준다.
|
|
216
|
+
const AI_KEYS = { GEMINI_API_KEY: "Gemini", OPENAI_API_KEY: "OpenAI", ANTHROPIC_API_KEY: "Anthropic",
|
|
217
|
+
GROQ_API_KEY: "Groq", MISTRAL_API_KEY: "Mistral", MODEL_API_KEY: "구 이름(서비스 자동 추정)" };
|
|
218
|
+
const foundAi = Object.entries(AI_KEYS).filter(([n]) => have.has(n));
|
|
219
|
+
rows.push({
|
|
220
|
+
name: "AI 요약 키 등록", purpose: "릴리스 노트를 AI로 다듬을지 (선택)",
|
|
221
|
+
status: "INFO",
|
|
222
|
+
value: foundAi.length ? foundAi.map(([n, label]) => `${n} (${label})`).join(", ") : "없음 — 커밋 분석으로 동작",
|
|
223
|
+
detail: foundAi.length ? null : [
|
|
224
|
+
"등록하지 않아도 릴리스 노트는 정상적으로 나옵니다.",
|
|
225
|
+
"AI를 쓰려면 GEMINI_API_KEY(무료)를 등록하세요 — https://aistudio.google.com/apikey",
|
|
226
|
+
],
|
|
227
|
+
});
|
|
193
228
|
const missing = requiredSecrets.filter((n) => !have.has(n));
|
|
194
229
|
add({
|
|
195
230
|
name: "Secret 등록 여부", purpose: "배포에 필요한 값",
|
|
@@ -408,6 +408,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
408
408
|
mode, types, version, deployBranch, deployBranchReady, migrationGuidePath,
|
|
409
409
|
counters: { workflows: result?.workflows?.copied ?? 0, workflowFiles: result?.workflows?.copiedFiles ?? [], utilModules: 0 },
|
|
410
410
|
verification: result?.verification, // #549 설치 검증 결과
|
|
411
|
+
aiPrSummary, codeReviewCoderabbit, // #569 — 고른 것만 안내
|
|
411
412
|
logDir: files ? MIGRATION_DIR : null, // #561 기록 위치 안내
|
|
412
413
|
logFile: files?.logFile ?? null,
|
|
413
414
|
traceFile: files?.traceFile ?? null,
|
package/src/core/options-ask.js
CHANGED
|
@@ -332,18 +332,22 @@ export async function askAllOptionalWorkflows({
|
|
|
332
332
|
// 저장소가 생겼다. 둘은 같은 자리(PR 댓글)를 놓고 경쟁하므로 한 번에 고르게 한다.
|
|
333
333
|
if (ask("code-review") || codeReviewCoderabbit === null || aiPrSummary === null) {
|
|
334
334
|
if (force || !tty || typeof io.select !== "function") {
|
|
335
|
-
// 비대화형 기본값: CodeRabbit은
|
|
335
|
+
// 비대화형 기본값: CodeRabbit은 별도 앱 설치가 있어야 실제로 동작하므로
|
|
336
|
+
// 자동화 환경에서 켜봐야 의미가 없다. 설정 없이 바로 도는 요약만 켠다.
|
|
336
337
|
codeReviewCoderabbit = codeReviewCoderabbit ?? false;
|
|
337
338
|
aiPrSummary = aiPrSummary ?? true;
|
|
338
339
|
} else {
|
|
339
340
|
say("");
|
|
340
|
-
say("💬 PR에
|
|
341
|
+
say("💬 PR에 어떤 댓글을 받으시겠어요?");
|
|
342
|
+
say(" 두 가지는 하는 일이 다르고 한도도 따로라, 같이 켜도 서로 방해하지 않습니다.");
|
|
343
|
+
say(" · 변경 요약 — 무엇이 바뀌었는지 (추가 설정 없이 바로 동작)");
|
|
344
|
+
say(" · 코드 리뷰 — 버그·개선점 지적 (CodeRabbit 앱 설치 필요, 공개 저장소 무료)");
|
|
341
345
|
const ans = await io.select({
|
|
342
346
|
message: "PR 댓글 방식을 선택하세요",
|
|
343
347
|
options: [
|
|
344
|
-
{ value: "
|
|
345
|
-
{ value: "
|
|
346
|
-
{ value: "
|
|
348
|
+
{ value: "both", label: "둘 다 (추천 · 무료로 쓸 수 있습니다)" },
|
|
349
|
+
{ value: "summary", label: "변경 요약만 (추가 설정 불필요)" },
|
|
350
|
+
{ value: "coderabbit", label: "코드 리뷰만 (CodeRabbit)" },
|
|
347
351
|
{ value: "none", label: "사용 안 함" },
|
|
348
352
|
],
|
|
349
353
|
});
|
package/src/core/verify.js
CHANGED
|
@@ -68,7 +68,7 @@ const AUTO_SECRETS = new Set(["GITHUB_TOKEN"]);
|
|
|
68
68
|
|
|
69
69
|
// 없어도 워크플로우가 도는 secret — 폴백이 문서화돼 있다. 필수와 섞어 "등록해야 동작합니다"라고
|
|
70
70
|
// 하면 안내 자체를 못 믿게 되므로 분리한다.
|
|
71
|
-
// MODEL_API_KEY → 없으면
|
|
71
|
+
// MODEL_API_KEY → 등록하면 외부 AI 요약, 없으면 Copilot → 커밋 분석 (#455·#566 사다리)
|
|
72
72
|
// _GITHUB_PAT_TOKEN → 없으면 GITHUB_TOKEN으로 머지하고 후속 워크플로우를 직접 깨운다 (#551)
|
|
73
73
|
export const OPTIONAL_SECRETS = new Set(["MODEL_API_KEY", "_GITHUB_PAT_TOKEN"]);
|
|
74
74
|
|
package/src/index.js
CHANGED
|
@@ -322,6 +322,8 @@ export async function run(argv, { cwd = process.cwd(), source = { type: "git" },
|
|
|
322
322
|
mode: opts.mode, types, version, deployBranch: context.deployBranch, migrationGuidePath,
|
|
323
323
|
counters: { workflows: result?.workflows?.copied ?? 0, workflowFiles: result?.workflows?.copiedFiles ?? [], utilModules: 0 },
|
|
324
324
|
verification: result?.verification, // #549 설치 후 검증 결과 (full/workflows 모드에서만 존재)
|
|
325
|
+
// #569 — 고른 것만 안내하려면 선택값이 필요하다
|
|
326
|
+
aiPrSummary: context.aiPrSummary, codeReviewCoderabbit: context.codeReviewCoderabbit,
|
|
325
327
|
logDir: files ? MIGRATION_DIR : null, // #561 기록 위치 안내
|
|
326
328
|
logFile: files?.logFile ?? null,
|
|
327
329
|
traceFile: files?.traceFile ?? null,
|
package/src/ui/summary.js
CHANGED
|
@@ -13,6 +13,7 @@ export function printSummary(ctx, targetRoot = ".") {
|
|
|
13
13
|
const err = (s = "") => process.stderr.write(`${s}\n`);
|
|
14
14
|
// 색상은 TTY일 때만 (.sh YELLOW/CYAN/NC 등가)
|
|
15
15
|
const isTty = !!process.stderr.isTTY;
|
|
16
|
+
const BOLD = isTty ? "\u001b[1m" : "";
|
|
16
17
|
const YELLOW = isTty ? "\x1b[1;33m" : "";
|
|
17
18
|
const CYAN = isTty ? "\x1b[0;36m" : "";
|
|
18
19
|
const NC = isTty ? "\x1b[0m" : "";
|
|
@@ -190,9 +191,32 @@ export function printSummary(ctx, targetRoot = ".") {
|
|
|
190
191
|
err(` → git checkout -b ${deployBranchName} && git push -u origin ${deployBranchName}`);
|
|
191
192
|
}
|
|
192
193
|
err("");
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
// #569 — 선택한 것만 안내한다. 끈 기능의 설정법을 보여주면 무엇을 해야 하는지 흐려진다.
|
|
195
|
+
let step = 3;
|
|
196
|
+
if (ctx?.aiPrSummary !== false) {
|
|
197
|
+
err(` ${step}️⃣ (선택) AI가 다듬은 릴리스 노트 받기`);
|
|
198
|
+
err(" → 지금 상태로도 릴리스 노트는 나옵니다. 커밋 내용을 분석해 만들기 때문에");
|
|
199
|
+
err(" 아무 설정을 하지 않아도 됩니다. 아래는 문장을 더 매끄럽게 하고 싶을 때만 하세요.");
|
|
200
|
+
err("");
|
|
201
|
+
err(" ① Google AI Studio에서 키 발급 (무료 · 신용카드 불필요 · 2분)");
|
|
202
|
+
err(" https://aistudio.google.com/apikey");
|
|
203
|
+
err(" ② 이 저장소에 등록");
|
|
204
|
+
err(" Settings > Secrets and variables > Actions > New repository secret");
|
|
205
|
+
err(` ${BOLD}Name: GEMINI_API_KEY${NC} Secret: 발급받은 키(AIza... 로 시작)`);
|
|
206
|
+
err("");
|
|
207
|
+
err(" 💡 다른 서비스를 쓴다면 이름만 바꿔 등록하면 됩니다 — 등록한 것이 자동으로 쓰입니다.");
|
|
208
|
+
err(" OPENAI_API_KEY · ANTHROPIC_API_KEY · GROQ_API_KEY · MISTRAL_API_KEY");
|
|
209
|
+
err("");
|
|
210
|
+
step++;
|
|
211
|
+
}
|
|
212
|
+
if (ctx?.codeReviewCoderabbit === true) {
|
|
213
|
+
err(` ${step}️⃣ CodeRabbit 활성화`);
|
|
214
|
+
err(" → https://coderabbit.ai 로그인 → GitHub 앱 설치 → 이 저장소에 접근 권한(grant access) 부여");
|
|
215
|
+
err(" → 이 단계를 안 하면 워크플로우는 켜져도 PR에 리뷰 댓글이 달리지 않습니다");
|
|
216
|
+
err(" → 공개 저장소는 무료입니다 (시간당 3회 리뷰 제한)");
|
|
217
|
+
err("");
|
|
218
|
+
step++;
|
|
219
|
+
}
|
|
196
220
|
err(SEPARATOR);
|
|
197
221
|
err("");
|
|
198
222
|
err(`${CYAN}📖 자세한 설정 방법은 다음 파일을 참고하세요:${NC}`);
|