Skip to content

refactor: options package returns errors instead of os.Exit#443

Merged
CybotTM merged 2 commits intomainfrom
refactor/options-return-errors
Feb 5, 2026
Merged

refactor: options package returns errors instead of os.Exit#443
CybotTM merged 2 commits intomainfrom
refactor/options-return-errors

Conversation

@CybotTM
Copy link
Member

@CybotTM CybotTM commented Feb 5, 2026

Summary

  • Changed Parse() to return (*Opts, error) instead of calling os.Exit
  • Created ConfigError type that collects multiple validation errors
  • Updated envIntOrDefault and envBoolOrDefault to accumulate errors
  • Added deprecated MustParse() for backward compatibility
  • Use custom FlagSet to allow multiple Parse() calls in tests
  • Fixed race condition in resettoken package by adding mutex protection for defaultClock global variable
  • Added comprehensive tests for error handling paths

Motivation

Libraries should return errors instead of calling os.Exit, which is not testable and not idiomatic Go. Only main() should decide whether to exit.

Test plan

  • All unit tests pass with race detection: go test ./... -race
  • Linting passes: golangci-lint run
  • Options package coverage improved from ~0% to 85.4%
  • Total coverage: 81.0%

🤖 Generated with Claude Code

- Changed Parse() to return (*Opts, error) instead of calling os.Exit
- Created ConfigError type that collects multiple validation errors
- Updated envIntOrDefault and envBoolOrDefault to accumulate errors
- Added deprecated MustParse() for backward compatibility
- Updated main.go to handle Parse() errors properly
- Use custom FlagSet to allow multiple Parse() calls in tests
- Fixed race condition in resettoken package by adding mutex protection
  for defaultClock global variable
- Added comprehensive tests for error handling paths
- Options package coverage improved from ~0% to 85.4%
Copilot AI review requested due to automatic review settings February 5, 2026 12:29
@gemini-code-assist
Copy link

Summary of Changes

Hello @CybotTM, 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 refactors the application's options parsing mechanism to adhere to more robust and idiomatic Go error handling principles. By transitioning from immediate program termination (os.Exit) to returning errors, the system gains improved testability and flexibility in how configuration issues are managed. The introduction of an aggregated error type further enhances user feedback by reporting all validation problems at once, leading to a more resilient and developer-friendly configuration process.

Highlights

  • Improved Error Handling in Options Package: The Parse() function in the options package now returns (*Opts, error) instead of directly calling os.Exit(), aligning with idiomatic Go practices for library functions. This change makes the configuration parsing more testable and allows the caller (e.g., main()) to decide how to handle configuration errors.
  • New ConfigError Type for Aggregated Validation Errors: A new ConfigError type has been introduced to collect and report multiple configuration validation errors simultaneously. This provides a more comprehensive error message to the user, detailing all issues found during parsing rather than exiting on the first error.
  • Enhanced Environment Variable Parsing Functions: The envIntOrDefault and envBoolOrDefault helper functions have been updated to accumulate validation errors into the ConfigError type instead of logging and exiting. They now return default values on error, allowing parsing to continue and collect all potential issues.
  • Backward Compatibility and Testability Improvements: A MustParse() function has been added for backward compatibility, which retains the original behavior of exiting on error. Additionally, the Parse() function now uses a custom flag.FlagSet to allow for multiple, independent calls to Parse() within tests, significantly improving test isolation and coverage.
  • Race Condition Fix in Reset Token Clock: A race condition in the resettoken package related to the defaultClock global variable has been resolved by introducing a sync.RWMutex to protect access during updates and reads, ensuring thread-safe operations, especially in testing scenarios.
  • Increased Test Coverage for Options Package: The test suite for the options package has been significantly expanded, improving coverage from approximately 0% to 85.4%. New tests cover various error handling paths, including missing required options and invalid environment variable values.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/options/app.go
    • Modified Parse() function signature to return (*Opts, error).
    • Introduced ConfigError struct with Error(), Add(), and HasErrors() methods for collecting multiple validation errors.
    • Updated envIntOrDefault and envBoolOrDefault to accept *ConfigError and record parsing errors, returning default values instead of exiting.
    • Replaced global flag.Parse() with flag.NewFlagSet to enable multiple Parse() calls in tests.
    • Removed direct calls to slog.Error and os.Exit from configuration validation logic.
    • Added MustParse() function for backward compatibility, which calls os.Exit on error.
    • Added ErrMissingRequired variable.
  • internal/options/app_test.go
    • Updated TestEnvIntOrDefault and TestEnvBoolOrDefault to include wantErr flag and assert ConfigError accumulation.
    • Added new test cases for invalid integer and boolean environment variable values.
    • Introduced TestConfigError to verify the functionality of the new ConfigError type.
    • Added TestParseWithMissingRequired, TestParseWithInvalidIntValue, and TestParseWithInvalidBoolValue to thoroughly test Parse() error handling.
  • internal/resettoken/clock.go
    • Added sync.RWMutex (clockMu) to protect the defaultClock global variable.
    • Introduced getClock() function to provide thread-safe access to the current clock.
  • internal/resettoken/clock_test.go
    • Modified setTestClock to use clockMu.Lock() and clockMu.Unlock() for thread-safe updates to defaultClock during testing.
  • internal/resettoken/store.go
    • Updated IsExpired() method to use getClock().Now() for thread-safe time retrieval.
  • main.go
    • Modified main() function to handle the error returned by options.Parse(), logging the error and exiting gracefully.
    • Removed a redundant var err error declaration.
