Skip to content

Instantly share code, notes, and snippets.

@NuroDev
Last active November 21, 2024 23:22
Show Gist options
  • Save NuroDev/489417db3f93da5dbc4a3b5c5c6071af to your computer and use it in GitHub Desktop.
Save NuroDev/489417db3f93da5dbc4a3b5c5c6071af to your computer and use it in GitHub Desktop.
πŸ”’ Remap environment variables ending in `_FILE` with the contents of the file
import { existsSync } from "@std/fs/exists";
interface LoadOptions {
/**
* Whether to overwrite existing env vars.
*
* @default false
*/
overwrite?: boolean;
/**
* Whether to remove the _FILE env var after loading the value.
*
* @default false
*/
prune?: boolean;
}
/**
* Load environment variables from files.
*
* @description This function will check all environment variables that end with `_FILE`
* and load the value from the file path specified in the variable.
*
* @param [options.overwrite=false] - Whether to overwrite existing env vars.
* @param [options.prune=false] - Whether to remove the _FILE env var after loading the value.
*/
export function load(options: LoadOptions = {}): void {
const { overwrite = false, prune = false } = options;
const env = Deno.env.toObject();
const entries = Object.entries(env).filter(([key]) => key.endsWith("_FILE"));
for (const [key, path] of entries) {
const keyWithoutSuffix = key.replace(/_FILE$/, "");
if (Deno.env.has(keyWithoutSuffix) && !overwrite) continue;
if (!existsSync(path)) continue;
if (!Deno.lstatSync(path).isFile) continue;
const value = Deno.readTextFileSync(path);
Deno.env.set(keyWithoutSuffix, value);
if (prune) Deno.env.delete(key);
}
}
import { existsSync, readFileSync, statSync } from "node:fs";
interface LoadOptions {
/**
* Whether to overwrite existing env vars.
*
* @default false
*/
overwrite?: boolean;
/**
* Whether to remove the _FILE env var after loading the value.
*
* @default false
*/
prune?: boolean;
}
/**
* Load environment variables from files.
*
* @description This function will check all environment variables that end with `_FILE`
* and load the value from the file path specified in the variable.
*
* @param [options.overwrite=false] - Whether to overwrite existing env vars.
* @param [options.prune=false] - Whether to remove the _FILE env var after loading the value.
*/
export function load(options: LoadOptions = {}): void {
const { overwrite = false, prune = false } = options;
const entries = Object.entries(process.env).filter(([key]) =>
key.endsWith("_FILE")
);
for (const [key, path] of entries) {
const keyWithoutSuffix = key.replace(/_FILE$/, "");
if (!!process.env[keyWithoutSuffix] && !overwrite) continue;
if (!existsSync(path)) continue;
if (!statSync(path).isFile()) continue;
const value = readFileSync(path, "utf8").trim();
process.env[keyWithoutSuffix] = value;
if (prune) delete process.env[key];
}
}
@NuroDev
Copy link
Author

NuroDev commented Nov 21, 2024

The primary use case for this helper function is for Docker Secrets.

Currently when using Docker Secrets it will expose the added secrets as files in the /run/secrets directory & the most widely used / accepted pattern is to take your existing environment variable name and add a _FILE suffix at the end to make it clear it is pointing to a file.
However, Node.js or any other JavaScript currently does not account for this (of course). As such this small helper function is designed to fix that by simply finding all environment variables ending with _FILE, attempting to load the value from the value or path & then setting a new environment variable to the contents of the loaded file. With additional options to remove any _FILE environment variables & also whether to overwrite any existing or duplicate environment variables.

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