or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

arguments.mdconfiguration-sources.mdcore-commands.mdexceptions.mdindex.mdoptions.mdparameter-groups.mdparameter-types.mdshell-completion.mdtesting-utilities.md

configuration-sources.mddocs/

0

# Configuration Sources

1

2

Load parameter values from external sources including environment variables, configuration files, and custom value providers with priority chaining.

3

4

## Capabilities

5

6

### Value Source Interface

7

8

Base interface for loading parameter values from external sources.

9

10

```kotlin { .api }

11

/**

12

* Interface for loading parameter values from external sources

13

*/

14

interface ValueSource {

15

/**

16

* Represents a parameter invocation with its values

17

*/

18

data class Invocation(val values: List<String>)

19

20

/**

21

* Get parameter values from this source

22

* @param context Current parsing context

23

* @param option Option to get values for

24

* @return List of invocations with values

25

*/

26

fun getValues(context: Context, option: Option): List<Invocation>

27

}

28

```

29

30

### Map Value Source

31

32

Load parameter values from a map (useful for testing and simple configuration).

33

34

```kotlin { .api }

35

/**

36

* Value source that loads values from a map

37

* @param map Map of parameter names to values

38

* @param getKey Function to get map key from option names

39

*/

40

class MapValueSource(

41

private val map: Map<String, String>,

42

private val getKey: (Option) -> String = { option ->

43

option.valueSourceKey ?: option.names.maxByOrNull { it.length } ?: ""

44

}

45

) : ValueSource {

46

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation>

47

}

48

```

49

50

**Usage Examples:**

51

52

```kotlin

53

class MyCommand : CliktCommand() {

54

private val host by option("--host", help = "Server host", valueSourceKey = "host")

55

private val port by option("--port", help = "Server port", valueSourceKey = "port").int()

56

private val debug by option("--debug", help = "Debug mode", valueSourceKey = "debug").flag()

57

58

override fun run() {

59

echo("Host: $host")

60

echo("Port: $port")

61

echo("Debug: $debug")

62

}

63

}

64

65

// Using map value source

66

fun main() {

67

val config = mapOf(

68

"host" to "example.com",

69

"port" to "8080",

70

"debug" to "true"

71

)

72

73

MyCommand()

74

.context {

75

valueSource = MapValueSource(config)

76

}

77

.main()

78

}

79

80

// Custom key mapping

81

val customKeySource = MapValueSource(

82

map = mapOf(

83

"server_host" to "localhost",

84

"server_port" to "3000"

85

),

86

getKey = { option ->

87

when (option.valueSourceKey) {

88

"host" -> "server_host"

89

"port" -> "server_port"

90

else -> option.valueSourceKey ?: ""

91

}

92

}

93

)

94

```

95

96

### Chained Value Source

97

98

Chain multiple value sources with priority ordering.

99

100

```kotlin { .api }

101

/**

102

* Value source that chains multiple sources in priority order

103

* Sources are checked in order, first match wins

104

* @param sources List of value sources in priority order

105

*/

106

class ChainedValueSource(private val sources: List<ValueSource>) : ValueSource {

107

constructor(vararg sources: ValueSource) : this(sources.toList())

108

109

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation>

110

}

111

```

112

113

**Usage Examples:**

114

115

```kotlin

116

class MyCommand : CliktCommand() {

117

private val apiKey by option("--api-key", help = "API key",

118

valueSourceKey = "api_key", envvar = "API_KEY")

119

private val timeout by option("--timeout", help = "Timeout seconds",

120

valueSourceKey = "timeout").int().default(30)

121

122

override fun run() {

123

echo("API Key: ${apiKey?.take(8)}...")

124

echo("Timeout: $timeout")

125

}

126

}

127

128

fun main() {

129

// Priority: command line > environment > config file > defaults

130

val configFile = mapOf(

131

"api_key" to "default-key-from-config",

132

"timeout" to "60"

133

)

134

135

val environmentVars = mapOf(

136

"API_KEY" to "env-api-key",

137

"TIMEOUT" to "45"

138

)

139

140

val chainedSource = ChainedValueSource(

141

MapValueSource(environmentVars), // Higher priority

142

MapValueSource(configFile) // Lower priority

143

)

144

145

MyCommand()

146

.context {

147

valueSource = chainedSource

148

}

149

.main()

150

}

151

152

// Complex chaining example

153

class DatabaseCommand : CliktCommand() {

154

private val dbUrl by option("--db-url", valueSourceKey = "database.url", envvar = "DATABASE_URL")

155

private val dbUser by option("--db-user", valueSourceKey = "database.user", envvar = "DATABASE_USER")

156

private val dbPassword by option("--db-password", valueSourceKey = "database.password", envvar = "DATABASE_PASSWORD")

157

158

override fun run() {

159

echo("Connecting to database...")

160

echo("URL: $dbUrl")

161

echo("User: $dbUser")

162

}

163

}

164

165

fun createConfiguredCommand(): DatabaseCommand {

166

// Load from multiple sources

167

val prodConfig = mapOf(

168

"database.url" to "postgres://prod-server:5432/myapp",

169

"database.user" to "prod_user"

170

)

171

172

val devConfig = mapOf(

173

"database.url" to "postgres://localhost:5432/myapp_dev",

174

"database.user" to "dev_user",

175

"database.password" to "dev_password"

176

)

177

178

val secrets = mapOf(

179

"database.password" to "super-secret-password"

180

)

181

182

val valueSource = ChainedValueSource(

183

MapValueSource(secrets), // Highest priority (secrets)

184

MapValueSource(prodConfig), // Production overrides

185

MapValueSource(devConfig) // Development defaults

186

)

187

188

return DatabaseCommand().context {

189

this.valueSource = valueSource

190

}

191

}

192

```

