0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

ci: init integration test (#771)

* ci: init integration test

* ci: complete integration test setup

* ci: remove pull request trigger
This commit is contained in:
Gao Sun 2022-05-10 15:29:53 +08:00 committed by GitHub
parent 92039f948b
commit 1a664beeee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 72 deletions

77
.github/workflows/integration-test.yml vendored Normal file
View file

@ -0,0 +1,77 @@
name: Integration Test
on:
push:
branches: [master]
jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node and pnpm
uses: logto-io/actions-node-pnpm-run-steps@v1.2.1
- name: Package
run: ./package.sh
- uses: actions/upload-artifact@v3
with:
name: integration-test-${{ github.sha }}
path: /tmp/logto.tar.gz
retention-days: 3
run-logto:
needs: package
strategy:
matrix:
os: [ubuntu-latest, ubuntu-18.04, macos-11, macos-12]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Start Postgres (Linux)
if: runner.os == 'Linux'
run: |
sudo service postgresql start
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';"
- name: Start Postgres (macOS)
if: runner.os == 'macOS'
run: |
pg_ctl -D /usr/local/var/postgres start
psql postgres -c "CREATE USER postgres WITH SUPERUSER PASSWORD 'postgres';"
- uses: actions/download-artifact@v3
with:
name: integration-test-${{ github.sha }}
- name: Display structure of downloaded files
run: ls -R
- name: Extract
run: tar -xzf logto.tar.gz
- name: Rebuild Argon2
run: npx node-pre-gyp rebuild -C .
working-directory: logto/packages/core/node_modules/argon2
- name: Run Logto
run: node . --from-root --all-yes &
working-directory: logto/packages/core
env:
NODE_ENV: production
DB_URL_DEFAULT: postgres://postgres:postgres@localhost:5432
- name: Sleep for 5 seconds
run: sleep 5
- name: Health check
run: curl http://localhost:3001/api/status -If

View file

@ -54,13 +54,20 @@ const postgresMajorVersion = 14;
}
}
// Download and extract
spawnSync(
'sh',
['-c', 'curl -L https://github.com/logto-io/logto/releases/latest/download/logto.tar.gz | tar -xz'],
{ stdio: 'inherit' },
);
// Rebuild Argon2
spawnSync(
'sh',
['-c', 'npx node-pre-gyp rebuild -C .'],
{ stdio: 'inherit', cwd: './logto/packages/core/node_modules/argon2' },
);
const startCommand = `cd ${directory} && npm start`;
const answer = await confirm('Would you like to start Logto now?');

View file

@ -2,16 +2,20 @@ import { readdir, readFile } from 'fs/promises';
import path from 'path';
import { SchemaLike, seeds } from '@logto/schemas';
import { conditionalString } from '@silverhand/essentials';
import chalk from 'chalk';
import decamelize from 'decamelize';
import { createPool, parseDsn, sql, stringifyDsn } from 'slonik';
import { createInterceptors } from 'slonik-interceptor-preset';
import { raw } from 'slonik-sql-tag-raw';
import { fromRoot } from '@/env-set/parameters';
import { convertToPrimitiveOrSql } from './utils';
const { managementResource, defaultSignInExperience, createDefaultSetting } = seeds;
const tableDirectory = 'node_modules/@logto/schemas/tables';
const tableDirectory =
conditionalString(fromRoot && 'packages/core/') + 'node_modules/@logto/schemas/tables';
export const replaceDsnDatabase = (dsn: string, databaseName: string): string =>
stringifyDsn({ ...parseDsn(dsn), databaseName });

View file

