Update release process (#192)

This commit is contained in:
Chris Anderson 2023-06-21 08:50:01 -05:00 committed by GitHub
parent 9d17439447
commit 5f8e061061
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 147 additions and 70 deletions

27
.github/workflows/publish.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Publish Packages
on:
push:
tags:
- "v*.*.*"
jobs:
build-package:
name: "Publish to GitHub Packages"
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
if: github.repository_owner == 'parcelvoy'
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
registry-url: https://npm.pkg.github.com/
- run: npm ci
- run: echo "registry=https://npm.pkg.github.com/@parcelvoy" >> .npmrc
- run: npm run package:publish --tag=$(echo ${GITHUB_REF_NAME:1})
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

View file

@ -64,7 +64,7 @@ jobs:
run: npm install && npm install --save-dev
- name: 'Run Jest Tests'
run: npm run test -- --scope=platform
run: npm run test -- --scope=@parcelvoy/platform
env:
NODE_ENV: test
APP_SECRET: testing

View file

@ -24,4 +24,4 @@ USER node
WORKDIR /usr/src/app
COPY --chown=node:node --from=build /usr/src/app ./
EXPOSE 3001
CMD ["dumb-init", "node", "index.js"]
CMD ["dumb-init", "node", "boot.js"]

View file

@ -1,6 +1,6 @@
{
"ignore": ["**/*.test.ts", "**/*.spec.ts", "node_modules"],
"watch": ["src", ".env"],
"exec": "TZ=utc ts-node --transpile-only ./src/index.ts",
"exec": "TZ=utc ts-node --transpile-only ./src/boot.ts",
"ext": "ts,js,json"
}

View file

@ -1,11 +1,11 @@
{
"name": "platform",
"name": "@parcelvoy/platform",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "platform",
"name": "@parcelvoy/platform",
"version": "0.1.0",
"dependencies": {
"@aws-sdk/client-s3": "^3.171.0",

View file

@ -1,6 +1,9 @@
{
"name": "platform",
"name": "@parcelvoy/platform",
"version": "0.1.0",
"repository":"https://github.com/parcelvoy/platform",
"main": "build/index.js",
"types": "build/index.d.ts",
"dependencies": {
"@aws-sdk/client-s3": "^3.171.0",
"@aws-sdk/client-ses": "^3.121.0",
@ -56,7 +59,8 @@
"test": "jest --forceExit --runInBand --testTimeout 10000",
"docker:build": "docker buildx build -f ./Dockerfile -t ghcr.io/parcelvoy/api:latest -t ghcr.io/parcelvoy/api:$npm_config_tag ../../",
"docker:build:push": "npm run docker:build -- --push",
"migration:create": "node ./scripts/create-migration.mjs"
"migration:create": "node ./scripts/create-migration.mjs",
"package:publish": "npm run build && npm version $npm_config_tag --no-git-tag-version && npm pack && npm publish --tag=latest"
},
"devDependencies": {
"@types/busboy": "^1.5.0",
@ -83,5 +87,6 @@
"ts-jest": "^28.0.7",
"ts-node": "^10.8.2",
"typescript": "^4.9.3"
}
},
"files": [ "/build", "/db" ]
}

View file

@ -2,11 +2,15 @@ import Koa from 'koa'
import koaBody from 'koa-body'
import cors from '@koa/cors'
import serve from 'koa-static'
import controllers from './config/controllers'
import controllers, { register } from './config/controllers'
import { RequestError } from './core/errors'
import { logger } from './config/logger'
import Router from '@koa/router'
export default class Api extends Koa {
router = new Router({ prefix: '/api' })
controllers?: Record<string, Router>
constructor(
public app: import('./app').default,
) {
@ -52,6 +56,21 @@ export default class Api extends Koa {
defer: !app.env.mono,
}))
controllers(this)
this.registerControllers()
}
getControllers() {
return controllers(this.app)
}
registerControllers() {
this.controllers = this.getControllers()
this.register(...Object.values(this.controllers))
}
register(...routers: Router[]) {
const root = register(this.router, ...routers)
this.use(root.routes())
.use(root.allowedMethods())
}
}

View file

