projectops 4.33.0 → 4.34.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/full.js +8 -3
- package/src/commands/interactive.js +7 -1
- package/src/commands/issues.js +5 -2
- package/src/core/copy/simple.js +23 -1
- package/src/core/repo-language.js +20 -10
- package/src/i18n/en.js +1 -0
- package/src/i18n/ko.js +1 -0
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
**Status: actively maintained.** Releases are batched about once a day; see [CONTRIBUTING](CONTRIBUTING.md#releases).
|
|
10
10
|
|
|
11
11
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
12
|
-
## Latest version : v4.33.
|
|
12
|
+
## Latest version : v4.33.1 (2026-10-06)
|
|
13
13
|
|
|
14
14
|
[View full version history](CHANGELOG.md)
|
|
15
15
|
|
package/package.json
CHANGED
package/src/commands/full.js
CHANGED
|
@@ -10,7 +10,7 @@ import { markerForType } from "../core/detect.js";
|
|
|
10
10
|
import { addVersionSectionToReadme } from "../core/copy/readme.js";
|
|
11
11
|
import { copyWorkflows } from "../core/copy/workflows.js";
|
|
12
12
|
import {
|
|
13
|
-
copyScripts, copyConfigFolder, copyIssueTemplates,
|
|
13
|
+
copyScripts, copyConfigFolder, copyIssueTemplates, stripIssueTemplateAssignees,
|
|
14
14
|
copyDiscussionTemplates, copySetupGuide,
|
|
15
15
|
} from "../core/copy/simple.js";
|
|
16
16
|
import { copyUtilModules } from "../core/copy/util.js";
|
|
@@ -18,6 +18,7 @@ import { copyCoderabbit } from "../core/copy/coderabbit.js";
|
|
|
18
18
|
import { ensureGitignore } from "../core/copy/gitignore.js";
|
|
19
19
|
import { applyLabelStyle } from "../core/label-style.js";
|
|
20
20
|
import { applyRepoLanguage } from "../core/repo-language.js";
|
|
21
|
+
import { t } from "../i18n/index.js";
|
|
21
22
|
import { verifyInstall } from "../core/verify.js";
|
|
22
23
|
|
|
23
24
|
// context: { version, types, paths:Map, branch, versionCode, deployTarget, publishTargets, includeSecretBackup,
|
|
@@ -70,8 +71,12 @@ export function runFull(context, tempDir, targetRoot = ".", hooks = {}) {
|
|
|
70
71
|
copyIssueTemplates(tempDir, targetRoot);
|
|
71
72
|
// 템플릿 언어(#769) — 원본은 영문이라 한국어 레포는 복사 직후 오버레이로 덮는다.
|
|
72
73
|
// 라벨 표기 변환(apply-label-style)보다 반드시 먼저여야 한다.
|
|
73
|
-
const overlaid = applyRepoLanguage(tempDir, targetRoot, language);
|
|
74
|
-
hooks.trace?.event("copy", "repo-language", language, { overlaid: overlaid.length });
|
|
74
|
+
const { changed: overlaid, failed: overlayFailed } = applyRepoLanguage(tempDir, targetRoot, language);
|
|
75
|
+
hooks.trace?.event("copy", "repo-language", language, { overlaid: overlaid.length, failed: overlayFailed.length });
|
|
76
|
+
for (const f of overlayFailed) hooks.trace?.event("copy", "overlay-failed", f.file, { reason: f.error });
|
|
77
|
+
// 담당자 고정값 제거(#782) — 오버레이가 다시 넣으므로 그 뒤에 한다.
|
|
78
|
+
hooks.trace?.event("copy", "strip-assignees", "", { files: stripIssueTemplateAssignees(targetRoot).length });
|
|
79
|
+
if (overlayFailed.length) console.error(t("language.overlayFailed", { n: overlayFailed.length, files: overlayFailed.map((f) => f.file).join(", ") }));
|
|
75
80
|
copyDiscussionTemplates(tempDir, targetRoot);
|
|
76
81
|
});
|
|
77
82
|
|
|
@@ -100,7 +100,13 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), source = {
|
|
|
100
100
|
|
|
101
101
|
// issues 모드는 정보 수집 없이 바로 실행
|
|
102
102
|
if (mode === "issues") {
|
|
103
|
-
|
|
103
|
+
// 기존 레포의 템플릿 언어와 라벨 표기는 저장값을 따른다. 빈 baseCtx 로 만들면 둘 다 null 이 되어
|
|
104
|
+
// runIssues 의 기본값(en)이 적용돼, 키 없는 기존 한국어 레포가 질문 없이 영문으로 바뀐다 (#769).
|
|
105
|
+
const ctx = createContext({
|
|
106
|
+
...baseCtx, mode, force: true,
|
|
107
|
+
language: resolveRepoLanguage({ flag: null, stored: existing?.options?.language, existing: !!existing }),
|
|
108
|
+
labelStyle: resolveLabelStyle({ flag: null, stored: existing?.options?.labelStyle, existing: !!existing }),
|
|
109
|
+
});
|
|
104
110
|
runIssues(ctx, tempDir, cwd);
|
|
105
111
|
io.summary?.({ mode, types: [], version: "", counters: {} }, cwd);
|
|
106
112
|
io.outro?.(t("flow.doneIssues"));
|
package/src/commands/issues.js
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// 이슈/PR 템플릿 + Discussion 템플릿만 복사.
|
|
3
3
|
import { applyLabelStyle } from "../core/label-style.js";
|
|
4
4
|
import { applyRepoLanguage } from "../core/repo-language.js";
|
|
5
|
-
import {
|
|
5
|
+
import { t } from "../i18n/index.js";
|
|
6
|
+
import { copyIssueTemplates, copyDiscussionTemplates, stripIssueTemplateAssignees } from "../core/copy/simple.js";
|
|
6
7
|
|
|
7
8
|
export function runIssues(context, tempDir, targetRoot = ".") {
|
|
8
9
|
copyIssueTemplates(tempDir, targetRoot);
|
|
9
|
-
applyRepoLanguage(tempDir, targetRoot, context.language ?? "en"); // #769 — 라벨 변환보다 먼저
|
|
10
|
+
const { failed } = applyRepoLanguage(tempDir, targetRoot, context.language ?? "en"); // #769 — 라벨 변환보다 먼저
|
|
11
|
+
if (failed.length) console.error(t("language.overlayFailed", { n: failed.length, files: failed.map((f) => f.file).join(", ") }));
|
|
12
|
+
stripIssueTemplateAssignees(targetRoot); // #782 — 오버레이가 다시 넣으므로 그 뒤에
|
|
10
13
|
applyLabelStyle(targetRoot, context.labelStyle ?? "en"); // #776
|
|
11
14
|
copyDiscussionTemplates(tempDir, targetRoot);
|
|
12
15
|
}
|
package/src/core/copy/simple.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 단순 복사 함수 (무조건 덮어쓰기류) — .sh copy_scripts/config/issue/discussion/setup_guide 등가.
|
|
2
2
|
// 실측: template_integrator.sh 3818, 3845, 3872, 3895, 4114.
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { chmodSync } from "node:fs";
|
|
4
|
+
import { chmodSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { PATHS } from "../paths.js";
|
|
6
6
|
import { exists, copyFileSync, copyDirSync } from "../fsutil.js";
|
|
7
7
|
|
|
@@ -60,6 +60,28 @@ export function copyConfigFolder(tempDir, targetRoot = ".") {
|
|
|
60
60
|
return true;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// 사용자 레포로 복사된 이슈 템플릿에서 담당자(assignees) 줄을 지운다 (#782).
|
|
64
|
+
// 원본에는 이 저장소 소유자가 박혀 있는데, 남의 레포에서 새 이슈를 만들 때 그 사람이 담당자로
|
|
65
|
+
// 지정되면 안 된다. 담당자는 이슈 헬퍼와 QA 봇이 version.yml 설정으로 정한다.
|
|
66
|
+
// frontmatter(첫 --- 와 둘째 --- 사이)의 줄만 대상이다 — 본문에 같은 문구가 있어도 건드리지 않는다.
|
|
67
|
+
// 반환: 바뀐 파일의 레포 루트 기준 상대경로. 오버레이 적용 뒤에 불러야 한다(오버레이가 다시 넣는다).
|
|
68
|
+
export function stripIssueTemplateAssignees(targetRoot = ".") {
|
|
69
|
+
const dir = join(targetRoot, ".github", "ISSUE_TEMPLATE");
|
|
70
|
+
if (!exists(dir)) return [];
|
|
71
|
+
const changed = [];
|
|
72
|
+
for (const name of readdirSync(dir).filter((f) => f.endsWith(".md"))) {
|
|
73
|
+
const file = join(dir, name);
|
|
74
|
+
const text = readFileSync(file, "utf8");
|
|
75
|
+
const m = text.match(/^(---\r?\n)([\s\S]*?)(\r?\n---)/);
|
|
76
|
+
if (!m) continue;
|
|
77
|
+
const front = m[2].split(/\r?\n/).filter((l) => !/^assignees:/.test(l)).join("\n");
|
|
78
|
+
if (front === m[2]) continue;
|
|
79
|
+
writeFileSync(file, text.replace(m[0], `${m[1]}${front}${m[3]}`));
|
|
80
|
+
changed.push(`.github/ISSUE_TEMPLATE/${name}`);
|
|
81
|
+
}
|
|
82
|
+
return changed;
|
|
83
|
+
}
|
|
84
|
+
|
|
63
85
|
// .github/ISSUE_TEMPLATE/ 전체 + PULL_REQUEST_TEMPLATE.md 덮어쓰기.
|
|
64
86
|
export function copyIssueTemplates(tempDir, targetRoot = ".") {
|
|
65
87
|
const srcIssue = join(tempDir, ".github", "ISSUE_TEMPLATE");
|
|
@@ -25,23 +25,33 @@ function walk(dir) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// 한국어 모드: 오버레이의 모든 파일을 targetRoot/.github/ 아래 같은 상대경로로 복사한다.
|
|
28
|
-
// 반환: 복사한 파일의 레포 루트 기준 상대경로(.github/...)
|
|
29
|
-
//
|
|
28
|
+
// 반환: { changed, failed }. changed 는 복사한 파일의 레포 루트 기준 상대경로(.github/...),
|
|
29
|
+
// failed 는 복사하지 못한 { file, error }. en 이거나 오버레이가 없으면 둘 다 빈 배열이다.
|
|
30
|
+
// 파일 하나가 실패해도 나머지는 계속 복사한다 — 첫 실패에서 멈추면 영문과 한글이 섞이거나 전부 영문으로 남는다.
|
|
31
|
+
// 예외는 던지지 않는다: 영문 템플릿이 남을 뿐 설치를 죽일 이유가 아니다. 대신 호출부가 failed 를 알린다.
|
|
30
32
|
export function applyRepoLanguage(tempDir, targetRoot, language) {
|
|
31
|
-
if (language !== "ko") return [];
|
|
32
|
-
const overlay = join(tempDir, ".github", "i18n", "ko");
|
|
33
|
-
if (!existsSync(overlay)) return [];
|
|
34
33
|
const changed = [];
|
|
34
|
+
const failed = [];
|
|
35
|
+
if (language !== "ko") return { changed, failed };
|
|
36
|
+
const overlay = join(tempDir, ".github", "i18n", "ko");
|
|
37
|
+
if (!existsSync(overlay)) return { changed, failed };
|
|
38
|
+
let files = [];
|
|
35
39
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
files = walk(overlay);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
failed.push({ file: ".github/i18n/ko", error: String(e?.message ?? e) });
|
|
43
|
+
return { changed, failed };
|
|
44
|
+
}
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const rel = relative(overlay, file).split("\\").join("/");
|
|
47
|
+
try {
|
|
38
48
|
const dst = join(targetRoot, ".github", rel);
|
|
39
49
|
mkdirSync(dirname(dst), { recursive: true });
|
|
40
50
|
copyFileSync(file, dst);
|
|
41
51
|
changed.push(`.github/${rel}`);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
failed.push({ file: `.github/${rel}`, error: String(e?.message ?? e) });
|
|
42
54
|
}
|
|
43
|
-
} catch {
|
|
44
|
-
return changed;
|
|
45
55
|
}
|
|
46
|
-
return changed;
|
|
56
|
+
return { changed, failed };
|
|
47
57
|
}
|
package/src/i18n/en.js
CHANGED
|
@@ -42,6 +42,7 @@ export default {
|
|
|
42
42
|
"edit.type": "Project type",
|
|
43
43
|
"edit.version": "Version",
|
|
44
44
|
"edit.branch": "Default branch",
|
|
45
|
+
"language.overlayFailed": "Warning: could not apply the Korean template files ({n}): {files}. English templates were kept for those files.",
|
|
45
46
|
"language.propose": "Switch the issue and PR templates in this repo to English? They are Korean now. Answer no to keep them as they are.",
|
|
46
47
|
"labelStyle.propose": "Rename Korean status labels (작업전, 작업중, ...) to English (status: todo, ...)? Issues keep their labels; Projects sync accepts both.",
|
|
47
48
|
"edit.intent": "Project kind (deployment style)",
|
package/src/i18n/ko.js
CHANGED
|
@@ -39,6 +39,7 @@ export default {
|
|
|
39
39
|
"edit.type": "프로젝트 타입",
|
|
40
40
|
"edit.version": "버전",
|
|
41
41
|
"edit.branch": "기본 브랜치",
|
|
42
|
+
"language.overlayFailed": "경고: 한국어 템플릿 파일 {n}개를 적용하지 못했습니다: {files}. 해당 파일은 영문 템플릿이 그대로 남습니다.",
|
|
42
43
|
"language.propose": "이 레포의 이슈/PR 템플릿을 영문으로 바꿀까요? 지금은 한국어입니다. 아니오를 고르면 그대로 둡니다.",
|
|
43
44
|
"labelStyle.propose": "한글 상태 라벨(작업전, 작업중 ...)을 영문(status: todo ...)으로 바꿀까요? 이슈에 붙은 라벨은 그대로 옮겨지고 Projects 동기화는 둘 다 인식합니다.",
|
|
44
45
|
"edit.intent": "프로젝트 성격 (배포 유형)",
|