Skip to content

Conversation

@aruokhai
Copy link
Contributor

@aruokhai aruokhai commented Sep 22, 2025

close #303

Summary by CodeRabbit

  • New Features

    • Expanded runtime configuration options and environment variable defaults exposed for easier setup.
  • Documentation

    • Added a generated environment variables reference and replaced the inline README table with a link.
    • README now includes Docker examples showing how to pass environment variables at runtime.
  • Chores

    • CI workflow added to ensure the generated environment documentation stays synchronized with the code.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 22, 2025

Walkthrough

Reflection-driven config defaults replace manual Viper wiring; a generator tool emits docs/environment.md from Config struct tags; README links to generated docs and shows Docker usage; application startup now uses cfg.GetLnConnectionOpts(); a GitHub Actions workflow enforces generated docs remain unchanged.

Changes

Cohort / File(s) Summary
CI / Workflow
\.github/workflows/env-doc-check.yaml
New GitHub Actions workflow that sets up Go (>=1.24.6), runs go generate for internal/config, and fails if docs/environment.md differs (git diff --exit-code).
Docs / README
README.md, docs/environment.md
README updated to link to docs/environment.md and add Docker -e usage examples. docs/environment.md added as a generated Markdown table of environment variables with Variable, Default, Type, Description.
Config core
internal/config/config.go
Large refactor: Config fields annotated with mapstructure, envDefault, envInfo; added setDefaultConfig(*viper.Viper) using reflection to set defaults and bind envs; LoadConfig now unmarshals into Config then runs initDb() and deriveLnConfig(); added GetLnConnectionOpts() accessor; removed old inline defaults and helpers.
Generator tool
tools/gen-env-doc/main.go
New tool that reflects over internal/config.Config tags to generate docs/environment.md (Markdown table) from mapstructure, envDefault, and envInfo.
Application init
cmd/fulmine/main.go
Call site updated to pass cfg.GetLnConnectionOpts() to application.NewService() instead of accessing cfg.LnConnectionOpts directly.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as LoadConfig()
    participant Viper
    participant Setter as setDefaultConfig()
    participant Reflect as reflect
    participant Config
    Caller->>Viper: New Viper, SetEnvPrefix("FULMINE")
    Caller->>Setter: setDefaultConfig(v)
    Setter->>Reflect: Inspect Config struct tags (mapstructure/envDefault/envInfo)
    Reflect-->>Setter: field metadata
    Setter->>Viper: v.SetDefault(key, default) & v.BindEnv(key)
    Caller->>Viper: v.Unmarshal(&Config)
    Viper-->>Config: populated fields
    Caller->>Config: initDb()
    Caller->>Config: deriveLnConfig()
    note right of Config: tools/gen-env-doc reads same tags\nand writes docs/environment.md
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to reflection/tag parsing and BindEnv behavior in internal/config/config.go::setDefaultConfig.
  • Review deriveLnConfig() and GetLnConnectionOpts() for compatibility and error handling.
  • Verify tools/gen-env-doc/main.go output format and ensure CI workflow invokes generation correctly.

Possibly related PRs

Suggested reviewers

  • altafan

Poem

🐰 I nibble tags, I hop and see,

Defaults born from reflection's tree,
Docs that grow from struct and pen,
CI watches, keeps them true again,
A little hop — and ship it then!

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR documents environment variables via docs/environment.md and provides a generation tool, addressing the primary objective. However, no test coverage was added as required by issue #303. Add test coverage to validate environment variable configuration behavior and loading, as specified in issue #303.
Out of Scope Changes check ⚠️ Warning The PR includes significant refactoring of internal/config/config.go beyond what is needed for documentation, including reflect-based binding and structural changes to Config fields and methods. Review whether the extensive refactoring of config loading is necessary for the documentation objective, or isolate documentation-specific changes from configuration architecture changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the PR: ensuring environment variable configurations are documented through the addition of docs/environment.md and related tooling.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add_env_md

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@aruokhai aruokhai marked this pull request as ready for review November 25, 2025 11:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
.github/workflows/env-doc-check.yaml (1)

13-19: Update GitHub Actions to v4.

The actions/checkout@v3 and actions/setup-go@v3 actions are outdated. GitHub recommends using v4 for improved security and Node.js 20 compatibility.

-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
             fetch-depth: 1
-      - uses: actions/setup-go@v3
+      - uses: actions/setup-go@v4
         with:
           go-version: '>=1.24.6'
           cache: true
tools/gen-env-doc/main.go (1)

36-41: Consider more robust path handling for the output directory.

The hardcoded relative paths ../../docs and ../../docs/environment.md assume the tool is run from tools/gen-env-doc/. While this works correctly when invoked via go generate from the expected location, it could be fragile if run manually from a different directory.

Consider one of these approaches for better robustness:

Option 1: Accept output path as a flag

import "flag"

func main() {
	outputPath := flag.String("output", "docs/environment.md", "output markdown file path")
	flag.Parse()
	
	// ... generate markdown ...
	
	dir := filepath.Dir(*outputPath)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		panic(err)
	}
	if err := os.WriteFile(*outputPath, []byte(md), 0o644); err != nil {
		panic(err)
	}
}