193

194

### Properties Value Source (JVM Platform)

195

196

Load parameter values from Java properties files (available on JVM platform only).

197

198

```kotlin { .api }

199

/**

200

* Value source that loads values from Java properties files

201

* Available on JVM platform only

202

* @param properties Properties object

203

* @param getKey Function to get property key from option

204

*/

205

class PropertiesValueSource(

206

private val properties: Properties,

207

private val getKey: (Option) -> String = { option ->

208

option.valueSourceKey ?: option.names.maxByOrNull { it.length }?.removePrefix("--") ?: ""

209

}

210

) : ValueSource {

211

companion object {

212

/**

213

* Create from properties file

214

* @param file Properties file to load

215

*/

216

fun fromFile(file: File): PropertiesValueSource

217

218

/**

219

* Create from properties file path

220

* @param path Path to properties file

221

*/

222

fun fromFile(path: String): PropertiesValueSource

223

224

/**

225

* Create from input stream

226

* @param inputStream Stream containing properties data

227

*/

228

fun fromInputStream(inputStream: InputStream): PropertiesValueSource

229

}

230

231

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation>

232

}

233

```

234

235

**Usage Examples:**

236

237

```kotlin

238

// app.properties file:

239

// server.host=localhost

240

// server.port=8080

241

// database.url=jdbc:postgresql://localhost:5432/myapp

242

// logging.level=INFO

243

244

class MyJvmCommand : CliktCommand() {

245

private val host by option("--host", valueSourceKey = "server.host")

246

private val port by option("--port", valueSourceKey = "server.port").int()

247

private val dbUrl by option("--db-url", valueSourceKey = "database.url")

248

private val logLevel by option("--log-level", valueSourceKey = "logging.level")

249

250

override fun run() {

251

echo("Server: $host:$port")

252

echo("Database: $dbUrl")

253

echo("Log level: $logLevel")

254

}

255

}

256

257

fun main() {

258

val propsSource = PropertiesValueSource.fromFile("app.properties")

259

260

MyJvmCommand()

261

.context {

262

valueSource = propsSource

263

}

264

.main()

265

}

266

267

// Multiple properties files with chaining

268

fun createProductionCommand(): MyJvmCommand {

269

val defaultProps = PropertiesValueSource.fromFile("defaults.properties")

270

val envProps = PropertiesValueSource.fromFile("production.properties")

271

val localProps = PropertiesValueSource.fromFile("local.properties")

272

273

val chainedSource = ChainedValueSource(

274

localProps, // Highest priority (local overrides)

275

envProps, // Environment-specific config

276

defaultProps // Lowest priority (defaults)

277

)

278

279

return MyJvmCommand().context {

280

valueSource = chainedSource

281

}

282

}

283

284

// Properties with custom key mapping

285

class DatabaseCommand : CliktCommand() {

286

private val host by option("--db-host", help = "Database host")

287

private val port by option("--db-port", help = "Database port").int()

288

private val name by option("--db-name", help = "Database name")

289

290

override fun run() {

291

echo("Connecting to $name at $host:$port")

292

}

293

}

294

295

val dbPropsSource = PropertiesValueSource(

296

Properties().apply {

297

setProperty("db_host", "prod-db-server")

298

setProperty("db_port", "5432")

299

setProperty("db_name", "production")

300

},

301

getKey = { option ->

302

when (option.names.first()) {

303

"--db-host" -> "db_host"

304

"--db-port" -> "db_port"

305

"--db-name" -> "db_name"

306

else -> ""

307

}

308

}

309

)

310

```

311

312

### Custom Value Sources

313

314

Create custom value sources for specialized configuration needs.

315

316

```kotlin { .api }

317

/**

318

* Custom value source implementation

319

*/

320

abstract class CustomValueSource : ValueSource {

321

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation>

322

}

323

```

324

325

**Usage Examples:**

326

327