@ -1,4 +1,10 @@
import { assertEnv, conditional, conditionalString, Optional } from '@silverhand/essentials';
import {
assertEnv,
conditional,
conditionalString,
getEnv,
Optional,
} from '@silverhand/essentials';
import inquirer from 'inquirer';
import { createPool } from 'slonik';
import { createInterceptors } from 'slonik-interceptor-preset';
@ -6,12 +12,16 @@ import { createInterceptors } from 'slonik-interceptor-preset';
import { createDatabase, createDatabaseCli } from '@/database/seed';
import { appendDotEnv } from './dot-env';
import { noInquiry } from './parameters';
import { allYes, noInquiry } from './parameters';
const defaultDatabaseUrl = 'postgres://localhost:5432';
const defaultDatabaseUrl = getEnv('DB_URL_DEFAULT', 'postgres://@localhost:5432');
const defaultDatabaseName = 'logto';
const inquireForLogtoDsn = async (key: string): Promise<[Optional<string>, boolean]> => {
if (allYes) {
return [await createDatabase(defaultDatabaseUrl, defaultDatabaseName), true];
}
const setUp = await inquirer.prompt({
type: 'confirm',
name: 'value',

View file

@ -3,7 +3,6 @@ import { DatabasePool } from 'slonik';
import createPoolByEnv from './create-pool-by-env';
import loadOidcValues from './oidc';
import loadPasswordValues from './password';
export enum MountedApps {
Api = 'api',
@ -25,7 +24,6 @@ const loadEnvValues = async () => {
port,
developmentUserId: getEnv('DEVELOPMENT_USER_ID'),
trustProxyHeader: getEnv('TRUST_PROXY_HEADER') === 'true',
password: await loadPasswordValues(isTest),
oidc: await loadOidcValues(port),
});
};

View file

@ -4,7 +4,7 @@ import { readFileSync, writeFileSync } from 'fs';
import { getEnv } from '@silverhand/essentials';
import inquirer from 'inquirer';
import { noInquiry } from './parameters';
import { allYes, noInquiry } from './parameters';
/**
* Try to read private key with the following order:
@ -33,14 +33,16 @@ const readPrivateKey = async (): Promise<string> => {
throw error;
}
const answer = await inquirer.prompt({
type: 'confirm',
name: 'confirm',
message: `No private key found in env \`OIDC_PRIVATE_KEY\` nor \`${privateKeyPath}\`, would you like to generate a new one?`,
});
if (!allYes) {
const answer = await inquirer.prompt({
type: 'confirm',
name: 'confirm',
message: `No private key found in env \`OIDC_PRIVATE_KEY\` nor \`${privateKeyPath}\`, would you like to generate a new one?`,
});
if (!answer.confirm) {
throw error;
if (!answer.confirm) {
throw error;
}
}
const { privateKey } = generateKeyPairSync('rsa', {

View file

@ -1,3 +1,4 @@
const parameters = new Set(process.argv.slice(2));
export const noInquiry = parameters.has('--no-inquiry');
export const fromRoot = parameters.has('--from-root');
export const allYes = parameters.has('--all-yes');

View file

@ -1,55 +0,0 @@
import { assertEnv, getEnv } from '@silverhand/essentials';
import inquirer from 'inquirer';
import { nanoid } from 'nanoid';
import { number, string } from 'zod';
import { appendDotEnv } from './dot-env';
import { noInquiry } from './parameters';
const loadPeppers = async (isTest: boolean): Promise<string[]> => {
if (isTest) {
return [nanoid()];
}
const key = 'PASSWORD_PEPPERS';
try {
return string()
.array()
.parse(JSON.parse(assertEnv(key)));
} catch (error: unknown) {
if (noInquiry) {
throw error;
}
if (!(error instanceof Error && error.message === `env variable ${key} not found`)) {
throw error;
}
const answer = await inquirer.prompt({
type: 'confirm',
name: 'confirm',
message: `No password peppers (${key}) found in env variables, would you like to generate a new set and save it into \`.env\`?`,
});
if (!answer.confirm) {
throw error;
}
const peppers = [nanoid(), nanoid(), nanoid()];
appendDotEnv(key, JSON.stringify(peppers));
return peppers;
}
};
const loadPasswordValues = async (isTest: boolean) => {
return Object.freeze({
peppers: await loadPeppers(isTest),
iterationCount: number()
.min(100)
.parse(Number(getEnv('PASSWORD_ITERATION_COUNT', '1000'))),
});
};
export default loadPasswordValues;

View file

@ -51,7 +51,6 @@ describe('koaSpaProxy middleware', () => {
const spy = jest.spyOn(envSet, 'values', 'get').mockReturnValue({
...envSet.values,
isProduction: true,
password: { peppers: ['foo'], iterationCount: 1000 },
});
const ctx = createContextWithRouteParameters({
@ -69,7 +68,6 @@ describe('koaSpaProxy middleware', () => {
const spy = jest.spyOn(envSet, 'values', 'get').mockReturnValue({
...envSet.values,
isProduction: true,
password: { peppers: ['foo'], iterationCount: 1000 },
});
const ctx = createContextWithRouteParameters({