Skip to content

Commit fe9435f

Browse files
authored
Allow alternate config file names and skipping using a config file by passing —project-ref (#940)
1 parent d7cc2c9 commit fe9435f

File tree

4 files changed

+121
-45
lines changed

4 files changed

+121
-45
lines changed

packages/cli-v3/src/commands/deploy.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { z } from "zod";
1818
import * as packageJson from "../../package.json";
1919
import { CliApiClient } from "../apiClient";
2020
import { CommonCommandOptions } from "../cli/common.js";
21-
import { getConfigPath, readConfig } from "../utilities/configFiles.js";
21+
import { readConfig } from "../utilities/configFiles.js";
2222
import { createTempDir, readJSONFile, writeJSONFile } from "../utilities/fileSystem";
2323
import { printStandloneInitialBanner } from "../utilities/initialBanner.js";
2424
import { detectPackageNameFromImportPath } from "../utilities/installPackages";
@@ -35,6 +35,8 @@ const DeployCommandOptions = CommonCommandOptions.extend({
3535
selfHosted: z.boolean().default(false),
3636
registry: z.string().optional(),
3737
pushImage: z.boolean().default(false),
38+
config: z.string().optional(),
39+
projectRef: z.string().optional(),
3840
});
3941

4042
type DeployCommandOptions = z.infer<typeof DeployCommandOptions>;
@@ -50,6 +52,15 @@ export function configureDeployCommand(program: Command) {
5052
"prod"
5153
)
5254
.option("-T, --skip-typecheck", "Whether to skip the pre-build typecheck")
55+
.option(
56+
"-c, --config <config file>",
57+
"The name of the config file, found at [path]",
58+
"trigger.config.mjs"
59+
)
60+
.option(
61+
"-p, --project-ref <project ref>",
62+
"The project ref. Required if there is no config file."
63+
)
5364
.addOption(
5465
new CommandOption(
5566
"--self-hosted",
@@ -133,8 +144,12 @@ export async function deployCommand(dir: string, anyOptions: unknown) {
133144

134145
await printStandloneInitialBanner(true);
135146

136-
const configPath = await getConfigPath(dir);
137-
const config = await readConfig(configPath);
147+
const { config } = await readConfig(dir, {
148+
configFile: options.data.config,
149+
projectRef: options.data.projectRef,
150+
});
151+
152+
logger.debug("Resolved config", { config });
138153

139154
const apiClient = new CliApiClient(authorization.config.apiUrl, authorization.config.accessToken);
140155

packages/cli-v3/src/commands/dev.tsx

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,22 @@ import { z } from "zod";
2525
import * as packageJson from "../../package.json";
2626
import { CliApiClient } from "../apiClient";
2727
import { CommonCommandOptions } from "../cli/common.js";
28-
import { BackgroundWorker, BackgroundWorkerCoordinator } from "../workers/dev/backgroundWorker.js";
29-
import { getConfigPath, readConfig } from "../utilities/configFiles";
28+
import { readConfig } from "../utilities/configFiles";
3029
import { printStandloneInitialBanner } from "../utilities/initialBanner.js";
30+
import { detectPackageNameFromImportPath } from "../utilities/installPackages";
3131
import { logger } from "../utilities/logger.js";
3232
import { isLoggedIn } from "../utilities/session.js";
3333
import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles";
34-
import { detectPackageNameFromImportPath } from "../utilities/installPackages";
3534
import { UncaughtExceptionError } from "../workers/common/errors";
35+
import { BackgroundWorker, BackgroundWorkerCoordinator } from "../workers/dev/backgroundWorker.js";
3636

3737
let apiClient: CliApiClient | undefined;
3838

3939
const DevCommandOptions = CommonCommandOptions.extend({
4040
debugger: z.boolean().default(false),
4141
debugOtel: z.boolean().default(false),
42+
config: z.string().optional(),
43+
projectRef: z.string().optional(),
4244
});
4345

4446
type DevCommandOptions = z.infer<typeof DevCommandOptions>;
@@ -53,6 +55,15 @@ export function configureDevCommand(program: Command) {
5355
"The log level to use (debug, info, log, warn, error, none)",
5456
"log"
5557
)
58+
.option(
59+
"-c, --config <config file>",
60+
"The name of the config file, found at [path]",
61+
"trigger.config.mjs"
62+
)
63+
.option(
64+
"-p, --project-ref <project ref>",
65+
"The project ref. Required if there is no config file."
66+
)
5667
.option("--debugger", "Enable the debugger")
5768
.option("--debug-otel", "Enable OpenTelemetry debugging")
5869
.action(async (path, options) => {
@@ -111,18 +122,30 @@ async function startDev(
111122

112123
await printStandloneInitialBanner(true);
113124

114-
const configPath = await getConfigPath(dir);
115-
let config = await readConfig(configPath);
116-
117-
watcher = watch(configPath, {
118-
persistent: true,
119-
}).on("change", async (_event) => {
120-
config = await readConfig(configPath);
121-
logger.log(`${basename(configPath)} changed...`);
122-
logger.debug("New config", { config });
123-
rerender(await getDevReactElement(config, authorization));
125+
let config = await readConfig(dir, {
126+
projectRef: options.projectRef,
127+
configFile: options.config,
124128
});
125129

130+
logger.debug("Initial config", { config });
131+
132+
if (config.status === "file") {
133+
watcher = watch(config.path, {
134+
persistent: true,
135+
}).on("change", async (_event) => {
136+
config = await readConfig(dir, { configFile: options.config });
137+
138+
if (config.status === "file") {
139+
logger.log(`${basename(config.path)} changed...`);
140+
logger.debug("New config", { config: config.config });
141+
rerender(await getDevReactElement(config.config, authorization));
142+
} else {
143+
logger.debug("New config", { config: config.config });
144+
rerender(await getDevReactElement(config.config, authorization));
145+
}
146+
});
147+
}
148+
126149
async function getDevReactElement(
127150
configParam: ResolvedConfig,
128151
authorization: { apiUrl: string; accessToken: string }
@@ -132,7 +155,10 @@ async function startDev(
132155

133156
apiClient = new CliApiClient(apiUrl, accessToken);
134157

135-
const devEnv = await apiClient.getProjectEnv({ projectRef: config.project, env: "dev" });
158+
const devEnv = await apiClient.getProjectEnv({
159+
projectRef: config.config.project,
160+
env: "dev",
161+
});
136162

137163
if (!devEnv.success) {
138164
throw new Error(devEnv.error);
@@ -153,7 +179,7 @@ async function startDev(
153179
);
154180
}
155181

156-
const devReactElement = render(await getDevReactElement(config, authorization));
182+
const devReactElement = render(await getDevReactElement(config.config, authorization));
157183

158184
rerender = devReactElement.rerender;
159185

packages/cli-v3/src/utilities/configFiles.ts

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
import { Config, ResolvedConfig } from "@trigger.dev/core/v3";
2+
import { findUp } from "find-up";
13
import { mkdirSync, writeFileSync } from "node:fs";
2-
import path, { dirname } from "node:path";
4+
import path from "node:path";
5+
import { pathToFileURL } from "node:url";
36
import xdgAppPaths from "xdg-app-paths";
47
import { z } from "zod";
8+
import { CLOUD_API_URL, CONFIG_FILES } from "../consts.js";
59
import { readJSONFileSync } from "./fileSystem.js";
610
import { logger } from "./logger.js";
7-
import { findUp } from "find-up";
8-
import { CLOUD_API_URL, CONFIG_FILES } from "../consts.js";
9-
import { pathToFileURL } from "node:url";
1011
import { findTriggerDirectories, resolveTriggerDirectories } from "./taskFiles.js";
11-
import { Config, ResolvedConfig } from "@trigger.dev/core/v3";
1212

1313
function getGlobalConfigFolderPath() {
1414
const configDir = xdgAppPaths("trigger").config();
@@ -51,28 +51,64 @@ export function readAuthConfigFile(): UserAuthConfig | undefined {
5151
}
5252
}
5353

54-
export async function getConfigPath(dir: string): Promise<string> {
55-
const path = await findUp(CONFIG_FILES, { cwd: dir });
54+
async function getConfigPath(dir: string, fileName?: string): Promise<string | undefined> {
55+
return await findUp(fileName ? [fileName] : CONFIG_FILES, { cwd: dir });
56+
}
5657

57-
if (!path) {
58-
throw new Error("No config file found.");
59-
}
58+
export type ReadConfigOptions = {
59+
projectRef?: string;
60+
configFile?: string;
61+
};
62+
63+
export type ReadConfigResult =
64+
| {
65+
status: "file";
66+
config: ResolvedConfig;
67+
path: string;
68+
}
69+
| {
70+
status: "in-memory";
71+
config: ResolvedConfig;
72+
};
73+
74+
export async function readConfig(
75+
dir: string,
76+
options?: ReadConfigOptions
77+
): Promise<ReadConfigResult> {
78+
const absoluteDir = path.resolve(process.cwd(), dir);
79+
80+
logger.debug("Searching for the config file", {
81+
dir,
82+
options,
83+
absoluteDir,
84+
});
6085

61-
return path;
62-
}
86+
const configPath = await getConfigPath(dir, options?.configFile);
6387

64-
export async function readConfig(path: string): Promise<ResolvedConfig> {
65-
try {
66-
// import the config file
67-
const userConfigModule = await import(`${pathToFileURL(path).href}?_ts=${Date.now()}`);
68-
const rawConfig = await normalizeConfig(userConfigModule ? userConfigModule.default : {});
69-
const config = Config.parse(rawConfig);
88+
if (!configPath) {
89+
if (options?.projectRef) {
90+
const rawConfig = await normalizeConfig({ project: options.projectRef });
91+
const config = Config.parse(rawConfig);
7092

71-
return resolveConfig(path, config);
72-
} catch (error) {
73-
console.error(`Failed to load config file at ${path}`);
74-
throw error;
93+
return {
94+
status: "in-memory",
95+
config: await resolveConfig(absoluteDir, config),
96+
};
97+
} else {
98+
throw new Error(`Config file not found in ${absoluteDir} or any parent directory.`);
99+
}
75100
}
101+
102+
// import the config file
103+
const userConfigModule = await import(`${pathToFileURL(configPath).href}?_ts=${Date.now()}`);
104+
const rawConfig = await normalizeConfig(userConfigModule ? userConfigModule.default : {});
105+
const config = Config.parse(rawConfig);
106+
107+
return {
108+
status: "file",
109+
config: await resolveConfig(absoluteDir, config),
110+
path: configPath,
111+
};
76112
}
77113

78114
export async function resolveConfig(path: string, config: Config): Promise<ResolvedConfig> {
@@ -87,7 +123,7 @@ export async function resolveConfig(path: string, config: Config): Promise<Resol
87123
}
88124

89125
if (!config.projectDir) {
90-
config.projectDir = dirname(path);
126+
config.projectDir = path;
91127
}
92128

93129
return config as ResolvedConfig;

packages/cli-v3/src/utilities/taskFiles.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import { ResolvedConfig } from "@trigger.dev/core/v3";
12
import fs from "node:fs";
2-
import { dirname, join, relative, resolve } from "node:path";
3+
import { join, relative, resolve } from "node:path";
34
import { TaskFile } from "../types";
4-
import { ResolvedConfig } from "@trigger.dev/core/v3";
55

66
export function createTaskFileImports(taskFiles: TaskFile[]) {
77
return taskFiles
@@ -45,8 +45,7 @@ export function resolveTriggerDirectories(dirs: string[]): string[] {
4545

4646
const IGNORED_DIRS = ["node_modules", ".git", "dist", "build"];
4747

48-
export async function findTriggerDirectories(filePath: string): Promise<string[]> {
49-
const dirPath = dirname(filePath);
48+
export async function findTriggerDirectories(dirPath: string): Promise<string[]> {
5049
return getTriggerDirectories(dirPath);
5150
}
5251

0 commit comments

Comments
 (0)