refactor: deprecated config マイグレーション処理を削除
log_level / observability の後方互換マイグレーションを完全削除。 logging キーに一本化し、レガシー変換コード・テストを除去。
This commit is contained in:
parent
cb3bc5e45e
commit
1cfae9f53b
@ -580,184 +580,6 @@ describe('loadGlobalConfig', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('deprecated migration: observability → logging', () => {
|
||||
it('should migrate observability.provider_events to logging.providerEvents', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'observability:',
|
||||
' provider_events: true',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
try {
|
||||
const config = loadGlobalConfig();
|
||||
expect(config.logging?.providerEvents).toBe(true);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('observability'),
|
||||
);
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should not overwrite explicit logging.provider_events with observability value', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'logging:',
|
||||
' provider_events: false',
|
||||
'observability:',
|
||||
' provider_events: true',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
try {
|
||||
const config = loadGlobalConfig();
|
||||
expect(config.logging?.providerEvents).toBe(false);
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should emit deprecation warning when observability is present', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'observability:',
|
||||
' provider_events: false',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
try {
|
||||
loadGlobalConfig();
|
||||
expect(warnSpy).toHaveBeenCalledTimes(1);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Deprecated'),
|
||||
);
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('deprecated migration: log_level → logging.level', () => {
|
||||
it('should migrate log_level to logging.level', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'log_level: warn',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
try {
|
||||
const config = loadGlobalConfig();
|
||||
expect(config.logging?.level).toBe('warn');
|
||||
expect(warnSpy).toHaveBeenCalled();
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should prefer logging.level over legacy log_level', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'logging:',
|
||||
' level: info',
|
||||
'log_level: warn',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
try {
|
||||
const config = loadGlobalConfig();
|
||||
expect(config.logging?.level).toBe('info');
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('logging.level → logLevel fallback', () => {
|
||||
it('should use logging.level as logLevel fallback when legacy log_level is absent', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'logging:',
|
||||
' level: warn',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
invalidateGlobalConfigCache();
|
||||
const fallback = loadGlobalMigratedProjectLocalFallback();
|
||||
expect(fallback.logLevel).toBe('warn');
|
||||
});
|
||||
|
||||
it('should prefer logging.level over legacy log_level', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'log_level: debug',
|
||||
'logging:',
|
||||
' level: warn',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
invalidateGlobalConfigCache();
|
||||
const fallback = loadGlobalMigratedProjectLocalFallback();
|
||||
expect(fallback.logLevel).toBe('warn');
|
||||
});
|
||||
|
||||
it('should fall back to legacy log_level when logging.level is absent', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
mkdirSync(taktDir, { recursive: true });
|
||||
writeFileSync(
|
||||
getGlobalConfigPath(),
|
||||
[
|
||||
'language: en',
|
||||
'log_level: debug',
|
||||
].join('\n'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
invalidateGlobalConfigCache();
|
||||
const fallback = loadGlobalMigratedProjectLocalFallback();
|
||||
expect(fallback.logLevel).toBe('debug');
|
||||
});
|
||||
});
|
||||
|
||||
it('should save and reload notification_sound_events config', () => {
|
||||
const taktDir = join(testHomeDir, '.takt');
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { migrateDeprecatedGlobalConfigKeys } from '../infra/config/global/globalConfigLegacyMigration.js';
|
||||
|
||||
describe('migrateDeprecatedGlobalConfigKeys', () => {
|
||||
it('should return migrated config without mutating input object', () => {
|
||||
const rawConfig: Record<string, unknown> = {
|
||||
log_level: 'warn',
|
||||
observability: {
|
||||
provider_events: true,
|
||||
},
|
||||
};
|
||||
|
||||
const originalSnapshot = JSON.parse(JSON.stringify(rawConfig)) as Record<string, unknown>;
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
try {
|
||||
const migrated = migrateDeprecatedGlobalConfigKeys(rawConfig);
|
||||
expect(migrated.migratedLogLevel).toBe('warn');
|
||||
expect(migrated.migratedConfig).toEqual({
|
||||
logging: {
|
||||
level: 'warn',
|
||||
provider_events: true,
|
||||
},
|
||||
});
|
||||
expect(rawConfig).toEqual(originalSnapshot);
|
||||
} finally {
|
||||
warnSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -19,10 +19,7 @@ import {
|
||||
removeMigratedProjectLocalKeys,
|
||||
type GlobalMigratedProjectLocalFallback,
|
||||
} from './globalMigratedProjectLocalFallback.js';
|
||||
import {
|
||||
sanitizeConfigValue,
|
||||
migrateDeprecatedGlobalConfigKeys,
|
||||
} from './globalConfigLegacyMigration.js';
|
||||
import { sanitizeConfigValue } from './globalConfigLegacyMigration.js';
|
||||
import { serializeGlobalConfig } from './globalConfigSerializer.js';
|
||||
export { validateCliPath } from './cliPathValidator.js';
|
||||
|
||||
@ -81,12 +78,8 @@ export class GlobalConfigManager {
|
||||
}
|
||||
|
||||
applyGlobalConfigEnvOverrides(rawConfig);
|
||||
const { migratedConfig, migratedLogLevel } = migrateDeprecatedGlobalConfigKeys(rawConfig);
|
||||
const migratedProjectLocalFallback = extractMigratedProjectLocalFallback({
|
||||
...migratedConfig,
|
||||
...(migratedLogLevel !== undefined ? { log_level: migratedLogLevel } : {}),
|
||||
});
|
||||
const schemaInput = { ...migratedConfig };
|
||||
const migratedProjectLocalFallback = extractMigratedProjectLocalFallback(rawConfig);
|
||||
const schemaInput = { ...rawConfig };
|
||||
removeMigratedProjectLocalKeys(schemaInput);
|
||||
|
||||
const parsed = GlobalConfigSchema.parse(schemaInput);
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
function getRecord(value: unknown): Record<string, unknown> | undefined {
|
||||
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
const FORBIDDEN_CONFIG_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
||||
|
||||
export function sanitizeConfigValue(value: unknown, path: string): unknown {
|
||||
@ -12,10 +5,10 @@ export function sanitizeConfigValue(value: unknown, path: string): unknown {
|
||||
return value.map((item, index) => sanitizeConfigValue(item, `${path}[${index}]`));
|
||||
}
|
||||
|
||||
const record = getRecord(value);
|
||||
if (!record) {
|
||||
if (typeof value !== 'object' || value === null) {
|
||||
return value;
|
||||
}
|
||||
const record = value as Record<string, unknown>;
|
||||
|
||||
const sanitized: Record<string, unknown> = {};
|
||||
for (const [key, nestedValue] of Object.entries(record)) {
|
||||
@ -27,73 +20,3 @@ export function sanitizeConfigValue(value: unknown, path: string): unknown {
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
type LegacyGlobalConfigMigrationResult = {
|
||||
migratedConfig: Record<string, unknown>;
|
||||
migratedLogLevel?: string;
|
||||
};
|
||||
|
||||
export function migrateDeprecatedGlobalConfigKeys(rawConfig: Record<string, unknown>): LegacyGlobalConfigMigrationResult {
|
||||
const migratedConfig: Record<string, unknown> = { ...rawConfig };
|
||||
const hasLegacyLogLevel = Object.prototype.hasOwnProperty.call(rawConfig, 'log_level');
|
||||
const legacyLogLevel = rawConfig.log_level;
|
||||
const hasLegacyObservability = Object.prototype.hasOwnProperty.call(rawConfig, 'observability');
|
||||
const observability = getRecord(rawConfig.observability);
|
||||
const initialLogging = getRecord(rawConfig.logging);
|
||||
let migratedLogging = initialLogging ? { ...initialLogging } : undefined;
|
||||
|
||||
if (hasLegacyObservability) {
|
||||
console.warn('Deprecated: "observability" is deprecated. Use "logging" instead.');
|
||||
if (observability) {
|
||||
const observabilityProviderEvents = observability.provider_events;
|
||||
if (observabilityProviderEvents !== undefined) {
|
||||
const hasExplicitProviderEvents = migratedLogging
|
||||
? Object.prototype.hasOwnProperty.call(migratedLogging, 'provider_events')
|
||||
: false;
|
||||
if (!hasExplicitProviderEvents) {
|
||||
migratedLogging = {
|
||||
...(migratedLogging ?? {}),
|
||||
provider_events: observabilityProviderEvents,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLegacyLogLevel) {
|
||||
console.warn('Deprecated: "log_level" is deprecated. Use "logging.level" instead.');
|
||||
}
|
||||
|
||||
const resolvedLoggingLevel = migratedLogging?.level;
|
||||
const migratedLogLevel = typeof resolvedLoggingLevel === 'string'
|
||||
? resolvedLoggingLevel
|
||||
: hasLegacyLogLevel && typeof legacyLogLevel === 'string'
|
||||
? legacyLogLevel
|
||||
: undefined;
|
||||
|
||||
if (migratedLogLevel !== undefined) {
|
||||
const hasExplicitLevel = migratedLogging
|
||||
? Object.prototype.hasOwnProperty.call(migratedLogging, 'level')
|
||||
: false;
|
||||
if (!hasExplicitLevel) {
|
||||
migratedLogging = {
|
||||
...(migratedLogging ?? {}),
|
||||
level: migratedLogLevel,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (migratedLogging) {
|
||||
migratedConfig.logging = migratedLogging;
|
||||
}
|
||||
|
||||
if (hasLegacyObservability) {
|
||||
delete migratedConfig.observability;
|
||||
}
|
||||
if (hasLegacyLogLevel) {
|
||||
delete migratedConfig.log_level;
|
||||
}
|
||||
|
||||
return {
|
||||
migratedConfig,
|
||||
migratedLogLevel,
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user