Skip to content
This repository was archived by the owner on Mar 29, 2026. It is now read-only.

🛠️ Refactor: Extract parsing logic and remove global state#39

Draft
lucasew wants to merge 4 commits into
masterfrom
refactor-extract-parsing-logic-1014517945792919149
Draft

🛠️ Refactor: Extract parsing logic and remove global state#39
lucasew wants to merge 4 commits into
masterfrom
refactor-extract-parsing-logic-1014517945792919149

Conversation

@lucasew

@lucasew lucasew commented Mar 7, 2026

Copy link
Copy Markdown
Collaborator

This PR refactors the entrypoint of the project by extracting parsing logic and eliminating global state to improve internal structure and adhere to software engineering principles.

Context

main.go previously contained both process lifecycle execution and .env parsing logic, and additionally utilized a global map env which decreased functional purity and increased cognitive load.

Solution

  • Extraction (Extract Method/File): Extracted parseEnvTerm and mergeEnv to cmd/dotenv/parser.go. As described by Martin in Clean Code, modules should adhere to the Single Responsibility Principle. main.go should handle CLI flags, the divider (--), and executing the subprocess, while parser.go should isolate parsing logic (via strings and godotenv).
  • Remove Global State: Initialized the env map locally in main() and explicitly passed it to parseEnvTerm and mergeEnv by reference. This makes the functions purer and more testable.
  • Created AGENTS.md: Codified the project conventions, centralized error reporting (handleError), building instructions, and mise setup rules to govern future changes.

Assumptions

  • The behavior of @.env overriding other arguments remains consistent as it was previously executed at the end of argument processing.
  • Extracting parsing into a sibling file parser.go within the main package does not violate Nix build rules for cmd/dotenv as go build ./cmd/dotenv encompasses the entire package.

Alternatives Not Chosen

  • Creating a separate package (e.g., pkg/parser) for parsing logic. This was avoided because the parsing logic is tightly coupled to the specific CLI structure and sparse directories (single files with no reason to be isolated) should be merged/kept within the domain package.

How To Pivot

  • If the parsing logic needs to become a reusable library across multiple commands or binaries in the future, parser.go can be easily relocated to a shared internal package (e.g., internal/envparser).
  • If you wish to revert to global state, simply recreate var env = map[string]string{} in main.go and remove the env map[string]string parameter from the parsing signatures.

Next Knobs

  • The handleError implementation in main.go could be expanded to support Sentry or other telemetry frameworks if necessary.
  • The go fmt enforcement documented in AGENTS.md can be integrated directly into a GitHub Actions step.

PR created automatically by Jules for task 1014517945792919149 started by @lucasew

Extracted `parseEnvTerm` and `mergeEnv` from `main.go` into `parser.go` to adhere to the Single Responsibility Principle, and eliminated the global `env` variable. Also documented project conventions in `AGENTS.md`.

Co-authored-by: lucasew <15693688+lucasew@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, 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 project's entry point by separating parsing logic into its own module and eliminating global state. These changes improve the internal structure, enhance functional purity, and increase testability, aligning the codebase with better software engineering principles.

Highlights

  • Parsing Logic Extracted: Moved environment variable parsing and merging functions (parseEnvTerm and mergeEnv) from main.go to a new dedicated file, parser.go, to improve modularity and adhere to the Single Responsibility Principle.
  • Global State Eliminated: Replaced the global env map with a locally initialized map within the main() function, passing it explicitly to relevant functions. This enhances functional purity and testability by removing shared mutable state.
  • Project Conventions Documented: Introduced a new AGENTS.md file to codify project conventions, including guidelines on minimizing global state, centralized error reporting, naming, directory structure, code style, and build/test procedures.
Changelog
  • AGENTS.md
    • Added a new file documenting project conventions, including philosophy on global state, centralized error reporting, naming, directory structure, code style, and build/test instructions.
  • cmd/dotenv/main.go
    • Removed the global env map declaration.
    • Removed the mergeEnv and parseEnvTerm functions, which were moved to parser.go.
    • Updated the main function to initialize env locally and pass it as an argument to parseEnvTerm.
    • Removed the github.com/joho/godotenv import, as parsing logic was moved.
  • cmd/dotenv/parser.go
    • Added a new file containing the mergeEnv and parseEnvTerm functions.
    • Included the github.com/joho/godotenv import for parsing functionality.
Activity
  • The pull request was automatically created by Jules for a task started by @lucasew.
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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This PR is a solid refactoring that improves the project's structure by extracting parsing logic and removing global state, making the code more modular and testable. However, a significant security vulnerability was identified: the application automatically loads a .env file from the current working directory, which could allow an attacker to execute arbitrary code if the tool is run in an untrusted directory. Additionally, several critical issues were found, including two potential panics that could lead to a denial of service with specific malformed inputs. Recommendations include removing the automatic .env loading and adding proper input validation to prevent crashes and enhance robustness.