@ -22,7 +22,7 @@ export default class App {
return App.$main
}
static async init(env: Env): Promise<App> {
static async init<T extends typeof App>(this: T, env: Env): Promise<InstanceType<T>> {
logger.info('parcelvoy initializing')
@ -42,15 +42,20 @@ export default class App {
const auth = loadAuth(env.auth)
// Setup app
App.$main = new App(env,
const app = new this(env,
database,
queue,
auth,
storage,
error,
)
) as any
return App.$main
return this.setMain(app)
}
static setMain<T extends typeof App>(this: T, app: InstanceType<T>) {
this.$main = app
return app
}
uuid = uuid()
@ -59,8 +64,7 @@ export default class App {
rateLimiter: RateLimiter
#registered: { [key: string | number]: unknown }
// eslint-disable-next-line no-useless-constructor
private constructor(
constructor(
public env: Env,
public db: Database,
public queue: Queue,
@ -73,23 +77,31 @@ export default class App {
this.unhandledErrorListener()
}
async start() {
start() {
const runners = this.env.runners
if (runners.includes('api')) {
this.api = new Api(this)
const server = this.api?.listen(this.env.port)
server.keepAliveTimeout = 65000
server.requestTimeout = 0
logger.info('parcelvoy:api ready')
this.startApi()
}
if (runners.includes('worker')) {
this.worker = new Worker(this)
this.worker?.run()
logger.info('parcelvoy:worker ready')
this.startWorker()
}
return this
}
startApi(api?: Api) {
this.api = api ?? new Api(this)
const server = this.api?.listen(this.env.port)
server.keepAliveTimeout = 65000
server.requestTimeout = 0
logger.info('parcelvoy:api ready')
}
startWorker(worker?: Worker) {
this.worker = worker ?? new Worker(this)
this.worker?.run()
logger.info('parcelvoy:worker ready')
}
async close() {
await this.worker?.close()
await this.db.destroy()

View file

@ -0,0 +1,5 @@
import App from './app'
import env from './config/env'
export default App.init(env())
.then(app => app.start())

View file

@ -20,28 +20,25 @@ import ProjectAdminController from '../projects/ProjectAdminController'
import ProjectApiKeyController from '../projects/ProjectApiKeyController'
import AdminController from '../auth/AdminController'
import OrganizationController from '../organizations/OrganizationController'
import App from '../app'
const register = (parent: Router, ...routers: Router[]) => {
export const register = (parent: Router, ...routers: Router[]) => {
for (const router of routers) {
parent.use(router.routes(), router.allowedMethods())
}
return parent
}
export default (api: import('../api').default) => {
export default (app: App) => {
// Register the three main levels of routers
const root = register(new Router({ prefix: '/api' }),
adminRouter(),
clientRouter(),
publicRouter(),
)
api.use(root.routes())
.use(root.allowedMethods())
const routers: Record<string, Router> = {
admin: adminRouter(),
client: clientRouter(),
public: publicRouter(),
}
// If we are running in mono mode, we need to also serve the UI
if (api.app.env.mono) {
if (app.env.mono) {
const ui = new Router()
ui.get('/(.*)', async (ctx, next) => {
try {
@ -50,9 +47,10 @@ export default (api: import('../api').default) => {
return next()
}
})
api.use(ui.routes())
.use(ui.allowedMethods())
routers.ui = ui
}
return routers
}
/**

View file

@ -1,4 +1,5 @@
import knex, { Knex as Database } from 'knex'
import path from 'path'
import { removeKey } from '../utilities'
import { logger } from './logger'
@ -10,6 +11,7 @@ export interface DatabaseConfig {
user: string
password: string
database?: string
migrationPaths: string[]
}
export type Query = (builder: Database.QueryBuilder<any>) => Database.QueryBuilder<any>
@ -23,7 +25,7 @@ knex.QueryBuilder.extend('when', function(
})
const connect = (config: DatabaseConfig, withDB = true) => {
let connection = config
let connection = removeKey('migrationPaths', config)
if (!withDB) {
connection = removeKey('database', connection)
}
@ -45,7 +47,7 @@ const connect = (config: DatabaseConfig, withDB = true) => {
const migrate = async (config: DatabaseConfig, db: Database, fresh = false) => {
if (fresh) await db.raw(`CREATE DATABASE ${config.database}`)
return db.migrate.latest({
directory: './db/migrations',
directory: [path.resolve(__dirname, '../../db/migrations'), ...config.migrationPaths],
tableName: 'migrations',
loadExtensions: ['.js', '.ts'],
})

View file

@ -55,6 +55,7 @@ export default (type?: EnvType): Env => {
password: process.env.DB_PASSWORD!,
port: parseInt(process.env.DB_PORT!),
database: process.env.DB_DATABASE!,
migrationPaths: process.env.DB_MIGRATION_PATHS?.split(',') ?? [],
},
redis: {
host: process.env.REDIS_HOST!,

View file

@ -21,8 +21,6 @@ import UserAliasJob from '../users/UserAliasJob'
import UserSchemaSyncJob from '../schema/UserSchemaSyncJob'
import UserDeviceJob from '../users/UserDeviceJob'
export type Queues = Record<number, Queue>
export const loadJobs = (queue: Queue) => {
queue.register(CampaignGenerateListJob)
queue.register(CampaignInteractJob)
@ -49,9 +47,3 @@ export const loadJobs = (queue: Queue) => {
export default (config: QueueConfig) => {
return new Queue(config)
}
export const loadWorker = (config: QueueConfig) => {
const queue = new Queue(config)
loadJobs(queue)
return queue
}

View file

@ -12,7 +12,7 @@ addErrors(validator)
export { JSONSchemaType, IsValidSchema, validator }
export const validate = <T>(schema: JSONSchemaType<T>, data: any): T => {
export function validate<T>(schema: JSONSchemaType<T>, data: any): T {
const validate = validator.getSchema<T>(schema.$id!)
|| validator.compile(schema)
if (validate(data)) {

View file

@ -1,5 +1,8 @@
import Api from './api'
import App from './app'
import env from './config/env'
import Model from './core/Model'
import Worker from './worker'
import Job from './queue/Job'
export default App.init(env())
.then(app => app.start())
export { App, env, Model, Api, Worker, Job }

View file

@ -18,7 +18,7 @@ export interface ProviderSetupMeta {
value: string | number
}
export const ProviderSchema = <T extends ExternalProviderParams, D>(id: string, data: JSONSchemaType<D>): JSONSchemaType<T> => {
export function ProviderSchema<_ extends ExternalProviderParams, D>(id: string, data: JSONSchemaType<D>): any {
return {
$id: id,
type: 'object',

View file

@ -1,15 +1,17 @@
import { loadWorker } from './config/queue'
import { loadJobs } from './config/queue'
import scheduler, { Scheduler } from './config/scheduler'
import Queue from './queue'
export default class Worker {
worker: Queue
scheduler: Scheduler
constructor(
public app: import('./app').default,
) {
this.worker = loadWorker(app.env.queue)
this.worker = new Queue(app.env.queue)
this.scheduler = scheduler(app)
this.loadJobs()
}
run() {
@ -20,4 +22,8 @@ export default class Worker {
await this.worker.close()
await this.scheduler.close()
}
loadJobs() {
loadJobs(this.worker)
}
}

View file

@ -1,11 +1,11 @@
{
"name": "ui",
"name": "@parcelvoy/ui",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ui",
"name": "@parcelvoy/ui",
"version": "0.1.0",
"dependencies": {
"@fontsource/inter": "^4.5.14",

View file

@ -1,6 +1,7 @@
{
"name": "ui",
"name": "@parcelvoy/ui",
"version": "0.1.0",
"repository":"https://github.com/parcelvoy/platform",
"dependencies": {
"@fontsource/inter": "^4.5.14",
"@headlessui/react": "1.7.13",
@ -45,7 +46,8 @@
"eject": "react-scripts eject",
"lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
"docker:build": "docker buildx build -f ./Dockerfile -t ghcr.io/parcelvoy/ui:latest -t ghcr.io/parcelvoy/ui:$npm_config_tag ../../",
"docker:build:push": "npm run docker:build -- --push"
"docker:build:push": "npm run docker:build -- --push",
"package:publish": "npm run build && npm version $npm_config_tag --no-git-tag-version && npm pack && npm publish --tag=latest"
},
"browserslist": {
"production": [
@ -68,5 +70,6 @@
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.31.11",
"typescript": "^4.9.3"
}
},
"files": [ "/build" ]
}

18
package-lock.json generated
View file

@ -13,6 +13,7 @@
}
},
"apps/platform": {
"name": "@parcelvoy/platform",
"version": "0.1.0",
"dependencies": {
"@aws-sdk/client-s3": "^3.171.0",
@ -137,6 +138,7 @@
}
},
"apps/ui": {
"name": "@parcelvoy/ui",
"version": "0.1.0",
"dependencies": {
"@fontsource/inter": "^4.5.14",
@ -6829,6 +6831,14 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcelvoy/platform": {
"resolved": "apps/platform",
"link": true
},
"node_modules/@parcelvoy/ui": {
"resolved": "apps/ui",
"link": true
},
"node_modules/@parse/node-apn": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@parse/node-apn/-/node-apn-5.1.3.tgz",
@ -23044,10 +23054,6 @@
"node": ">=4"
}
},
"node_modules/platform": {
"resolved": "apps/platform",
"link": true
},
"node_modules/postcss": {
"version": "8.4.22",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.22.tgz",
@ -29409,10 +29415,6 @@
"node": ">=0.8.0"
}
},
"node_modules/ui": {
"resolved": "apps/ui",
"link": true
},
"node_modules/unbox-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",

View file

@ -14,6 +14,7 @@
"test": "lerna run test",
"docker:build": "lerna run docker:build",
"docker:build:push": "lerna run docker:build:push",
"analyze:ui:bundle": "lerna run build && npx source-map-explorer './apps/ui/build/static/js/*.js'"
"analyze:ui:bundle": "lerna run build && npx source-map-explorer './apps/ui/build/static/js/*.js'",
"package:publish": "lerna run package:publish"
}
}

View file

@ -12,7 +12,8 @@
"pretty": true,
"sourceMap": true,
"allowJs": true,
"noEmit": false
"noEmit": false,
"declaration": true
}
}