```kotlin

328

// JSON configuration file value source

329

class JsonValueSource(private val jsonFile: File) : ValueSource {

330

private val config: Map<String, Any> by lazy {

331

// Parse JSON file (using your preferred JSON library)

332

parseJsonFile(jsonFile)

333

}

334

335

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation> {

336

val key = option.valueSourceKey ?: return emptyList()

337

val value = getNestedValue(config, key) ?: return emptyList()

338

return listOf(ValueSource.Invocation(listOf(value.toString())))

339

}

340

341

private fun getNestedValue(map: Map<String, Any>, key: String): Any? {

342

val parts = key.split(".")

343

var current: Any? = map

344

345

for (part in parts) {

346

current = (current as? Map<*, *>)?.get(part)

347

if (current == null) break

348

}

349

350

return current

351

}

352

}

353

354

// YAML configuration value source

355

class YamlValueSource(private val yamlFile: File) : ValueSource {

356

private val config: Map<String, Any> by lazy {

357

parseYamlFile(yamlFile)

358

}

359

360

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation> {

361

val key = option.valueSourceKey ?: return emptyList()

362

val value = getNestedValue(config, key)

363

364

return when (value) {

365

is List<*> -> listOf(ValueSource.Invocation(value.map { it.toString() }))

366

null -> emptyList()

367

else -> listOf(ValueSource.Invocation(listOf(value.toString())))

368

}

369

}

370

}

371

372

// Database configuration value source

373

class DatabaseConfigSource(private val connectionString: String) : ValueSource {

374

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation> {

375

val key = option.valueSourceKey ?: return emptyList()

376

377

// Query database for configuration value

378

val value = queryDatabase(connectionString, key)

379

return if (value != null) {

380

listOf(ValueSource.Invocation(listOf(value)))

381

} else {

382

emptyList()

383

}

384

}

385

386

private fun queryDatabase(connection: String, key: String): String? {

387

// Implementation would connect to database and query config table

388

// Return null if key not found

389

return null

390

}

391

}

392

393

// HTTP API configuration source

394

class HttpConfigSource(private val apiUrl: String, private val apiKey: String) : ValueSource {

395

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation> {

396

val key = option.valueSourceKey ?: return emptyList()

397

398

try {

399

val value = fetchConfigValue(apiUrl, key, apiKey)

400

return if (value != null) {

401

listOf(ValueSource.Invocation(listOf(value)))

402

} else {

403

emptyList()

404

}

405

} catch (e: Exception) {

406

// Log error and return empty result

407

return emptyList()

408

}

409

}

410

411

private fun fetchConfigValue(url: String, key: String, token: String): String? {

412

// Implementation would make HTTP request to fetch config

413

return null

414

}

415

}

416

```

417

418

### Environment Variable Integration

419

420

Combine value sources with environment variable support.

421

422

```kotlin { .api }

423

/**

424

* Environment variable value source

425

*/

426

class EnvironmentValueSource : ValueSource {

427

override fun getValues(context: Context, option: Option): List<ValueSource.Invocation>

428

}

429

```

430

431

**Usage Examples:**

432

433

```kotlin

434

class MyCommand : CliktCommand() {

435

// Options with environment variable support

436

private val apiKey by option("--api-key",

437

help = "API key",

438

envvar = "API_KEY",

439

valueSourceKey = "api.key")

440

441

private val dbUrl by option("--database-url",

442

help = "Database URL",

443

envvar = "DATABASE_URL",

444

valueSourceKey = "database.url")

445

446

override fun run() {

447

echo("API Key: ${apiKey?.take(8)}...")

448

echo("Database URL: $dbUrl")

449

}

450

}

451

452

// Priority chain: CLI args > env vars > config file > defaults

453

fun createConfiguredApp(): MyCommand {

454

val configFileSource = JsonValueSource(File("config.json"))

455

val envSource = EnvironmentValueSource()

456

457

val chainedSource = ChainedValueSource(

458

envSource, // Environment variables (higher priority)

459

configFileSource // Config file (lower priority)

460

)

461

462

return MyCommand().context {

463

valueSource = chainedSource

464

}

465

}

466

467

// Complex configuration hierarchy

468

class WebServerCommand : CliktCommand() {

469

private val port by option("--port", valueSourceKey = "server.port", envvar = "PORT").int()

470

private val host by option("--host", valueSourceKey = "server.host", envvar = "HOST")

471

private val ssl by option("--ssl", valueSourceKey = "server.ssl.enabled", envvar = "SSL_ENABLED").flag()

472

private val certFile by option("--cert", valueSourceKey = "server.ssl.cert", envvar = "SSL_CERT")

473

474

override fun run() {

475

echo("Starting server on $host:$port")

476

if (ssl) {

477

echo("SSL enabled with cert: $certFile")

478

}

479

}

480

}

481

482

fun createWebServer(): WebServerCommand {

483

// Multiple configuration sources

484

val sources = ChainedValueSource(

485

EnvironmentValueSource(), // Environment variables

486

PropertiesValueSource.fromFile("production.properties"), // Production config

487

PropertiesValueSource.fromFile("application.properties"), // Default config

488

MapValueSource(mapOf( // Fallback defaults

489

"server.port" to "8080",

490

"server.host" to "0.0.0.0",

491

"server.ssl.enabled" to "false"

492

))

493

)

494

495

return WebServerCommand().context {

496

valueSource = sources

497

}

498

}

499

```

500

501

## Experimental API

502

503

```kotlin { .api }

504

/**

505

* Annotation marking experimental value source APIs

506

*/

507

@RequiresOptIn("Value source API is experimental and may change")

508

annotation class ExperimentalValueSourceApi

509

```