kedovi 0.77.1 → 0.77.2
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/CHANGELOG.md +7 -0
- package/bin/kedovi.js +23 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
`kedovi upgrade` 가 올린 버전 사이의 항목을 표로 보여 준다. 배포할 때 이번 버전 항목을 맨 위에 더한다 (`## 버전` 아래 `| 구분 | 내용 |` 표, 한 줄씩 짧게).
|
|
4
4
|
|
|
5
|
+
## 0.77.2
|
|
6
|
+
|
|
7
|
+
| 구분 | 내용 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 수정 | `kedovi team whoami --refresh` 가 계정 입력 창을 띄우다 `TypeError (reading 'log')` 로 죽던 것 |
|
|
10
|
+
| 수정 | 화면 없는 실행(에이전트)에서 저장소 계정을 못 읽으면 이유(원격 없음 · gh · glab 로그인 · ssh 키 · git credential)를 한 줄 알림 |
|
|
11
|
+
|
|
5
12
|
## 0.77.1
|
|
6
13
|
|
|
7
14
|
| 구분 | 내용 |
|
package/bin/kedovi.js
CHANGED
|
@@ -79,8 +79,10 @@ const { spawnSync, execFileSync } = require('child_process');
|
|
|
79
79
|
const kit = require('./kit.js');
|
|
80
80
|
const gateway = require('./gateway.js');
|
|
81
81
|
|
|
82
|
-
let
|
|
83
|
-
const clack = () => (
|
|
82
|
+
let clackMod;
|
|
83
|
+
const clack = () => (clackMod = clackMod || require('@clack/prompts'));
|
|
84
|
+
// p.log 등을 어디서 먼저 쓰든 화면 라이브러리를 불러온다 (늦게 불러오기 때문에 생기던 "undefined (reading 'log')" 방지)
|
|
85
|
+
const p = new Proxy({}, { get: (_, k) => clack()[k] });
|
|
84
86
|
|
|
85
87
|
const BACK = Symbol('kedovi:back');
|
|
86
88
|
|
|
@@ -1620,32 +1622,39 @@ function requireAdmin(root, what, { soft = false } = {}) {
|
|
|
1620
1622
|
|
|
1621
1623
|
// 이 PC 사용자 확인 — 워크스페이스 repo 원격의 저장소 계정 username. ssh 면 ssh -T 인사말, https 면 gh · glab · git credential 의 사용자 이름
|
|
1622
1624
|
const NOT_USER = new Set(['oauth2', 'x-access-token', 'gitlab-ci-token', 'personalaccesstoken', 'token', 'git']);
|
|
1623
|
-
|
|
1625
|
+
/** why: 못 찾았을 때 이유를 쌓는다 (화면 없는 실행에서 한 줄로 알린다) */
|
|
1626
|
+
function detectAccount(root, why = []) {
|
|
1624
1627
|
const url = (spawnSync('git', ['-C', root, 'remote', 'get-url', 'origin'], { encoding: 'utf8' }).stdout || '').trim();
|
|
1625
|
-
if (!url) return null;
|
|
1628
|
+
if (!url) { why.push('워크스페이스 repo 에 origin 원격이 없음'); return null; }
|
|
1626
1629
|
const ssh = url.match(/^ssh:\/\/([\w.-]+)@([^:/]+)(?::(\d+))?\//) || url.match(/^([\w.-]+)@([^:/]+):/);
|
|
1627
1630
|
// 로그인 창 · 브라우저가 뜨지 않게 (Git Credential Manager · askpass)
|
|
1628
1631
|
const env = { ...process.env, GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'never' };
|
|
1629
1632
|
delete env.GIT_ASKPASS; delete env.SSH_ASKPASS;
|
|
1630
|
-
const
|
|
1633
|
+
const missing = new Set();
|
|
1634
|
+
const run = (cmd, args, input) => { try { const r = spawnSync(cmd, args, { encoding: 'utf8', timeout: 8000, input, env }); if (r.error && r.error.code === 'ENOENT') missing.add(cmd); return `${r.stdout || ''}\n${r.stderr || ''}`; } catch { return ''; } };
|
|
1631
1635
|
if (ssh && !/^https?:/.test(url)) {
|
|
1632
1636
|
const [, user, host, port] = ssh;
|
|
1633
1637
|
const out = run('ssh', ['-T', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', '-o', 'StrictHostKeyChecking=accept-new', ...(port ? ['-p', port] : []), `${user}@${host}`]);
|
|
1634
1638
|
const m = out.match(/Hi ([\w.-]+)!/) || out.match(/Welcome to GitLab, @([\w.-]+)!/) || out.match(/logged in as ([\w.-]+)/i);
|
|
1635
|
-
|
|
1639
|
+
if (m) return { username: m[1], host, source: 'ssh' };
|
|
1640
|
+
why.push(`ssh -T ${user}@${host}${port ? ` -p ${port}` : ''} 이 계정 이름을 돌려주지 않음 (키 등록 · 접속 확인)`);
|
|
1641
|
+
return null;
|
|
1636
1642
|
}
|
|
1637
1643
|
let host = '';
|
|
1638
|
-
try { host = new URL(url).hostname; } catch { return null; }
|
|
1644
|
+
try { host = new URL(url).hostname; } catch { why.push(`원격 주소를 읽지 못함 (${url})`); return null; }
|
|
1639
1645
|
if (/github/.test(host)) {
|
|
1640
1646
|
const u = run('gh', ['api', 'user', '--hostname', host, '--jq', '.login']).trim().split('\n')[0];
|
|
1641
1647
|
if (/^[\w.-]+$/.test(u)) return { username: u, host, source: 'gh' };
|
|
1648
|
+
why.push(missing.has('gh') ? 'gh 없음' : `gh 로그인 안 됨 (gh auth login --hostname ${host})`);
|
|
1642
1649
|
} else {
|
|
1643
1650
|
const j = run('glab', ['api', 'user', '--hostname', host]);
|
|
1644
1651
|
const m = j.match(/"username"\s*:\s*"([\w.-]+)"/);
|
|
1645
1652
|
if (m) return { username: m[1], host, source: 'glab' };
|
|
1653
|
+
why.push(missing.has('glab') ? 'glab 없음' : `glab 로그인 안 됨 (glab auth login --hostname ${host})`);
|
|
1646
1654
|
}
|
|
1647
1655
|
const cred = run('git', ['-C', root, 'credential', 'fill'], `protocol=https\nhost=${host}\n\n`).match(/^username=(.+)$/m);
|
|
1648
1656
|
if (cred && !NOT_USER.has(cred[1].trim().toLowerCase())) return { username: cred[1].trim(), host, source: 'git credential' };
|
|
1657
|
+
why.push(cred ? `git credential 의 사용자가 토큰 이름(${cred[1].trim()})` : 'git credential 에 저장된 사용자 없음');
|
|
1649
1658
|
return null;
|
|
1650
1659
|
}
|
|
1651
1660
|
|
|
@@ -1656,10 +1665,13 @@ async function ensureAccount(root, { refresh = false, ask = process.stdin.isTTY
|
|
|
1656
1665
|
if (cur && cur.username && !refresh) return cur;
|
|
1657
1666
|
// 못 찾은 기록이 있으면 7일 동안 다시 묻지 않는다 (start 마다 느려지지 않게. 다시: kedovi team whoami --refresh)
|
|
1658
1667
|
if (cur && cur.source === 'none' && !refresh && (Date.now() - Date.parse(cur.checked || 0)) < 7 * 864e5) return null;
|
|
1659
|
-
const
|
|
1668
|
+
const why = [];
|
|
1669
|
+
const found = detectAccount(root, why);
|
|
1660
1670
|
let me = found ? { ...found, checked: today } : null;
|
|
1671
|
+
// 화면 없는 실행(에이전트 등)은 물을 수 없으니 이유를 한 줄 알린다
|
|
1672
|
+
if (!me && !ask) clack().log.warn(`저장소 계정을 읽지 못했습니다: ${why.join(' · ') || '이유 모름'}. 터미널에서 kedovi team whoami --refresh 를 직접 실행하면 입력해 적을 수 있습니다`);
|
|
1661
1673
|
if (!me && ask) {
|
|
1662
|
-
|
|
1674
|
+
clack().log.info('워크스페이스 원격에서 저장소 계정을 읽지 못했습니다. 팀장 · 담당 repo 판정에 쓰니 직접 적어 주세요 (나중에 다시 확인: kedovi team whoami --refresh)');
|
|
1663
1675
|
const v = await clack().text({ message: '저장소 계정 username', placeholder: '예: hong01' });
|
|
1664
1676
|
if (!clack().isCancel(v) && String(v || '').trim()) me = { username: String(v).trim(), host: '', source: 'input', checked: today };
|
|
1665
1677
|
}
|
|
@@ -1682,7 +1694,7 @@ function adoptAccount(root, username) {
|
|
|
1682
1694
|
const all = [...(t.leads || []), ...(t.admins || []), ...(t.members || [])];
|
|
1683
1695
|
if (all.some((x) => sameUser(x, username)) || !all.some((x) => sameUser(x, gitName))) return;
|
|
1684
1696
|
const lead = [...(t.leads || []), ...(t.admins || [])].some((x) => sameUser(x, gitName));
|
|
1685
|
-
|
|
1697
|
+
clack().log.info(lead
|
|
1686
1698
|
? `팀 설정의 팀장 ${gitName} 이(가) git user.name 으로 적혀 있습니다. 저장소 계정으로 바꾸세요: kedovi team lead add ${username} --nick <이름> 뒤 kedovi team lead remove ${gitName}`
|
|
1687
1699
|
: `팀 설정에 ${gitName} 이(가) git user.name 으로 적혀 있습니다. 팀장에게 저장소 계정 ${username} 으로 바꿔 달라고 요청하세요 (kedovi team add ${username} --nick <이름> --repos …)`);
|
|
1688
1700
|
}
|
|
@@ -1769,7 +1781,7 @@ async function cmdWhoami(root, args) {
|
|
|
1769
1781
|
console.log(`이 PC 사용자: ${w.nickname ? `${w.nickname} · ` : ''}${w.username || '(모름)'} (확인: ${w.source})`);
|
|
1770
1782
|
console.log(`역할: ${w.lead ? '팀장 (모든 repo · 설정 수정)' : w.person ? `팀원 · 담당 ${(w.repos || []).join(', ') || '없음'}` : '명단에 없음'}`);
|
|
1771
1783
|
if (blocked.length) console.log(`고칠 수 없는 repo: ${blocked.join(', ')} (담당자 · 팀장에게 요청)`);
|
|
1772
|
-
if (w.source === 'git user.name') console.log(' 저장소 계정을 아직 확인하지 못했습니다: kedovi team whoami --refresh');
|
|
1784
|
+
if (w.source === 'git user.name' && !args.includes('--refresh')) console.log(' 저장소 계정을 아직 확인하지 못했습니다: kedovi team whoami --refresh');
|
|
1773
1785
|
}
|
|
1774
1786
|
|
|
1775
1787
|
/** 팀장 · 팀원 · 담당 repo · 부를 이름 (workspace.json team.leads · members) */
|