@sk8metal/michi-cli 0.4.0 → 0.5.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/CHANGELOG.md +40 -0
- package/dist/scripts/__tests__/spec-impl-workflow.test.js +4 -2
- package/dist/scripts/__tests__/spec-impl-workflow.test.js.map +1 -1
- package/dist/scripts/config/config-schema.d.ts +52 -0
- package/dist/scripts/config/config-schema.d.ts.map +1 -1
- package/dist/scripts/config/config-schema.js +25 -0
- package/dist/scripts/config/config-schema.js.map +1 -1
- package/dist/scripts/pr-automation.d.ts.map +1 -1
- package/dist/scripts/pr-automation.js +11 -3
- package/dist/scripts/pr-automation.js.map +1 -1
- package/dist/scripts/spec-impl-workflow.d.ts.map +1 -1
- package/dist/scripts/spec-impl-workflow.js +22 -6
- package/dist/scripts/spec-impl-workflow.js.map +1 -1
- package/dist/scripts/utils/__tests__/config-loader.test.js +114 -1
- package/dist/scripts/utils/__tests__/config-loader.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/config-validator.test.js +2 -0
- package/dist/scripts/utils/__tests__/config-validator.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/env-config.test.js +0 -2
- package/dist/scripts/utils/__tests__/env-config.test.js.map +1 -1
- package/dist/scripts/utils/__tests__/project-meta.test.d.ts +6 -0
- package/dist/scripts/utils/__tests__/project-meta.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/project-meta.test.js +154 -0
- package/dist/scripts/utils/__tests__/project-meta.test.js.map +1 -0
- package/dist/scripts/utils/__tests__/security-validator.test.d.ts +6 -0
- package/dist/scripts/utils/__tests__/security-validator.test.d.ts.map +1 -0
- package/dist/scripts/utils/__tests__/security-validator.test.js +219 -0
- package/dist/scripts/utils/__tests__/security-validator.test.js.map +1 -0
- package/dist/scripts/utils/config-loader.d.ts +10 -4
- package/dist/scripts/utils/config-loader.d.ts.map +1 -1
- package/dist/scripts/utils/config-loader.js +214 -47
- package/dist/scripts/utils/config-loader.js.map +1 -1
- package/dist/scripts/utils/env-config.d.ts +1 -1
- package/dist/scripts/utils/env-config.d.ts.map +1 -1
- package/dist/scripts/utils/env-config.js +2 -14
- package/dist/scripts/utils/env-config.js.map +1 -1
- package/dist/scripts/utils/project-meta.d.ts +9 -0
- package/dist/scripts/utils/project-meta.d.ts.map +1 -1
- package/dist/scripts/utils/project-meta.js +22 -0
- package/dist/scripts/utils/project-meta.js.map +1 -1
- package/dist/scripts/utils/security-validator.d.ts +55 -0
- package/dist/scripts/utils/security-validator.d.ts.map +1 -0
- package/dist/scripts/utils/security-validator.js +232 -0
- package/dist/scripts/utils/security-validator.js.map +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +41 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/__tests__/init.test.d.ts +5 -0
- package/dist/src/commands/__tests__/init.test.d.ts.map +1 -0
- package/dist/src/commands/__tests__/init.test.js +255 -0
- package/dist/src/commands/__tests__/init.test.js.map +1 -0
- package/dist/src/commands/__tests__/migrate.test.d.ts +5 -0
- package/dist/src/commands/__tests__/migrate.test.d.ts.map +1 -0
- package/dist/src/commands/__tests__/migrate.test.js +216 -0
- package/dist/src/commands/__tests__/migrate.test.js.map +1 -0
- package/dist/src/commands/config-validate.d.ts +9 -0
- package/dist/src/commands/config-validate.d.ts.map +1 -0
- package/dist/src/commands/config-validate.js +90 -0
- package/dist/src/commands/config-validate.js.map +1 -0
- package/dist/src/commands/init.d.ts +1 -0
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +29 -6
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/migrate.d.ts +25 -0
- package/dist/src/commands/migrate.d.ts.map +1 -0
- package/dist/src/commands/migrate.js +341 -0
- package/dist/src/commands/migrate.js.map +1 -0
- package/dist/src/commands/setup-existing.d.ts.map +1 -1
- package/dist/src/commands/setup-existing.js +0 -1
- package/dist/src/commands/setup-existing.js.map +1 -1
- package/dist/vitest.config.d.ts.map +1 -1
- package/dist/vitest.config.js +32 -8
- package/dist/vitest.config.js.map +1 -1
- package/docs/michi-development/design/config-unification.md +4789 -0
- package/docs/user-guide/getting-started/github-token-setup.md +2 -1
- package/docs/user-guide/getting-started/new-repository-setup.md +1 -1
- package/docs/user-guide/getting-started/quick-start.md +1 -1
- package/docs/user-guide/getting-started/setup.md +4 -11
- package/docs/user-guide/hands-on/claude-agent-setup.md +2 -2
- package/docs/user-guide/hands-on/claude-setup.md +2 -2
- package/docs/user-guide/hands-on/cursor-setup.md +2 -2
- package/docs/user-guide/hands-on/workflow-walkthrough.md +4 -1
- package/env.example +1 -1
- package/package.json +2 -2
- package/scripts/__tests__/spec-impl-workflow.test.ts +5 -2
- package/scripts/config/config-schema.ts +40 -0
- package/scripts/pr-automation.ts +15 -5
- package/scripts/spec-impl-workflow.ts +22 -6
- package/scripts/utils/__tests__/config-loader.test.ts +149 -0
- package/scripts/utils/__tests__/config-validator.test.ts +2 -0
- package/scripts/utils/__tests__/env-config.test.ts +0 -2
- package/scripts/utils/__tests__/project-meta.test.ts +192 -0
- package/scripts/utils/__tests__/security-validator.test.ts +272 -0
- package/scripts/utils/config-loader.ts +232 -53
- package/scripts/utils/env-config.ts +2 -14
- package/scripts/utils/project-meta.ts +27 -0
- package/scripts/utils/security-validator.ts +286 -0
- package/templates/claude/commands/kiro/kiro-spec-impl.md +1 -1
- package/templates/claude-agent/commands/kiro/kiro-spec-impl.md +1 -1
- package/templates/cursor/commands/kiro/kiro-spec-impl.md +1 -1
|
@@ -7,7 +7,7 @@ import { readFileSync, existsSync, statSync } from 'fs';
|
|
|
7
7
|
import { resolve, relative, isAbsolute } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { homedir } from 'os';
|
|
10
|
-
import { config } from 'dotenv';
|
|
10
|
+
import { config, parse as dotenvParse } from 'dotenv';
|
|
11
11
|
import { AppConfigSchema, type AppConfig } from '../config/config-schema.js';
|
|
12
12
|
|
|
13
13
|
// 環境変数読み込み
|
|
@@ -27,6 +27,14 @@ export function getGlobalConfigPath(): string {
|
|
|
27
27
|
return resolve(home, GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_FILE);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* グローバル.envファイルのパスを取得
|
|
32
|
+
*/
|
|
33
|
+
export function getGlobalEnvPath(): string {
|
|
34
|
+
const home = process.env.HOME || homedir();
|
|
35
|
+
return resolve(home, GLOBAL_CONFIG_DIR, '.env');
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
/**
|
|
31
39
|
* 深いマージ(Deep Merge)
|
|
32
40
|
* オブジェクトを再帰的にマージする
|
|
@@ -244,73 +252,172 @@ function loadGlobalConfig(): Partial<AppConfig> | null {
|
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
254
|
|
|
255
|
+
/**
|
|
256
|
+
* グローバル.envを読み込み
|
|
257
|
+
* ~/.michi/.env から組織共通の環境変数を読み込む
|
|
258
|
+
*/
|
|
259
|
+
function loadGlobalEnv(): Record<string, string> {
|
|
260
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
261
|
+
|
|
262
|
+
if (!existsSync(globalEnvPath)) {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
const content = readFileSync(globalEnvPath, 'utf-8');
|
|
268
|
+
return dotenvParse(content);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.warn(`⚠️ Failed to load global env from ${globalEnvPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* プロジェクト.envを読み込み
|
|
277
|
+
* .env からプロジェクト固有の環境変数を読み込む
|
|
278
|
+
*/
|
|
279
|
+
function loadProjectEnv(projectRoot: string): Record<string, string> {
|
|
280
|
+
const projectEnvPath = resolve(projectRoot, '.env');
|
|
281
|
+
|
|
282
|
+
if (!existsSync(projectEnvPath)) {
|
|
283
|
+
return {};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const content = readFileSync(projectEnvPath, 'utf-8');
|
|
288
|
+
return dotenvParse(content);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.warn(`⚠️ Failed to load project env from ${projectEnvPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
291
|
+
return {};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* プロジェクトメタデータを読み込み
|
|
297
|
+
* .kiro/project.json からプロジェクト情報を読み込む
|
|
298
|
+
*/
|
|
299
|
+
function loadProjectMetadata(projectRoot: string): Partial<AppConfig> | null {
|
|
300
|
+
const projectJsonPath = resolve(projectRoot, '.kiro/project.json');
|
|
301
|
+
|
|
302
|
+
if (!existsSync(projectJsonPath)) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const content = readFileSync(projectJsonPath, 'utf-8');
|
|
308
|
+
const meta = JSON.parse(content);
|
|
309
|
+
|
|
310
|
+
// project フィールドとして返す
|
|
311
|
+
return { project: meta } as Partial<AppConfig>;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
if (error instanceof SyntaxError) {
|
|
314
|
+
console.warn(`⚠️ Invalid JSON in ${projectJsonPath}: ${error.message}`);
|
|
315
|
+
} else {
|
|
316
|
+
console.warn(`⚠️ Failed to load project metadata from ${projectJsonPath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* 環境変数から設定へのマッピング
|
|
324
|
+
*/
|
|
325
|
+
const ENV_TO_CONFIG_MAPPING: Record<string, string> = {
|
|
326
|
+
'ATLASSIAN_URL': 'atlassian.url',
|
|
327
|
+
'ATLASSIAN_EMAIL': 'atlassian.email',
|
|
328
|
+
'ATLASSIAN_API_TOKEN': 'atlassian.apiToken',
|
|
329
|
+
'GITHUB_ORG': 'github.org',
|
|
330
|
+
'GITHUB_TOKEN': 'github.token',
|
|
331
|
+
'CONFLUENCE_PRD_SPACE': 'confluence.spaces.requirements',
|
|
332
|
+
'CONFLUENCE_QA_SPACE': 'confluence.spaces.qa',
|
|
333
|
+
'CONFLUENCE_RELEASE_SPACE': 'confluence.spaces.release',
|
|
334
|
+
'JIRA_ISSUE_TYPE_STORY': 'jira.issueTypes.story',
|
|
335
|
+
'JIRA_ISSUE_TYPE_SUBTASK': 'jira.issueTypes.subtask',
|
|
336
|
+
'JIRA_PROJECT_KEYS': 'jira.projectKeys',
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* ドットパスでオブジェクトの値を設定
|
|
341
|
+
* 例: setValueByPath(obj, 'a.b.c', value) => obj.a.b.c = value
|
|
342
|
+
*/
|
|
343
|
+
function setValueByPath(obj: Record<string, unknown>, path: string, value: string): void {
|
|
344
|
+
const keys = path.split('.');
|
|
345
|
+
let current: Record<string, unknown> = obj;
|
|
346
|
+
|
|
347
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
348
|
+
const key = keys[i];
|
|
349
|
+
if (!current[key] || typeof current[key] !== 'object') {
|
|
350
|
+
current[key] = {};
|
|
351
|
+
}
|
|
352
|
+
current = current[key] as Record<string, unknown>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
current[keys[keys.length - 1]] = value;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* 環境変数を設定オブジェクトに適用
|
|
360
|
+
*/
|
|
361
|
+
function applyEnvVarsToConfig(
|
|
362
|
+
config: AppConfig,
|
|
363
|
+
envVars: Record<string, string>
|
|
364
|
+
): AppConfig {
|
|
365
|
+
const result = { ...config } as Record<string, unknown>;
|
|
366
|
+
|
|
367
|
+
for (const [envKey, configPath] of Object.entries(ENV_TO_CONFIG_MAPPING)) {
|
|
368
|
+
if (envVars[envKey]) {
|
|
369
|
+
setValueByPath(result, configPath, envVars[envKey]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return result as AppConfig;
|
|
374
|
+
}
|
|
375
|
+
|
|
247
376
|
/**
|
|
248
377
|
* 設定を読み込んでマージ
|
|
249
378
|
*
|
|
250
|
-
*
|
|
379
|
+
* マージ順序(優先度: 低 → 高):
|
|
251
380
|
* 1. デフォルト設定
|
|
252
|
-
* 2.
|
|
253
|
-
* 3.
|
|
254
|
-
* 4.
|
|
381
|
+
* 2. グローバル.env(~/.michi/.env)
|
|
382
|
+
* 3. グローバル設定(~/.michi/config.json)
|
|
383
|
+
* 4. プロジェクトメタデータ(.kiro/project.json)
|
|
384
|
+
* 5. プロジェクト設定(.michi/config.json)
|
|
385
|
+
* 6. プロジェクト.env(.env)- 最高優先度
|
|
255
386
|
*/
|
|
256
387
|
export function loadConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// グローバル設定を読み込み
|
|
261
|
-
const globalConfig = loadGlobalConfig();
|
|
388
|
+
// 1. デフォルト設定を読み込み(最低優先度)
|
|
389
|
+
let mergedConfig: AppConfig = loadDefaultConfig();
|
|
262
390
|
|
|
263
|
-
//
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
391
|
+
// 2. グローバル.envを読み込み
|
|
392
|
+
const globalEnvVars = loadGlobalEnv();
|
|
393
|
+
if (Object.keys(globalEnvVars).length > 0) {
|
|
394
|
+
mergedConfig = applyEnvVarsToConfig(mergedConfig, globalEnvVars);
|
|
395
|
+
}
|
|
268
396
|
|
|
397
|
+
// 3. グローバル設定を読み込み
|
|
398
|
+
const globalConfig = loadGlobalConfig();
|
|
269
399
|
if (globalConfig) {
|
|
270
400
|
mergedConfig = deepMerge(mergedConfig, globalConfig);
|
|
271
401
|
}
|
|
272
402
|
|
|
403
|
+
// 4. プロジェクトメタデータを読み込み
|
|
404
|
+
const projectMeta = loadProjectMetadata(projectRoot);
|
|
405
|
+
if (projectMeta) {
|
|
406
|
+
mergedConfig = deepMerge(mergedConfig, projectMeta);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// 5. プロジェクト固有設定を読み込み
|
|
410
|
+
const projectConfig = loadProjectConfig(projectRoot);
|
|
273
411
|
if (projectConfig) {
|
|
274
412
|
mergedConfig = deepMerge(mergedConfig, projectConfig);
|
|
275
413
|
}
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
mergedConfig.confluence = {
|
|
282
|
-
pageCreationGranularity: 'single',
|
|
283
|
-
pageTitleFormat: '[{projectName}] {featureName} {docTypeLabel}',
|
|
284
|
-
autoLabels: ['{projectLabel}', '{docType}', '{featureName}', 'github-sync']
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
// spacesオブジェクトを確実に作成
|
|
288
|
-
if (!mergedConfig.confluence.spaces) {
|
|
289
|
-
mergedConfig.confluence.spaces = {};
|
|
290
|
-
}
|
|
291
|
-
// 各フィールドを個別にチェックし、未定義のフィールドのみ環境変数で設定
|
|
292
|
-
// 既に定義されている値は変更しない
|
|
293
|
-
if (!mergedConfig.confluence.spaces.requirements) {
|
|
294
|
-
mergedConfig.confluence.spaces.requirements = process.env.CONFLUENCE_PRD_SPACE;
|
|
295
|
-
}
|
|
296
|
-
if (!mergedConfig.confluence.spaces.design) {
|
|
297
|
-
mergedConfig.confluence.spaces.design = process.env.CONFLUENCE_PRD_SPACE;
|
|
298
|
-
}
|
|
299
|
-
if (!mergedConfig.confluence.spaces.tasks) {
|
|
300
|
-
mergedConfig.confluence.spaces.tasks = process.env.CONFLUENCE_PRD_SPACE;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// JIRA issue type IDを環境変数から取得(インスタンス固有の値のため)
|
|
305
|
-
if (mergedConfig.jira && mergedConfig.jira.issueTypes) {
|
|
306
|
-
if (process.env.JIRA_ISSUE_TYPE_STORY) {
|
|
307
|
-
mergedConfig.jira.issueTypes.story = process.env.JIRA_ISSUE_TYPE_STORY;
|
|
308
|
-
}
|
|
309
|
-
if (process.env.JIRA_ISSUE_TYPE_SUBTASK) {
|
|
310
|
-
mergedConfig.jira.issueTypes.subtask = process.env.JIRA_ISSUE_TYPE_SUBTASK;
|
|
311
|
-
}
|
|
414
|
+
|
|
415
|
+
// 6. プロジェクト.envを読み込み(最高優先度)
|
|
416
|
+
const projectEnvVars = loadProjectEnv(projectRoot);
|
|
417
|
+
if (Object.keys(projectEnvVars).length > 0) {
|
|
418
|
+
mergedConfig = applyEnvVarsToConfig(mergedConfig, projectEnvVars);
|
|
312
419
|
}
|
|
313
|
-
|
|
420
|
+
|
|
314
421
|
// スキーマで最終バリデーション
|
|
315
422
|
return AppConfigSchema.parse(mergedConfig);
|
|
316
423
|
}
|
|
@@ -324,10 +431,16 @@ let cachedProjectRoot: string | null = null;
|
|
|
324
431
|
let cachedConfigMtime: number | null = null;
|
|
325
432
|
let cachedDefaultConfigMtime: number | null = null;
|
|
326
433
|
let cachedGlobalConfigMtime: number | null = null;
|
|
434
|
+
let cachedGlobalEnvMtime: number | null = null;
|
|
435
|
+
let cachedProjectMetaMtime: number | null = null;
|
|
436
|
+
let cachedProjectEnvMtime: number | null = null;
|
|
327
437
|
|
|
328
438
|
export function getConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
329
439
|
const projectConfigPath = resolveConfigPath(projectRoot);
|
|
330
440
|
const globalConfigPath = getGlobalConfigPath();
|
|
441
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
442
|
+
const projectMetaPath = resolve(projectRoot, '.kiro/project.json');
|
|
443
|
+
const projectEnvPath = resolve(projectRoot, '.env');
|
|
331
444
|
const currentFileUrl = import.meta.url;
|
|
332
445
|
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
333
446
|
const currentDir = resolve(currentFilePath, '..');
|
|
@@ -397,13 +510,76 @@ export function getConfig(projectRoot: string = process.cwd()): AppConfig {
|
|
|
397
510
|
cachedConfigMtime = null;
|
|
398
511
|
}
|
|
399
512
|
|
|
513
|
+
// グローバル.envファイルの更新時刻をチェック
|
|
514
|
+
let globalEnvChanged = false;
|
|
515
|
+
try {
|
|
516
|
+
if (existsSync(globalEnvPath)) {
|
|
517
|
+
const globalEnvStats = statSync(globalEnvPath);
|
|
518
|
+
if (cachedGlobalEnvMtime !== globalEnvStats.mtimeMs) {
|
|
519
|
+
globalEnvChanged = true;
|
|
520
|
+
cachedGlobalEnvMtime = globalEnvStats.mtimeMs;
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
if (cachedGlobalEnvMtime !== null) {
|
|
524
|
+
globalEnvChanged = true;
|
|
525
|
+
cachedGlobalEnvMtime = null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} catch {
|
|
529
|
+
globalEnvChanged = true;
|
|
530
|
+
cachedGlobalEnvMtime = null;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// プロジェクトメタデータファイルの更新時刻をチェック
|
|
534
|
+
let projectMetaChanged = false;
|
|
535
|
+
try {
|
|
536
|
+
if (existsSync(projectMetaPath)) {
|
|
537
|
+
const projectMetaStats = statSync(projectMetaPath);
|
|
538
|
+
if (cachedProjectMetaMtime !== projectMetaStats.mtimeMs) {
|
|
539
|
+
projectMetaChanged = true;
|
|
540
|
+
cachedProjectMetaMtime = projectMetaStats.mtimeMs;
|
|
541
|
+
}
|
|
542
|
+
} else {
|
|
543
|
+
if (cachedProjectMetaMtime !== null) {
|
|
544
|
+
projectMetaChanged = true;
|
|
545
|
+
cachedProjectMetaMtime = null;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} catch {
|
|
549
|
+
projectMetaChanged = true;
|
|
550
|
+
cachedProjectMetaMtime = null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// プロジェクト.envファイルの更新時刻をチェック
|
|
554
|
+
let projectEnvChanged = false;
|
|
555
|
+
try {
|
|
556
|
+
if (existsSync(projectEnvPath)) {
|
|
557
|
+
const projectEnvStats = statSync(projectEnvPath);
|
|
558
|
+
if (cachedProjectEnvMtime !== projectEnvStats.mtimeMs) {
|
|
559
|
+
projectEnvChanged = true;
|
|
560
|
+
cachedProjectEnvMtime = projectEnvStats.mtimeMs;
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
if (cachedProjectEnvMtime !== null) {
|
|
564
|
+
projectEnvChanged = true;
|
|
565
|
+
cachedProjectEnvMtime = null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
} catch {
|
|
569
|
+
projectEnvChanged = true;
|
|
570
|
+
cachedProjectEnvMtime = null;
|
|
571
|
+
}
|
|
572
|
+
|
|
400
573
|
// キャッシュが有効で、設定ファイルが変更されていない場合はキャッシュを返す
|
|
401
574
|
if (
|
|
402
575
|
cachedConfig &&
|
|
403
576
|
cachedProjectRoot === projectRoot &&
|
|
404
577
|
!defaultConfigChanged &&
|
|
405
578
|
!globalConfigChanged &&
|
|
406
|
-
!
|
|
579
|
+
!globalEnvChanged &&
|
|
580
|
+
!projectConfigChanged &&
|
|
581
|
+
!projectMetaChanged &&
|
|
582
|
+
!projectEnvChanged
|
|
407
583
|
) {
|
|
408
584
|
return cachedConfig;
|
|
409
585
|
}
|
|
@@ -424,6 +600,9 @@ export function clearConfigCache(): void {
|
|
|
424
600
|
cachedConfigMtime = null;
|
|
425
601
|
cachedDefaultConfigMtime = null;
|
|
426
602
|
cachedGlobalConfigMtime = null;
|
|
603
|
+
cachedGlobalEnvMtime = null;
|
|
604
|
+
cachedProjectMetaMtime = null;
|
|
605
|
+
cachedProjectEnvMtime = null;
|
|
427
606
|
}
|
|
428
607
|
|
|
429
608
|
/**
|
|
@@ -61,11 +61,6 @@ const ENV_CONFIG: EnvValue[] = [
|
|
|
61
61
|
required: false,
|
|
62
62
|
sensitive: true,
|
|
63
63
|
},
|
|
64
|
-
{
|
|
65
|
-
key: 'GITHUB_REPO',
|
|
66
|
-
description: 'GitHubリポジトリ (例: owner/repo)',
|
|
67
|
-
required: false,
|
|
68
|
-
},
|
|
69
64
|
{
|
|
70
65
|
key: 'CONFLUENCE_PRD_SPACE',
|
|
71
66
|
description: 'Confluence PRDスペースキー',
|
|
@@ -435,7 +430,7 @@ export function generateEnvContent(values: Map<string, string>): string {
|
|
|
435
430
|
|
|
436
431
|
// GitHub設定
|
|
437
432
|
content += '\n# GitHub設定\n';
|
|
438
|
-
const githubKeys = ['GITHUB_ORG', 'GITHUB_TOKEN'
|
|
433
|
+
const githubKeys = ['GITHUB_ORG', 'GITHUB_TOKEN'];
|
|
439
434
|
for (const key of githubKeys) {
|
|
440
435
|
const value = values.get(key) || '';
|
|
441
436
|
content += `${key}=${value}\n`;
|
|
@@ -477,7 +472,7 @@ export function generateEnvContent(values: Map<string, string>): string {
|
|
|
477
472
|
export async function configureEnvInteractive(
|
|
478
473
|
existingValues?: Map<string, string>,
|
|
479
474
|
jiraKey?: string,
|
|
480
|
-
|
|
475
|
+
_repoUrl?: string,
|
|
481
476
|
): Promise<Map<string, string>> {
|
|
482
477
|
const rl = readline.createInterface({
|
|
483
478
|
input: process.stdin,
|
|
@@ -500,13 +495,6 @@ export async function configureEnvInteractive(
|
|
|
500
495
|
if (config.key === 'JIRA_PROJECT_KEYS' && jiraKey && !existingValue) {
|
|
501
496
|
existingValue = jiraKey;
|
|
502
497
|
}
|
|
503
|
-
if (config.key === 'GITHUB_REPO' && repoUrl && !existingValue) {
|
|
504
|
-
// URLからリポジトリ名を抽出 (例: https://github.com/owner/repo -> owner/repo)
|
|
505
|
-
const match = repoUrl.match(/github\.com[/:]([\w-]+\/[\w-]+)/);
|
|
506
|
-
if (match) {
|
|
507
|
-
existingValue = match[1];
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
498
|
|
|
511
499
|
// JIRA Issue Type ID設定時は、プロジェクトキーを渡す
|
|
512
500
|
const projectKey = jiraKey || existingValues?.get('JIRA_PROJECT_KEYS');
|
|
@@ -67,3 +67,30 @@ Team: ${meta.team.join(', ')}
|
|
|
67
67
|
`.trim();
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* GitHub リポジトリ情報を取得
|
|
72
|
+
* repository フィールドから owner/repo 形式を抽出
|
|
73
|
+
*
|
|
74
|
+
* @param projectRoot プロジェクトルートディレクトリ(デフォルト: カレントディレクトリ)
|
|
75
|
+
* @returns owner/repo 形式のリポジトリ情報
|
|
76
|
+
* @throws リポジトリ情報が見つからない、または無効な形式の場合
|
|
77
|
+
*/
|
|
78
|
+
export function getRepositoryInfo(projectRoot: string = process.cwd()): string {
|
|
79
|
+
const meta = loadProjectMeta(projectRoot);
|
|
80
|
+
|
|
81
|
+
if (!meta.repository) {
|
|
82
|
+
throw new Error('Repository information not found in project.json');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// URL形式から owner/repo を抽出
|
|
86
|
+
// 例: https://github.com/owner/repo.git -> owner/repo
|
|
87
|
+
// 例: git@github.com:owner/repo.git -> owner/repo
|
|
88
|
+
const match = meta.repository.match(/github\.com[:/]([\w-]+\/[\w-]+)(\.git)?/);
|
|
89
|
+
|
|
90
|
+
if (!match) {
|
|
91
|
+
throw new Error(`Invalid GitHub repository format: ${meta.repository}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return match[1];
|
|
95
|
+
}
|
|
96
|
+
|