Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3a88812
Initial release
jongpie Nov 5, 2025
5ce3475
Got both tests and transpiled plugin running successfully
jamessimone Nov 5, 2025
2591a25
Got all tests working (again) and added sf config functionality for s…
jamessimone Nov 7, 2025
f74a7f3
Fix linting error
jamessimone Nov 7, 2025
9f8a103
Update to use ConfigAggregator's customConfigMeta property to allow f…
jamessimone Nov 12, 2025
cdd5655
Changed the default behavior to have logging enabled (which can still…
jongpie Dec 5, 2025
27ad57a
Updated prerun.ts to re-use the default export in configMeta
jongpie Dec 5, 2025
9b5114b
Updated dependencies & devDependencies in package.json + added .githu…
jongpie Mar 4, 2026
6f182c7
Cleaned up some of the tests & test setup in prerun.test.ts
jongpie Mar 4, 2026
f79d8d3
Removed a bunch of old/unnecessary files
jongpie Mar 5, 2026
af636fa
Updated devDependencies + npm scripts + add lint-staged & husky for p…
jongpie Mar 5, 2026
fc4497f
Expanded the dotenv.ts command to support flags for specifying an .en…
jongpie Mar 5, 2026
cbf4427
Rewrote README.md - it was wildly out of date, and very poorly writte…
jongpie Mar 5, 2026
eeaf935
Add a build.yml for testing PRs, and corrected build.yml to publish t…
jongpie Mar 5, 2026
faad6c1
Added a few VS Code-specific config files to make it easier to run Je…
jongpie Mar 5, 2026
10deada
Updated environment.ts so existing environment variables are (intenti…
jongpie Mar 10, 2026
4738670
A little bit more clean up of devDependencies & regenerated package-l…
jongpie Mar 10, 2026
9385b52
Updated trigger critier in build.yml
jongpie Mar 10, 2026
ae815e2
Fixed a prettier formatting issue
jongpie Mar 10, 2026
512f557
Updated jest config file to (hopefully) work in GitHub Actions (it pr…
jongpie Mar 10, 2026
ba62c05
Simplified eslint config and fixed a few linting errors
jamessimone Mar 11, 2026
30fc121
Removes unnecessary config passing in lint:verify
jamessimone Mar 12, 2026
176b026
Add additional linting and fixes for said linting
jamessimone Mar 12, 2026
783462e
Consolidated where the default .env path is referenced. Fixed up some…
jamessimone Mar 12, 2026
02994f9
Upgrade node version, clean up config value validator message
jamessimone Mar 12, 2026
967850a
Even more linting
jamessimone Mar 14, 2026
d07e44f
even more cleanup
jamessimone Mar 14, 2026
33079cb
Updated dependencies and modified tsconfig.json in preparation for up…
jamessimone Mar 24, 2026
265b3e1
Updated build.yml to use Node 20 (instead of 18)
jongpie Mar 12, 2026
f4baacb
Renamed the existing command 'dotenv' to 'dotenv inspect', updated te…
jongpie Mar 12, 2026
8f81eff
Fixed an issue in environment to better handle when a directory is (i…
jongpie Mar 12, 2026
1a6975a
Added configMeta.test.ts (all *.ts files in src now have a correspond…
jongpie Mar 12, 2026
451608d
Added new export command to export existing environment variables tha…
jongpie Mar 16, 2026
8eef020
Updated .prettierrc to have 120 print width, updated eslint.config.js…
jongpie Mar 24, 2026
241ec48
Made some improvements to the release packaging and local tooling setup
jongpie Jun 1, 2026
ff6979d
Little bit o' clean up for v1.0 release
jongpie Jun 1, 2026
2771833
Bumped the versions of some github actions
jongpie Jun 1, 2026
463c7aa
Refactored to use named imports for dotenv parse & populate functions…
jongpie Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
allow:
- dependency-type: 'production'
labels:
- 'dependency-update'
38 changes: 38 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Build

on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- 'README.md'
- 'LICENSE'
- '.gitignore'
- '.npmrc'
- '.prettierrc'
- '.prettierignore'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'

- name: Install dependencies
run: npm ci

- name: Verify code with ESLint
run: npm run lint:verify

- name: 'Verify formatting with Prettier'
run: npm run prettier:verify

- name: Build
run: npm run build

- name: Test
run: npm run test:coverage
40 changes: 40 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Publish to NPM

on:
push:
tags:
- 'v*'

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v5

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'

- name: Verify tag matches package.json version
env:
REF_NAME: ${{ github.ref_name }}
run: |
TAG_VERSION="${REF_NAME#v}"
PKG_VERSION="$(node -p "require('./package.json').version")"
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) does not match package.json version ($PKG_VERSION)."
exit 1
fi

- name: Install dependencies
run: npm ci

- name: Publish to npm
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
43 changes: 43 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Environment files
.env*
.sfdx

# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build output
lib/
dist/
build/
oclif.manifest.json

# Logs
logs
*.log

# Coverage directory used by tools like istanbul
coverage/
*.lcov

# IDE files (project shared .vscode/settings.json and extensions.json are committed)
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
.idea/
*.swp
*.swo
*~

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db


1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
11 changes: 11 additions & 0 deletions .lintstagedrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
'*.{ts,js,json,yml,yaml,md}': (filenames) => filenames.map((filename) => `prettier --write '${filename}'`),
'{src,test}/**/*.ts': (filenames) => {
if (filenames.length === 0) {
return [];
}

const quoted = filenames.map((f) => `"${f}"`).join(' ');
return [`eslint --fix ${quoted}`, 'npm run test'];
},
};
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
registry=https://registry.npmjs.org
12 changes: 12 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Dependencies
node_modules/

# Build output
lib/
coverage/
oclif.manifest.json

# Local IDE / agent state
.claude/
.sfdx/
.vscode/
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"printWidth": 120,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
}
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "Orta.vscode-jest"]
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
// Used to ensure that the VS Code extension Orta.vscode-jest correctly shows the repo's tests
"jest.jestCommandLine": "npx jest --runInBand"
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Jonathan Gillespie

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
166 changes: 165 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,165 @@
# sf-dotenv-plugin
# Salesforce CLI DotEnv Plugin

A Salesforce CLI plugin that loads environment variables from `.env` files when you run `sf` commands. No need to export project-specific vars in your shell or OS.

## What It Does

- **Automatic loading**: A prerun hook loads a `.env` file (by default `.env` in the current directory) before every `sf` command. Values from the `.env` file override existing environment variables of the same name; variables not defined in the file are left unchanged.
- **Per-command file**: Use `--env` or `-e` with any command to load a specific file, e.g. `sf --env .env.prod project deploy start`.
- **Inspect loaded vars**: Run `sf dotenv inspect` to see which variables are loaded from `.env`; use `sf dotenv inspect --show-values` to print names and values (with a security warning).
- **Export from `sfdx-project.json`**: Run `sf dotenv export` to scaffold (or update) a `.env` file with every variable referenced by `replaceWithEnv` entries in your `sfdx-project.json`.

## Quick Start

Create a `.env` file in your project root:

```bash
FOO=123
BAR=some other value
```

Run any `sf` command, and the plugin automatically loads the file `.env` (if it exists). In this example of deploying Apex classes, the names of the loaded environment variables are displayed before the command executes.

<img src="./images/prerun-hook-example-deploy-command-output.png" width="500" alt="Environment Variables Automatically Loaded During Apex Class Deployment">

## Installation

```bash
sf plugins install @jongpie/sf-dotenv-cli-plugin
```

**Unsigned plugin**: This is a community plugin, so the CLI may prompt you to confirm installation. In CI/CD or non-interactive use, add the plugin to the CLI allowlist so it installs without a prompt:

| Platform | AllowList Path |
| ----------- | ------------------------------------------------------- |
| Linux/macOS | `$HOME/.config/sf/unsignedPluginAllowList.json` |
| Windows | `%USERPROFILE%\.config\sf\unsignedPluginAllowList.json` |

Example allowlist (array of package names):

```json
["@jongpie/sf-dotenv-cli-plugin"]
```

## Usage: Automatically Load `.env` Files with Any `sf` CLI Commands

**Automatic**: With a `.env` in your project root & the plugin installed, just run a `sf` command. Variables are loaded before the command runs.

**Specific file**: Pass the path of a specific `.env` file to load to the parameter `--env`

```bash
sf org list --env .env.local
sf project deploy start --env .env.production
```

### Using with `sf` CLI's Deployment String Replacements

The Salesforce CLI can [replace placeholders in metadata with environment variables](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_string_replace.htm) during deployment. You define replacements in `sfdx-project.json` (e.g. `replaceWithEnv` pointing at an env var name), and the CLI substitutes those variables when you deploy. This plugin pairs well with that feature: put the replacement values in a `.env` file and they’re loaded automatically before `sf project deploy start` (or `sf deploy metadata`), so the CLI sees the variables without you exporting them in the shell.

**Example:** In `sfdx-project.json` you configure a replacement that uses the env var `NAMED_CREDENTIAL_URL`:

```json
{
"name": "My Salesforce Project",
"packageDirectories": [
{
"path": "force-app",
"default": true
}
],
"replacements": [
{
"filename": "force-app/main/default/namedCredentials/My_Named_Credential.namedCredential-meta.xml",
"stringToReplace": "https://placeholder.example.com",
"replaceWithEnv": "NAMED_CREDENTIAL_URL"
}
]
}
```

In your `.env`, add the environment variable value for `NAMED_CREDENTIAL_URL`:

```bash
NAMED_CREDENTIAL_URL=https://api.my-org.example.com
```

Then run your deploy as usual; the plugin loads `.env` before the command, and the CLI performs the replacement:

```bash
sf project deploy start --source-dir force-app
```

For different environments, use a separate env file and pass it to the command:

```bash
sf project deploy start --source-dir force-app -e .env.production
```

#### Scaffolding a `.env` File from `sfdx-project.json`

Rather than authoring a `.env` file by hand, you can let the plugin generate one from the `replaceWithEnv` entries in `sfdx-project.json`:

```bash
sf dotenv export
```

This reads `sfdx-project.json`, finds every `replaceWithEnv` reference under `replacements`, and writes the keys to a `.env` file in the current directory (default: `.env`). If the output file already exists, only missing keys are appended underneath an `# Auto-added by sf dotenv export` marker — existing entries are left untouched. Any matching variable already set in your shell's environment will be used as the default value; otherwise the key is written with an empty value for you to fill in.

Use `--output-file` (alias `-o`) to write to a different file:

```bash
sf dotenv export --output-file .env.local
sf dotenv export -o .env.production
```

## Usage: Debug & Test with `sf dotenv inspect` Command

**Inspect**: The plugin is primarily focused on automatically running during any `sf` CLI command. But to aid with debugging & testing, you can run the command `sf dotenv inspect` to see how your `.env` files are being loaded.

To see the names of environment variables being loaded, simply run the command:

```bash
sf dotenv inspect
```

This will attempt to load a `.env` file, and displays the names of any variables loaded:

<img src="./images/dotenv-command-default-output.png" width="500" alt="Environment Variables Names Displayed">

> [!WARNING]
> If you want to see the names _and_ values, pass the flag `--show-values`. But doing so will display potentially sensitive values, so use this with caution.

```bash
sf dotenv inspect --env .env.staging --show-values
```

<img src="./images/dotenv-command-show-values-output.png" width="1000" alt="Environment Variables Names & Values Displayed">

More details about this command (shown below) can be seen by running `sf dotenv inspect --help` or `sf dotenv inspect -h`.

```bash
USAGE
$ sf dotenv inspect [--json] [--flags-dir <value>] [-e <value>] [--show-values]

FLAGS
-e, --env=<value> [default: .env] Path to the .env file to load.
--show-values Print the loaded environment variable names and values.

GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.
```

## Configuration

- **Logging**: By default, the plugin logs the _names_ of loaded variables before each command. To turn this off, run:

```bash
sf config set should-log-env false
```

You can also change this setting globally by adding the flag `--global`. To re-enable logging, run: `sf config set should-log-env true`.

- **Plugin-Specific Environment Variables**: A few environment variables can be set in your shell to control plugin behavior:
- `SF_DOTENV_DISABLED` – set to `true` to disable automatic loading.
- `SF_DOTENV_FILE` – path to the `.env` file used by automatic loading (default: `.env` in the current directory).
Loading