Cli v3 e2e: fixtures (#1184)
* Run tests * Remove useless comments * Fix readme * Add fixtures for config step * Add fixture for additionalDependencies name parsing scope issue * Add URL to commit fix * Some fixtures will only use 1 package manager * Add resolve-trigger-deps fixture * use rimraf in test setup * Add legacy-peer-deps fixture * Use vitest fixtures * Add nested peer dep locking issue * Add missing peer dep resolving fixture * Get rid of global namespace, fix parallelism * Add console logs to debug log level * Add changeset * Remove comments * Update README.md
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"trigger.dev": patch
|
||||
---
|
||||
|
||||
Add e2e fixtures corresponding to past issues
|
||||
Implement e2e suite parallelism
|
||||
Enhance log level for specific e2e suite messages
|
||||
@@ -92,7 +92,15 @@ This will test your fixture project, and generate outputs in the `packages/cli-v
|
||||
|
||||
This is necessary to allow to use `yarn` without having a warning on the current project being a `pnpm` project.
|
||||
|
||||
5. Install the fixture dependencies and generate lockfiles.
|
||||
5. Add the following `.yarnrc.yaml` in your fixture folder.
|
||||
|
||||
This will avoid having `.pnp.cjs` and `.pnp.loader.mjs` and keep versioned files to a minimum.
|
||||
|
||||
```yaml .yarnrc.yml
|
||||
nodeLinker: node-modules
|
||||
```
|
||||
|
||||
6. Install the fixture dependencies and generate lockfiles.
|
||||
|
||||
Like you would in any project.
|
||||
E.g. if your fixture contains a trigger task that uses the `jsdom` library:
|
||||
@@ -105,19 +113,19 @@ This will test your fixture project, and generate outputs in the `packages/cli-v
|
||||
|
||||
> This will update the `package.json` and generate the `pnpm-lock.yaml` file.
|
||||
|
||||
6. To run the test suite against multiple package manager, we need to generate the other lockfiles.
|
||||
7. To run the test suite against multiple package manager, we need to generate the other lockfiles.
|
||||
|
||||
```sh
|
||||
cd packages/cli-v3/e2e/fixtures/<fixture-name>
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
rm -rf node_modules
|
||||
corepack use yarn # will update the yarn lockfile
|
||||
corepack use yarn@4.2.2 # will update the yarn lockfile
|
||||
```
|
||||
|
||||
> Do it in this order, otherwise `npm install` will update the existing `yarn.lock` file with legacy version 1.
|
||||
|
||||
7. Create a new `packages/cli-v3/e2e/fixtures/trigger` folder, and create a trigger task in it.
|
||||
8. Create a new `packages/cli-v3/e2e/fixtures/trigger` folder, and create a trigger task in it.
|
||||
|
||||
Here is an example:
|
||||
|
||||
@@ -132,7 +140,7 @@ This will test your fixture project, and generate outputs in the `packages/cli-v
|
||||
});
|
||||
```
|
||||
|
||||
8. Add a trigger configuration file.
|
||||
9. Add a trigger configuration file.
|
||||
|
||||
The configuration file is mandatory here, the E2E suite does not execute `trigger.dev` commands.
|
||||
|
||||
@@ -145,33 +153,18 @@ This will test your fixture project, and generate outputs in the `packages/cli-v
|
||||
|
||||
> The project reference can be anything here, as the suite runs locally without connecting to the platform.
|
||||
|
||||
9. Commit your changes.
|
||||
10. Commit your changes.
|
||||
|
||||
10. Add your fixture test configuration in `testCases.json`.
|
||||
11. Add your fixture test configuration in `fixtures.config.js`.
|
||||
|
||||
```json testCases.json
|
||||
[
|
||||
...
|
||||
```javascript fixtures.config.js
|
||||
export const fixturesConfig = [
|
||||
// ...
|
||||
{
|
||||
"name": "<fixture-name>",
|
||||
id: "<fixture-name>",
|
||||
},
|
||||
...
|
||||
]
|
||||
// ...
|
||||
];
|
||||
```
|
||||
|
||||
You can configure your test case by adding other properties to the JSON object. Here is the `TestCase` type for reference:
|
||||
|
||||
```typescript
|
||||
type TestCase = {
|
||||
name: string;
|
||||
skipTypecheck?: boolean;
|
||||
wantConfigNotFoundError?: boolean;
|
||||
wantBadConfigError?: boolean;
|
||||
wantCompilationError?: boolean;
|
||||
wantWorkerError?: boolean;
|
||||
wantDependenciesError?: boolean;
|
||||
wantInstallationError?: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
> You might expect a specific error at a specific test, so use those configuration option at your discretion.
|
||||
> You might expect a specific error for a specific test, so use those configuration option at your discretion.
|
||||
|
||||
@@ -33,19 +33,9 @@ export async function compile(options: CompileOptions) {
|
||||
} = options;
|
||||
const configPath =
|
||||
options.resolvedConfig.status === "file" ? options.resolvedConfig.path : undefined;
|
||||
|
||||
// COPIED FROM compileProject()
|
||||
// const compileSpinner = spinner();
|
||||
// compileSpinner.start(`Building project in ${config.projectDir}`);
|
||||
|
||||
const taskFiles = await gatherTaskFiles(config);
|
||||
const workerFacade = readFileSync(
|
||||
resolve("./dist/workers/prod/worker-facade.js"),
|
||||
// join(cliRootPath(), "workers", "prod", "worker-facade.js"),
|
||||
"utf-8"
|
||||
);
|
||||
const workerFacade = readFileSync(resolve("./dist/workers/prod/worker-facade.js"), "utf-8");
|
||||
|
||||
// const workerSetupPath = join(cliRootPath(), "workers", "prod", "worker-setup.js");
|
||||
const workerSetupPath = resolve("./dist/workers/prod/worker-setup.js");
|
||||
|
||||
let workerContents = workerFacade
|
||||
@@ -74,7 +64,6 @@ export async function compile(options: CompileOptions) {
|
||||
const result = await build({
|
||||
stdin: {
|
||||
contents: workerContents,
|
||||
// resolveDir: process.cwd(),
|
||||
resolveDir: config.projectDir,
|
||||
sourcefile: "__entryPoint.ts",
|
||||
},
|
||||
@@ -87,11 +76,7 @@ export async function compile(options: CompileOptions) {
|
||||
platform: "node",
|
||||
format: "cjs", // This is needed to support opentelemetry instrumentation that uses module patching
|
||||
target: ["node18", "es2020"],
|
||||
// outdir: "out",
|
||||
outdir: resolve(config.projectDir, "out"),
|
||||
// banner: {
|
||||
// js: `process.on("uncaughtException", function(error, origin) { if (error instanceof Error) { process.send && process.send({ type: "EVENT", message: { type: "UNCAUGHT_EXCEPTION", payload: { error: { name: error.name, message: error.message, stack: error.stack }, origin }, version: "v1" } }); } else { process.send && process.send({ type: "EVENT", message: { type: "UNCAUGHT_EXCEPTION", payload: { error: { name: "Error", message: typeof error === "string" ? error : JSON.stringify(error) }, origin }, version: "v1" } }); } });`,
|
||||
// },
|
||||
footer: {
|
||||
js: "process.exit();",
|
||||
},
|
||||
@@ -112,14 +97,6 @@ export async function compile(options: CompileOptions) {
|
||||
});
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
// compileSpinner.stop("Build failed, aborting deployment");
|
||||
|
||||
// span.setAttributes({
|
||||
// "build.workerErrors": result.errors.map(
|
||||
// (error) => `Error: ${error.text} at ${error.location?.file}`
|
||||
// ),
|
||||
// });
|
||||
|
||||
throw new Error("Build failed, aborting deployment");
|
||||
}
|
||||
|
||||
@@ -127,16 +104,11 @@ export async function compile(options: CompileOptions) {
|
||||
await writeJSONFile(join(options.outputMetafile, "worker.json"), result.metafile);
|
||||
}
|
||||
|
||||
const entryPointContents = readFileSync(
|
||||
resolve("./dist/workers/prod/entry-point.js"),
|
||||
// join(cliRootPath(), "workers", "prod", "entry-point.js"),
|
||||
"utf-8"
|
||||
);
|
||||
const entryPointContents = readFileSync(resolve("./dist/workers/prod/entry-point.js"), "utf-8");
|
||||
|
||||
const entryPointResult = await build({
|
||||
stdin: {
|
||||
contents: entryPointContents,
|
||||
// resolveDir: process.cwd(),
|
||||
resolveDir: config.projectDir,
|
||||
sourcefile: "index.ts",
|
||||
},
|
||||
@@ -150,7 +122,6 @@ export async function compile(options: CompileOptions) {
|
||||
packages: "external",
|
||||
format: "cjs", // This is needed to support opentelemetry instrumentation that uses module patching
|
||||
target: ["node18", "es2020"],
|
||||
// outdir: "out",
|
||||
outdir: resolve(config.projectDir, "out"),
|
||||
define: {
|
||||
__PROJECT_CONFIG__: JSON.stringify(config),
|
||||
@@ -161,14 +132,6 @@ export async function compile(options: CompileOptions) {
|
||||
});
|
||||
|
||||
if (entryPointResult.errors.length > 0) {
|
||||
// compileSpinner.stop("Build failed, aborting deployment");
|
||||
|
||||
// span.setAttributes({
|
||||
// "build.entryPointErrors": entryPointResult.errors.map(
|
||||
// (error) => `Error: ${error.text} at ${error.location?.file}`
|
||||
// ),
|
||||
// });
|
||||
|
||||
throw new Error("Build failed, aborting deployment");
|
||||
}
|
||||
|
||||
@@ -179,13 +142,9 @@ export async function compile(options: CompileOptions) {
|
||||
);
|
||||
}
|
||||
|
||||
// Create a tmp directory to store the build
|
||||
// const tempDir = await createTempDir();
|
||||
|
||||
logger.debug(`Writing compiled files to ${tempDir}`);
|
||||
|
||||
// Get the metaOutput for the result build
|
||||
// const metaOutput = result.metafile!.outputs[posix.join("out", "stdin.js")];
|
||||
const metaOutput =
|
||||
result.metafile!.outputs[
|
||||
posix.join("e2e", "fixtures", basename(config.projectDir), "out", "stdin.js")
|
||||
@@ -194,8 +153,6 @@ export async function compile(options: CompileOptions) {
|
||||
invariant(metaOutput, "Meta output for the result build is missing");
|
||||
|
||||
// Get the metaOutput for the entryPoint build
|
||||
// const entryPointMetaOutput =
|
||||
// entryPointResult.metafile!.outputs[posix.join("out", "stdin.js")];
|
||||
const entryPointMetaOutput =
|
||||
entryPointResult.metafile!.outputs[
|
||||
posix.join("e2e", "fixtures", basename(config.projectDir), "out", "stdin.js")
|
||||
|
||||
@@ -8,7 +8,7 @@ type CreateDeployHashOptions = {
|
||||
};
|
||||
|
||||
export async function createDeployHash(options: CreateDeployHashOptions) {
|
||||
const { entryPointOutputFile, workerOutputFile } = options;
|
||||
const { dependencies, entryPointOutputFile, workerOutputFile } = options;
|
||||
|
||||
// COPIED FROM compileProject()
|
||||
const contentHasher = createHash("sha256");
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
export interface TestCase {
|
||||
resolveEnv?: { [key: string]: string };
|
||||
id: string;
|
||||
skipTypecheck?: boolean;
|
||||
wantConfigNotFoundError?: boolean;
|
||||
wantConfigInvalidError?: boolean;
|
||||
wantCompilationError?: boolean;
|
||||
wantWorkerError?: boolean;
|
||||
wantDependenciesError?: boolean;
|
||||
wantInstallationError?: boolean;
|
||||
}
|
||||
|
||||
export const fixturesConfig: TestCase[] = [
|
||||
{
|
||||
id: "config-invalid",
|
||||
wantConfigInvalidError: true,
|
||||
},
|
||||
{
|
||||
id: "config-not-found",
|
||||
wantConfigNotFoundError: true,
|
||||
},
|
||||
{
|
||||
id: "dep-to-add-scope-parsing",
|
||||
skipTypecheck: true,
|
||||
},
|
||||
{
|
||||
id: "infisical-sdk",
|
||||
skipTypecheck: true,
|
||||
// Should not fail
|
||||
wantCompilationError: true,
|
||||
},
|
||||
{
|
||||
id: "lock-nested-peer-deps",
|
||||
skipTypecheck: true,
|
||||
resolveEnv: {
|
||||
npm_config_legacy_peer_deps: "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "resolve-legacy-peer-deps",
|
||||
skipTypecheck: true,
|
||||
// Should fail with better error at resolve
|
||||
wantWorkerError: true,
|
||||
},
|
||||
{
|
||||
id: "resolve-trigger-deps",
|
||||
skipTypecheck: true,
|
||||
},
|
||||
{
|
||||
id: "server-only",
|
||||
skipTypecheck: true,
|
||||
},
|
||||
{
|
||||
id: "trigger-dir-missing",
|
||||
skipTypecheck: true,
|
||||
},
|
||||
{
|
||||
id: "trigger-dir-not-found",
|
||||
skipTypecheck: true,
|
||||
// Should fail way before
|
||||
wantCompilationError: true,
|
||||
},
|
||||
];
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "no-config",
|
||||
"name": "config-invalid",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "no-config",
|
||||
"name": "config-invalid",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "no-config",
|
||||
"name": "config-invalid",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589",
|
||||
"engines": {
|
||||
@@ -0,0 +1,4 @@
|
||||
export const config = {
|
||||
// 'project' field is mandatory
|
||||
triggerDirectories: [],
|
||||
};
|
||||
+2
-2
@@ -5,8 +5,8 @@ __metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"no-config@workspace:.":
|
||||
"config-invalid@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "no-config@workspace:."
|
||||
resolution: "config-invalid@workspace:."
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "config-not-found",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "config-not-found",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "config-not-found",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
@@ -0,0 +1 @@
|
||||
# https://github.com/pnpm/pnpm/issues/2412
|
||||
@@ -0,0 +1,12 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"config-not-found@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "config-not-found@workspace:."
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "dep-to-add-scope-parsing",
|
||||
"description": "https://github.com/triggerdotdev/trigger.dev/commit/b8477ea2b08828e547885a6f2a14c15074852879",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
MESSAGE: "Hello, World!",
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "@dep-to-add-scope-parsing/dep-to-add",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { task } from "@trigger.dev/sdk/v3";
|
||||
import { MESSAGE } from "@dep-to-add-scope-parsing/dep-to-add";
|
||||
|
||||
export const helloWorldTask = task({
|
||||
id: "hello-world",
|
||||
run: async (payload) => {
|
||||
console.log(MESSAGE, payload);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@dep-to-add-scope-parsing/trigger",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dep-to-add-scope-parsing/dep-to-add": "*",
|
||||
"@trigger.dev/sdk": "3.0.0-beta.37"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const config = {
|
||||
project: "dep-to-add-scope-parsing",
|
||||
additionalPackages: ["@dep-to-add-scope-parsing/dep-to-add"],
|
||||
triggerDirectories: ["./packages/trigger"],
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "lock-nested-peer-deps",
|
||||
"description": "nested peer dep resolved (installed), but legacy-peer-dep true when creating container lockfile. https://github.com/triggerdotdev/trigger.dev/commit/c092c0f9db70039e2f1bc495547a723c1f1a21cf",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/sdk": "3.0.0-beta.40",
|
||||
"react-dom": "18.3.1"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
# https://github.com/pnpm/pnpm/issues/2412
|
||||
@@ -0,0 +1,3 @@
|
||||
export const config = {
|
||||
project: "lock-nested-peer-deps",
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { task } from "@trigger.dev/sdk/v3";
|
||||
import { renderToPipeableStream } from "react-dom/server";
|
||||
|
||||
function App() {
|
||||
return <div>Hello World</div>;
|
||||
}
|
||||
|
||||
export const helloWorldTask = task({
|
||||
id: "hello-world",
|
||||
run: async (payload) => {
|
||||
const stream = renderToPipeableStream(<App />, {
|
||||
onShellReady() {
|
||||
stream.pipe(payload);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
legacy-peer-deps=true
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "resolve-legacy-peer-deps",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/sdk": "3.0.0-beta.40"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lodash": "4.17.21"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
# https://github.com/pnpm/pnpm/issues/2412
|
||||
@@ -0,0 +1,3 @@
|
||||
export const config = {
|
||||
project: "resolve-legacy-peer-deps",
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { concat } from "lodash/array";
|
||||
import { task } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const helloWorldTask = task({
|
||||
id: "hello-world",
|
||||
run: async (payload) => {
|
||||
console.log(concat(["Hello"], "World!").join(", "), payload);
|
||||
},
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "resolve-trigger-deps",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "resolve-trigger-deps",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "resolve-trigger-deps",
|
||||
"description": "https://github.com/triggerdotdev/trigger.dev/commit/6406924b025de766af68dcceea04d26993211ed0",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@8.15.5+sha256.4b4efa12490e5055d59b9b9fc9438b7d581a6b7af3b5675eb5c5f447cee1a589",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
@@ -0,0 +1 @@
|
||||
# https://github.com/pnpm/pnpm/issues/2412
|
||||
@@ -0,0 +1,4 @@
|
||||
export const config = {
|
||||
project: "resolve-trigger-deps",
|
||||
triggerDirectories: ["./trigger"],
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import { task } from "@trigger.dev/sdk/v3";
|
||||
|
||||
export const helloWorldTask = task({
|
||||
id: "hello-world",
|
||||
run: async (payload) => {
|
||||
console.log("Hello, World!", payload);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"resolve-trigger-deps@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "resolve-trigger-deps@workspace:."
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "trigger-dir-missing",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "trigger-dir-missing",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "trigger-dir-missing",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.2.2+sha256.1aa43a5304405be7a7cb9cb5de7b97de9c4e8ddd3273e4dad00d6ae3eb39f0ef",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
@@ -0,0 +1 @@
|
||||
# https://github.com/pnpm/pnpm/issues/2412
|
||||
@@ -0,0 +1,3 @@
|
||||
export const config = {
|
||||
project: "bad-config",
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"trigger-dir-missing@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "trigger-dir-missing@workspace:."
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "trigger-dir-not-found",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "trigger-dir-not-found",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "trigger-dir-not-found",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.2.2+sha256.1aa43a5304405be7a7cb9cb5de7b97de9c4e8ddd3273e4dad00d6ae3eb39f0ef",
|
||||
"engines": {
|
||||
"pnpm": "8.15.5",
|
||||
"yarn": "4.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.: {}
|
||||
@@ -0,0 +1 @@
|
||||
# https://github.com/pnpm/pnpm/issues/2412
|
||||
@@ -0,0 +1,4 @@
|
||||
export const config = {
|
||||
project: "bad-config",
|
||||
triggerDirectories: ["./unknown-folder"],
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"trigger-dir-not-found@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "trigger-dir-not-found@workspace:."
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -50,7 +50,6 @@ export async function handleDependencies(options: HandleDependenciesOptions) {
|
||||
tempDir,
|
||||
} = options;
|
||||
|
||||
// COPIED FROM compileProject()
|
||||
logger.debug("Getting the imports for the worker and entryPoint builds", {
|
||||
workerImports: metaOutput.imports,
|
||||
entryPointImports: entryPointMetaOutput.imports,
|
||||
@@ -59,7 +58,6 @@ export async function handleDependencies(options: HandleDependenciesOptions) {
|
||||
// Get all the required dependencies from the metaOutputs and save them to /tmp/dir/package.json
|
||||
const allImports = [...metaOutput.imports, ...entryPointMetaOutput.imports];
|
||||
|
||||
// const javascriptProject = new JavascriptProject(config.projectDir);
|
||||
const javascriptProject = new JavascriptProjectLocal(config.projectDir, packageManager);
|
||||
|
||||
const dependencies = await resolveRequiredDependencies(allImports, config, javascriptProject);
|
||||
@@ -75,18 +73,11 @@ export async function handleDependencies(options: HandleDependenciesOptions) {
|
||||
...javascriptProject.scripts,
|
||||
},
|
||||
};
|
||||
|
||||
// span.setAttributes({
|
||||
// ...flattenAttributes(packageJsonContents, "packageJson.contents"),
|
||||
// });
|
||||
|
||||
await writeJSONFile(join(tempDir, "package.json"), packageJsonContents);
|
||||
|
||||
const copyResult = await copyAdditionalFiles(config, tempDir);
|
||||
|
||||
if (!copyResult.ok) {
|
||||
// compileSpinner.stop("Project built with warnings");
|
||||
|
||||
log.warn(
|
||||
`No additionalFiles matches for:\n\n${copyResult.noMatches
|
||||
.map((glob) => `- "${glob}"`)
|
||||
@@ -96,9 +87,6 @@ export async function handleDependencies(options: HandleDependenciesOptions) {
|
||||
)} are valid.`
|
||||
);
|
||||
}
|
||||
// } else {
|
||||
// compileSpinner.stop("Project built successfully");
|
||||
// }
|
||||
|
||||
const resolvingDependenciesResult = await resolveDependencies(
|
||||
tempDir,
|
||||
@@ -110,5 +98,5 @@ export async function handleDependencies(options: HandleDependenciesOptions) {
|
||||
throw new SkipLoggingError("Failed to resolve dependencies");
|
||||
}
|
||||
|
||||
return { dependencies };
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
+285
-289
@@ -1,333 +1,314 @@
|
||||
import { execa, execaNode } from "execa";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { mkdir, rename, rm } from "node:fs/promises";
|
||||
import { join, resolve } from "node:path";
|
||||
import { rimraf } from "rimraf";
|
||||
|
||||
import { typecheckProject } from "../src/commands/deploy";
|
||||
import { readConfig, ReadConfigFileResult } from "../src/utilities/configFiles";
|
||||
import { PackageManager } from "../src/utilities/getUserPackageManager";
|
||||
import { readConfig, ReadConfigFileResult, ReadConfigResult } from "../src/utilities/configFiles";
|
||||
import {
|
||||
detectPackageManagerFromArtifacts,
|
||||
LOCKFILES,
|
||||
PackageManager,
|
||||
} from "../src/utilities/getUserPackageManager";
|
||||
import { logger } from "../src/utilities/logger";
|
||||
import { compile } from "./compile";
|
||||
import { createContainerFile } from "./createContainerFile";
|
||||
import { createDeployHash } from "./createDeployHash";
|
||||
import { handleDependencies } from "./handleDependencies";
|
||||
import { Loglevel, LogLevelSchema, PackageManagerSchema } from "./schemas";
|
||||
import allTestCases from "./testCases.json";
|
||||
import { E2EOptions, E2EOptionsSchema } from "./schemas";
|
||||
import { fixturesConfig, TestCase } from "./fixtures.config";
|
||||
import { Metafile, OutputFile } from "esbuild";
|
||||
|
||||
type TestCase = {
|
||||
name: string;
|
||||
skipTypecheck?: boolean;
|
||||
wantConfigNotFoundError?: boolean;
|
||||
wantBadConfigError?: boolean;
|
||||
wantCompilationError?: boolean;
|
||||
wantWorkerError?: boolean;
|
||||
wantDependenciesError?: boolean;
|
||||
wantInstallationError?: boolean;
|
||||
};
|
||||
interface E2EFixtureTest extends TestCase {
|
||||
dir: string;
|
||||
packageManager: PackageManager;
|
||||
tempDir: string;
|
||||
}
|
||||
|
||||
const TIMEOUT = 120_000;
|
||||
|
||||
const testCases: TestCase[] = process.env.MOD
|
||||
? allTestCases.filter(({ name }) => process.env.MOD === name)
|
||||
: allTestCases;
|
||||
? fixturesConfig.filter(({ id }) => process.env.MOD === id)
|
||||
: fixturesConfig;
|
||||
|
||||
let logLevel: Loglevel = "log";
|
||||
let packageManager: PackageManager = "npm";
|
||||
let options: E2EOptions;
|
||||
|
||||
try {
|
||||
logLevel = LogLevelSchema.parse(process.env.LOG);
|
||||
options = E2EOptionsSchema.parse({
|
||||
logLevel: process.env.LOG,
|
||||
packageManager: process.env.PM,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.log("Using default log level 'log'");
|
||||
options = {
|
||||
logLevel: "log",
|
||||
};
|
||||
}
|
||||
|
||||
logger.loggerLevel = logLevel;
|
||||
|
||||
try {
|
||||
packageManager = PackageManagerSchema.parse(process.env.PM);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.log("Using default package manager 'npm'");
|
||||
}
|
||||
logger.loggerLevel = options.logLevel;
|
||||
|
||||
if (testCases.length > 0) {
|
||||
console.log(`Using ${packageManager}`);
|
||||
describe.concurrent("bundling", async () => {
|
||||
beforeEach<E2EFixtureTest>(async ({ dir, packageManager }) => {
|
||||
await rimraf(join(dir, "**/node_modules/**"), {
|
||||
glob: true,
|
||||
});
|
||||
await rimraf(join(dir, ".yarn"), { glob: true });
|
||||
if (
|
||||
packageManager === "npm" &&
|
||||
(existsSync(resolve(join(dir, "yarn.lock"))) ||
|
||||
existsSync(resolve(join(dir, "yarn.lock.copy"))))
|
||||
) {
|
||||
// `npm ci` & `npm install` will update an existing yarn.lock
|
||||
try {
|
||||
await rename(resolve(join(dir, "yarn.lock")), resolve(join(dir, "yarn.lock.copy")));
|
||||
} catch (e) {
|
||||
await rename(resolve(join(dir, "yarn.lock.copy")), resolve(join(dir, "yarn.lock")));
|
||||
}
|
||||
}
|
||||
|
||||
describe.each(testCases)(
|
||||
"fixture $name",
|
||||
async ({
|
||||
name,
|
||||
skipTypecheck,
|
||||
wantConfigNotFoundError,
|
||||
wantBadConfigError,
|
||||
wantCompilationError,
|
||||
wantWorkerError,
|
||||
wantDependenciesError,
|
||||
wantInstallationError,
|
||||
}: TestCase) => {
|
||||
const fixtureDir = resolve(join(process.cwd(), "e2e/fixtures", name));
|
||||
await installFixtureDeps(dir, packageManager);
|
||||
}, TIMEOUT);
|
||||
|
||||
beforeAll(async () => {
|
||||
await rm(resolve(join(fixtureDir, ".trigger")), { force: true, recursive: true });
|
||||
await rm(resolve(join(fixtureDir, "node_modules")), { force: true, recursive: true });
|
||||
if (packageManager === "npm") {
|
||||
// `npm ci` & `npm install` will update an existing yarn.lock
|
||||
try {
|
||||
await rename(
|
||||
resolve(join(fixtureDir, "yarn.lock")),
|
||||
resolve(join(fixtureDir, "yarn.lock.copy"))
|
||||
);
|
||||
} catch (e) {
|
||||
await rename(
|
||||
resolve(join(fixtureDir, "yarn.lock.copy")),
|
||||
resolve(join(fixtureDir, "yarn.lock"))
|
||||
);
|
||||
afterEach<E2EFixtureTest>(async ({ dir, packageManager }) => {
|
||||
if (packageManager === "npm") {
|
||||
try {
|
||||
await rename(resolve(join(dir, "yarn.lock.copy")), resolve(join(dir, "yarn.lock")));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
for (let testCase of testCases) {
|
||||
test.extend<E2EFixtureTest>({
|
||||
...testCase,
|
||||
dir: async ({ id }, use) => await use(resolve(join(process.cwd(), "e2e/fixtures", id))),
|
||||
packageManager: async ({ dir }, use) => await use(await parsePackageManager(options, dir)),
|
||||
tempDir: async ({ dir }, use) => {
|
||||
const existingTempDir = resolve(join(dir, ".trigger"));
|
||||
|
||||
if (existsSync(existingTempDir)) {
|
||||
await rm(existingTempDir, { force: true, recursive: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (packageManager === "npm") {
|
||||
try {
|
||||
await rename(
|
||||
resolve(join(fixtureDir, "yarn.lock.copy")),
|
||||
resolve(join(fixtureDir, "yarn.lock"))
|
||||
);
|
||||
} catch {}
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
"installs",
|
||||
async () => {
|
||||
await expect(
|
||||
(async () => {
|
||||
if (["pnpm", "yarn"].includes(packageManager)) {
|
||||
const buffer = readFileSync(resolve(join(fixtureDir, "package.json")), "utf8");
|
||||
const pkgJSON = JSON.parse(buffer.toString());
|
||||
const version = pkgJSON.engines[packageManager];
|
||||
console.log(
|
||||
`Detected ${packageManager}@${version} from package.json 'engines' field`
|
||||
);
|
||||
const { stdout, stderr } = await execa(
|
||||
"corepack",
|
||||
["use", `${packageManager}@${version}`],
|
||||
{
|
||||
cwd: fixtureDir,
|
||||
}
|
||||
);
|
||||
console.log(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
} else {
|
||||
const { stdout, stderr } = await execa(
|
||||
packageManager,
|
||||
installArgs(packageManager),
|
||||
{
|
||||
cwd: fixtureDir,
|
||||
}
|
||||
);
|
||||
console.log(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
}
|
||||
})()
|
||||
).resolves.not.toThrowError();
|
||||
await use((await mkdir(join(dir, ".trigger"), { recursive: true })) as string);
|
||||
},
|
||||
{ timeout: 60_000 }
|
||||
);
|
||||
})(
|
||||
`fixture '${testCase.id}'`,
|
||||
{ timeout: TIMEOUT },
|
||||
async ({
|
||||
dir,
|
||||
packageManager,
|
||||
resolveEnv,
|
||||
skip,
|
||||
skipTypecheck,
|
||||
tempDir,
|
||||
wantCompilationError,
|
||||
wantConfigInvalidError,
|
||||
wantConfigNotFoundError,
|
||||
wantDependenciesError,
|
||||
wantInstallationError,
|
||||
wantWorkerError,
|
||||
}) => {
|
||||
if (
|
||||
options.packageManager &&
|
||||
!existsSync(resolve(dir, LOCKFILES[options.packageManager]))
|
||||
) {
|
||||
skip();
|
||||
}
|
||||
|
||||
test(
|
||||
wantConfigNotFoundError || wantBadConfigError
|
||||
? "does not resolve config"
|
||||
: "resolves config",
|
||||
async () => {
|
||||
const expectation = expect(
|
||||
let resolvedConfig: ReadConfigResult;
|
||||
const configExpect = expect(
|
||||
(async () => {
|
||||
global.resolvedConfig = await readConfig(fixtureDir, { cwd: fixtureDir });
|
||||
})()
|
||||
resolvedConfig = await readConfig(dir, { cwd: dir });
|
||||
})(),
|
||||
wantConfigNotFoundError || wantConfigInvalidError
|
||||
? "does not resolve config"
|
||||
: "resolves config"
|
||||
);
|
||||
|
||||
if (wantConfigNotFoundError) {
|
||||
await expectation.rejects.toThrowError();
|
||||
} else {
|
||||
await expectation.resolves.not.toThrowError();
|
||||
await configExpect.rejects.toThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (wantBadConfigError) {
|
||||
expect(global.resolvedConfig).toBe("error");
|
||||
} else {
|
||||
expect(global.resolvedConfig).not.toBe("error");
|
||||
await configExpect.resolves.not.toThrowError();
|
||||
|
||||
if (wantConfigInvalidError) {
|
||||
expect(resolvedConfig!.status).toBe("error");
|
||||
return;
|
||||
}
|
||||
|
||||
expect(resolvedConfig!).not.toBe("error");
|
||||
|
||||
if (!skipTypecheck) {
|
||||
await expect(
|
||||
(async () =>
|
||||
await typecheckProject((resolvedConfig! as ReadConfigFileResult).config))(),
|
||||
"typechecks"
|
||||
).resolves.not.toThrowError();
|
||||
}
|
||||
|
||||
let entryPointMetaOutput: Metafile["outputs"]["out/stdin.js"];
|
||||
let entryPointOutputFile: OutputFile;
|
||||
let workerMetaOutput: Metafile["outputs"]["out/stdin.js"];
|
||||
let workerOutputFile: OutputFile;
|
||||
|
||||
const compileExpect = expect(
|
||||
(async () => {
|
||||
const compilationResult = await compile({
|
||||
resolvedConfig: resolvedConfig!,
|
||||
tempDir,
|
||||
});
|
||||
entryPointMetaOutput = compilationResult.entryPointMetaOutput;
|
||||
entryPointOutputFile = compilationResult.entryPointOutputFile;
|
||||
workerMetaOutput = compilationResult.workerMetaOutput;
|
||||
workerOutputFile = compilationResult.workerOutputFile;
|
||||
})(),
|
||||
wantCompilationError ? "does not compile" : "compiles"
|
||||
);
|
||||
|
||||
if (wantCompilationError) {
|
||||
await compileExpect.rejects.toThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
await compileExpect.resolves.not.toThrowError();
|
||||
|
||||
let dependencies: { [k: string]: string };
|
||||
|
||||
if (resolveEnv) {
|
||||
for (let envKey in resolveEnv) {
|
||||
vi.stubEnv(envKey, resolveEnv[envKey]!);
|
||||
}
|
||||
}
|
||||
|
||||
const depsExpectation = expect(
|
||||
(async () => {
|
||||
dependencies = await handleDependencies({
|
||||
entryPointMetaOutput: entryPointMetaOutput!,
|
||||
metaOutput: workerMetaOutput!,
|
||||
resolvedConfig: resolvedConfig!,
|
||||
tempDir,
|
||||
packageManager,
|
||||
});
|
||||
})(),
|
||||
wantDependenciesError ? "does not resolve dependencies" : "resolves dependencies"
|
||||
);
|
||||
|
||||
if (wantDependenciesError) {
|
||||
await depsExpectation.rejects.toThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
await depsExpectation.resolves.not.toThrowError();
|
||||
|
||||
if (resolveEnv) {
|
||||
vi.unstubAllEnvs();
|
||||
}
|
||||
|
||||
await expect(
|
||||
(async () => {
|
||||
await createContainerFile({
|
||||
resolvedConfig: resolvedConfig!,
|
||||
tempDir,
|
||||
});
|
||||
})(),
|
||||
"copies postinstall command into Containerfile.prod"
|
||||
).resolves.not.toThrowError();
|
||||
|
||||
await expect(
|
||||
(async () => {
|
||||
await createDeployHash({
|
||||
dependencies: dependencies!,
|
||||
entryPointOutputFile: entryPointOutputFile!,
|
||||
workerOutputFile: workerOutputFile!,
|
||||
});
|
||||
})(),
|
||||
"creates deploy hash"
|
||||
).resolves.not.toThrowError();
|
||||
|
||||
const installBundleDepsExpect = expect(
|
||||
(async () => {
|
||||
const { stdout: installStdout, stderr: installStderr } = await execa(
|
||||
"npm",
|
||||
["ci", "--no-audit", "--no-fund"],
|
||||
{
|
||||
cwd: tempDir,
|
||||
NODE_PATH: resolve(join(tempDir, "node_modules")),
|
||||
}
|
||||
);
|
||||
debug(installStdout);
|
||||
if (installStderr) console.error(installStderr);
|
||||
})(),
|
||||
wantInstallationError ? "does not install dependencies" : "installs dependencies"
|
||||
);
|
||||
|
||||
if (wantInstallationError) {
|
||||
await installBundleDepsExpect.rejects.toThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
await installBundleDepsExpect.resolves.not.toThrowError();
|
||||
|
||||
const workerStartExpect = expect(
|
||||
(async () => {
|
||||
const { stdout, stderr } = await execaNode("worker.js", {
|
||||
cwd: tempDir,
|
||||
env: {
|
||||
// Since we don't start the worker in a container, limit node resolution algorithm to the '.trigger/node_modules' folder
|
||||
NODE_PATH: resolve(join(tempDir, "node_modules")),
|
||||
},
|
||||
});
|
||||
debug(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
})(),
|
||||
wantWorkerError ? "worker does not start" : "worker starts"
|
||||
);
|
||||
|
||||
if (wantWorkerError) {
|
||||
await workerStartExpect.rejects.toThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
await workerStartExpect.resolves.not.toThrowError();
|
||||
}
|
||||
);
|
||||
|
||||
describe.skipIf(wantConfigNotFoundError || wantBadConfigError)("with resolved config", () => {
|
||||
beforeAll(async () => {
|
||||
global.tempDir = await mkdir(
|
||||
join((global.resolvedConfig as ReadConfigFileResult).config.projectDir, ".trigger"),
|
||||
{ recursive: true }
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
delete global.tempDir;
|
||||
delete global.resolvedConfig;
|
||||
});
|
||||
|
||||
test.skipIf(skipTypecheck).concurrent("typechecks", async () => {
|
||||
await expect(
|
||||
(async () =>
|
||||
await typecheckProject((global.resolvedConfig as ReadConfigFileResult).config))()
|
||||
).resolves.not.toThrowError();
|
||||
});
|
||||
|
||||
test.concurrent(
|
||||
wantCompilationError ? "does not compile" : "compiles",
|
||||
async () => {
|
||||
const expectation = expect(
|
||||
(async () => {
|
||||
const {
|
||||
workerMetaOutput,
|
||||
workerOutputFile,
|
||||
entryPointMetaOutput,
|
||||
entryPointOutputFile,
|
||||
} = await compile({
|
||||
resolvedConfig: global.resolvedConfig!,
|
||||
tempDir: global.tempDir!,
|
||||
});
|
||||
global.entryPointMetaOutput = entryPointMetaOutput;
|
||||
global.entryPointOutputFile = entryPointOutputFile;
|
||||
global.workerMetaOutput = workerMetaOutput;
|
||||
global.workerOutputFile = workerOutputFile;
|
||||
})()
|
||||
);
|
||||
|
||||
if (wantCompilationError) {
|
||||
await expectation.rejects.toThrowError();
|
||||
} else {
|
||||
await expectation.resolves.not.toThrowError();
|
||||
}
|
||||
},
|
||||
{ timeout: 60_000 }
|
||||
);
|
||||
|
||||
describe.skipIf(wantCompilationError)("with successful compilation", () => {
|
||||
afterAll(() => {
|
||||
delete global.entryPointMetaOutput;
|
||||
delete global.entryPointOutputFile;
|
||||
delete global.workerMetaOutput;
|
||||
delete global.workerOutputFile;
|
||||
});
|
||||
|
||||
test(
|
||||
wantDependenciesError ? "does not resolve dependencies" : "resolves dependencies",
|
||||
async () => {
|
||||
const expectation = expect(
|
||||
(async () => {
|
||||
const { dependencies } = await handleDependencies({
|
||||
entryPointMetaOutput: global.entryPointMetaOutput!,
|
||||
metaOutput: global.workerMetaOutput!,
|
||||
resolvedConfig: global.resolvedConfig!,
|
||||
tempDir: global.tempDir!,
|
||||
packageManager,
|
||||
});
|
||||
global.dependencies = dependencies;
|
||||
})()
|
||||
);
|
||||
|
||||
if (wantDependenciesError) {
|
||||
await expectation.rejects.toThrowError();
|
||||
} else {
|
||||
await expectation.resolves.not.toThrowError();
|
||||
}
|
||||
},
|
||||
{ timeout: 120_000 }
|
||||
);
|
||||
|
||||
describe.skipIf(wantDependenciesError)("with resolved dependencies", () => {
|
||||
afterAll(() => {
|
||||
delete global.dependencies;
|
||||
});
|
||||
|
||||
test.concurrent("copies postinstall command into Containerfile.prod", async () => {
|
||||
await expect(
|
||||
(async () => {
|
||||
await createContainerFile({
|
||||
resolvedConfig: global.resolvedConfig!,
|
||||
tempDir: global.tempDir!,
|
||||
});
|
||||
})()
|
||||
).resolves.not.toThrowError();
|
||||
});
|
||||
|
||||
test.concurrent("creates deploy hash", async () => {
|
||||
await expect(
|
||||
(async () => {
|
||||
await createDeployHash({
|
||||
dependencies: global.dependencies!,
|
||||
entryPointOutputFile: global.entryPointOutputFile!,
|
||||
workerOutputFile: global.workerOutputFile!,
|
||||
});
|
||||
})()
|
||||
).resolves.not.toThrowError();
|
||||
});
|
||||
|
||||
describe("with Containerfile ready", () => {
|
||||
test(
|
||||
"installs dependencies",
|
||||
async () => {
|
||||
const expectation = expect(
|
||||
(async () => {
|
||||
const { stdout, stderr } = await execa(
|
||||
"npm",
|
||||
["ci", "--no-audit", "--no-fund"],
|
||||
{
|
||||
cwd: resolve(join(fixtureDir, ".trigger")),
|
||||
}
|
||||
);
|
||||
console.log(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
})()
|
||||
);
|
||||
|
||||
if (wantInstallationError) {
|
||||
await expectation.rejects.toThrowError();
|
||||
} else {
|
||||
await expectation.resolves.not.toThrowError();
|
||||
}
|
||||
},
|
||||
{ timeout: 60_000 }
|
||||
);
|
||||
|
||||
test(
|
||||
wantWorkerError ? "'node worker.js' fails" : "'node worker.js' succeeds",
|
||||
async () => {
|
||||
const expectation = expect(
|
||||
(async () => {
|
||||
const { stdout, stderr } = await execaNode("worker.js", {
|
||||
cwd: resolve(join(fixtureDir, ".trigger")),
|
||||
});
|
||||
console.log(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
})()
|
||||
);
|
||||
|
||||
if (wantWorkerError) {
|
||||
await expectation.rejects.toThrowError();
|
||||
} else {
|
||||
await expectation.resolves.not.toThrowError();
|
||||
}
|
||||
},
|
||||
{ timeout: 60_000 }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
} else if (process.env.MOD) {
|
||||
throw new Error(`Unknown fixture '${process.env.MOD}'`);
|
||||
} else {
|
||||
throw new Error("Nothing to test");
|
||||
}
|
||||
|
||||
function debug(message: string) {
|
||||
if (options.logLevel === "debug") {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
async function installFixtureDeps(dir: string, packageManager: PackageManager) {
|
||||
if (["pnpm", "yarn"].includes(packageManager)) {
|
||||
const buffer = readFileSync(resolve(join(dir, "package.json")), "utf8");
|
||||
const pkgJSON = JSON.parse(buffer.toString());
|
||||
const version = pkgJSON.engines[packageManager];
|
||||
debug(`Detected ${packageManager}@${version} from package.json 'engines' field`);
|
||||
const { stdout, stderr } = await execa("corepack", ["use", `${packageManager}@${version}`], {
|
||||
cwd: dir,
|
||||
});
|
||||
debug(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
} else {
|
||||
const { stdout, stderr } = await execa(packageManager, installArgs(packageManager), {
|
||||
cwd: dir,
|
||||
NODE_PATH: resolve(join(dir, "node_modules")),
|
||||
});
|
||||
debug(stdout);
|
||||
if (stderr) console.error(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
function installArgs(packageManager: string) {
|
||||
switch (packageManager) {
|
||||
case "bun":
|
||||
@@ -341,3 +322,18 @@ function installArgs(packageManager: string) {
|
||||
throw new Error(`Unknown package manager '${packageManager}'`);
|
||||
}
|
||||
}
|
||||
|
||||
async function parsePackageManager(
|
||||
options: E2EOptions,
|
||||
fixtureDir: string
|
||||
): Promise<PackageManager> {
|
||||
let packageManager: PackageManager;
|
||||
|
||||
if (options.packageManager) {
|
||||
packageManager = options.packageManager;
|
||||
} else {
|
||||
packageManager = await detectPackageManagerFromArtifacts(fixtureDir);
|
||||
}
|
||||
|
||||
return packageManager;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const LogLevelSchema = z
|
||||
.enum(["debug", "info", "log", "warn", "error", "none"])
|
||||
.default("log");
|
||||
export type Loglevel = z.infer<typeof LogLevelSchema>;
|
||||
export const PackageManagerSchema = z.enum(["npm", "pnpm", "yarn"]).default("npm");
|
||||
const LogLevelSchema = z.enum(["debug", "info", "log", "warn", "error", "none"]).default("log");
|
||||
const PackageManagerSchema = z.enum(["npm", "pnpm", "yarn"]);
|
||||
export const E2EOptionsSchema = z.object({
|
||||
logLevel: LogLevelSchema,
|
||||
packageManager: PackageManagerSchema.optional(),
|
||||
});
|
||||
export type E2EOptions = z.infer<typeof E2EOptionsSchema>;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "no-config",
|
||||
"wantConfigNotFoundError": true
|
||||
},
|
||||
{
|
||||
"name": "server-only",
|
||||
"skipTypecheck": true
|
||||
}
|
||||
]
|
||||
@@ -2,7 +2,6 @@ import { configDefaults, defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
setupFiles: ["e2e/vitest.d.ts"],
|
||||
globals: true,
|
||||
exclude: [...configDefaults.exclude, "src/**/*"],
|
||||
},
|
||||
|
||||
Vendored
-13
@@ -1,13 +0,0 @@
|
||||
import { Metafile, OutputFile } from "esbuild";
|
||||
|
||||
import { ReadConfigResult } from "../src/utilities/configFiles";
|
||||
|
||||
declare global {
|
||||
var dependencies: { [k: string]: string } | undefined;
|
||||
var entryPointMetaOutput: Metafile["outputs"]["out/stdin.js"] | undefined;
|
||||
var entryPointOutputFile: OutputFile | undefined;
|
||||
var resolvedConfig: ReadConfigResult | undefined;
|
||||
var tempDir: string | undefined;
|
||||
var workerMetaOutput: Metafile["outputs"]["out/stdin.js"] | undefined;
|
||||
var workerOutputFile: OutputFile | undefined;
|
||||
}
|
||||
@@ -49,7 +49,7 @@
|
||||
"npm-run-all": "^4.1.5",
|
||||
"open": "^10.0.3",
|
||||
"p-retry": "^6.1.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "^5.0.7",
|
||||
"tsup": "^8.0.1",
|
||||
"type-fest": "^3.6.0",
|
||||
"typescript": "^5.3.3",
|
||||
@@ -122,12 +122,12 @@
|
||||
"terminal-link": "^3.0.0",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.4.0",
|
||||
"update-check": "^1.5.4",
|
||||
"url": "^0.11.1",
|
||||
"ws": "^8.12.0",
|
||||
"zod": "3.22.3",
|
||||
"zod-validation-error": "^1.5.0",
|
||||
"typescript": "^5.4.0"
|
||||
"zod-validation-error": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -227,9 +227,11 @@ export async function readConfig(
|
||||
export async function resolveConfig(path: string, config: Config): Promise<ResolvedConfig> {
|
||||
if (!config.triggerDirectories) {
|
||||
config.triggerDirectories = await findTriggerDirectories(path);
|
||||
// TODO trigger-dir-missing: throw error if no trigger directory is found
|
||||
}
|
||||
|
||||
config.triggerDirectories = resolveTriggerDirectories(path, config.triggerDirectories);
|
||||
// TODO trigger-dir-not-found: throw error if trigger directories do not exist
|
||||
|
||||
logger.debug("Resolved trigger directories", { triggerDirectories: config.triggerDirectories });
|
||||
|
||||
|
||||
@@ -3,6 +3,12 @@ import { basename } from "path";
|
||||
import { logger } from "./logger";
|
||||
|
||||
export type PackageManager = "npm" | "pnpm" | "yarn";
|
||||
export const LOCKFILES = {
|
||||
npm: "package-lock.json",
|
||||
npmShrinkwrap: "npm-shrinkwrap.json",
|
||||
pnpm: "pnpm-lock.yaml",
|
||||
yarn: "yarn.lock",
|
||||
};
|
||||
|
||||
export async function getUserPackageManager(path: string): Promise<PackageManager> {
|
||||
const packageManager = await detectPackageManager(path);
|
||||
@@ -36,15 +42,8 @@ function detectPackageManagerFromCurrentCommand(): PackageManager {
|
||||
}
|
||||
}
|
||||
|
||||
async function detectPackageManagerFromArtifacts(path: string): Promise<PackageManager> {
|
||||
const artifacts = {
|
||||
yarn: "yarn.lock",
|
||||
pnpm: "pnpm-lock.yaml",
|
||||
npm: "package-lock.json",
|
||||
npmShrinkwrap: "npm-shrinkwrap.json",
|
||||
};
|
||||
|
||||
const foundPath = await findUp(Object.values(artifacts), { cwd: path });
|
||||
export async function detectPackageManagerFromArtifacts(path: string): Promise<PackageManager> {
|
||||
const foundPath = await findUp(Object.values(LOCKFILES), { cwd: path });
|
||||
|
||||
if (!foundPath) {
|
||||
throw new Error("Could not detect package manager from artifacts");
|
||||
@@ -53,12 +52,12 @@ async function detectPackageManagerFromArtifacts(path: string): Promise<PackageM
|
||||
logger.debug("Found path from package manager artifacts", { foundPath });
|
||||
|
||||
switch (basename(foundPath)) {
|
||||
case artifacts.yarn:
|
||||
case LOCKFILES.yarn:
|
||||
return "yarn";
|
||||
case artifacts.pnpm:
|
||||
case LOCKFILES.pnpm:
|
||||
return "pnpm";
|
||||
case artifacts.npm:
|
||||
case artifacts.npmShrinkwrap:
|
||||
case LOCKFILES.npm:
|
||||
case LOCKFILES.npmShrinkwrap:
|
||||
return "npm";
|
||||
default:
|
||||
throw new Error(`Unhandled package manager detection path: ${foundPath}`);
|
||||
|
||||
Generated
+12
-2
@@ -1701,8 +1701,8 @@ importers:
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0
|
||||
rimraf:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2
|
||||
specifier: ^5.0.7
|
||||
version: 5.0.7
|
||||
tsup:
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.1(patch_hash=a5ztaafw5l4qfghy2hjjuynb34)(postcss@8.4.27)(typescript@5.4.5)
|
||||
@@ -23437,6 +23437,7 @@ packages:
|
||||
|
||||
/glob@7.1.7:
|
||||
resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
@@ -31554,6 +31555,7 @@ packages:
|
||||
|
||||
/rimraf@2.7.1:
|
||||
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
@@ -31580,6 +31582,14 @@ packages:
|
||||
glob: 10.3.10
|
||||
dev: false
|
||||
|
||||
/rimraf@5.0.7:
|
||||
resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==}
|
||||
engines: {node: '>=14.18'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
glob: 10.3.10
|
||||
dev: true
|
||||
|
||||
/rollup-plugin-inject@3.0.2:
|
||||
resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==}
|
||||
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.
|
||||
|
||||
Reference in New Issue
Block a user