Add ability to load development-only environment variables#22
Add ability to load development-only environment variables#22
Conversation
- Added `addDevEnvVariables` util function to allow the extension to load the local `.env` file from the project root if it exists, using Node's `loadEnvFile` API, and load the environment variables into `process.env`. The `.env` file will only exist on the development machine and is crucial to properly test the extension in vscode's extension testing environment. This fixes the ability to mistakenly commit the dev testing path to git. As seen in commit be9d972 and removed in commit 9ff0857 of PR #20. With the .env file, this will never happen again. - Added the `addDevEnvVariables` function call to the top of the `extension.ts` file so it is the first thing it does and the env variables will be available in all files in development. - Added the usage of the `DEV_USER_EXTENSIONS_PATH` env variable to `ExtensionData::setExtensionDiscoveryPaths` method. So that when its available to use, it will override the `userExtensionsPath` variable with the path set in the env variable.
There was a problem hiding this comment.
Pull request overview
Adds developer-focused configuration hooks by loading local .env variables and allowing an environment-variable override for the user extensions discovery path.
Changes:
- Introduces
addDevEnvVariables()to load a local.envfile at startup. - Allows overriding the computed
userExtensionsPathviaDEV_USER_EXTENSIONS_PATH. - Ignores
.envin git to avoid committing local secrets.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/utils.ts |
Adds .env loading helper using process.loadEnvFile. |
src/extension.ts |
Invokes .env loading during extension module initialization. |
src/extensionData.ts |
Uses DEV_USER_EXTENSIONS_PATH to override extension discovery path. |
.gitignore |
Adds .env to ignored files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Try to load the local .env file from the project root. | ||
| try { | ||
| process.loadEnvFile(path.join(__dirname, "../../.env")); | ||
| } catch (error) { | ||
| // Ignore errors if the .env file doesn't exist |
There was a problem hiding this comment.
addDevEnvVariables silently swallows all errors from process.loadEnvFile, but the comment says it only ignores missing .env. This will also hide parse/permission errors and even the case where process.loadEnvFile is unavailable at runtime. Consider: (1) checking typeof process.loadEnvFile === "function" before calling, (2) only ignoring ENOENT (or pre-checking with fs.existsSync), and (3) logging unexpected errors at least at debug/warn level so dev setups fail loudly when the .env exists but is invalid.
| // Try to load the local .env file from the project root. | |
| try { | |
| process.loadEnvFile(path.join(__dirname, "../../.env")); | |
| } catch (error) { | |
| // Ignore errors if the .env file doesn't exist | |
| // Path to the local .env file in the project root. | |
| const envPath = path.join(__dirname, "../../.env"); | |
| // If process.loadEnvFile is not available (older Node / different runtime), | |
| // log and skip loading the .env file. | |
| const loadEnvFile = (process as any).loadEnvFile; | |
| if (typeof loadEnvFile !== "function") { | |
| if (typeof logger?.debug === "function") { | |
| logger.debug(`process.loadEnvFile is not available; skipping loading "${envPath}".`); | |
| } | |
| return; | |
| } | |
| // If the .env file does not exist, silently ignore (original behaviour). | |
| if (!fs.existsSync(envPath)) { | |
| return; | |
| } | |
| // Try to load the local .env file from the project root. | |
| try { | |
| loadEnvFile(envPath); | |
| } catch (error) { | |
| // Log unexpected errors (parse/permission/etc.) so they don't fail silently. | |
| const message = error instanceof Error ? error.message : String(error); | |
| if (typeof logger?.warn === "function") { | |
| logger.warn(`Failed to load development .env file at "${envPath}": ${message}`); | |
| } |
| import {addDevEnvVariables} from "./utils"; | ||
|
|
||
| addDevEnvVariables(); | ||
|
|
||
| const extensionData = new ExtensionData(); |
There was a problem hiding this comment.
addDevEnvVariables() is executed at module import time, which creates a global side effect (mutating process.env) even when the extension module is imported by tests or tooling. If this is intended to be development-only (per PR description), consider calling it inside activate() and gating it with context.extensionMode === vscode.ExtensionMode.Development (and/or Test) so production installs don’t unexpectedly load an .env file if one exists.
| // Get the DEV_USER_EXTENSIONS_PATH env variable if it exists. | ||
| const devUserExtensionsPath = process.env.DEV_USER_EXTENSIONS_PATH; | ||
|
|
||
| // The path to the user extensions. | ||
| // | ||
| // On Windows/Linux/Mac: ~/.vscode[-server|remote]/extensions | ||
| // On WSL: ~/.vscode-[server|remote]/extensions | ||
| const userExtensionsPath = isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.extensionPath, "../"); | ||
| // | ||
| // Because the extensionPath is created from __dirname and retrieves where this extension | ||
| // is located, in extension testing/development mode, this path will point to the local | ||
| // development path, not the actual user extensions path. So we use the custom | ||
| // DEV_USER_EXTENSIONS_PATH env variable to override it. | ||
| const userExtensionsPath = | ||
| devUserExtensionsPath || (isWsl ? path.join(vscode.env.appRoot, "../../", "extensions") : path.join(this.extensionPath, "../")); | ||
|
|
There was a problem hiding this comment.
DEV_USER_EXTENSIONS_PATH overrides the discovery directory without validation. If it’s set to a non-existent/invalid directory (easy to do when loaded from .env), fs.readdirSync in Configuration.readExtensionsFromDirectory() will throw and can break activation. Consider verifying the path exists/is a directory (and perhaps path.resolve/trim) before using it, and falling back to the default path with a logged warning when invalid.
|
@yCodeTech I've opened a new pull request, #23, to work on those changes. Once the pull request is ready, I'll request review from you. |
This pull request introduces support for loading development environment variables from a local
.envfile, and allows overriding the user extensions path for development and testing purposes. The changes improve the developer experience by making it easier to configure local environments and test extensions with custom paths, and prevents the unintentional committing of the changed testing path.Development environment support:
addDevEnvVariablesinutils.tsto load variables from a local.envfile at startup. This function is called at the beginning of the extension's activation inextension.tsto ensure environment variables are available early.Extension path configuration:
ExtensionDatato check for aDEV_USER_EXTENSIONS_PATHenvironment variable. If set, this value overrides the default user extensions path, making it easier to test the extension in development environments.