feat: allow CLI dev to use injected environment variables (#532)

* feat: allow CLI dev to use injected environment variables

* Remove console.log

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
This commit is contained in:
Hemachandar
2023-10-06 18:48:58 +05:30
committed by GitHub
parent 63a65dabe4
commit be9b51131d
4 changed files with 125 additions and 56 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli": patch
---
allow CLI dev to use injected environment variable
+3 -3
View File
@@ -79,8 +79,8 @@ export async function devCommand(path: string, anyOptions: any) {
telemetryClient.dev.failed("missing_api_key", resolvedOptions);
return;
}
const { apiUrl, envFile, apiKey } = apiDetails;
logger.success(`✔️ [trigger.dev] Found API Key in ${envFile} file`);
const { apiUrl, apiKey, apiKeySource } = apiDetails;
logger.success(`✔️ [trigger.dev] Found API Key in ${apiKeySource}`);
//verify that the endpoint can be reached
const verifiedEndpoint = await verifyEndpoint(resolvedOptions, endpointId, apiKey, framework);
@@ -118,7 +118,7 @@ export async function devCommand(path: string, anyOptions: any) {
const refreshedEndpointId = await getEndpointIdFromPackageJson(resolvedPath, resolvedOptions);
// Read from env file to get the TRIGGER_API_KEY and TRIGGER_API_URL
const apiDetails = await getTriggerApiDetails(resolvedPath, envFile);
const apiDetails = await getTriggerApiDetails(resolvedPath, resolvedOptions.envFile);
if (!apiDetails) {
connectingSpinner.fail(`[trigger.dev] Failed to connect: Missing API Key`);
+15 -53
View File
@@ -1,66 +1,23 @@
import pathModule from "path";
import { pathExists, readFile } from "./fileSystem";
import { logger } from "./logger";
import dotenv from "dotenv";
import { CLOUD_API_URL } from "../consts";
import { checkApiKeyIsDevServer } from "./getApiKeyType";
export async function readEnvFilesWithBackups(
path: string,
envFile: string,
backups: string[]
): Promise<{ content: string; fileName: string } | undefined> {
const envFilePath = pathModule.join(path, envFile);
const envFileExists = await pathExists(envFilePath);
if (envFileExists) {
const content = await readFile(envFilePath);
return { content, fileName: envFile };
}
for (const backup of backups) {
const backupPath = pathModule.join(path, backup);
const backupExists = await pathExists(backupPath);
if (backupExists) {
const content = await readFile(backupPath);
return { content, fileName: backup };
}
}
return;
}
import { readEnvVariables } from "./readEnvVariables";
export async function getTriggerApiDetails(path: string, envFile: string) {
const resolvedEnvFile = await readEnvFilesWithBackups(path, envFile, [
".env",
".env.local",
".env.development.local",
]);
const envVarsToRead = ["TRIGGER_API_KEY", "TRIGGER_API_URL"];
const resolvedEnvVars = await readEnvVariables(path, envFile, envVarsToRead);
if (!resolvedEnvFile) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
const parsedEnvFile = dotenv.parse(resolvedEnvFile.content);
if (!parsedEnvFile) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
return;
}
const apiKey = parsedEnvFile.TRIGGER_API_KEY;
const apiUrl = parsedEnvFile.TRIGGER_API_URL;
const apiKey = resolvedEnvVars.TRIGGER_API_KEY;
const apiUrl = resolvedEnvVars.TRIGGER_API_URL;
if (!apiKey) {
logger.error(`You must add TRIGGER_API_KEY to your ${envFile} file.`);
logger.error(
`You must add TRIGGER_API_KEY to your ${envFile} file or set as runtime environment variable.`
);
return;
}
const result = checkApiKeyIsDevServer(apiKey);
const result = checkApiKeyIsDevServer(apiKey.value);
if (!result.success) {
if (result.type) {
@@ -75,5 +32,10 @@ export async function getTriggerApiDetails(path: string, envFile: string) {
return;
}
return { apiKey, apiUrl: apiUrl ?? CLOUD_API_URL, envFile: resolvedEnvFile.fileName };
return {
apiKey: apiKey.value,
apiUrl: apiUrl?.value ?? CLOUD_API_URL,
apiKeySource:
apiKey.source.type === "runtime" ? "process runtime" : `${apiKey.source.name} file`,
};
}
+102
View File
@@ -0,0 +1,102 @@
import pathModule from "path";
import { pathExists, readFile } from "./fileSystem";
import dotenv from "dotenv";
const ENV_FILES_FALLBACK = [".env", ".env.local", ".env.development.local"];
export type EnvVarSourceRuntime = {
type: "runtime";
};
export type EnvVarSourceFile = {
type: "file";
name: string;
};
export type EnvVarSource = EnvVarSourceRuntime | EnvVarSourceFile;
export type EnvironmentVariable = {
value: string;
source: EnvVarSource;
};
export type EnvironmentVariables = {
[name: string]: EnvironmentVariable | undefined;
};
// Reads `varsToRead` from `process.env` and `envFile` (with fallbacks).
// `process.env` takes precedence over the `envFile`.
export async function readEnvVariables(
path: string,
envFile: string,
varsToRead: string[]
): Promise<EnvironmentVariables> {
const resolvedEnvFile = await readEnvFilesWithBackups(path, envFile);
const parsedEnvFile = resolvedEnvFile
? { output: dotenv.parse(resolvedEnvFile.content), filename: resolvedEnvFile.fileName }
: {};
return Object.fromEntries(
varsToRead.map((envVar) => [
envVar,
readFromRuntime(envVar) ?? readFromFile(envVar, parsedEnvFile),
])
);
}
async function readEnvFilesWithBackups(
path: string,
envFile: string
): Promise<{ content: string; fileName: string } | undefined> {
const envFilePath = pathModule.join(path, envFile);
const envFileExists = await pathExists(envFilePath);
if (envFileExists) {
const content = await readFile(envFilePath);
return { content, fileName: envFile };
}
for (const fallBack of ENV_FILES_FALLBACK) {
const fallbackPath = pathModule.join(path, fallBack);
const fallbackExists = await pathExists(fallbackPath);
if (fallbackExists) {
const content = await readFile(fallbackPath);
return { content, fileName: fallBack };
}
}
return;
}
function readFromRuntime(envVar: string): EnvironmentVariable | undefined {
const val = process.env[envVar];
if (!val) {
return;
}
return {
value: val,
source: {
type: "runtime",
} as EnvVarSourceRuntime,
};
}
function readFromFile(
envVar: string,
parsedEnvFile: { output?: dotenv.DotenvParseOutput; filename?: string }
): EnvironmentVariable | undefined {
const val = parsedEnvFile.output ? parsedEnvFile.output[envVar] : undefined;
if (!val) {
return;
}
return {
value: val,
source: {
type: "file",
name: parsedEnvFile.filename,
} as EnvVarSourceFile,
};
}