Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/trigger-api-tasks-deploy-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ jobs:
- name: Install DB package dependencies
working-directory: ./packages/db
run: bun install --frozen-lockfile --ignore-scripts
- name: Install Integration Platform package dependencies
working-directory: ./packages/integration-platform
run: bun install --frozen-lockfile --ignore-scripts
- name: Build Integration Platform package
working-directory: ./packages/integration-platform
run: bun run build
- name: Build DB package
working-directory: ./packages/db
run: bun run build
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/trigger-api-tasks-deploy-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ jobs:
working-directory: ./packages/db
run: bun install --frozen-lockfile --ignore-scripts

- name: Install Integration Platform package dependencies
working-directory: ./packages/integration-platform
run: bun install --frozen-lockfile --ignore-scripts
- name: Build Integration Platform package
working-directory: ./packages/integration-platform
run: bun run build

- name: Build DB package
working-directory: ./packages/db
run: bun run build
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/trigger-tasks-deploy-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ jobs:
- name: Install Email package dependencies
working-directory: ./packages/email
run: bun install --frozen-lockfile --ignore-scripts
- name: Install Integration Platform package dependencies
working-directory: ./packages/integration-platform
run: bun install --frozen-lockfile --ignore-scripts
- name: Build DB package
working-directory: ./packages/db
run: bun run build
- name: Build Integration Platform package
working-directory: ./packages/integration-platform
run: bun run build
- name: Copy schema to app and generate client
working-directory: ./apps/app
run: |
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/trigger-tasks-deploy-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,18 @@ jobs:
- name: Install Email package dependencies
working-directory: ./packages/email
run: bun install --frozen-lockfile --ignore-scripts
- name: Install Integration Platform package dependencies
working-directory: ./packages/integration-platform
run: bun install --frozen-lockfile --ignore-scripts

- name: Build DB package
working-directory: ./packages/db
run: bun run build

- name: Build Integration Platform package
working-directory: ./packages/integration-platform
run: bun run build

- name: Copy schema to app and generate client
working-directory: ./apps/app
run: |
Expand Down
129 changes: 129 additions & 0 deletions apps/api/integrationPlatformExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type {
BuildContext,
BuildExtension,
BuildManifest,
} from '@trigger.dev/build';
import type { Plugin } from 'esbuild';
import { existsSync } from 'node:fs';
import { cp, mkdir } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';

const PACKAGE_NAME = '@comp/integration-platform';

/**
* Custom Trigger.dev build extension for @comp/integration-platform workspace package.
*
* Since @comp/integration-platform is a workspace package (not published to npm),
* we need to:
* 1. Add an esbuild plugin to resolve the import path during build
* 2. Copy the built dist files into the trigger.dev deployment
*/
export function integrationPlatformExtension(): IntegrationPlatformExtension {
return new IntegrationPlatformExtension();
}