Comment thread cmd/dotenv/parser.go
Comment on lines +20 to +21
f, err := os.Open(filename)
defer f.Close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

There's a critical issue here that will cause a panic. The defer f.Close() is registered before checking the error from os.Open. If os.Open fails (e.g., file not found), f will be nil, and the deferred call to f.Close() will cause a nil pointer dereference. The defer statement must be moved to after the error check.

Here is the corrected code:

f, err := os.Open(filename)
if err != nil {
    return err
}
defer f.Close()

Comment thread cmd/dotenv/main.go
if !foundDivider {
handleError(fmt.Errorf("missing divider (--)"))
}
parseEnvTerm("@.env", env)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

This line automatically loads environment variables from a .env file in the current working directory, which is a significant security vulnerability. An attacker could inject dangerous environment variables, leading to arbitrary code execution or privilege escalation. The current implementation also silently swallows errors if the .env file is malformed, which should be addressed if automatic loading were to be kept. However, it is recommended to remove this automatic loading for security reasons.

Suggested change
parseEnvTerm("@.env", env)
// parseEnvTerm("@.env", env) // Removed for security: automatic loading of .env from CWD is dangerous.

Comment thread cmd/dotenv/main.go
handleError(fmt.Errorf("missing divider (--)"))
}
parseEnvTerm("@.env", env)
cmd := exec.Command(command[0], command[1:]...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

This line can cause a panic and crash the application if no command is provided after the -- divider, due to an out-of-bounds slice access when calling exec.Command(command[0], ...). This can lead to a denial of service. A check should be added to ensure the command slice is not empty before accessing its elements to prevent this vulnerability.

if len(command) == 0 {
	handleError(fmt.Errorf("no command provided"))
}
cmd := exec.Command(command[0], command[1:]...)

Comment thread cmd/dotenv/parser.go
}

func parseEnvTerm(term string, env map[string]string) error {
if term[0] == '@' {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The parseEnvTerm function can panic and crash the application if an empty string is provided as an argument (e.g., dotenv "" -- ls), because it accesses term[0] without checking if term is empty. This can lead to a denial of service. A check for an empty term string should be added at the beginning of the function to prevent this.

Suggested change
if term[0] == '@' {
if term == "" {
return nil
}
if term[0] == '@' {

Comment thread cmd/dotenv/parser.go
Comment on lines +12 to +14
for k := range m {
env[k] = m[k]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This loop can be written more idiomatically and efficiently by ranging over both the key and value of the map. This avoids a second map lookup for the value inside the loop.

Suggested change
for k := range m {
env[k] = m[k]
}
for k, v := range m {
env[k] = v
}

Comment thread cmd/dotenv/parser.go
}
if strings.HasPrefix(term, "--") {
termBody := term[2:]
elems := strings.Split(termBody, "=")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using strings.Split prevents environment variable values from containing an = character. For example, --VAR="foo=bar" would be incorrectly parsed. To make the parsing more robust and allow = in values, you should use strings.SplitN with a count of 2.

Suggested change
elems := strings.Split(termBody, "=")
elems := strings.SplitN(termBody, "=", 2)

google-labs-jules Bot and others added 3 commits March 7, 2026 09:54
Extracted `parseEnvTerm` and `mergeEnv` from `main.go` into `parser.go` to adhere to the Single Responsibility Principle, and eliminated the global `env` variable. Documented project conventions in `AGENTS.md`. Fixed CI build failure by configuring `subPackages` in `package.nix`.

Co-authored-by: lucasew <15693688+lucasew@users.noreply.github.com>
Extracted `parseEnvTerm` and `mergeEnv` from `main.go` into `parser.go` to adhere to the Single Responsibility Principle, and eliminated the global `env` variable. Documented project conventions in `AGENTS.md`. Fixed CI build failure by configuring `subPackages` in `package.nix` and pointing github actions to `cmd/dotenv` instead of `cmd/dotenv/main.go`.

Co-authored-by: lucasew <15693688+lucasew@users.noreply.github.com>
Extracted `parseEnvTerm` and `mergeEnv` from `main.go` into `parser.go` to adhere to the Single Responsibility Principle, and eliminated the global `env` variable. Documented project conventions in `AGENTS.md`. Fixed CI build failure by configuring `subPackages` in `package.nix` and pointing GitHub Actions (`autorelease.yml`) to `./cmd/dotenv` instead of explicitly `./cmd/dotenv/main.go`.

Co-authored-by: lucasew <15693688+lucasew@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant