-
-
Notifications
You must be signed in to change notification settings - Fork 94
Add configuration file support for CLI #566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The globals.ts file mixed unrelated concerns: CLI option definitions and
logging configuration. This refactoring improves code organization by moving
each piece to its natural home:
- debugOption moved to options.ts, where all other CLI options are defined
- configureLogging() moved to log.ts, where recordingSink and other logging
utilities already reside
This eliminates globals.ts entirely and makes the codebase easier to navigate
by grouping related functionality together.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Introduce config.ts that consolidates configuration handling:
- Valibot schemas for validating config structure (global debug option,
command-specific sections for webfinger, lookup, inbox, relay, nodeinfo)
- TOML parsing via smol-toml library
- Hierarchical config loading from system (/etc/fedify/config.toml),
user (~/.config/fedify/config.toml), and project (.fedify.toml) levels
- Recursive merging where later configs override earlier ones
This lays the groundwork for issue fedify-dev#265.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Introduce configOption with two mutually exclusive flags:
- -c, --config PATH: loads an additional configuration file on top of the
standard hierarchy (system, user, project)
- -I, --ignore-config: skips all configuration files entirely, useful for
CI environments requiring reproducible behavior
Co-Authored-By: Claude Opus 4.5 <[email protected]>
The config module now delegates file loading and merging to @optique/config instead of implementing custom logic. This removes: - Manual config file path resolution - Multi-file loading and merging logic - Custom TOML parsing with error handling The configOption now uses withDefault() to properly handle the case when neither --ignore-config nor --config is specified. Also removed short flags (-I, -c) to keep only long flags (--ignore-config, --config) for clarity. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Implement config file loading that merges multiple sources in order: 1. /etc/fedify/config.toml (system-wide, silently skipped if missing) 2. ~/.config/fedify/config.toml (user config) 3. ./.fedify.toml (project config) 4. --config PATH (custom, throws on error if specified but missing) Use Optique's runWithConfig with load callback for two-pass parsing, and es-toolkit's merge for deep merging config objects. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Integrate Optique's bindConfig() to allow CLI options to fall back to config file values. Uses type assertion on the key function return type to properly narrow types when defaults are provided. Commands updated: - relay: protocol, port, name - inbox: actorName, actorSummary, authorizedFetch - webfinger: userAgent, allowPrivateAddress, maxRedirection - lookup: userAgent, timeout - nodeinfo: userAgent Also fixes version mismatches across workspace packages (1.10.3). Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add tunnel section to config schema and wrap the service option with bindConfig() to allow tunnel service to be configured via config file. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add missing fields to config schema: - lookup: firstKnock, defaultFormat, separator - relay: persistent, acceptFollow, rejectFollow - nodeinfo: raw, bestEffort, noFavicon, metadata Add bindConfig() for: - lookup: separator - relay: persistent Co-Authored-By: Claude Opus 4.5 <[email protected]>
Extend config file support for more CLI options: - lookup: authorizedFetch, traverse, suppressErrors, format (defaultFormat) - nodeinfo: raw, bestEffort, noFavicon, metadata The nodeinfo options were restructured from an or() discriminated union to a flat object structure to enable bindConfig integration. This simplifies the runtime checks from `"raw" in command && command.raw` to `command.raw`. Co-Authored-By: Claude Opus 4.5 <[email protected]>
This version includes a fix for type inference with bindConfig and multiple() for array options. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Refine config schema to only support fields listed in the issue spec: - Global settings: debug, userAgent, logFile, tunnelService - webfinger: allowPrivateAddress, maxRedirection - lookup: authorizedFetch, firstKnock, traverse, suppressErrors, defaultFormat, separator, timeout - inbox: actorName, actorSummary, authorizedFetch, noTunnel, follow, acceptFollow - relay: protocol, port, name, persistent, noTunnel, acceptFollow, rejectFollow - nodeinfo: raw, bestEffort, showFavicon, showMetadata Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Bind follow and acceptFollow arrays with config defaults
- Fix actorName/actorSummary to use ?? fallback instead of type cast
- Fix authorizedFetch to have proper boolean default
- Use createTunnelOption("inbox") for tunnel options
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Bind userAgent to global config.userAgent (optional) - Bind allowPrivateAddresses with default false - Bind maxRedirection without default (optional) - Remove type casts in favor of proper undefined handling Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Bind authorizedFetch, firstKnock, traverse, suppressErrors with defaults - Bind format (defaultFormat) and separator with defaults - Bind timeout without default (optional) - Add shared userAgentOption from options.ts - Replace type casts with ?? fallbacks for type safety Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Upgrade @optique/config, @optique/core, @optique/run to 0.10.0-dev.348 which fixes a bug where bindConfig.complete crashed with undefined state when no CLI args were consumed. Update test expectations to reflect bindConfig behavior: - userAgent now returns default value instead of undefined - Invalid option values fall back to default instead of failing See: dahlia/optique#94 Co-Authored-By: Claude Opus 4.5 <[email protected]>
The noFavicon option was incorrectly reading showFavicon from config. When showFavicon was true, noFavicon would also be true, which is inverted. Changed to use strict equality check (=== false) so noFavicon is only true when showFavicon is explicitly set to false. Also updated CHANGES.md to include inbox and nodeinfo in the list of commands with configuration support. Additionally simplified some bindConfig key functions: - Removed redundant type assertion in lookup.ts firstKnock - Removed redundant nullish coalescing in determineSpec - Changed `as number` to `?? default` for port and maxRedirection Co-Authored-By: Claude Opus 4.5 <[email protected]>
Summary of ChangesHello @sij411, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the Fedify CLI by introducing a robust configuration system. Users can now define default settings for various commands through TOML files, which are loaded hierarchically to provide flexible and predictable behavior. This change streamlines the user experience by reducing the need for repetitive command-line arguments and improves the CLI's maintainability by centralizing configuration logic and removing a global state file. Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces configuration file support for the CLI, which is a great enhancement for usability. The implementation uses @optique/config to bind command-line options to values from TOML configuration files loaded from system, user, and project locations. The code is well-structured, with a new config.ts for schemas and refactoring of global options into a dedicated options.ts file.
A key point of confusion is the discrepancy between the pull request description, which states support for fedify.config.jsonc and fedify.config.json files, and the actual implementation and documentation which use the TOML format. It would be beneficial to update the PR description to accurately reflect that TOML is the supported format to avoid misleading future contributors or users.
I've added one suggestion to improve error handling when loading configuration files, which should enhance the user experience by providing feedback on malformed configs.
| } catch { | ||
| return {}; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation of tryLoadToml uses a bare catch block, which suppresses all errors, including syntax errors in the TOML file. This can be confusing for a user who has a typo in their config and doesn't understand why it's not being applied. It would be better to only ignore 'file not found' errors and log a warning for other issues like parsing or permission errors.
} catch (error) {
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
return {}; // File not found, which is fine.
}
// For other errors (e.g., parsing, permissions), warn the user.
console.warn(
`Warning: Could not load or parse config file at ${path}. It will be ignored. Error: ${error instanceof Error ? error.message : String(error)}`,
);
return {};
}
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds unified configuration-file support to the Fedify CLI using Optique, enabling CLI options across commands to be defaulted from a hierarchical config (plus --config / --ignore-config) and removing the old globals.ts in favor of shared modules.
Changes:
- Introduce CLI config context + schema and load TOML config from system/user/project/custom locations via
runWithConfig. - Bind command options (webfinger/lookup/inbox/nodeinfo/relay/tunnel/global) to config values using
bindConfig. - Update CLI docs/changelog and adjust CLI tests for new config-binding behavior; bump Optique dependencies.
Reviewed changes
Copilot reviewed 26 out of 28 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Updates lockfile for new Optique/config + TOML/schema deps. |
| deno.lock | Updates Deno lock for new Optique/config + TOML/schema deps and transitive bumps. |
| packages/webfinger/deno.json | Formatting change to publish exclude list. |
| packages/vocab/deno.json | Formatting change to publish exclude list. |
| packages/vocab-tools/deno.json | Formatting change to publish exclude list. |
| packages/vocab-runtime/deno.json | Formatting change to publish exclude list. |
| packages/relay/deno.json | Formatting change to publish exclude list. |
| packages/lint/deno.json | Formatting change to publish exclude list. |
| packages/fresh/deno.json | Formatting change to publish exclude list. |
| packages/elysia/deno.json | Formatting change to publish exclude list. |
| packages/cli/src/config.ts | Adds config schema + config context + TOML loader helper. |
| packages/cli/src/options.ts | Centralizes shared options and binds them to config. |
| packages/cli/src/mod.ts | Switches CLI entrypoint to runWithConfig and implements config loading/merging. |
| packages/cli/src/webfinger/command.ts | Binds webfinger options to config; uses shared options module. |
| packages/cli/src/webfinger/mod.test.ts | Updates webfinger command parsing tests for new defaults/binding. |
| packages/cli/src/lookup.ts | Binds lookup options to config and adds shared user-agent option. |
| packages/cli/src/lookup.test.ts | Updates lookup option parsing tests to run with config context/binding. |
| packages/cli/src/inbox.tsx | Binds inbox options to config; uses new tunnel option factory + logging module. |
| packages/cli/src/nodeinfo.ts | Binds nodeinfo options to config; uses shared user-agent/debug options. |
| packages/cli/src/relay.ts | Binds relay options to config and uses new tunnel option factory + logging module. |
| packages/cli/src/tunnel.ts | Moves debug/logging imports to new modules. |
| packages/cli/src/log.ts | Adds configureLogging() and keeps shared LogTape setup/recording sink. |
| packages/cli/src/init/command.ts | Updates debug option import to new shared options module. |
| packages/cli/src/globals.ts | Removes legacy globals module (debug option + logging config). |
| packages/cli/package.json | Adds Optique/config + TOML/schema deps and adjusts package metadata. |
| packages/cli/deno.json | Adds Optique/config + TOML/schema deps for Deno and updates Optique versions. |
| docs/cli.md | Documents TOML config format, locations, and available settings. |
| CHANGES.md | Adds changelog entry for CLI config support. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| "files": [ | ||
| "README.md", | ||
| "package.json", | ||
| "src/install.mjs", | ||
| "src/run.mjs" | ||
| ], | ||
| "bin": { | ||
| "fedify": "./dist/mod.js" | ||
| }, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
package.json sets a restrictive files whitelist that does not include the built dist/ output (and also references src/install.mjs/src/run.mjs, which don't exist in this package). As a result, @fedify/cli would be published without dist/mod.js, breaking the bin/exports entrypoints. Include dist/ in files (and remove or fix the missing src/*.mjs entries).
| "engines": { | ||
| "node": ">=20.0.0", | ||
| "bun": ">=1.2.0", | ||
| "denp": ">=2.0.0" |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
engines uses the key denp, but other packages in this repo use engines.deno. If this is intended to declare a Deno engine constraint, it should be deno; as-is it’s likely a typo and won’t be picked up by tooling.
| "denp": ">=2.0.0" | |
| "deno": ">=2.0.0" |
| persistent: bindConfig( | ||
| optional( | ||
| option("--persistent", string({ metavar: "PATH" }), { | ||
| description: | ||
| message`Path to SQLite database file for persistent storage. If not specified, uses in-memory storage which is lost when the server stops.`, | ||
| }), | ||
| ), | ||
| { | ||
| context: configContext, | ||
| key: (config) => config.relay?.persistent, | ||
| }, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
persistent is wrapped in optional(...) before bindConfig(...). If optional(...) succeeds with undefined when the flag is absent, bindConfig may never fall back to config.relay?.persistent, making the config file value ineffective. Prefer binding the raw option(...) and letting bindConfig resolve to the config value (or undefined) when the CLI option is omitted.
| const userConfigDir = envPaths("fedify", { suffix: "" }).config; | ||
| const system = tryLoadToml("/etc/fedify/config.toml"); | ||
| const user = tryLoadToml(join(userConfigDir, "config.toml")); | ||
| const project = tryLoadToml(join(process.cwd(), ".fedify.toml")); | ||
|
|
||
| // Custom config via --config throws on error (required file) | ||
| const custom = parsed.configPath | ||
| ? parseToml(readFileSync(parsed.configPath, "utf-8")) | ||
| : {}; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description says default config discovery uses fedify.config.jsonc/json, but the CLI implementation here loads TOML files (/etc/fedify/config.toml, user config.toml, .fedify.toml, plus --config TOML). Update either the implementation or the PR description so they match, otherwise reviewers/users will be misled about the supported config format and filenames.
| settings from *.fedify.toml* in the current directory. | ||
| [[#555], [#566] by Jiwon Kwon] | ||
|
|
||
| - Added `--config` option to specify a custom configuration file path. | ||
| - Added `--ignore-config` option to skip configuration file loading. | ||
| - All command options (`inbox`, `lookup`, `webfinger`, `nodeinfo`, | ||
| `tunnel`, `relay`) can now be configured via the configuration file. |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Release note says the CLI loads settings from .fedify.toml in the current directory, but the implementation also loads system-wide and user-level config files (and --config). Consider updating this entry to reflect the full precedence chain so the changelog matches actual behavior.
| settings from *.fedify.toml* in the current directory. | |
| [[#555], [#566] by Jiwon Kwon] | |
| - Added `--config` option to specify a custom configuration file path. | |
| - Added `--ignore-config` option to skip configuration file loading. | |
| - All command options (`inbox`, `lookup`, `webfinger`, `nodeinfo`, | |
| `tunnel`, `relay`) can now be configured via the configuration file. | |
| settings from configuration files at multiple levels, including a | |
| project-local *.fedify.toml* in the current directory, with a | |
| well-defined precedence chain. | |
| [[#555], [#566] by Jiwon Kwon] | |
| - By default, configuration is loaded (in order of increasing | |
| precedence) from a system-wide configuration file, a user-level | |
| configuration file, and *.fedify.toml* in the current directory; | |
| later files override earlier ones. | |
| - Added `--config` option to specify a custom configuration file path; | |
| this file has the highest precedence over all other configuration | |
| sources. | |
| - Added `--ignore-config` option to skip configuration file loading. | |
| - All command options (`inbox`, `lookup`, `webfinger`, `nodeinfo`, | |
| `tunnel`, `relay`) can now be configured via any of the configuration | |
| files. |
| // Global settings | ||
| debug: optional(boolean()), | ||
| userAgent: optional(string()), | ||
| logFile: optional(string()), |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
configSchema defines a global logFile setting, but the CLI logging implementation currently only reads FEDIFY_LOG_FILE from the environment and does not appear to consume config.logFile. Either wire this field into logging configuration or remove it from the schema/docs to avoid a non-functional config knob.
| logFile: optional(string()), |
Summary
fedify.config.jsonc,fedify.config.json, or custom path via--config--ignore-configoption to skip config file loadingTest plan
--configoption loads custom config file--ignore-configskips config loading🤖 Generated with Claude Code