class IntegrationPlatformExtension implements BuildExtension {
public readonly name = 'IntegrationPlatformExtension';
private _packagePath: string | undefined;

async onBuildStart(context: BuildContext) {
if (context.target === 'dev') {
return;
}

// Find the package path
this._packagePath = this.findPackageRoot(context.workingDir);

if (!this._packagePath) {
throw new Error(
[
`IntegrationPlatformExtension could not find ${PACKAGE_NAME}.`,
'Make sure the package is built (run `bun run build` in packages/integration-platform).',
].join('\n'),
);
}

context.logger.debug(`Found integration-platform at ${this._packagePath}`);

// Register esbuild plugin to resolve the workspace package
const packagePath = this._packagePath;
const resolvePlugin: Plugin = {
name: 'resolve-integration-platform',
setup(build) {
// Resolve bare import
build.onResolve({ filter: /^@comp\/integration-platform$/ }, () => {
return {
path: resolve(packagePath, 'dist/index.js'),
};
});

// Resolve subpath imports like @comp/integration-platform/types
build.onResolve(
{ filter: /^@comp\/integration-platform\// },
(args) => {
const subpath = args.path.replace(`${PACKAGE_NAME}/`, '');
return {
path: resolve(packagePath, 'dist', `${subpath}/index.js`),
};
},
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Subpath resolution incorrectly appends /index.js for flat exports

The subpath import resolution unconditionally appends /index.js to the subpath, which doesn't match the package's actual export structure. According to @comp/integration-platform's package.json, the ./types export maps to ./dist/types.js, not ./dist/types/index.js. When resolving @comp/integration-platform/types, the code produces dist/types/index.js instead of the correct dist/types.js. This would cause build failures if any Trigger.dev task imports from the /types subpath.

Fix in Cursor Fix in Web

},
};

context.registerPlugin(resolvePlugin);
}

async onBuildComplete(context: BuildContext, manifest: BuildManifest) {
if (context.target === 'dev') {
return;
}

const packagePath = this._packagePath;
if (!packagePath) {
return;
}

const packageDistPath = resolve(packagePath, 'dist');

// Copy the entire dist to the build output
const destPath = resolve(
manifest.outputPath,
'node_modules/@comp/integration-platform',
);
const destDistPath = resolve(destPath, 'dist');

await mkdir(destDistPath, { recursive: true });

// Copy dist files
await cp(packageDistPath, destDistPath, { recursive: true });

// Copy package.json for proper module resolution
const packageJsonPath = resolve(packagePath, 'package.json');
if (existsSync(packageJsonPath)) {
await cp(packageJsonPath, resolve(destPath, 'package.json'));
}

context.logger.log(
'Copied @comp/integration-platform to deployment bundle',
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing AWS SDK dependencies cause runtime deployment failures

The integrationPlatformExtension copies the @comp/integration-platform dist files to the Trigger.dev deployment but doesn't install its runtime dependencies. The integration-platform package (built with plain tsc, not bundled) requires @aws-sdk/client-cloudtrail and @aws-sdk/client-iam, which are not in apps/api/package.json and won't be available at runtime. Unlike the customPrismaExtension which uses context.addLayer() to ensure dependencies are installed, this extension only copies files. The deployment will fail at runtime when code tries to import these missing AWS SDK packages.

Fix in Cursor Fix in Web


private findPackageRoot(workingDir: string): string | undefined {
// Look for the package relative to the api app
const candidates = [
resolve(workingDir, '../../packages/integration-platform'),
resolve(workingDir, '../packages/integration-platform'),
];

for (const candidate of candidates) {
if (
existsSync(candidate) &&
existsSync(resolve(candidate, 'dist/index.js'))
) {
return candidate;
}
}

return undefined;
}
}
2 changes: 2 additions & 0 deletions apps/api/trigger.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PrismaInstrumentation } from '@prisma/instrumentation';
import { syncVercelEnvVars } from '@trigger.dev/build/extensions/core';
import { defineConfig } from '@trigger.dev/sdk';
import { prismaExtension } from './customPrismaExtension';
import { integrationPlatformExtension } from './integrationPlatformExtension';

export default defineConfig({
project: 'proj_zhioyrusqertqgafqgpj', // API project
Expand All @@ -14,6 +15,7 @@ export default defineConfig({
version: '6.13.0',
dbPackageVersion: '^1.3.15', // Version of @trycompai/db package with compiled JS
}),
integrationPlatformExtension(),
syncVercelEnvVars(),
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
const { disconnectConnection } = useIntegrationMutations();

const { data: findings = initialFindings, mutate: mutateFindings } = useSWR<Finding[]>(
'/api/cloud-tests/findings',
`/api/cloud-tests/findings?orgId=${orgId}`,
async (url) => {
const res = await fetch(url);
if (!res.ok) throw new Error('Failed to fetch');
Expand All @@ -89,7 +89,7 @@ export function TestsLayout({ initialFindings, initialProviders, orgId }: TestsL
);

const { data: providers = initialProviders, mutate: mutateProviders } = useSWR<Provider[]>(
'/api/cloud-tests/providers',
`/api/cloud-tests/providers?orgId=${orgId}`,
async (url) => {
const res = await fetch(url);
if (!res.ok) throw new Error('Failed to fetch');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,10 @@ export function SingleTask({
{/* Browser Automations Section */}
{isWebAutomationsEnabled && <BrowserAutomations taskId={task.id} />}

{/* Custom Automations Section - only show if no mapped integration checks available */}
{!hasMappedChecks && <TaskAutomations automations={automations || []} />}
{/* Custom Automations Section - always show if automations exist, or show empty state if no integration checks */}
{((automations && automations.length > 0) || !hasMappedChecks) && (
<TaskAutomations automations={automations || []} />
)}

{/* Comments Section */}
<div>
Expand Down
26 changes: 22 additions & 4 deletions apps/app/src/app/api/cloud-tests/findings/route.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
import { auth } from '@/utils/auth';
import { db } from '@db';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import { NextRequest, NextResponse } from 'next/server';

const CLOUD_PROVIDER_SLUGS = ['aws', 'gcp', 'azure'];

export async function GET() {
export async function GET(request: NextRequest) {
try {
const session = await auth.api.getSession({
headers: await headers(),
});

const orgId = session?.session.activeOrganizationId;
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const { searchParams } = new URL(request.url);
const orgId = searchParams.get('orgId');

if (!orgId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
return NextResponse.json({ error: 'Organization ID is required' }, { status: 400 });
}

// Verify the user belongs to the requested organization
const member = await db.member.findFirst({
where: {
userId: session.user.id,
organizationId: orgId,
deactivated: false,
},
});

if (!member) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}

// ====================================================================
Expand Down
26 changes: 22 additions & 4 deletions apps/app/src/app/api/cloud-tests/providers/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { auth } from '@/utils/auth';
import { getManifest } from '@comp/integration-platform';
import { db } from '@db';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import { NextRequest, NextResponse } from 'next/server';

const CLOUD_PROVIDER_SLUGS = ['aws', 'gcp', 'azure'];

Expand All @@ -24,16 +24,34 @@ const getRequiredVariables = (providerSlug: string): string[] => {
return Array.from(requiredVars);
};

export async function GET() {
export async function GET(request: NextRequest) {
try {
const session = await auth.api.getSession({
headers: await headers(),
});

const orgId = session?.session.activeOrganizationId;
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const { searchParams } = new URL(request.url);
const orgId = searchParams.get('orgId');

if (!orgId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
return NextResponse.json({ error: 'Organization ID is required' }, { status: 400 });
}

// Verify the user belongs to the requested organization
const member = await db.member.findFirst({
where: {
userId: session.user.id,
organizationId: orgId,
deactivated: false,
},
});

if (!member) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}

// Fetch from NEW integration platform (IntegrationConnection)
Expand Down
Loading