Option 2: Determine paths relative to the Go module root

import (
	"os/exec"
	"path/filepath"
	"strings"
)

func main() {
	// Get module root
	out, err := exec.Command("go", "env", "GOMOD").Output()
	if err != nil {
		panic(err)
	}
	modFile := strings.TrimSpace(string(out))
	moduleRoot := filepath.Dir(modFile)
	
	docsDir := filepath.Join(moduleRoot, "docs")
	outputPath := filepath.Join(docsDir, "environment.md")
	
	// ... rest of the code ...
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a00599c and 887742f.

📒 Files selected for processing (6)
  • .github/workflows/env-doc-check.yaml (1 hunks)
  • README.md (1 hunks)
  • cmd/fulmine/main.go (1 hunks)
  • docs/environment.md (1 hunks)
  • internal/config/config.go (6 hunks)
  • tools/gen-env-doc/main.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
tools/gen-env-doc/main.go (1)
internal/config/config.go (1)
  • Config (27-58)
🪛 actionlint (1.7.8)
.github/workflows/env-doc-check.yaml

13-13: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


16-16: the runner of "actions/setup-go@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: integration tests
🔇 Additional comments (12)
docs/environment.md (1)

1-29: LGTM! Documentation structure is clear and comprehensive.

The generated environment variable documentation covers all configuration options with appropriate defaults, types, and descriptions. The warning about not editing manually is helpful for maintainers.

internal/config/config.go (5)

27-58: LGTM! Config struct is well-structured with appropriate tags.

The use of mapstructure, envDefault, and envInfo tags enables reflection-based configuration binding and documentation generation. The unexported lnConnectionOpts field with a public accessor maintains good encapsulation.


60-93: LGTM! LoadConfig flow is clean and well-organized.

The initialization sequence is logical, with proper error handling at each step. Creating a new Viper instance rather than using the global one improves testability.


95-163: LGTM! Lightning config derivation logic is thorough.

The method properly validates mutual exclusion of LND/CLN configurations, handles both connect-scheme URLs and standard URLs with datadir requirements, and validates URL formats where appropriate.


169-171: LGTM!

Clean accessor method for the encapsulated LN connection options.


241-259: LGTM! Reflection-based config binding is well-implemented.

The function correctly iterates over exported fields, extracts tags, sets defaults when present, and binds environment variables. Skipping unexported fields prevents panics on inaccessible fields.

cmd/fulmine/main.go (1)

125-125: LGTM!

The change correctly uses the new GetLnConnectionOpts() accessor, aligning with the encapsulation pattern introduced in the config refactor.

README.md (1)

66-66: LGTM!

Linking to the generated docs/environment.md instead of maintaining a duplicate table is a good approach—keeps the README concise and ensures environment variable documentation stays in sync with the code.

.github/workflows/env-doc-check.yaml (1)

20-21: LGTM! Generated documentation validation is a good CI practice.

Running go generate and checking for uncommitted changes ensures docs/environment.md stays synchronized with the Config struct.

tools/gen-env-doc/main.go (3)

1-2: LGTM!

The build constraints correctly mark this as a generator tool that should be excluded from regular builds.


6-12: LGTM!

Imports are appropriate for the reflection-based documentation generator.


14-20: LGTM!

The reflection setup and markdown table header are correctly structured. The warning about manual edits is helpful for maintainers.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
tools/gen-env-doc/main.go (2)

14-43: Reflection-based env docs generation and tag validation look solid (minor wording nit).

The reflection over config.Config with IsExported() plus explicit checks for mapstructure and envInfo tags is a good way to keep env docs in sync and fail fast when new fields are added without proper tags. The generated table columns (Variable, Default, Type, Description) line up cleanly with the struct tags and intended usage.

Very small nit: the header text "Generated from \config Structure`"is slightly odd; consider changing it to something like"Generated from `config.Config` struct"` for clarity.


45-50: Make the output path more resilient to different working directories.

Using hard-coded ../../docs assumes this tool is always executed from internal/config via go generate. If someone runs go run tools/gen-env-doc/main.go from the repo root, it may write outside the repo tree.

Consider deriving the docs path from the current working directory (e.g., filepath.Abs + filepath.Join) or accepting the output path as an argument, with a default matching the current go:generate usage.

internal/config/config.go (3)

95-155: LN connection derivation covers key combinations; consider ensuring tests exhaust cases.

The standalone deriveLnConfig correctly:

  • Normalizes datadir paths.
  • Rejects simultaneous LND/CLN URLs or datadirs.
  • Handles lndconnect:// and clnconnect:// specially (no datadir required).
  • Requires corresponding datadirs for non-*connect URLs.
  • Validates non-*connect URLs via utils.ValidateURL before constructing LnConnectionOpts.

Given the number of configuration combinations here, it would be worth having (or extending) a table-driven test suite that exercises all branches (no URLs, both URLs, only LND, only CLN, with/without datadirs, invalid URLs, *connect schemes, stray datadir values). If such tests don’t already exist, I’d recommend adding them.


175-197: DB type validation looks good; consider also creating the default datadir.

The supportedDbType check provides clear feedback for unsupported DB backends, and the non-default datadir path is normalized and created via makeDirectoryIfNotExists.

One potential improvement: in the c.Datadir == "fulmine" branch you compute the OS-specific app datadir but don’t ensure the directory exists, while the else branch does. For symmetry and first-run ergonomics, you may want to run makeDirectoryIfNotExists in both branches:

-	if c.Datadir == "fulmine" {
-		c.Datadir = appDatadir("fulmine", false)
-	} else {
-		datadir := cleanAndExpandPath(c.Datadir)
-		if err := makeDirectoryIfNotExists(datadir); err != nil {
-			return fmt.Errorf("failed to create data directory: %w", err)
-		}
-		c.Datadir = datadir
-	}
+	if c.Datadir == "fulmine" {
+		datadir := appDatadir("fulmine", false)
+		if err := makeDirectoryIfNotExists(datadir); err != nil {
+			return fmt.Errorf("failed to create data directory: %w", err)
+		}
+		c.Datadir = datadir
+	} else {
+		datadir := cleanAndExpandPath(c.Datadir)
+		if err := makeDirectoryIfNotExists(datadir); err != nil {
+			return fmt.Errorf("failed to create data directory: %w", err)
+		}
+		c.Datadir = datadir
+	}

244-262: Add explicit validation for missing mapstructure tags in setDefaultConfig.

BindEnv with an empty key does not error—it binds using the empty string as the config key, producing an empty env name (or "PREFIX_" if an env prefix is set). This means if a new exported field is added without a mapstructure tag, the function silently passes an empty key to v.BindEnv(key), resulting in misbinding that could be difficult to debug.

To prevent this and mirror the stricter checks in the env-doc generator, add validation:

 func setDefaultConfig(v *viper.Viper) error {
 	t := reflect.TypeOf(Config{})
 	for i := 0; i < t.NumField(); i++ {
 		f := t.Field(i)
 		if !f.IsExported() {
 			continue
 		}
-		key := f.Tag.Get("mapstructure")
+		key := strings.TrimSpace(f.Tag.Get("mapstructure"))
+		if key == "" {
+			return fmt.Errorf("config field %s missing mapstructure tag", f.Name)
+		}
 		def := f.Tag.Get("envDefault")
 		if def != "" {
 			v.SetDefault(key, def)
 		}
-		err := v.BindEnv(key)
-		if err != nil {
+		if err := v.BindEnv(key); err != nil {
 			return fmt.Errorf("error binding env variable for key %s: %w", key, err)
 		}
 	}
 	return nil
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f07758 and a96c653.

📒 Files selected for processing (2)
  • internal/config/config.go (6 hunks)
  • tools/gen-env-doc/main.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tools/gen-env-doc/main.go (1)
internal/config/config.go (1)
  • Config (27-58)
internal/config/config.go (2)
internal/core/domain/settings.go (4)
  • LnConnectionOpts (14-18)
  • ConnectionType (7-7)
  • LND_CONNECTION (11-11)
  • CLN_CONNECTION (10-10)
utils/url.go (1)
  • ValidateURL (25-57)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: integration tests
🔇 Additional comments (6)
internal/config/config.go (6)

27-58: Config struct tagging cleanly centralizes env defaults and docs.

The mapstructure, envDefault, and envInfo tags on Config give a single source of truth for both runtime config (via Viper) and env docs generation. The new LN-related fields and NoMacaroons flag are documented with clear descriptions and examples, and unexported internals (lnConnectionOpts, unlocker, macaroonSvc) stay out of the env surface.


60-92: LoadConfig flow and initialization ordering look correct.

Creating a fresh viper.Viper, applying setDefaultConfig, then unmarshalling into Config followed by initDb, deriveLnConfig, unlocker, and macaroon initialization results in a clear, linear startup path. Using methods on Config keeps the load logic cohesive and makes it easier to test in isolation.


157-165: Config.deriveLnConfig nicely encapsulates LN opts wiring.

Delegating to the pure function and stashing the result in c.lnConnectionOpts keeps the stateful part minimal and makes it easy to reuse deriveLnConfig in tests.


171-173: Getter for LN connection options is a reasonable encapsulation step.

Exposing GetLnConnectionOpts() instead of the field allows you to keep lnConnectionOpts unexported and maintain flexibility for future changes (e.g., lazy derivation or caching tweaks) without breaking callers.


230-239: NoMacaroons toggle integrates cleanly with macaroon service init.

Guarding macaroon.NewService with if !c.NoMacaroons makes the opt-out behavior explicit and keeps the initialization idempotent via the existing c.macaroonSvc != nil guard.


361-361: go:generate hook is a nice way to keep env docs in sync.

Linking the config package to ../../tools/gen-env-doc/main.go via //go:generate (plus the CI check) is a good way to ensure docs/environment.md stays consistent with Config changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document ENV vars and add tests

3 participants