From 227011b834fefbbf321c4b3da1f08bd6b39828b1 Mon Sep 17 00:00:00 2001 From: zhongsongzhi-cmyk Date: Sun, 25 Oct 2026 23:04:53 +0800 Subject: [PATCH] fix(config): make project .env override global config as documented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/config.ts loaded ~/.config/gmgn/.env with override:true and then the cwd .env without override, so the global file always won — the opposite of what Readme, Readme.zh, CLAUDE.md and the gmgn-cooking skill promise. A user who updated GMGN_PRIVATE_KEY in a project .env kept signing with the stale global key and got AUTH_SIGNATURE_INVALID. Load the cwd .env first and the global file second, both without override, so precedence is shell env > project .env > global .env. `config --check` reuses the same loader instead of its own copy. Co-Authored-By: Claude --- CLAUDE.md | 2 ++ Readme.md | 2 +- Readme.zh.md | 2 +- src/commands/config.ts | 5 ++--- src/config.ts | 12 +++++++++--- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 163ebb6..ac5f602 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -97,6 +97,8 @@ Config lookup order (project overrides global): 1. `~/.config/gmgn/.env` — global config, set once for all projects 2. `.env` in the current working directory — project-level override (takes precedence) +Variables already exported in the shell environment override both files. + Required variables: - `GMGN_API_KEY` — apply at https://gmgn.ai/ai - `GMGN_PRIVATE_KEY` — PEM content, required for swap/order commands only diff --git a/Readme.md b/Readme.md index dbbf9d0..314d427 100644 --- a/Readme.md +++ b/Readme.md @@ -329,7 +329,7 @@ cp .env.example .env # Edit .env and fill in your values ``` -Config lookup order: `~/.config/gmgn/.env` → project `.env` (project takes precedence). +Config lookup order: `~/.config/gmgn/.env` → project `.env` (project takes precedence). Variables already exported in your shell override both files. ## 5. Try in AI Clients diff --git a/Readme.zh.md b/Readme.zh.md index 4c0a934..3c2e46e 100644 --- a/Readme.zh.md +++ b/Readme.zh.md @@ -333,7 +333,7 @@ cp .env.example .env # 编辑 .env,填入实际值 ``` -配置加载顺序:`~/.config/gmgn/.env` → 项目 `.env`(项目级优先)。 +配置加载顺序:`~/.config/gmgn/.env` → 项目 `.env`(项目级优先)。Shell 中已 export 的同名环境变量优先级最高,会覆盖两个文件。 ## 5. 在 AI 客户端中使用 diff --git a/src/commands/config.ts b/src/commands/config.ts index 9119f24..916f704 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,10 +1,10 @@ -import { config as loadDotenv } from "dotenv"; import { Command } from "commander"; import * as crypto from "crypto"; import * as fs from "fs"; import * as path from "path"; import * as os from "os"; import { execSync } from "child_process"; +import { loadEnvFiles } from "../config.js"; const GMGN_CONFIG_DIR = path.join(os.homedir(), ".config", "gmgn"); const KEYPAIR_FILE = path.join(GMGN_CONFIG_DIR, "keypair.pem"); @@ -116,8 +116,7 @@ export function registerConfigCommands(program: Command): void { const lang = detectLang(); if (opts.check) { - loadDotenv({ path: ENV_FILE, override: true }); - loadDotenv(); + loadEnvFiles(); process.exit(process.env.GMGN_API_KEY ? 0 : 1); } diff --git a/src/config.ts b/src/config.ts index 9b43f9f..3937d0f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -28,9 +28,15 @@ function enforceCredentialFilePermissions(path: string): void { enforceCredentialFilePermissions(GLOBAL_ENV_PATH); -// Load global config first (~/.config/gmgn/.env, takes precedence), then project .env (supplements only) -loadDotenv({ path: GLOBAL_ENV_PATH, override: true }); -loadDotenv(); +// Precedence: shell environment > project .env (cwd) > global ~/.config/gmgn/.env. +// dotenv never overwrites a variable that is already set, so the higher-priority +// source must be loaded first and the global file only fills in what is missing. +export function loadEnvFiles(): void { + loadDotenv(); + loadDotenv({ path: GLOBAL_ENV_PATH }); +} + +loadEnvFiles(); export interface Config { apiKey: string;