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:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
allow CLI dev to use injected environment variable
|
||||
@@ -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`);
|
||||
|
||||
@@ -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`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user