refactor: deprecated config マイグレーション処理を削除

log_level / observability の後方互換マイグレーションを完全削除。
logging キーに一本化し、レガシー変換コード・テストを除去。
This commit is contained in:
nrslib 2026-03-04 21:04:40 +09:00
parent cb3bc5e45e
commit 1cfae9f53b
4 changed files with 5 additions and 297 deletions

View File

@ -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', () => { it('should save and reload notification_sound_events config', () => {
const taktDir = join(testHomeDir, '.takt'); const taktDir = join(testHomeDir, '.takt');

View File

@ -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();
}
});
});

View File

@ -19,10 +19,7 @@ import {
removeMigratedProjectLocalKeys, removeMigratedProjectLocalKeys,
type GlobalMigratedProjectLocalFallback, type GlobalMigratedProjectLocalFallback,
} from './globalMigratedProjectLocalFallback.js'; } from './globalMigratedProjectLocalFallback.js';
import { import { sanitizeConfigValue } from './globalConfigLegacyMigration.js';
sanitizeConfigValue,
migrateDeprecatedGlobalConfigKeys,
} from './globalConfigLegacyMigration.js';
import { serializeGlobalConfig } from './globalConfigSerializer.js'; import { serializeGlobalConfig } from './globalConfigSerializer.js';
export { validateCliPath } from './cliPathValidator.js'; export { validateCliPath } from './cliPathValidator.js';
@ -81,12 +78,8 @@ export class GlobalConfigManager {
} }
applyGlobalConfigEnvOverrides(rawConfig); applyGlobalConfigEnvOverrides(rawConfig);
const { migratedConfig, migratedLogLevel } = migrateDeprecatedGlobalConfigKeys(rawConfig); const migratedProjectLocalFallback = extractMigratedProjectLocalFallback(rawConfig);
const migratedProjectLocalFallback = extractMigratedProjectLocalFallback({ const schemaInput = { ...rawConfig };
...migratedConfig,
...(migratedLogLevel !== undefined ? { log_level: migratedLogLevel } : {}),
});
const schemaInput = { ...migratedConfig };
removeMigratedProjectLocalKeys(schemaInput); removeMigratedProjectLocalKeys(schemaInput);
const parsed = GlobalConfigSchema.parse(schemaInput); const parsed = GlobalConfigSchema.parse(schemaInput);

View File

@ -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']); const FORBIDDEN_CONFIG_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
export function sanitizeConfigValue(value: unknown, path: string): unknown { 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}]`)); return value.map((item, index) => sanitizeConfigValue(item, `${path}[${index}]`));
} }
const record = getRecord(value); if (typeof value !== 'object' || value === null) {
if (!record) {
return value; return value;
} }
const record = value as Record<string, unknown>;
const sanitized: Record<string, unknown> = {}; const sanitized: Record<string, unknown> = {};
for (const [key, nestedValue] of Object.entries(record)) { for (const [key, nestedValue] of Object.entries(record)) {
@ -27,73 +20,3 @@ export function sanitizeConfigValue(value: unknown, path: string): unknown {
return sanitized; 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,
};
}