@aaronshaf/ger
Version:
Gerrit CLI and SDK - A modern CLI tool and TypeScript SDK for Gerrit Code Review, built with Effect-TS
88 lines (77 loc) • 3.29 kB
text/typescript
import { Schema } from '@effect/schema'
// Flat Application Configuration (similar to ji structure)
export const AppConfig: Schema.Struct<{
host: typeof Schema.String
username: typeof Schema.String
password: typeof Schema.String
aiTool: Schema.optional<Schema.Literal<['claude', 'llm', 'opencode', 'gemini']>>
aiAutoDetect: Schema.optionalWith<typeof Schema.Boolean, { default: () => boolean }>
retriggerComment: Schema.optional<typeof Schema.String>
}> = Schema.Struct({
// Gerrit credentials (flattened)
host: Schema.String.pipe(
Schema.pattern(/^https?:\/\/.+$/),
Schema.annotations({ description: 'Gerrit server URL' }),
),
username: Schema.String.pipe(
Schema.minLength(1),
Schema.annotations({ description: 'Gerrit username' }),
),
password: Schema.String.pipe(
Schema.minLength(1),
Schema.annotations({ description: 'HTTP password or API token' }),
),
// AI configuration (flattened)
aiTool: Schema.optional(Schema.Literal('claude', 'llm', 'opencode', 'gemini')),
aiAutoDetect: Schema.optionalWith(Schema.Boolean, { default: (): boolean => true }),
// CI retrigger comment
retriggerComment: Schema.optional(Schema.String),
})
export type AppConfig = Schema.Schema.Type<typeof AppConfig>
// Legacy schemas for backward compatibility (deprecated)
export const AiConfig: Schema.Struct<{
tool: Schema.optional<Schema.Literal<['claude', 'llm', 'opencode', 'gemini']>>
autoDetect: Schema.optionalWith<typeof Schema.Boolean, { default: () => boolean }>
}> = Schema.Struct({
tool: Schema.optional(Schema.Literal('claude', 'llm', 'opencode', 'gemini')),
autoDetect: Schema.optionalWith(Schema.Boolean, { default: (): boolean => true }),
})
export type AiConfig = Schema.Schema.Type<typeof AiConfig>
// Helper to convert from flat config to legacy AI config
export const aiConfigFromFlat = (config: AppConfig): AiConfig => ({
tool: config.aiTool,
autoDetect: config.aiAutoDetect,
})
// Schema for validating legacy nested config structure
const LegacyNestedConfig = Schema.Struct({
credentials: Schema.Struct({
host: Schema.String.pipe(
Schema.pattern(/^https?:\/\/.+$/),
Schema.annotations({ description: 'Gerrit server URL' }),
),
username: Schema.String.pipe(Schema.minLength(1)),
password: Schema.String.pipe(Schema.minLength(1)),
}),
ai: Schema.optional(
Schema.Struct({
tool: Schema.optional(Schema.Literal('claude', 'llm', 'opencode', 'gemini')),
autoDetect: Schema.optional(Schema.Boolean),
}),
),
})
type LegacyNestedConfig = Schema.Schema.Type<typeof LegacyNestedConfig>
// Helper to convert from legacy nested format to flat format with validation
export const migrateFromNestedConfig = (nested: unknown): AppConfig => {
// Validate input structure using Schema
const validatedNested = Schema.decodeUnknownSync(LegacyNestedConfig)(nested)
// Convert to flat structure
const flatConfig = {
host: validatedNested.credentials.host,
username: validatedNested.credentials.username,
password: validatedNested.credentials.password,
aiTool: validatedNested.ai?.tool,
aiAutoDetect: validatedNested.ai?.autoDetect ?? true,
}
// Validate the resulting flat config
return Schema.decodeUnknownSync(AppConfig)(flatConfig)
}