Activity
  • The author changed Parse() to return (*Opts, error) instead of calling os.Exit.
  • A ConfigError type was created to collect multiple validation errors.
  • The envIntOrDefault and envBoolOrDefault functions were updated to accumulate errors.
  • A deprecated MustParse() function was added for backward compatibility.
  • A custom FlagSet is now used to allow multiple Parse() calls in tests.
  • A race condition in the resettoken package was fixed by adding mutex protection for the defaultClock global variable.
  • Comprehensive tests were added for error handling paths.
  • All unit tests pass with race detection (go test ./... -race).
  • Linting passes (golangci-lint run).
  • Options package coverage improved from ~0% to 85.4%.
  • Total project coverage is 81.0%.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

❌ Patch coverage is 90.27778% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.41%. Comparing base (3d66959) to head (1c7691e).
⚠️ Report is 54 commits behind head on main.

Files with missing lines Patch % Lines
internal/options/app.go 89.55% 7 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main     #443       +/-   ##
===========================================
+ Coverage   74.71%   92.41%   +17.69%     
===========================================
  Files          15       15               
  Lines         704      725       +21     
===========================================
+ Hits          526      670      +144     
+ Misses        167       46      -121     
+ Partials       11        9        -2     
Flag Coverage Δ
backend 92.41% <90.27%> (+17.69%) ⬆️
integration 92.41% <90.27%> (+17.69%) ⬆️
unittests 90.06% <90.27%> (+17.76%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/resettoken/clock.go 100.00% <100.00%> (ø)
internal/resettoken/store.go 100.00% <100.00%> (ø)
internal/options/app.go 82.01% <89.55%> (+73.28%) ⬆️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3d66959...1c7691e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@entelligence-ai-pr-reviews entelligence-ai-pr-reviews bot left a comment

Choose a reason for hiding this comment

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

Walkthrough

This PR refactors configuration parsing to improve error handling and testability. The main change replaces immediate exit-on-failure behavior with error accumulation, allowing all configuration issues to be reported simultaneously through a new ConfigError type. The Parse() function now returns errors instead of calling os.Exit(), with a deprecated MustParse() wrapper preserving backward compatibility. Thread-safety improvements were added to the clock implementation in the reset token module using read-write mutexes to prevent race conditions during concurrent access. The main function was updated to handle configuration errors gracefully with proper logging. Comprehensive test coverage was added for all new error handling paths.

Changes

File(s) Summary
internal/options/app.go Refactored configuration parsing to accumulate errors instead of immediate exit. Introduced ConfigError type for collecting multiple validation errors. Modified Parse() to return (*Opts, error) and updated helper functions to accept *ConfigError parameter. Added deprecated MustParse() wrapper for backward compatibility. Replaced os.Exit(1) calls with error collection and return.
internal/options/app_test.go Enhanced test coverage with error handling validation for environment variable parsing. Added test cases for invalid inputs (non-numeric, negative, overflow, invalid booleans). Introduced integration tests for missing required fields and invalid values. Added TestConfigError to validate error accumulation and formatting. Updated existing tests to verify ConfigError state.
internal/resettoken/clock.go Added thread-safety with sync.RWMutex to protect defaultClock variable. Implemented getClock() helper function with read-locking for safe concurrent access.
internal/resettoken/clock_test.go Added mutex locking to setTestClock helper function and its cleanup logic to prevent race conditions during concurrent test clock operations.
internal/resettoken/store.go Refactored IsExpired() method to use getClock() function instead of directly accessing defaultClock for improved testability.
main.go Enhanced error handling for configuration parsing with explicit error checking, logging via slog.Error(), and exit with status code 1 on failure. Moved err variable declaration inline with Parse() call.

Sequence Diagram

This diagram shows the interactions between components:

sequenceDiagram
    participant Caller
    participant Parse
    participant ConfigError
    participant FlagSet
    participant EnvHelpers as envIntOrDefault/envBoolOrDefault
    participant Validators as checkRequired
    participant MustParse

    Note over Caller,MustParse: Configuration Parsing Flow

    alt Direct Parse() call
        Caller->>Parse: Parse()
        activate Parse
        Parse->>Parse: Load .env files
        Parse->>ConfigError: Create new ConfigError{}
        Parse->>FlagSet: NewFlagSet("gopherpass")
        
        loop For each configuration flag
            Parse->>EnvHelpers: envStringOrDefault(name, default)
            EnvHelpers-->>Parse: string value
            Parse->>EnvHelpers: envIntOrDefault(name, default, errs)
            alt Invalid integer format
                EnvHelpers->>ConfigError: Add(error message)
                EnvHelpers-->>Parse: return default value
            else Valid integer
                EnvHelpers-->>Parse: return parsed value
            end
            Parse->>EnvHelpers: envBoolOrDefault(name, default, errs)
            alt Invalid boolean format
                EnvHelpers->>ConfigError: Add(error message)
                EnvHelpers-->>Parse: return default value
            else Valid boolean
                EnvHelpers-->>Parse: return parsed value
            end
        end
        
        Parse->>FlagSet: Parse(os.Args[1:])
        alt Flag parsing error
            FlagSet-->>Parse: error
            Parse->>ConfigError: Add(flag parsing error)
        end
        
        loop For each required field
            Parse->>Validators: checkRequired(name, value, missing)
            Validators-->>Parse: updates missing list
        end
        
        alt Missing required fields
            Parse->>ConfigError: Add(missing options message)
        end
        
        Parse->>ConfigError: HasErrors()
        alt Has errors
            ConfigError-->>Parse: true
            Parse-->>Caller: nil, ConfigError
        else No errors
            ConfigError-->>Parse: false
            Parse->>Parse: Create Opts struct
            Parse-->>Caller: *Opts, nil
        end
        deactivate Parse
    else MustParse() call (backward compatibility)
        Caller->>MustParse: MustParse()
        activate MustParse
        MustParse->>Parse: Parse()
        Parse-->>MustParse: *Opts, error
        alt Error occurred
            MustParse->>MustParse: fmt.Fprintf(stderr, error)
            MustParse->>MustParse: os.Exit(1)
        else Success
            MustParse-->>Caller: *Opts
        end
        deactivate MustParse
    end
Loading

🔗 Cross-Repository Impact Analysis

Enable automatic detection of breaking changes across your dependent repositories. → Set up now

Learn more about Cross-Repository Analysis

What It Does

  • Automatically identifies repositories that depend on this code
  • Analyzes potential breaking changes across your entire codebase
  • Provides risk assessment before merging to prevent cross-repo issues

How to Enable

  1. Visit Settings → Code Management
  2. Configure repository dependencies
  3. Future PRs will automatically include cross-repo impact analysis!

Benefits

  • 🛡️ Prevent breaking changes across repositories
  • 🔍 Catch integration issues before they reach production
  • 📊 Better visibility into your multi-repo architecture

▶️AI Code Reviews for VS Code, Cursor, Windsurf
Install the extension

Note for Windsurf Please change the default marketplace provider to the following in the windsurf settings:

Marketplace Extension Gallery Service URL: https://marketplace.visualstudio.com/_apis/public/gallery

Marketplace Gallery Item URL: https://marketplace.visualstudio.com/items

Entelligence.ai can learn from your feedback. Simply add 👍 / 👎 emojis to teach it your preferences. More shortcuts below

Emoji Descriptions:

  • ⚠️ Potential Issue - May require further investigation.
  • 🔒 Security Vulnerability - Fix to ensure system safety.
  • 💻 Code Improvement - Suggestions to enhance code quality.
  • 🔨 Refactor Suggestion - Recommendations for restructuring code.
  • ℹ️ Others - General comments and information.

Interact with the Bot:

  • Send a message or request using the format:
    @entelligenceai + *your message*
Example: @entelligenceai Can you suggest improvements for this code?
  • Help the Bot learn by providing feedback on its responses.
    @entelligenceai + *feedback*
Example: @entelligenceai Do not comment on `save_auth` function !

Also you can trigger various commands with the bot by doing
@entelligenceai command

The current supported commands are

  1. config - shows the current config
  2. retrigger_review - retriggers the review

More commands to be added soon.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the options package to return errors instead of calling os.Exit, making it more testable and idiomatic. The changes include error aggregation via a ConfigError type, comprehensive test coverage for error paths, and a race condition fix in the resettoken package.

Changes:

  • Refactored Parse() to return (*Opts, error) instead of exiting on errors
  • Introduced ConfigError type to collect and return multiple validation errors
  • Fixed race condition in resettoken package by adding mutex protection for the global clock variable

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
main.go Updated to handle error from options.Parse() and exit gracefully with logging
internal/options/app.go Refactored Parse() to return errors; added ConfigError type and deprecated MustParse() for backward compatibility
internal/options/app_test.go Added comprehensive tests for error handling paths including invalid integers, booleans, and missing required fields
internal/resettoken/clock.go Added sync.RWMutex and getClock() helper for thread-safe access to defaultClock
internal/resettoken/clock_test.go Updated test helper to use mutex when setting/resetting test clock
internal/resettoken/store.go Changed IsExpired() to use getClock() instead of directly accessing defaultClock

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@gemini-code-assist gemini-code-assist bot left a 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 is a significant improvement, refactoring the options parsing to return errors instead of exiting, which greatly enhances testability and follows Go best practices. The introduction of ConfigError to aggregate multiple configuration issues is well-implemented. Additionally, the fix for the race condition in the resettoken package is a crucial improvement for concurrent test execution. I've identified a few minor issues, including a potential bug in integer parsing logic and some dead code, but overall this is excellent work.

- Use strconv.IntSize instead of 16 for proper uint parsing
- Suppress FlagSet stderr output with io.Discard
- Fix typo: "password can include the password" -> "username"
- Fix MustParse comment: says "log.Fatal" but actually prints and exits
- Remove unused ErrMissingRequired variable
- Update test for decimal value instead of overflow (999999 is valid on 64-bit)
@CybotTM CybotTM merged commit df7fc75 into main Feb 5, 2026
17 checks passed
@CybotTM CybotTM deleted the refactor/options-return-errors branch February 5, 2026 12:46
@github-actions github-actions bot added the released:v1.2.0 Released in v1.2.0 label Feb 22, 2026
@github-actions
Copy link
Contributor

Released in v1.2.0

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

Labels

released:v1.2.0 Released in v1.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants