Update stuff so we can pass build hopefully
This commit is contained in:
@@ -23,6 +23,8 @@
|
||||
- Preserve `typescript.ignoreBuildErrors` in Next config.
|
||||
- Do not modify Sentry config or `tools/tailwind/theme.css` unless requested.
|
||||
- Generated `.cache`, `.turbo`, `.local`, and environment files are ignored.
|
||||
- `scripts/convex-codegen` generates Convex API files for checks and image
|
||||
builds when deployment env is available; do not hand-edit generated output.
|
||||
|
||||
## Environment rules
|
||||
|
||||
@@ -36,6 +38,9 @@
|
||||
variables from Infisical into the selected Convex deployment. Backend
|
||||
dev/setup scripts run it before `convex dev`.
|
||||
- CI uses Gitea-injected secrets or `CI_ENV_FILE` and must not call Infisical.
|
||||
- CI must provide Convex deployment env for codegen, either
|
||||
`CONVEX_SELF_HOSTED_URL` plus `CONVEX_SELF_HOSTED_ADMIN_KEY`, or
|
||||
`CONVEX_DEPLOYMENT`.
|
||||
- App code imports validated variables from `@/env`, never `process.env`.
|
||||
- Add cache-relevant variables to `turbo.json` `globalEnv`.
|
||||
|
||||
@@ -70,5 +75,6 @@ never connects to staging.
|
||||
## Validation
|
||||
|
||||
Use `bun typecheck`, never a production build for routine validation. The full
|
||||
gate is `SKIP_E2E=1 bun run ci:check`; local-stack smoke e2e is `bun test:e2e`.
|
||||
gate is `SKIP_E2E=1 bun run ci:check`; it runs Convex codegen before checking.
|
||||
Local-stack smoke e2e is `bun test:e2e`.
|
||||
Pre-commit runs lint-staged serially and pre-push runs the bounded full gate.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { access, readFile, rm } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { ConvexHttpClient } from 'convex/browser';
|
||||
import { makeFunctionReference } from 'convex/server';
|
||||
|
||||
import type { Id } from '@spoon/backend/convex/_generated/dataModel.js';
|
||||
import { api } from '@spoon/backend/convex/_generated/api.js';
|
||||
|
||||
import { runOpenAiEdit } from './agent';
|
||||
import { env } from './env';
|
||||
@@ -16,8 +18,6 @@ import { getInstallationToken, openDraftPullRequest } from './github';
|
||||
import { createRedactor, truncate } from './redact';
|
||||
import { runInJobContainer } from './runtime/docker';
|
||||
|
||||
type Id<TableName extends string> = string & { __tableName: TableName };
|
||||
|
||||
type Claim = {
|
||||
job: {
|
||||
_id: Id<'agentJobs'>;
|
||||
@@ -44,95 +44,6 @@ type Claim = {
|
||||
secrets: { name: string; value: string }[];
|
||||
};
|
||||
|
||||
const appendEventFunction = makeFunctionReference<
|
||||
'mutation',
|
||||
{
|
||||
workerToken: string;
|
||||
workerId: string;
|
||||
jobId: Id<'agentJobs'>;
|
||||
level: 'debug' | 'info' | 'warn' | 'error';
|
||||
phase:
|
||||
| 'queued'
|
||||
| 'clone'
|
||||
| 'plan'
|
||||
| 'edit'
|
||||
| 'install'
|
||||
| 'check'
|
||||
| 'test'
|
||||
| 'commit'
|
||||
| 'push'
|
||||
| 'pr'
|
||||
| 'cleanup';
|
||||
message: string;
|
||||
metadata?: string;
|
||||
},
|
||||
unknown
|
||||
>('agentJobs:appendEvent');
|
||||
|
||||
const updateStatusFunction = makeFunctionReference<
|
||||
'mutation',
|
||||
{
|
||||
workerToken: string;
|
||||
workerId: string;
|
||||
jobId: Id<'agentJobs'>;
|
||||
status:
|
||||
| 'queued'
|
||||
| 'claimed'
|
||||
| 'preparing'
|
||||
| 'running'
|
||||
| 'checks_running'
|
||||
| 'changes_ready'
|
||||
| 'draft_pr_opened'
|
||||
| 'failed'
|
||||
| 'cancelled'
|
||||
| 'timed_out';
|
||||
error?: string;
|
||||
summary?: string;
|
||||
},
|
||||
unknown
|
||||
>('agentJobs:updateStatus');
|
||||
|
||||
const addArtifactFunction = makeFunctionReference<
|
||||
'mutation',
|
||||
{
|
||||
workerToken: string;
|
||||
workerId: string;
|
||||
jobId: Id<'agentJobs'>;
|
||||
kind: 'plan' | 'diff' | 'test_output' | 'summary' | 'error' | 'pr_body';
|
||||
title: string;
|
||||
content: string;
|
||||
contentType:
|
||||
| 'text/markdown'
|
||||
| 'text/plain'
|
||||
| 'application/json'
|
||||
| 'text/x-diff';
|
||||
},
|
||||
unknown
|
||||
>('agentJobs:addArtifact');
|
||||
|
||||
const completeWithDraftPrFunction = makeFunctionReference<
|
||||
'mutation',
|
||||
{
|
||||
workerToken: string;
|
||||
workerId: string;
|
||||
jobId: Id<'agentJobs'>;
|
||||
commitSha: string;
|
||||
pullRequestUrl: string;
|
||||
pullRequestNumber: number;
|
||||
summary: string;
|
||||
},
|
||||
unknown
|
||||
>('agentJobs:completeWithDraftPr');
|
||||
|
||||
const claimNextForWorkerFunction = makeFunctionReference<
|
||||
'action',
|
||||
{
|
||||
workerId: string;
|
||||
workerToken: string;
|
||||
},
|
||||
Claim | null
|
||||
>('agentJobsNode:claimNextForWorker');
|
||||
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const client = new ConvexHttpClient(env.convexUrl);
|
||||
@@ -155,7 +66,7 @@ const appendEvent = async (
|
||||
message: string,
|
||||
metadata?: string,
|
||||
) =>
|
||||
await client.mutation(appendEventFunction, {
|
||||
await client.mutation(api.agentJobs.appendEvent, {
|
||||
workerToken: env.workerToken,
|
||||
workerId: env.workerId,
|
||||
jobId,
|
||||
@@ -180,7 +91,7 @@ const updateStatus = async (
|
||||
| 'timed_out',
|
||||
extra?: { error?: string; summary?: string },
|
||||
) =>
|
||||
await client.mutation(updateStatusFunction, {
|
||||
await client.mutation(api.agentJobs.updateStatus, {
|
||||
workerToken: env.workerToken,
|
||||
workerId: env.workerId,
|
||||
jobId,
|
||||
@@ -199,7 +110,7 @@ const addArtifact = async (args: {
|
||||
| 'application/json'
|
||||
| 'text/x-diff';
|
||||
}) =>
|
||||
await client.mutation(addArtifactFunction, {
|
||||
await client.mutation(api.agentJobs.addArtifact, {
|
||||
workerToken: env.workerToken,
|
||||
workerId: env.workerId,
|
||||
...args,
|
||||
@@ -212,7 +123,7 @@ const completeWithDraftPr = async (args: {
|
||||
pullRequestNumber: number;
|
||||
summary: string;
|
||||
}) =>
|
||||
await client.mutation(completeWithDraftPrFunction, {
|
||||
await client.mutation(api.agentJobs.completeWithDraftPr, {
|
||||
workerToken: env.workerToken,
|
||||
workerId: env.workerId,
|
||||
...args,
|
||||
@@ -495,7 +406,7 @@ export const startWorker = async () => {
|
||||
console.log(`Spoon agent worker ${env.workerId} polling ${env.convexUrl}`);
|
||||
for (;;) {
|
||||
try {
|
||||
const claim = await client.action(claimNextForWorkerFunction, {
|
||||
const claim = await client.action(api.agentJobsNode.claimNextForWorker, {
|
||||
workerId: env.workerId,
|
||||
workerToken: env.workerToken,
|
||||
});
|
||||
|
||||
+10
-9
@@ -58,25 +58,26 @@
|
||||
"dev:backend": "turbo run dev -F @spoon/backend",
|
||||
"dev:staging": "INFISICAL_ENV=staging turbo run dev -F @spoon/next -F @spoon/backend",
|
||||
"dev:expo:tunnel": "turbo run dev:tunnel -F @spoon/expo -F @spoon/backend",
|
||||
"codegen:convex": "bash scripts/convex-codegen",
|
||||
"sync:convex": "scripts/sync-convex-env ${INFISICAL_ENV:-dev}",
|
||||
"db:up": "bash scripts/db/up",
|
||||
"db:down": "bash scripts/db/down",
|
||||
"db:down:wipe": "bash scripts/db/down --wipe",
|
||||
"format": "turbo run format --continue -- --cache --cache-location .cache/.prettiercache",
|
||||
"format:fix": "turbo run format --continue -- --write --cache --cache-location .cache/.prettiercache",
|
||||
"lint": "turbo run lint --continue -- --cache --cache-location .cache/.eslintcache",
|
||||
"lint:fix": "turbo run lint --continue -- --fix --cache --cache-location .cache/.eslintcache",
|
||||
"lint": "bun codegen:convex && turbo run lint --continue -- --cache --cache-location .cache/.eslintcache",
|
||||
"lint:fix": "bun codegen:convex && turbo run lint --continue -- --fix --cache --cache-location .cache/.eslintcache",
|
||||
"lint:ws": "bunx sherif@latest",
|
||||
"patch:usesend": "node scripts/patch-usesend.mjs",
|
||||
"postinstall": "bun patch:usesend && bun lint:ws",
|
||||
"typecheck": "turbo run typecheck",
|
||||
"test": "turbo run test:unit test:integration test:component",
|
||||
"test:unit": "turbo run test:unit",
|
||||
"test:integration": "turbo run test:integration",
|
||||
"test:component": "turbo run test:component",
|
||||
"typecheck": "bun codegen:convex && turbo run typecheck",
|
||||
"test": "bun codegen:convex && turbo run test:unit test:integration test:component",
|
||||
"test:unit": "bun codegen:convex && turbo run test:unit",
|
||||
"test:integration": "bun codegen:convex && turbo run test:integration",
|
||||
"test:component": "bun codegen:convex && turbo run test:component",
|
||||
"test:e2e": "bash scripts/e2e",
|
||||
"test:all": "turbo run test:unit test:integration test:component && bun test:e2e",
|
||||
"ci:check": "bun lint:ws && turbo run lint typecheck test:unit test:integration test:component --concurrency=2 && bun test:e2e",
|
||||
"test:all": "bun codegen:convex && turbo run test:unit test:integration test:component && bun test:e2e",
|
||||
"ci:check": "bun codegen:convex && bun lint:ws && turbo run lint typecheck test:unit test:integration test:component --concurrency=2 && bun test:e2e",
|
||||
"prepare": "husky",
|
||||
"ui-add": "turbo run ui-add",
|
||||
"android": "expo run:android",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"dev:tunnel": "bun sync-env && bun with-env convex dev",
|
||||
"dev:web": "bun sync-env && bun with-env convex dev",
|
||||
"setup": "bun sync-env && bun with-env convex dev --until-success",
|
||||
"codegen": "convex codegen --typecheck disable",
|
||||
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||
"lint": "eslint --flag unstable_native_nodejs_ts_config",
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import { convexTest } from 'convex-test';
|
||||
import { makeFunctionReference } from 'convex/server';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { api } from '../../convex/_generated/api.js';
|
||||
import schema from '../../convex/schema';
|
||||
|
||||
const createManualSpoon = makeFunctionReference<'mutation'>(
|
||||
'spoons:createManual',
|
||||
);
|
||||
const listMySpoons = makeFunctionReference<'query'>('spoons:listMine');
|
||||
const getSpoon = makeFunctionReference<'query'>('spoons:get');
|
||||
const createAgentRequest = makeFunctionReference<'mutation'>(
|
||||
'agentRequests:create',
|
||||
);
|
||||
const modules = import.meta.glob('../../convex/**/*.*s');
|
||||
|
||||
const createUser = async (t: ReturnType<typeof convexTest>, email: string) =>
|
||||
@@ -49,9 +41,9 @@ describe('convex-test harness', () => {
|
||||
|
||||
test('requires authentication to create a Spoon', async () => {
|
||||
const t = convexTest(schema, modules);
|
||||
await expect(t.mutation(createManualSpoon, spoonInput)).rejects.toThrow(
|
||||
'Not authenticated.',
|
||||
);
|
||||
await expect(
|
||||
t.mutation(api.spoons.createManual, spoonInput),
|
||||
).rejects.toThrow('Not authenticated.');
|
||||
});
|
||||
|
||||
test('creates and lists Spoons for the current user', async () => {
|
||||
@@ -59,8 +51,8 @@ describe('convex-test harness', () => {
|
||||
const userId = await createUser(t, 'one@example.com');
|
||||
const session = authed(t, userId);
|
||||
|
||||
const spoonId = await session.mutation(createManualSpoon, spoonInput);
|
||||
const spoons = await session.query(listMySpoons, {});
|
||||
const spoonId = await session.mutation(api.spoons.createManual, spoonInput);
|
||||
const spoons = await session.query(api.spoons.listMine, {});
|
||||
|
||||
expect(spoons).toHaveLength(1);
|
||||
expect(spoons[0]?._id).toBe(spoonId);
|
||||
@@ -72,12 +64,12 @@ describe('convex-test harness', () => {
|
||||
const ownerId = await createUser(t, 'owner@example.com');
|
||||
const otherId = await createUser(t, 'other@example.com');
|
||||
const spoonId = await authed(t, ownerId).mutation(
|
||||
createManualSpoon,
|
||||
api.spoons.createManual,
|
||||
spoonInput,
|
||||
);
|
||||
|
||||
await expect(
|
||||
authed(t, otherId).query(getSpoon, { spoonId }),
|
||||
authed(t, otherId).query(api.spoons.get, { spoonId }),
|
||||
).rejects.toThrow('Spoon not found.');
|
||||
});
|
||||
|
||||
@@ -86,12 +78,12 @@ describe('convex-test harness', () => {
|
||||
const ownerId = await createUser(t, 'owner@example.com');
|
||||
const otherId = await createUser(t, 'other@example.com');
|
||||
const spoonId = await authed(t, ownerId).mutation(
|
||||
createManualSpoon,
|
||||
api.spoons.createManual,
|
||||
spoonInput,
|
||||
);
|
||||
|
||||
await expect(
|
||||
authed(t, otherId).mutation(createAgentRequest, {
|
||||
authed(t, otherId).mutation(api.agentRequests.create, {
|
||||
spoonId,
|
||||
prompt: 'Add a settings page',
|
||||
}),
|
||||
|
||||
@@ -8,5 +8,10 @@ ENV_FILE="${CI_ENV_FILE:-}"
|
||||
cleanup() { [[ -n "$ENV_FILE" && "$ENV_FILE" != "${CI_ENV_FILE:-}" ]] && rm -f "$ENV_FILE" || true; }
|
||||
trap cleanup EXIT
|
||||
if [[ -z "$ENV_FILE" && -z "${CI:-}" ]]; then ENV_FILE="$(mktemp)"; sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"; fi
|
||||
if [[ -n "$ENV_FILE" ]]; then
|
||||
bunx dotenv -e "$ENV_FILE" -- bash "$ROOT_DIR/scripts/convex-codegen"
|
||||
else
|
||||
bash "$ROOT_DIR/scripts/convex-codegen"
|
||||
fi
|
||||
args=(); [[ -z "$ENV_FILE" ]] || args+=(--env-file "$ENV_FILE")
|
||||
docker compose "${args[@]}" -f "$ROOT_DIR/docker/compose.yml" build spoon-next
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
GENERATED_DIR="$ROOT_DIR/packages/backend/convex/_generated"
|
||||
|
||||
if [[ -z "${CONVEX_SELF_HOSTED_URL:-}" ]]; then
|
||||
if [[ -n "${CONVEX_URL:-}" ]]; then
|
||||
export CONVEX_SELF_HOSTED_URL="$CONVEX_URL"
|
||||
elif [[ -n "${NEXT_PUBLIC_CONVEX_URL:-}" ]]; then
|
||||
export CONVEX_SELF_HOSTED_URL="$NEXT_PUBLIC_CONVEX_URL"
|
||||
fi
|
||||
fi
|
||||
|
||||
has_generated_files() {
|
||||
[[ -f "$GENERATED_DIR/api.js" ]] \
|
||||
&& [[ -f "$GENERATED_DIR/api.d.ts" ]] \
|
||||
&& [[ -f "$GENERATED_DIR/server.js" ]] \
|
||||
&& [[ -f "$GENERATED_DIR/server.d.ts" ]] \
|
||||
&& [[ -f "$GENERATED_DIR/dataModel.d.ts" ]]
|
||||
}
|
||||
|
||||
has_self_hosted_env() {
|
||||
[[ -n "${CONVEX_SELF_HOSTED_URL:-}" ]] \
|
||||
&& [[ -n "${CONVEX_SELF_HOSTED_ADMIN_KEY:-}" ]]
|
||||
}
|
||||
|
||||
has_cloud_deployment_env() {
|
||||
[[ -n "${CONVEX_DEPLOYMENT:-}" ]]
|
||||
}
|
||||
|
||||
if has_self_hosted_env || has_cloud_deployment_env; then
|
||||
cd "$ROOT_DIR/packages/backend"
|
||||
bun run codegen
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if has_generated_files; then
|
||||
echo "Convex generated files already exist; skipping codegen because no deployment env is configured."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat >&2 <<'EOF'
|
||||
Convex generated files are missing.
|
||||
|
||||
Run this command with either:
|
||||
- CONVEX_SELF_HOSTED_URL and CONVEX_SELF_HOSTED_ADMIN_KEY, or
|
||||
- CONVEX_DEPLOYMENT
|
||||
|
||||
CI should provide these through its env file before running checks.
|
||||
EOF
|
||||
exit 1
|
||||
@@ -5,5 +5,6 @@ ENVIRONMENT="${1:-staging}"
|
||||
[[ "$ENVIRONMENT" == dev || "$ENVIRONMENT" == staging ]] || { echo "usage: update-next-app [dev|staging]" >&2; exit 2; }
|
||||
ENV_FILE="$(mktemp)"; trap 'rm -f "$ENV_FILE"' EXIT
|
||||
sh "$ROOT_DIR/scripts/export-env" "$ENVIRONMENT" > "$ENV_FILE"
|
||||
bunx dotenv -e "$ENV_FILE" -- bash "$ROOT_DIR/scripts/convex-codegen"
|
||||
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" build spoon-next
|
||||
docker compose --env-file "$ENV_FILE" -f "$ROOT_DIR/docker/compose.yml" up -d spoon-next
|
||||
|
||||
Reference in New Issue
Block a user