@atlassian-dc-mcp/common 0.16.0 → 0.17.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 +11 -0
- package/build/__tests__/runtime-config.test.js +65 -14
- package/build/__tests__/runtime-config.test.js.map +1 -1
- package/build/__tests__/setup-cli.test.d.ts +2 -0
- package/build/__tests__/setup-cli.test.d.ts.map +1 -0
- package/build/__tests__/setup-cli.test.js +166 -0
- package/build/__tests__/setup-cli.test.js.map +1 -0
- package/build/config/__tests__/registry.test.d.ts +2 -0
- package/build/config/__tests__/registry.test.d.ts.map +1 -0
- package/build/config/__tests__/registry.test.js +105 -0
- package/build/config/__tests__/registry.test.js.map +1 -0
- package/build/config/__tests__/sources/env-file.test.d.ts +2 -0
- package/build/config/__tests__/sources/env-file.test.d.ts.map +1 -0
- package/build/config/__tests__/sources/env-file.test.js +93 -0
- package/build/config/__tests__/sources/env-file.test.js.map +1 -0
- package/build/config/__tests__/sources/home-file.test.d.ts +2 -0
- package/build/config/__tests__/sources/home-file.test.d.ts.map +1 -0
- package/build/config/__tests__/sources/home-file.test.js +114 -0
- package/build/config/__tests__/sources/home-file.test.js.map +1 -0
- package/build/config/__tests__/sources/macos-keychain.test.d.ts +2 -0
- package/build/config/__tests__/sources/macos-keychain.test.d.ts.map +1 -0
- package/build/config/__tests__/sources/macos-keychain.test.js +109 -0
- package/build/config/__tests__/sources/macos-keychain.test.js.map +1 -0
- package/build/config/__tests__/sources/process-env.test.d.ts +2 -0
- package/build/config/__tests__/sources/process-env.test.d.ts.map +1 -0
- package/build/config/__tests__/sources/process-env.test.js +51 -0
- package/build/config/__tests__/sources/process-env.test.js.map +1 -0
- package/build/config/index.d.ts +8 -0
- package/build/config/index.d.ts.map +1 -0
- package/build/config/index.js +8 -0
- package/build/config/index.js.map +1 -0
- package/build/config/registry.d.ts +26 -0
- package/build/config/registry.d.ts.map +1 -0
- package/build/config/registry.js +51 -0
- package/build/config/registry.js.map +1 -0
- package/build/config/runtime-config.d.ts +14 -0
- package/build/config/runtime-config.d.ts.map +1 -0
- package/build/config/runtime-config.js +41 -0
- package/build/config/runtime-config.js.map +1 -0
- package/build/config/source.d.ts +28 -0
- package/build/config/source.d.ts.map +1 -0
- package/build/config/source.js +11 -0
- package/build/config/source.js.map +1 -0
- package/build/config/sources/dotenv-file-cache.d.ts +12 -0
- package/build/config/sources/dotenv-file-cache.d.ts.map +1 -0
- package/build/config/sources/dotenv-file-cache.js +41 -0
- package/build/config/sources/dotenv-file-cache.js.map +1 -0
- package/build/config/sources/env-file.d.ts +16 -0
- package/build/config/sources/env-file.d.ts.map +1 -0
- package/build/config/sources/env-file.js +48 -0
- package/build/config/sources/env-file.js.map +1 -0
- package/build/config/sources/home-file.d.ts +19 -0
- package/build/config/sources/home-file.d.ts.map +1 -0
- package/build/config/sources/home-file.js +102 -0
- package/build/config/sources/home-file.js.map +1 -0
- package/build/config/sources/macos-keychain.d.ts +26 -0
- package/build/config/sources/macos-keychain.d.ts.map +1 -0
- package/build/config/sources/macos-keychain.js +88 -0
- package/build/config/sources/macos-keychain.js.map +1 -0
- package/build/config/sources/process-env.d.ts +8 -0
- package/build/config/sources/process-env.d.ts.map +1 -0
- package/build/config/sources/process-env.js +12 -0
- package/build/config/sources/process-env.js.map +1 -0
- package/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -1
- package/build/index.js.map +1 -1
- package/build/setup-cli.d.ts +25 -0
- package/build/setup-cli.d.ts.map +1 -0
- package/build/setup-cli.js +215 -0
- package/build/setup-cli.js.map +1 -0
- package/package.json +5 -4
- package/src/__tests__/runtime-config.test.ts +75 -27
- package/src/__tests__/setup-cli.test.ts +175 -0
- package/src/config/__tests__/registry.test.ts +118 -0
- package/src/config/__tests__/sources/env-file.test.ts +113 -0
- package/src/config/__tests__/sources/home-file.test.ts +129 -0
- package/src/config/__tests__/sources/macos-keychain.test.ts +136 -0
- package/src/config/__tests__/sources/process-env.test.ts +59 -0
- package/src/config/index.ts +7 -0
- package/src/config/registry.ts +79 -0
- package/src/config/runtime-config.ts +60 -0
- package/src/config/source.ts +37 -0
- package/src/config/sources/dotenv-file-cache.ts +54 -0
- package/src/config/sources/env-file.ts +61 -0
- package/src/config/sources/home-file.ts +121 -0
- package/src/config/sources/macos-keychain.ts +117 -0
- package/src/config/sources/process-env.ts +15 -0
- package/src/index.ts +2 -1
- package/src/setup-cli.ts +308 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/build/runtime-config.d.ts +0 -14
- package/build/runtime-config.d.ts.map +0 -1
- package/build/runtime-config.js +0 -123
- package/build/runtime-config.js.map +0 -1
- package/src/runtime-config.ts +0 -176
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-cli.js","sourceRoot":"","sources":["../src/setup-cli.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlassian-dc-mcp/common",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"main": "build/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
"dev": "nodemon --exec ts-node --esm src/index.ts"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@inquirer/prompts": "7.10.1",
|
|
29
30
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
30
|
-
"dotenv": "
|
|
31
|
-
"zod": "
|
|
31
|
+
"dotenv": "16.4.7",
|
|
32
|
+
"zod": "3.25.76"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/node": "^22.13.5",
|
|
@@ -38,5 +39,5 @@
|
|
|
38
39
|
"publishConfig": {
|
|
39
40
|
"access": "public"
|
|
40
41
|
},
|
|
41
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "3f2e386c4f08b0ff6781dde37ce6e54f6dcb8d51"
|
|
42
43
|
}
|
|
@@ -1,20 +1,66 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import type { ProductDefinition } from '../config/source.js';
|
|
5
|
+
|
|
6
|
+
const JIRA: ProductDefinition = {
|
|
7
|
+
id: 'jira',
|
|
8
|
+
envVars: {
|
|
9
|
+
host: 'JIRA_HOST',
|
|
10
|
+
apiBasePath: 'JIRA_API_BASE_PATH',
|
|
11
|
+
token: 'JIRA_API_TOKEN',
|
|
12
|
+
defaultPageSize: 'JIRA_DEFAULT_PAGE_SIZE',
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const CONFLUENCE: ProductDefinition = {
|
|
17
|
+
id: 'confluence',
|
|
18
|
+
envVars: {
|
|
19
|
+
host: 'CONFLUENCE_HOST',
|
|
20
|
+
apiBasePath: 'CONFLUENCE_API_BASE_PATH',
|
|
21
|
+
token: 'CONFLUENCE_API_TOKEN',
|
|
22
|
+
defaultPageSize: 'CONFLUENCE_DEFAULT_PAGE_SIZE',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const BITBUCKET: ProductDefinition = {
|
|
27
|
+
id: 'bitbucket',
|
|
28
|
+
envVars: {
|
|
29
|
+
host: 'BITBUCKET_HOST',
|
|
30
|
+
apiBasePath: 'BITBUCKET_API_BASE_PATH',
|
|
31
|
+
token: 'BITBUCKET_API_TOKEN',
|
|
32
|
+
defaultPageSize: 'BITBUCKET_DEFAULT_PAGE_SIZE',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe('runtime config facade', () => {
|
|
6
37
|
const originalEnv = process.env;
|
|
7
38
|
let tempDir: string;
|
|
39
|
+
let tempHome: string;
|
|
40
|
+
let homedirSpy: jest.SpyInstance;
|
|
8
41
|
|
|
9
42
|
beforeEach(() => {
|
|
10
43
|
jest.resetModules();
|
|
11
44
|
process.env = { ...originalEnv };
|
|
12
45
|
delete process.env.ATLASSIAN_DC_MCP_CONFIG_FILE;
|
|
46
|
+
delete process.env.JIRA_HOST;
|
|
47
|
+
delete process.env.JIRA_API_TOKEN;
|
|
48
|
+
delete process.env.JIRA_API_BASE_PATH;
|
|
49
|
+
delete process.env.JIRA_DEFAULT_PAGE_SIZE;
|
|
50
|
+
delete process.env.CONFLUENCE_HOST;
|
|
51
|
+
delete process.env.CONFLUENCE_API_TOKEN;
|
|
52
|
+
delete process.env.CONFLUENCE_DEFAULT_PAGE_SIZE;
|
|
53
|
+
delete process.env.BITBUCKET_HOST;
|
|
54
|
+
delete process.env.BITBUCKET_API_TOKEN;
|
|
13
55
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'atlassian-dc-mcp-config-'));
|
|
56
|
+
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'atlassian-dc-mcp-home-'));
|
|
57
|
+
homedirSpy = jest.spyOn(os, 'homedir').mockReturnValue(tempHome);
|
|
14
58
|
});
|
|
15
59
|
|
|
16
60
|
afterEach(() => {
|
|
61
|
+
homedirSpy.mockRestore();
|
|
17
62
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
63
|
+
fs.rmSync(tempHome, { recursive: true, force: true });
|
|
18
64
|
});
|
|
19
65
|
|
|
20
66
|
afterAll(() => {
|
|
@@ -26,11 +72,9 @@ describe('runtime config loader', () => {
|
|
|
26
72
|
fs.writeFileSync(sharedConfigPath, 'JIRA_HOST=file-host\nJIRA_API_TOKEN=file-token\n');
|
|
27
73
|
process.env.ATLASSIAN_DC_MCP_CONFIG_FILE = sharedConfigPath;
|
|
28
74
|
|
|
29
|
-
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../
|
|
30
|
-
|
|
75
|
+
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../index.js');
|
|
31
76
|
initializeRuntimeConfig({ cwd: tempDir });
|
|
32
|
-
|
|
33
|
-
expect(getProductRuntimeConfig('jira')).toEqual({
|
|
77
|
+
expect(getProductRuntimeConfig(JIRA)).toEqual({
|
|
34
78
|
host: 'file-host',
|
|
35
79
|
apiBasePath: undefined,
|
|
36
80
|
token: 'file-token',
|
|
@@ -40,9 +84,7 @@ describe('runtime config loader', () => {
|
|
|
40
84
|
|
|
41
85
|
it('throws when the explicit shared config file is missing', async () => {
|
|
42
86
|
process.env.ATLASSIAN_DC_MCP_CONFIG_FILE = path.join(tempDir, 'missing.env');
|
|
43
|
-
|
|
44
|
-
const { initializeRuntimeConfig } = await import('../runtime-config.js');
|
|
45
|
-
|
|
87
|
+
const { initializeRuntimeConfig } = await import('../index.js');
|
|
46
88
|
expect(() => initializeRuntimeConfig({ cwd: tempDir })).toThrow(
|
|
47
89
|
`ATLASSIAN_DC_MCP_CONFIG_FILE points to a missing file: ${path.join(tempDir, 'missing.env')}`,
|
|
48
90
|
);
|
|
@@ -50,26 +92,22 @@ describe('runtime config loader', () => {
|
|
|
50
92
|
|
|
51
93
|
it('requires an absolute shared config file path', async () => {
|
|
52
94
|
process.env.ATLASSIAN_DC_MCP_CONFIG_FILE = 'relative/shared.env';
|
|
53
|
-
|
|
54
|
-
const { initializeRuntimeConfig } = await import('../runtime-config.js');
|
|
55
|
-
|
|
95
|
+
const { initializeRuntimeConfig } = await import('../index.js');
|
|
56
96
|
expect(() => initializeRuntimeConfig({ cwd: tempDir })).toThrow(
|
|
57
97
|
'ATLASSIAN_DC_MCP_CONFIG_FILE must be an absolute path: relative/shared.env',
|
|
58
98
|
);
|
|
59
99
|
});
|
|
60
100
|
|
|
61
|
-
it('keeps
|
|
101
|
+
it('keeps env variables higher priority than file values', async () => {
|
|
62
102
|
const sharedConfigPath = path.join(tempDir, 'shared.env');
|
|
63
103
|
fs.writeFileSync(sharedConfigPath, 'JIRA_HOST=file-host\nJIRA_API_TOKEN=file-token\nJIRA_DEFAULT_PAGE_SIZE=50\n');
|
|
64
104
|
process.env.ATLASSIAN_DC_MCP_CONFIG_FILE = sharedConfigPath;
|
|
65
105
|
process.env.JIRA_API_TOKEN = 'env-token';
|
|
66
106
|
process.env.JIRA_DEFAULT_PAGE_SIZE = '10';
|
|
67
107
|
|
|
68
|
-
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../
|
|
69
|
-
|
|
108
|
+
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../index.js');
|
|
70
109
|
initializeRuntimeConfig({ cwd: tempDir });
|
|
71
|
-
|
|
72
|
-
expect(getProductRuntimeConfig('jira')).toEqual({
|
|
110
|
+
expect(getProductRuntimeConfig(JIRA)).toEqual({
|
|
73
111
|
host: 'file-host',
|
|
74
112
|
apiBasePath: undefined,
|
|
75
113
|
token: 'env-token',
|
|
@@ -77,17 +115,14 @@ describe('runtime config loader', () => {
|
|
|
77
115
|
});
|
|
78
116
|
});
|
|
79
117
|
|
|
80
|
-
it('falls back to
|
|
118
|
+
it('falls back to cwd/.env when no explicit shared file is configured', async () => {
|
|
81
119
|
fs.writeFileSync(
|
|
82
120
|
path.join(tempDir, '.env'),
|
|
83
121
|
'CONFLUENCE_HOST=cwd-host\nCONFLUENCE_API_TOKEN=cwd-token\nCONFLUENCE_DEFAULT_PAGE_SIZE=30\n',
|
|
84
122
|
);
|
|
85
|
-
|
|
86
|
-
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../runtime-config.js');
|
|
87
|
-
|
|
123
|
+
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../index.js');
|
|
88
124
|
initializeRuntimeConfig({ cwd: tempDir });
|
|
89
|
-
|
|
90
|
-
expect(getProductRuntimeConfig('confluence')).toEqual({
|
|
125
|
+
expect(getProductRuntimeConfig(CONFLUENCE)).toEqual({
|
|
91
126
|
host: 'cwd-host',
|
|
92
127
|
apiBasePath: undefined,
|
|
93
128
|
token: 'cwd-token',
|
|
@@ -102,15 +137,28 @@ describe('runtime config loader', () => {
|
|
|
102
137
|
fs.utimesSync(sharedConfigPath, initialTime, initialTime);
|
|
103
138
|
process.env.ATLASSIAN_DC_MCP_CONFIG_FILE = sharedConfigPath;
|
|
104
139
|
|
|
105
|
-
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../
|
|
106
|
-
|
|
140
|
+
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../index.js');
|
|
107
141
|
initializeRuntimeConfig({ cwd: tempDir });
|
|
108
|
-
expect(getProductRuntimeConfig(
|
|
142
|
+
expect(getProductRuntimeConfig(BITBUCKET).token).toBe('token-a');
|
|
109
143
|
|
|
110
144
|
fs.writeFileSync(sharedConfigPath, 'BITBUCKET_HOST=file-host\nBITBUCKET_API_TOKEN=token-b\n');
|
|
111
145
|
const updatedTime = new Date('2026-01-01T00:00:01.000Z');
|
|
112
146
|
fs.utimesSync(sharedConfigPath, updatedTime, updatedTime);
|
|
113
147
|
|
|
114
|
-
expect(getProductRuntimeConfig(
|
|
148
|
+
expect(getProductRuntimeConfig(BITBUCKET).token).toBe('token-b');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('reads the home file when env and env-file are empty', async () => {
|
|
152
|
+
fs.mkdirSync(path.join(tempHome, '.atlassian-dc-mcp'), { recursive: true });
|
|
153
|
+
fs.writeFileSync(
|
|
154
|
+
path.join(tempHome, '.atlassian-dc-mcp', 'jira.env'),
|
|
155
|
+
'JIRA_HOST=home-host\nJIRA_API_TOKEN=home-token\n',
|
|
156
|
+
{ mode: 0o600 },
|
|
157
|
+
);
|
|
158
|
+
const { getProductRuntimeConfig, initializeRuntimeConfig } = await import('../index.js');
|
|
159
|
+
initializeRuntimeConfig({ cwd: tempDir });
|
|
160
|
+
const config = getProductRuntimeConfig(JIRA);
|
|
161
|
+
expect(config.host).toBe('home-host');
|
|
162
|
+
expect(config.token).toBe('home-token');
|
|
115
163
|
});
|
|
116
164
|
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { DefaultConfigRegistry } from '../config/registry.js';
|
|
2
|
+
import { HomeFileSource } from '../config/sources/home-file.js';
|
|
3
|
+
import { MacosKeychainSource, type KeychainDeps } from '../config/sources/macos-keychain.js';
|
|
4
|
+
import { ProcessEnvSource } from '../config/sources/process-env.js';
|
|
5
|
+
import { runSetup, type SetupPrompts } from '../setup-cli.js';
|
|
6
|
+
import type { ConfigKey, ProductDefinition } from '../config/source.js';
|
|
7
|
+
|
|
8
|
+
const JIRA: ProductDefinition = {
|
|
9
|
+
id: 'jira',
|
|
10
|
+
envVars: {
|
|
11
|
+
host: 'JIRA_HOST',
|
|
12
|
+
apiBasePath: 'JIRA_API_BASE_PATH',
|
|
13
|
+
token: 'JIRA_API_TOKEN',
|
|
14
|
+
defaultPageSize: 'JIRA_DEFAULT_PAGE_SIZE',
|
|
15
|
+
},
|
|
16
|
+
defaultApiBasePath: '/rest/api/2',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
class FakeKeychain extends MacosKeychainSource {
|
|
20
|
+
available = true;
|
|
21
|
+
store: string | undefined;
|
|
22
|
+
writeCalls = 0;
|
|
23
|
+
clearCalls = 0;
|
|
24
|
+
constructor() {
|
|
25
|
+
const deps: Partial<KeychainDeps> = {
|
|
26
|
+
execFileSync: (() => '') as unknown as KeychainDeps['execFileSync'],
|
|
27
|
+
existsSync: () => true,
|
|
28
|
+
getPlatform: () => 'darwin' as NodeJS.Platform,
|
|
29
|
+
};
|
|
30
|
+
super(deps);
|
|
31
|
+
}
|
|
32
|
+
override isAvailable(): boolean {
|
|
33
|
+
return this.available;
|
|
34
|
+
}
|
|
35
|
+
override read(_p: ProductDefinition, key: ConfigKey): string | undefined {
|
|
36
|
+
return key === 'token' ? this.store : undefined;
|
|
37
|
+
}
|
|
38
|
+
override write(_p: ProductDefinition, _k: ConfigKey, v: string): void {
|
|
39
|
+
this.writeCalls++;
|
|
40
|
+
this.store = v;
|
|
41
|
+
}
|
|
42
|
+
override clear(_p: ProductDefinition, _k: ConfigKey): void {
|
|
43
|
+
this.clearCalls++;
|
|
44
|
+
this.store = undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class FakeHomeFile extends HomeFileSource {
|
|
49
|
+
values: Record<string, Partial<Record<ConfigKey, string>>> = {
|
|
50
|
+
jira: {},
|
|
51
|
+
confluence: {},
|
|
52
|
+
bitbucket: {},
|
|
53
|
+
};
|
|
54
|
+
writes: Array<[string, ConfigKey, string]> = [];
|
|
55
|
+
clears: Array<[string, ConfigKey]> = [];
|
|
56
|
+
override read(product: ProductDefinition, key: ConfigKey): string | undefined {
|
|
57
|
+
return this.values[product.id]?.[key];
|
|
58
|
+
}
|
|
59
|
+
override write(product: ProductDefinition, key: ConfigKey, value: string): void {
|
|
60
|
+
this.values[product.id] = this.values[product.id] ?? {};
|
|
61
|
+
this.values[product.id][key] = value;
|
|
62
|
+
this.writes.push([product.id, key, value]);
|
|
63
|
+
}
|
|
64
|
+
override clear(product: ProductDefinition, key: ConfigKey): void {
|
|
65
|
+
if (this.values[product.id]) {
|
|
66
|
+
delete this.values[product.id][key];
|
|
67
|
+
}
|
|
68
|
+
this.clears.push([product.id, key]);
|
|
69
|
+
}
|
|
70
|
+
override describeForProduct(product: ProductDefinition): string {
|
|
71
|
+
return `home-file-fake(${product.id})`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function makeRegistry(keychain: FakeKeychain, home: FakeHomeFile) {
|
|
76
|
+
return new DefaultConfigRegistry([new ProcessEnvSource(), home, keychain]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makePrompts(overrides: Partial<SetupPrompts> = {}): SetupPrompts {
|
|
80
|
+
return {
|
|
81
|
+
input: async (opts) => opts.default ?? '',
|
|
82
|
+
password: async () => '',
|
|
83
|
+
confirm: async (opts) => opts.default ?? false,
|
|
84
|
+
...overrides,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe('runSetup', () => {
|
|
89
|
+
const originalEnv = process.env;
|
|
90
|
+
let keychain: FakeKeychain;
|
|
91
|
+
let home: FakeHomeFile;
|
|
92
|
+
let logs: string[];
|
|
93
|
+
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
process.env = { ...originalEnv };
|
|
96
|
+
delete process.env.JIRA_API_TOKEN;
|
|
97
|
+
delete process.env.JIRA_HOST;
|
|
98
|
+
keychain = new FakeKeychain();
|
|
99
|
+
home = new FakeHomeFile();
|
|
100
|
+
logs = [];
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
afterAll(() => {
|
|
104
|
+
process.env = originalEnv;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('writes the token to keychain first on darwin and clears home file token', async () => {
|
|
108
|
+
const prompts = makePrompts({
|
|
109
|
+
input: async (opts) => {
|
|
110
|
+
if (opts.message.startsWith('Host')) return 'j-host';
|
|
111
|
+
if (opts.message.startsWith('API base path')) return '/rest/api/2';
|
|
112
|
+
return '25';
|
|
113
|
+
},
|
|
114
|
+
password: async () => 'secret',
|
|
115
|
+
});
|
|
116
|
+
const registry = makeRegistry(keychain, home);
|
|
117
|
+
await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: () => undefined, prompts });
|
|
118
|
+
|
|
119
|
+
expect(keychain.writeCalls).toBe(1);
|
|
120
|
+
expect(keychain.store).toBe('secret');
|
|
121
|
+
expect(home.clears).toContainEqual(['jira', 'token']);
|
|
122
|
+
expect(home.values.jira.host).toBe('j-host');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('falls back to home file when keychain is unavailable', async () => {
|
|
126
|
+
keychain.available = false;
|
|
127
|
+
const prompts = makePrompts({
|
|
128
|
+
input: async (opts) => {
|
|
129
|
+
if (opts.message.startsWith('Host')) return 'j-host';
|
|
130
|
+
if (opts.message.startsWith('API base path')) return '/rest/api/2';
|
|
131
|
+
return '25';
|
|
132
|
+
},
|
|
133
|
+
password: async () => 'secret',
|
|
134
|
+
});
|
|
135
|
+
const registry = makeRegistry(keychain, home);
|
|
136
|
+
await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: () => undefined, prompts });
|
|
137
|
+
|
|
138
|
+
expect(keychain.writeCalls).toBe(0);
|
|
139
|
+
const tokenWrites = home.writes.filter(([, k]) => k === 'token');
|
|
140
|
+
expect(tokenWrites).toHaveLength(1);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('prints shadowing warning when env var is set higher than target', async () => {
|
|
144
|
+
process.env.JIRA_API_TOKEN = 'shadow';
|
|
145
|
+
const prompts = makePrompts({
|
|
146
|
+
input: async (opts) => {
|
|
147
|
+
if (opts.message.startsWith('Host')) return 'j-host';
|
|
148
|
+
if (opts.message.startsWith('API base path')) return '/rest/api/2';
|
|
149
|
+
return '25';
|
|
150
|
+
},
|
|
151
|
+
password: async () => 'secret',
|
|
152
|
+
});
|
|
153
|
+
const registry = makeRegistry(keychain, home);
|
|
154
|
+
await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: () => undefined, prompts });
|
|
155
|
+
|
|
156
|
+
expect(logs.some((l) => l.includes('JIRA_API_TOKEN') && l.includes('Warning'))).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('exits 130 on SIGINT cancellation before any write', async () => {
|
|
160
|
+
const exitErr: Error & { name: string } = Object.assign(new Error('closed'), {
|
|
161
|
+
name: 'ExitPromptError',
|
|
162
|
+
});
|
|
163
|
+
const prompts = makePrompts({
|
|
164
|
+
input: async () => {
|
|
165
|
+
throw exitErr;
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
const exitFn = jest.fn();
|
|
169
|
+
const registry = makeRegistry(keychain, home);
|
|
170
|
+
await runSetup(JIRA, { registry, log: (m) => logs.push(m), exit: exitFn, prompts });
|
|
171
|
+
expect(exitFn).toHaveBeenCalledWith(130);
|
|
172
|
+
expect(keychain.writeCalls).toBe(0);
|
|
173
|
+
expect(home.writes).toHaveLength(0);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { DefaultConfigRegistry } from '../registry.js';
|
|
2
|
+
import type {
|
|
3
|
+
ConfigKey,
|
|
4
|
+
ProductDefinition,
|
|
5
|
+
ReadableSource,
|
|
6
|
+
SourceId,
|
|
7
|
+
WritableSource,
|
|
8
|
+
} from '../source.js';
|
|
9
|
+
|
|
10
|
+
const PRODUCT: ProductDefinition = {
|
|
11
|
+
id: 'jira',
|
|
12
|
+
envVars: {
|
|
13
|
+
host: 'JIRA_HOST',
|
|
14
|
+
apiBasePath: 'JIRA_API_BASE_PATH',
|
|
15
|
+
token: 'JIRA_API_TOKEN',
|
|
16
|
+
defaultPageSize: 'JIRA_DEFAULT_PAGE_SIZE',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class ReadOnlySource implements ReadableSource {
|
|
21
|
+
constructor(
|
|
22
|
+
public readonly id: SourceId,
|
|
23
|
+
public readonly priority: number,
|
|
24
|
+
public readonly values: Partial<Record<ConfigKey, string>> = {},
|
|
25
|
+
) {}
|
|
26
|
+
read(_p: ProductDefinition, key: ConfigKey): string | undefined {
|
|
27
|
+
return this.values[key];
|
|
28
|
+
}
|
|
29
|
+
describe(): string {
|
|
30
|
+
return this.id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class FakeWritableSource implements WritableSource {
|
|
35
|
+
calls: string[] = [];
|
|
36
|
+
available = true;
|
|
37
|
+
constructor(
|
|
38
|
+
public readonly id: SourceId,
|
|
39
|
+
public readonly priority: number,
|
|
40
|
+
) {}
|
|
41
|
+
read(): string | undefined {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
describe(): string {
|
|
45
|
+
return this.id;
|
|
46
|
+
}
|
|
47
|
+
isAvailable(): boolean {
|
|
48
|
+
return this.available;
|
|
49
|
+
}
|
|
50
|
+
write(_p: ProductDefinition, key: ConfigKey, value: string): void {
|
|
51
|
+
this.calls.push(`write:${key}:${value}`);
|
|
52
|
+
}
|
|
53
|
+
clear(_p: ProductDefinition, key: ConfigKey): void {
|
|
54
|
+
this.calls.push(`clear:${key}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe('DefaultConfigRegistry', () => {
|
|
59
|
+
it('sorts sources by priority descending', () => {
|
|
60
|
+
const a = new ReadOnlySource('home-file', 60);
|
|
61
|
+
const b = new ReadOnlySource('process-env', 100);
|
|
62
|
+
const c = new ReadOnlySource('env-file', 80);
|
|
63
|
+
const registry = new DefaultConfigRegistry([a, b, c]);
|
|
64
|
+
expect(registry.sources.map((s) => s.id)).toEqual(['process-env', 'env-file', 'home-file']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('resolve returns the first non-empty value in priority order', () => {
|
|
68
|
+
const envFile = new ReadOnlySource('env-file', 80, { token: 'file-token' });
|
|
69
|
+
const processEnv = new ReadOnlySource('process-env', 100, { token: 'env-token' });
|
|
70
|
+
const registry = new DefaultConfigRegistry([envFile, processEnv]);
|
|
71
|
+
const resolved = registry.resolve(PRODUCT, 'token');
|
|
72
|
+
expect(resolved.value).toBe('env-token');
|
|
73
|
+
expect(resolved.from?.sourceId).toBe('process-env');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('resolve falls through when a higher-priority source is empty', () => {
|
|
77
|
+
const envFile = new ReadOnlySource('env-file', 80, { token: 'file-token' });
|
|
78
|
+
const processEnv = new ReadOnlySource('process-env', 100, {});
|
|
79
|
+
const registry = new DefaultConfigRegistry([envFile, processEnv]);
|
|
80
|
+
expect(registry.resolve(PRODUCT, 'token').value).toBe('file-token');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('locate returns all sources with values, priority order', () => {
|
|
84
|
+
const home = new ReadOnlySource('home-file', 60, { token: 'home-token' });
|
|
85
|
+
const env = new ReadOnlySource('process-env', 100, { token: 'env-token' });
|
|
86
|
+
const envFile = new ReadOnlySource('env-file', 80, {});
|
|
87
|
+
const registry = new DefaultConfigRegistry([home, env, envFile]);
|
|
88
|
+
const locs = registry.locate(PRODUCT, 'token');
|
|
89
|
+
expect(locs.map((l) => l.sourceId)).toEqual(['process-env', 'home-file']);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('getWritableSource returns the first source matching the predicate', () => {
|
|
93
|
+
const a = new FakeWritableSource('home-file', 60);
|
|
94
|
+
const b = new FakeWritableSource('macos-keychain', 40);
|
|
95
|
+
const registry = new DefaultConfigRegistry([a, b]);
|
|
96
|
+
const found = registry.getWritableSource(
|
|
97
|
+
(s): s is FakeWritableSource => s instanceof FakeWritableSource && s.id === 'macos-keychain',
|
|
98
|
+
);
|
|
99
|
+
expect(found).toBe(b);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('calls initialize on all sources when registry.initialize runs', () => {
|
|
103
|
+
const calls: string[] = [];
|
|
104
|
+
const makeSource = (id: SourceId, priority: number) =>
|
|
105
|
+
({
|
|
106
|
+
id,
|
|
107
|
+
priority,
|
|
108
|
+
read: () => undefined,
|
|
109
|
+
describe: () => id,
|
|
110
|
+
initialize: (opts?: { cwd?: string }) => {
|
|
111
|
+
calls.push(`${id}:${opts?.cwd ?? ''}`);
|
|
112
|
+
},
|
|
113
|
+
}) satisfies ReadableSource;
|
|
114
|
+
const registry = new DefaultConfigRegistry([makeSource('env-file', 80), makeSource('process-env', 100)]);
|
|
115
|
+
registry.initialize({ cwd: '/tmp' });
|
|
116
|
+
expect(calls.sort()).toEqual(['env-file:/tmp', 'process-env:/tmp']);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { EnvFileSource, ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR } from '../../sources/env-file.js';
|
|
5
|
+
import type { ProductDefinition } from '../../source.js';
|
|
6
|
+
|
|
7
|
+
const JIRA: ProductDefinition = {
|
|
8
|
+
id: 'jira',
|
|
9
|
+
envVars: {
|
|
10
|
+
host: 'JIRA_HOST',
|
|
11
|
+
apiBasePath: 'JIRA_API_BASE_PATH',
|
|
12
|
+
token: 'JIRA_API_TOKEN',
|
|
13
|
+
defaultPageSize: 'JIRA_DEFAULT_PAGE_SIZE',
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const BITBUCKET: ProductDefinition = {
|
|
18
|
+
id: 'bitbucket',
|
|
19
|
+
envVars: {
|
|
20
|
+
host: 'BITBUCKET_HOST',
|
|
21
|
+
apiBasePath: 'BITBUCKET_API_BASE_PATH',
|
|
22
|
+
token: 'BITBUCKET_API_TOKEN',
|
|
23
|
+
defaultPageSize: 'BITBUCKET_DEFAULT_PAGE_SIZE',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('EnvFileSource', () => {
|
|
28
|
+
const originalEnv = process.env;
|
|
29
|
+
let tempDir: string;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
process.env = { ...originalEnv };
|
|
33
|
+
delete process.env[ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR];
|
|
34
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'env-file-src-'));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterAll(() => {
|
|
42
|
+
process.env = originalEnv;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('reads an explicit shared config file', () => {
|
|
46
|
+
const filePath = path.join(tempDir, 'shared.env');
|
|
47
|
+
fs.writeFileSync(filePath, 'JIRA_HOST=file-host\nJIRA_API_TOKEN=file-token\n');
|
|
48
|
+
process.env[ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR] = filePath;
|
|
49
|
+
const source = new EnvFileSource();
|
|
50
|
+
source.initialize({ cwd: tempDir });
|
|
51
|
+
expect(source.read(JIRA, 'host')).toBe('file-host');
|
|
52
|
+
expect(source.read(JIRA, 'token')).toBe('file-token');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('reads cwd/.env when no explicit file is configured', () => {
|
|
56
|
+
fs.writeFileSync(path.join(tempDir, '.env'), 'BITBUCKET_HOST=cwd-host\n');
|
|
57
|
+
const source = new EnvFileSource();
|
|
58
|
+
source.initialize({ cwd: tempDir });
|
|
59
|
+
expect(source.read(BITBUCKET, 'host')).toBe('cwd-host');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('throws when explicit file is relative', () => {
|
|
63
|
+
process.env[ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR] = 'relative/shared.env';
|
|
64
|
+
const source = new EnvFileSource();
|
|
65
|
+
expect(() => source.initialize({ cwd: tempDir })).toThrow(/must be an absolute path/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('throws when explicit file is missing', () => {
|
|
69
|
+
const missing = path.join(tempDir, 'missing.env');
|
|
70
|
+
process.env[ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR] = missing;
|
|
71
|
+
const source = new EnvFileSource();
|
|
72
|
+
expect(() => source.initialize({ cwd: tempDir })).toThrow(
|
|
73
|
+
`${ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR} points to a missing file: ${missing}`,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('revalidates cache when mtime changes', () => {
|
|
78
|
+
const filePath = path.join(tempDir, 'shared.env');
|
|
79
|
+
fs.writeFileSync(filePath, 'JIRA_API_TOKEN=token-a\n');
|
|
80
|
+
const initial = new Date('2026-01-01T00:00:00.000Z');
|
|
81
|
+
fs.utimesSync(filePath, initial, initial);
|
|
82
|
+
process.env[ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR] = filePath;
|
|
83
|
+
|
|
84
|
+
const source = new EnvFileSource();
|
|
85
|
+
source.initialize({ cwd: tempDir });
|
|
86
|
+
expect(source.read(JIRA, 'token')).toBe('token-a');
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(filePath, 'JIRA_API_TOKEN=token-b\n');
|
|
89
|
+
const updated = new Date('2026-01-01T00:00:01.000Z');
|
|
90
|
+
fs.utimesSync(filePath, updated, updated);
|
|
91
|
+
expect(source.read(JIRA, 'token')).toBe('token-b');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('returns empty object when cwd/.env is missing', () => {
|
|
95
|
+
const source = new EnvFileSource();
|
|
96
|
+
source.initialize({ cwd: tempDir });
|
|
97
|
+
expect(source.read(JIRA, 'host')).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('evicts cache when an explicit file disappears after a successful read', () => {
|
|
101
|
+
const filePath = path.join(tempDir, 'shared.env');
|
|
102
|
+
fs.writeFileSync(filePath, 'JIRA_API_TOKEN=token-a\n');
|
|
103
|
+
process.env[ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR] = filePath;
|
|
104
|
+
const source = new EnvFileSource();
|
|
105
|
+
source.initialize({ cwd: tempDir });
|
|
106
|
+
expect(source.read(JIRA, 'token')).toBe('token-a');
|
|
107
|
+
|
|
108
|
+
fs.unlinkSync(filePath);
|
|
109
|
+
expect(() => source.read(JIRA, 'token')).toThrow(
|
|
110
|
+
`${ATLASSIAN_DC_MCP_CONFIG_FILE_ENV_VAR} points to a missing file: ${filePath}`,
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
});
|