@sk8metal/michi-cli 0.8.2 → 0.8.3

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/scripts/confluence-sync.js +2 -2
  3. package/dist/scripts/confluence-sync.js.map +1 -1
  4. package/dist/scripts/jira-sync.js +2 -2
  5. package/dist/scripts/jira-sync.js.map +1 -1
  6. package/dist/scripts/multi-project-estimate.js +2 -2
  7. package/dist/scripts/multi-project-estimate.js.map +1 -1
  8. package/dist/scripts/pr-automation.js +2 -2
  9. package/dist/scripts/pr-automation.js.map +1 -1
  10. package/dist/scripts/pre-flight-check.js +2 -2
  11. package/dist/scripts/pre-flight-check.js.map +1 -1
  12. package/dist/scripts/resource-dashboard.js +2 -2
  13. package/dist/scripts/resource-dashboard.js.map +1 -1
  14. package/dist/scripts/spec-impl-workflow.js +2 -2
  15. package/dist/scripts/spec-impl-workflow.js.map +1 -1
  16. package/dist/scripts/template/multi-repo-renderer.d.ts +1 -1
  17. package/dist/scripts/template/multi-repo-renderer.d.ts.map +1 -1
  18. package/dist/scripts/template/multi-repo-renderer.js +8 -3
  19. package/dist/scripts/template/multi-repo-renderer.js.map +1 -1
  20. package/dist/scripts/test-workflow-stages.js +2 -2
  21. package/dist/scripts/test-workflow-stages.js.map +1 -1
  22. package/dist/scripts/utils/config-loader.d.ts.map +1 -1
  23. package/dist/scripts/utils/config-loader.js +3 -2
  24. package/dist/scripts/utils/config-loader.js.map +1 -1
  25. package/dist/scripts/utils/env-loader.d.ts +11 -0
  26. package/dist/scripts/utils/env-loader.d.ts.map +1 -0
  27. package/dist/scripts/utils/env-loader.js +23 -0
  28. package/dist/scripts/utils/env-loader.js.map +1 -0
  29. package/dist/scripts/workflow-orchestrator.js +2 -2
  30. package/dist/scripts/workflow-orchestrator.js.map +1 -1
  31. package/dist/src/cli.js +3 -3
  32. package/dist/src/cli.js.map +1 -1
  33. package/package.json +1 -1
  34. package/scripts/confluence-sync.ts +2 -2
  35. package/scripts/jira-sync.ts +2 -2
  36. package/scripts/multi-project-estimate.ts +2 -2
  37. package/scripts/pr-automation.ts +2 -2
  38. package/scripts/pre-flight-check.ts +2 -2
  39. package/scripts/resource-dashboard.ts +2 -2
  40. package/scripts/spec-impl-workflow.ts +2 -2
  41. package/scripts/template/__tests__/multi-repo-renderer.test.ts +15 -10
  42. package/scripts/template/multi-repo-renderer.ts +9 -3
  43. package/scripts/test-workflow-stages.ts +2 -2
  44. package/scripts/utils/__tests__/env-loader.test.ts +145 -0
  45. package/scripts/utils/config-loader.ts +3 -2
  46. package/scripts/utils/env-loader.ts +25 -0
  47. package/scripts/workflow-orchestrator.ts +2 -2
@@ -3,10 +3,10 @@
3
3
  * testとreleaseステージのみを実行
4
4
  */
5
5
 
6
- import { config } from 'dotenv';
6
+ import { loadEnv } from './utils/env-loader.js';
7
7
  import { WorkflowOrchestrator, WorkflowConfig } from './workflow-orchestrator.js';
8
8
 
9
- config();
9
+ loadEnv();
10
10
 
