diff --git a/apps/api/src/api-v1/deployments/create-deployment.ts b/apps/api/src/api-v1/deployments/create-deployment.ts index 03cd69e1..c7f07ed4 100644 --- a/apps/api/src/api-v1/deployments/create-deployment.ts +++ b/apps/api/src/api-v1/deployments/create-deployment.ts @@ -12,6 +12,7 @@ import { acl } from '@/lib/acl' import { normalizeDeploymentVersion } from '@/lib/deployments/normalize-deployment-version' import { publishDeployment } from '@/lib/deployments/publish-deployment' import { ensureAuthUser } from '@/lib/ensure-auth-user' +import { env } from '@/lib/env' import { openapiAuthenticatedSecuritySchemas, openapiErrorResponse404, @@ -77,9 +78,12 @@ export function registerV1CreateDeployment( }) if (!project) { + // Used for testing e2e fixtures in the development marketplace + const isPrivate = !(user.username === 'dev' && env.isDev) + // Upsert the project if it doesn't already exist - // The typecast doesn't match exactly here because we're not populating - // the lastPublishedDeployment, but that's fine because it's a new project + // The typecast is necessary here because we're not populating the + // lastPublishedDeployment, but that's fine because it's a new project // so it will be empty anyway. project = ( await db @@ -90,6 +94,7 @@ export function registerV1CreateDeployment( name: projectName, userId: user.id, teamId: teamMember?.teamId, + private: isPrivate, _secret: await sha256() }) .returning() diff --git a/apps/api/src/api-v1/projects/create-project.ts b/apps/api/src/api-v1/projects/create-project.ts index afb8d1c1..be89213b 100644 --- a/apps/api/src/api-v1/projects/create-project.ts +++ b/apps/api/src/api-v1/projects/create-project.ts @@ -5,6 +5,7 @@ import { createRoute, type OpenAPIHono } from '@hono/zod-openapi' import type { AuthenticatedHonoEnv } from '@/lib/types' import { db, schema } from '@/db' import { ensureAuthUser } from '@/lib/ensure-auth-user' +import { env } from '@/lib/env' import { openapiAuthenticatedSecuritySchemas, openapiErrorResponses @@ -61,6 +62,9 @@ export function registerV1CreateProject( `Invalid project identifier "${identifier}"` ) + // Used for testing e2e fixtures in the development marketplace + const isPrivate = !(user.username === 'dev' && env.isDev) + const [project] = await db .insert(schema.projects) .values({ @@ -70,6 +74,7 @@ export function registerV1CreateProject( name: parsedProjectIdentifier.projectName, teamId: teamMember?.teamId, userId: user.id, + private: isPrivate, _secret: await sha256() }) .returning() diff --git a/apps/e2e/bin/deploy-fixtures.ts b/apps/e2e/bin/deploy-fixtures.ts index 16bf844c..e15a02e7 100644 --- a/apps/e2e/bin/deploy-fixtures.ts +++ b/apps/e2e/bin/deploy-fixtures.ts @@ -1,9 +1,13 @@ -import { deployFixtures } from '../src/deploy-fixtures' +import { deployFixtures } from '../src/project-fixtures' async function main() { - const deployments = await deployFixtures() + console.log('\n\nDeploying fixtures...\n\n') + const deployments = await deployFixtures() console.log(JSON.stringify(deployments, null, 2)) + + // eslint-disable-next-line unicorn/no-process-exit + process.exit(0) } await main() diff --git a/apps/e2e/bin/publish-fixtures.ts b/apps/e2e/bin/publish-fixtures.ts new file mode 100644 index 00000000..37cbe3d1 --- /dev/null +++ b/apps/e2e/bin/publish-fixtures.ts @@ -0,0 +1,18 @@ +import { deployFixtures, publishDeployments } from '../src/project-fixtures' + +async function main() { + console.log('\n\nDeploying fixtures...\n\n') + + const deployments = await deployFixtures() + console.log(JSON.stringify(deployments, null, 2)) + + console.log('\n\nPublishing deployments...\n\n') + + const publishedDeployments = await publishDeployments(deployments) + console.log(JSON.stringify(publishedDeployments, null, 2)) + + // eslint-disable-next-line unicorn/no-process-exit + process.exit(0) +} + +await main() diff --git a/apps/e2e/bin/seed-db.ts b/apps/e2e/bin/seed-db.ts index d9cbed6b..727ce315 100644 --- a/apps/e2e/bin/seed-db.ts +++ b/apps/e2e/bin/seed-db.ts @@ -1,7 +1,7 @@ import { AgenticApiClient } from '@agentic/platform-api-client' -import { deployFixtures } from '../src/deploy-fixtures' import { env, isProd } from '../src/env' +import { deployFixtures, publishDeployments } from '../src/project-fixtures' export const client = new AgenticApiClient({ apiBaseUrl: env.AGENTIC_API_BASE_URL @@ -10,6 +10,8 @@ export const client = new AgenticApiClient({ async function main() { // TODO: clear existing tables? and include prompt to double check if so... + console.log('\n\nCreating dev user...\n\n') + const devAuthSession = await client.signUpWithPassword({ username: 'dev', email: env.AGENTIC_DEV_EMAIL, @@ -21,8 +23,18 @@ async function main() { `\n\nREMEMBER TO UPDATE "AGENTIC_DEV_ACCESS_TOKEN" in e2e/.env${isProd ? '.production' : ''}\n\n` ) + console.log('\n\nDeploying fixtures...\n\n') + const deployments = await deployFixtures() console.log(JSON.stringify(deployments, null, 2)) + + console.log('\n\nPublishing deployments...\n\n') + + const publishedDeployments = await publishDeployments(deployments) + console.log(JSON.stringify(publishedDeployments, null, 2)) + + // eslint-disable-next-line unicorn/no-process-exit + process.exit(0) } await main() diff --git a/apps/e2e/package.json b/apps/e2e/package.json index 201bfc29..b10b6afe 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -14,6 +14,8 @@ "scripts": { "deploy-fixtures": "dotenvx run -- tsx bin/deploy-fixtures.ts", "deploy-fixtures:prod": "dotenvx run -o -f .env.production -- tsx bin/deploy-fixtures.ts", + "publish-fixtures": "dotenvx run -- tsx bin/publish-fixtures.ts", + "publish-fixtures:prod": "dotenvx run -o -f .env.production -- tsx bin/publish-fixtures.ts", "seed-db": "dotenvx run -- tsx bin/seed-db.ts", "seed-db:prod": "dotenvx run -o -f .env.production -- tsx bin/seed-db.ts", "test": "run-s test:*", @@ -29,14 +31,17 @@ "dotenv": "^16.5.0", "ky": "catalog:", "p-map": "catalog:", - "p-times": "^4.0.0" + "p-times": "^4.0.0", + "semver": "catalog:" }, "devDependencies": { "@agentic/platform": "workspace:*", "@agentic/platform-api-client": "workspace:*", "@agentic/platform-core": "workspace:*", "@agentic/platform-fixtures": "workspace:*", + "@agentic/platform-types": "workspace:*", "@modelcontextprotocol/sdk": "catalog:", + "@types/semver": "catalog:", "fast-content-type-parse": "catalog:" } } diff --git a/apps/e2e/src/deploy-fixtures.ts b/apps/e2e/src/project-fixtures.ts similarity index 50% rename from apps/e2e/src/deploy-fixtures.ts rename to apps/e2e/src/project-fixtures.ts index 8f78c743..1700c029 100644 --- a/apps/e2e/src/deploy-fixtures.ts +++ b/apps/e2e/src/project-fixtures.ts @@ -1,8 +1,11 @@ import path from 'node:path' import { fileURLToPath } from 'node:url' +import type { Deployment } from '@agentic/platform-types' import { loadAgenticConfig } from '@agentic/platform' +import { assert } from '@agentic/platform-core' import pMap from 'p-map' +import semver from 'semver' import { client } from './client' @@ -42,9 +45,9 @@ export async function deployFixtures({ const fixtureDir = path.join(validFixturesDir, fixture) const config = await loadAgenticConfig({ cwd: fixtureDir }) - console.log(config) - const deployment = await client.createDeployment(config) + console.log(`Deployed ${fixture} => ${deployment.identifier}`) + return deployment }, { @@ -54,3 +57,41 @@ export async function deployFixtures({ return deployments } + +export async function publishDeployments( + deployments: Deployment[], + { + concurrency = 1 + }: { + concurrency?: number + } = {} +) { + const publishedDeployments = await pMap( + deployments, + async (deployment) => { + const project = await client.getProject({ + projectId: deployment.projectId, + populate: ['lastDeployment'] + }) + + const baseVersion = project.lastPublishedDeploymentVersion || '0.0.0' + const version = semver.inc(baseVersion, 'patch') + assert(version, `Failed to increment deployment version "${baseVersion}"`) + + const publishedDeployment = await client.publishDeployment( + { version }, + { + deploymentId: deployment.id + } + ) + console.log(`Published ${deployment.identifier} => ${version}`) + + return publishedDeployment + }, + { + concurrency + } + ) + + return publishedDeployments +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10325466..77419e8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,27 +6,252 @@ settings: catalogs: default: + '@apideck/better-ajv-errors': + specifier: ^0.3.6 + version: 0.3.6 + '@clack/prompts': + specifier: ^0.11.0 + version: 0.11.0 + '@cloudflare/workers-types': + specifier: ^4.20250614.0 + version: 4.20250614.0 + '@commander-js/extra-typings': + specifier: ^14.0.0 + version: 14.0.0 + '@edge-runtime/vm': + specifier: ^5.0.0 + version: 5.0.0 + '@fisch0920/config': + specifier: ^1.1.2 + version: 1.1.2 + '@fisch0920/drizzle-orm': + specifier: ^0.43.7 + version: 0.43.7 + '@fisch0920/drizzle-zod': + specifier: ^0.7.9 + version: 0.7.9 + '@hono/node-server': + specifier: ^1.14.4 + version: 1.14.4 + '@hono/sentry': + specifier: ^1.2.2 + version: 1.2.2 + '@hono/zod-openapi': + specifier: ^0.19.8 + version: 0.19.8 + '@hono/zod-validator': + specifier: ^0.7.0 + version: 0.7.0 + '@modelcontextprotocol/sdk': + specifier: ^1.12.3 + version: 1.12.3 + '@paralleldrive/cuid2': + specifier: ^2.2.2 + version: 2.2.2 + '@react-email/components': + specifier: ^0.0.42 + version: 0.0.42 + '@redocly/openapi-core': + specifier: ^1.34.3 + version: 1.34.3 + '@sentry/cli': + specifier: ^2.46.0 + version: 2.46.0 + '@sentry/cloudflare': + specifier: ^9.29.0 + version: 9.29.0 + '@sentry/core': + specifier: ^9.29.0 + version: 9.29.0 + '@sentry/node': + specifier: ^9.29.0 + version: 9.29.0 + '@types/ms': + specifier: ^2.1.0 + version: 2.1.0 + '@types/node': + specifier: ^24.0.1 + version: 24.0.1 '@types/react': specifier: ^19.1.8 version: 19.1.8 '@types/react-dom': specifier: ^19.1.6 version: 19.1.6 + '@types/semver': + specifier: ^7.7.0 + version: 7.7.0 + agents: + specifier: ^0.0.95 + version: 0.0.95 + ajv: + specifier: ^8.17.1 + version: 8.17.1 + ajv-formats: + specifier: ^3.0.1 + version: 3.0.1 + camelcase: + specifier: ^8.0.0 + version: 8.0.0 + commander: + specifier: ^14.0.0 + version: 14.0.0 + conf: + specifier: ^14.0.0 + version: 14.0.0 + decamelize: + specifier: ^6.0.0 + version: 6.0.0 + del-cli: + specifier: ^6.0.0 + version: 6.0.0 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.1 + drizzle-orm: + specifier: ^0.44.2 + version: 0.44.2 + email-validator: + specifier: ^2.0.4 + version: 2.0.4 + eslint: + specifier: ^9.29.0 + version: 9.29.0 + eslint-plugin-drizzle: + specifier: ^0.2.3 + version: 0.2.3 + eventid: + specifier: ^2.0.1 + version: 2.0.1 + exit-hook: + specifier: ^4.0.0 + version: 4.0.0 + fast-content-type-parse: + specifier: ^3.0.0 + version: 3.0.0 + fast-uri: + specifier: ^3.0.6 + version: 3.0.6 + fastmcp: + specifier: ^3.3.0 + version: 3.3.0 + get-port: + specifier: ^7.1.0 + version: 7.1.0 + hono: + specifier: ^4.7.11 + version: 4.7.11 + knip: + specifier: ^5.61.0 + version: 5.61.0 ky: specifier: 1.8.1 version: 1.8.1 + lint-staged: + specifier: ^16.1.1 + version: 16.1.1 + ms: + specifier: ^2.1.3 + version: 2.1.3 + npm-run-all2: + specifier: ^8.0.4 + version: 8.0.4 + octokit: + specifier: ^5.0.3 + version: 5.0.3 + only-allow: + specifier: ^1.2.1 + version: 1.2.1 + open: + specifier: ^10.1.2 + version: 10.1.2 + openapi-typescript: + specifier: ^7.8.0 + version: 7.8.0 + ora: + specifier: ^8.2.0 + version: 8.2.0 + p-all: + specifier: ^5.0.0 + version: 5.0.0 + p-map: + specifier: ^7.0.3 + version: 7.0.3 + parse-json: + specifier: ^8.3.0 + version: 8.3.0 + plur: + specifier: ^5.1.0 + version: 5.1.0 + postgres: + specifier: ^3.4.7 + version: 3.4.7 + prettier: + specifier: ^3.5.3 + version: 3.5.3 react: specifier: ^19.1.0 version: 19.1.0 react-dom: specifier: ^19.1.0 version: 19.1.0 + react-email: + specifier: ^4.0.16 + version: 4.0.16 + resend: + specifier: ^4.6.0 + version: 4.6.0 + restore-cursor: + specifier: ^5.1.0 + version: 5.1.0 + semver: + specifier: ^7.7.2 + version: 7.7.2 + simple-git-hooks: + specifier: ^2.13.0 + version: 2.13.0 + sort-keys: + specifier: ^5.1.0 + version: 5.1.0 stripe: specifier: ^18.2.1 version: 18.2.1 + tsup: + specifier: ^8.5.0 + version: 8.5.0 + tsx: + specifier: ^4.20.3 + version: 4.20.3 + turbo: + specifier: ^2.5.4 + version: 2.5.4 type-fest: specifier: ^4.41.0 version: 4.41.0 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + unconfig: + specifier: ^7.3.2 + version: 7.3.2 + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4 + vitest: + specifier: ^3.2.3 + version: 3.2.3 + wrangler: + specifier: ^4.20.0 + version: 4.20.0 + zod: + specifier: ^3.25.64 + version: 3.25.64 + zod-to-json-schema: + specifier: ^3.24.5 + version: 3.24.5 + zod-validation-error: + specifier: ^3.5.0 + version: 3.5.0 importers: @@ -189,6 +414,9 @@ importers: p-times: specifier: ^4.0.0 version: 4.0.0 + semver: + specifier: 'catalog:' + version: 7.7.2 devDependencies: '@agentic/platform': specifier: workspace:* @@ -202,9 +430,15 @@ importers: '@agentic/platform-fixtures': specifier: workspace:* version: link:../../packages/fixtures + '@agentic/platform-types': + specifier: workspace:* + version: link:../../packages/types '@modelcontextprotocol/sdk': specifier: 'catalog:' version: 1.12.3 + '@types/semver': + specifier: 'catalog:' + version: 7.7.0 fast-content-type-parse: specifier: 'catalog:' version: 3.0.0