Skip to content

Commit

Permalink
feat: allow CLI dev to use injected environment variables (#532)
Browse files Browse the repository at this point in the history
* feat: allow CLI dev to use injected environment variables

* Remove console.log

---------

Co-authored-by: Eric Allam <[email protected]>
  • Loading branch information
hmacr and ericallam authored Oct 6, 2023
1 parent 63a65da commit be9b511
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 56 deletions.
5 changes: 5 additions & 0 deletions .changeset/tidy-toys-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli": patch
---

allow CLI dev to use injected environment variable
6 changes: 3 additions & 3 deletions packages/cli/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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`);
Expand Down
68 changes: 15 additions & 53 deletions packages/cli/src/utils/getTriggerApiDetails.ts
Original file line number Diff line number Diff line change
@@ -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",
]);

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 envVarsToRead = ["TRIGGER_API_KEY", "TRIGGER_API_URL"];
const resolvedEnvVars = await readEnvVariables(path, envFile, envVarsToRead);

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) {
Expand All @@ -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 changes: 102 additions & 0 deletions packages/cli/src/utils/readEnvVariables.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}

0 comments on commit be9b511

Please sign in to comment.