11
11
  async function main() {
12
12
  const args = process.argv.slice(2);
@@ -0,0 +1,145 @@
1
+ /**
2
+ * env-loader.ts のユニットテスト
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { writeFileSync, existsSync, mkdirSync, rmSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { config } from 'dotenv';
9
+
10
+ describe('env-loader', () => {
11
+ const testDir = join(process.cwd(), 'test-temp-env-loader');
12
+ const testGlobalDir = join(testDir, '.michi');
13
+ const testGlobalEnvPath = join(testGlobalDir, '.env');
14
+ const testLocalEnvPath = join(testDir, '.env');
15
+
16
+ // 元の環境変数を保存
17
+ const originalEnv = { ...process.env };
18
+ const originalHome = process.env.HOME;
19
+ const originalCwd = process.cwd();
20
+
21
+ beforeEach(() => {
22
+ // テスト用ディレクトリ作成
23
+ if (!existsSync(testDir)) {
24
+ mkdirSync(testDir, { recursive: true });
25
+ }
26
+ if (!existsSync(testGlobalDir)) {
27
+ mkdirSync(testGlobalDir, { recursive: true });
28
+ }
29
+
30
+ // カレントディレクトリをテストディレクトリに変更
31
+ process.chdir(testDir);
32
+
33
+ // 環境変数をクリア
34
+ for (const key in process.env) {
35
+ if (key.startsWith('TEST_')) {
36
+ delete process.env[key];
37
+ }
38
+ }
39
+ });
40
+
41
+ afterEach(() => {
42
+ // カレントディレクトリを戻す
43
+ process.chdir(originalCwd);
44
+
45
+ // クリーンアップ
46
+ if (existsSync(testDir)) {
47
+ rmSync(testDir, { recursive: true, force: true });
48
+ }
49
+
50
+ // 環境変数を復元
51
+ process.env = { ...originalEnv };
52
+ if (originalHome) {
53
+ process.env.HOME = originalHome;
54
+ }
55
+ });
56
+
57
+ it('should load global .env if exists', () => {
58
+ // グローバル .env を作成
59
+ const globalContent = `TEST_GLOBAL_VAR=global_value
60
+ TEST_SHARED_VAR=global_shared`;
61
+ writeFileSync(testGlobalEnvPath, globalContent, 'utf-8');
62
+
63
+ // HOMEを一時的に変更
64
+ process.env.HOME = testDir;
65
+
66
+ // dotenvで読み込みをシミュレート(env-loader.tsと同じロジック)
67
+ if (existsSync(testGlobalEnvPath)) {
68
+ config({ path: testGlobalEnvPath });
69
+ }
70
+
71
+ expect(process.env.TEST_GLOBAL_VAR).toBe('global_value');
72
+ expect(process.env.TEST_SHARED_VAR).toBe('global_shared');
73
+ });
74
+
75
+ it('should load local .env if exists', () => {
76
+ // ローカル .env を作成
77
+ const localContent = `TEST_LOCAL_VAR=local_value
78
+ TEST_SHARED_VAR=local_shared`;
79
+ writeFileSync(testLocalEnvPath, localContent, 'utf-8');
80
+
81
+ // dotenvでローカル.envを読み込み
82
+ config();
83
+
84
+ expect(process.env.TEST_LOCAL_VAR).toBe('local_value');
85
+ expect(process.env.TEST_SHARED_VAR).toBe('local_shared');
86
+ });
87
+
88
+ it('should override global with local', () => {
89
+ // グローバル .env を作成
90
+ const globalContent = `TEST_GLOBAL_VAR=global_value
91
+ TEST_SHARED_VAR=global_shared`;
92
+ writeFileSync(testGlobalEnvPath, globalContent, 'utf-8');
93
+
94
+ // ローカル .env を作成
95
+ const localContent = `TEST_LOCAL_VAR=local_value
96
+ TEST_SHARED_VAR=local_shared`;
97
+ writeFileSync(testLocalEnvPath, localContent, 'utf-8');
98
+
99
+ // HOMEを一時的に変更
100
+ process.env.HOME = testDir;
101
+
102
+ // env-loader.tsと同じロジックで読み込み
103
+ if (existsSync(testGlobalEnvPath)) {
104
+ config({ path: testGlobalEnvPath });
105
+ }
106
+ config({ override: true }); // ローカル .env(グローバルを上書き)
107
+
108
+ // ローカルがグローバルを上書きする
109
+ expect(process.env.TEST_GLOBAL_VAR).toBe('global_value');
110
+ expect(process.env.TEST_LOCAL_VAR).toBe('local_value');
111
+ expect(process.env.TEST_SHARED_VAR).toBe('local_shared'); // ローカルで上書き
112
+ });
113
+
114
+ it('should handle missing files gracefully', () => {
115
+ // 両方のファイルを作成しない
116
+ process.env.HOME = join(testDir, 'nonexistent');
117
+
118
+ // エラーを投げないことを確認
119
+ expect(() => {
120
+ if (existsSync(join(process.env.HOME!, '.michi', '.env'))) {
121
+ config({ path: join(process.env.HOME!, '.michi', '.env') });
122
+ }
123
+ config();
124
+ }).not.toThrow();
125
+ });
126
+
127
+ it('should not fail if global directory does not exist', () => {
128
+ // ローカル .env のみ作成
129
+ const localContent = `TEST_LOCAL_VAR=local_value`;
130
+ writeFileSync(testLocalEnvPath, localContent, 'utf-8');
131
+
132
+ // .michiディレクトリが存在しないHOMEを設定
133
+ process.env.HOME = join(testDir, 'no-michi-dir');
134
+
135
+ expect(() => {
136
+ const globalPath = join(process.env.HOME!, '.michi', '.env');
137
+ if (existsSync(globalPath)) {
138
+ config({ path: globalPath });
139
+ }
140
+ config();
141
+ }).not.toThrow();
142
+
143
+ expect(process.env.TEST_LOCAL_VAR).toBe('local_value');
144
+ });
145
+ });
@@ -7,7 +7,8 @@ import { readFileSync, writeFileSync, existsSync, statSync, renameSync, unlinkSy
7
7
  import { resolve, relative, isAbsolute, dirname } from 'path';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { homedir } from 'os';
10
- import { config, parse as dotenvParse } from 'dotenv';
10
+ import { parse as dotenvParse } from 'dotenv';
11
+ import { loadEnv } from './env-loader.js';
11
12
  import {
12
13
  AppConfigSchema,
13
14
  MultiRepoProjectSchema,
@@ -18,7 +19,7 @@ import {
18
19
  } from '../config/config-schema.js';
19
20
 
20
21
  // 環境変数読み込み
21
- config();
22
+ loadEnv();
22
23
 
23
24
  /**
24
25
  * グローバル設定ファイルのパス定数
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 環境変数読み込みユーティリティ
3
+ * グローバル設定(~/.michi/.env)とローカル設定(.env)を読み込む
4
+ */
5
+
6
+ import { config } from 'dotenv';
7
+ import { existsSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { homedir } from 'os';
10
+
11
+ /**
12
+ * 環境変数を読み込む
13
+ * グローバル設定(~/.michi/.env)→ローカル設定(.env)の順で読み込み
14
+ * ローカル設定がグローバル設定を上書きする
15
+ */
16
+ export function loadEnv(): void {
17
+ // 1. グローバル設定 ~/.michi/.env を読み込む
18
+ const globalEnvPath = join(homedir(), '.michi', '.env');
19
+ if (existsSync(globalEnvPath)) {
20
+ config({ path: globalEnvPath });
21
+ }
22
+
23
+ // 2. ローカル設定 .env を読み込む(グローバル設定を上書き)
24
+ config({ override: true });
25
+ }
@@ -3,7 +3,7 @@
3
3
  * AI開発フロー全体を統合実行
4
4
  */
5
5
 
6
- import { config } from 'dotenv';
6
+ import { loadEnv } from './utils/env-loader.js';
7
7
  import { loadProjectMeta } from './utils/project-meta.js';
8
8
  import { syncToConfluence, getConfluenceConfig } from './confluence-sync.js';
9
9
  import { syncTasksToJIRA } from './jira-sync.js';
@@ -12,7 +12,7 @@ import { executeTests, generateTestReport } from './utils/test-runner.js';
12
12
  import { createReleaseNotes } from './utils/release-notes-generator.js';
13
13
  import { pollForApproval, waitForManualApproval } from './utils/confluence-approval.js';
14
14
 
15
- config();
15
+ loadEnv();
16
16
 
17
17
  export interface WorkflowConfig {
18
18
  feature: string;