From 48f290f7f0bfdffbbec236096f1951c31782d2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emanuel=20Tesa=C5=99?= Date: Fri, 20 Oct 2023 10:39:45 +0200 Subject: [PATCH] Implement node version --- Dockerfile | 6 ++---- packages/api/package.json | 2 +- packages/pusher/README.md | 8 ++++++++ packages/pusher/config/pusher.example.json | 5 ++++- packages/pusher/package.json | 4 ++-- packages/pusher/src/validation/config.ts | 7 ++++++- packages/pusher/src/validation/schema.test.ts | 20 +++++++++++++++++++ packages/pusher/src/validation/schema.ts | 7 +++++++ packages/pusher/test/fixtures.ts | 4 ++++ tsconfig.json | 1 + 10 files changed, 55 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0df89607..01800818 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,8 +40,7 @@ FROM node:18-alpine as pusher WORKDIR /app ENV NODE_ENV=production COPY --from=deployed-pusher /app/deployed-pusher . -# Enabling source maps adds small runtime overhead, but improves debugging experience and the performance is worth it. -ENTRYPOINT ["node", "--enable-source-maps", "dist/index.js"] +ENTRYPOINT ["node", "dist/src/index.js"] # Create a separate stage for api package. We create a temporary stage for deployment and then copy the result into # the final stage. Only the production dependencies and package implementation is part of this last stage. @@ -51,5 +50,4 @@ FROM node:18-alpine as api WORKDIR /app ENV NODE_ENV=production COPY --from=deployed-api /app/deployed-api . -# Enabling source maps adds small runtime overhead, but improves debugging experience and the performance is worth it. -ENTRYPOINT ["node", "--enable-source-maps", "dist/index.js"] +ENTRYPOINT ["node", "dist/index.js"] diff --git a/packages/api/package.json b/packages/api/package.json index 28d02667..9b057584 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -16,7 +16,7 @@ "eslint:fix": "eslint . --ext .js,.ts --fix", "prettier:check": "prettier --check \"./**/*.{js,ts,md,json}\"", "prettier:fix": "prettier --write \"./**/*.{js,ts,md,json}\"", - "start-prod": "node --enable-source-maps dist/index.js", + "start-prod": "node dist/index.js", "test": "jest", "tsc": "tsc --project ." }, diff --git a/packages/pusher/README.md b/packages/pusher/README.md index e9deef01..971cb281 100644 --- a/packages/pusher/README.md +++ b/packages/pusher/README.md @@ -337,6 +337,14 @@ Refer to the [OIS documentation](https://dapi-docs.api3.org/reference/ois/latest Refer to Airnode's [API credentials](https://dapi-docs.api3.org/reference/airnode/latest/deployment-files/config-json.html#apicredentials). +#### `nodeSettings` + +Contains general deployment parameters of the pusher. + +##### `nodeVersion` + +The version of the pusher. The version specified in the config must match the version of the pusher at deployment time. + ## Deployment TODO: Write example how to deploy on AWS diff --git a/packages/pusher/config/pusher.example.json b/packages/pusher/config/pusher.example.json index 144392b4..7dc2b8d0 100644 --- a/packages/pusher/config/pusher.example.json +++ b/packages/pusher/config/pusher.example.json @@ -93,5 +93,8 @@ "securitySchemeName": "NodarySecurityScheme1ApiKey", "securitySchemeValue": "${NODARY_API_KEY}" } - ] + ], + "nodeSettings": { + "nodeVersion": "0.1.0" + } } diff --git a/packages/pusher/package.json b/packages/pusher/package.json index 98118b84..a403f017 100644 --- a/packages/pusher/package.json +++ b/packages/pusher/package.json @@ -1,6 +1,6 @@ { "name": "pusher", - "version": "1.0.0", + "version": "0.1.0", "engines": { "node": "^18.14.0", "pnpm": "^8.8.0" @@ -15,7 +15,7 @@ "eslint:fix": "eslint . --ext .js,.ts --fix", "prettier:check": "prettier --check \"./**/*.{js,ts,md,yml,json}\"", "prettier:fix": "prettier --write \"./**/*.{js,ts,md,yml,json}\"", - "start-prod": "node --enable-source-maps dist/index.js", + "start-prod": "node dist/src/index.js", "test": "jest", "tsc": "tsc --project ." }, diff --git a/packages/pusher/src/validation/config.ts b/packages/pusher/src/validation/config.ts index 1b8709e5..67cf7f8e 100644 --- a/packages/pusher/src/validation/config.ts +++ b/packages/pusher/src/validation/config.ts @@ -1,5 +1,6 @@ import fs, { readFileSync } from 'node:fs'; import { join } from 'node:path'; +import { cwd } from 'node:process'; import { go } from '@api3/promise-utils'; import dotenv from 'dotenv'; @@ -8,7 +9,11 @@ import { configSchema } from './schema'; import { interpolateSecrets, parseSecrets } from './utils'; export const loadConfig = async () => { - const configPath = join(__dirname, '../../config'); + // When pusher is built the "/dist" file contains "src" folder and "package.json" and the config is expected to be + // located next to the "/dist" folder. When run in development, the config is expected to be located next to the "src" + // folder (one less import level). We resolve the config by CWD as a workaround. Since the pusher is dockerized, this + // is hidden from the user. + const configPath = join(cwd(), './config'); const rawSecrets = dotenv.parse(readFileSync(join(configPath, 'secrets.env'), 'utf8')); const goLoadConfig = await go(async () => { diff --git a/packages/pusher/src/validation/schema.test.ts b/packages/pusher/src/validation/schema.test.ts index 60ecfa47..d2e0c13e 100644 --- a/packages/pusher/src/validation/schema.test.ts +++ b/packages/pusher/src/validation/schema.test.ts @@ -29,6 +29,26 @@ test('validates example config', async () => { ); }); +test('ensures nodeVersion matches pusher version', async () => { + const invalidConfig: Config = { + ...config, + nodeSettings: { + ...config.nodeSettings, + nodeVersion: '0.0.1', + }, + }; + + await expect(configSchema.parseAsync(invalidConfig)).rejects.toStrictEqual( + new ZodError([ + { + code: 'custom', + message: 'Invalid node version', + path: ['nodeSettings', 'nodeVersion'], + }, + ]) + ); +}); + test('ensures signed API names are unique', () => { expect(() => signedApisSchema.parse([ diff --git a/packages/pusher/src/validation/schema.ts b/packages/pusher/src/validation/schema.ts index 038f5019..68fde3fc 100644 --- a/packages/pusher/src/validation/schema.ts +++ b/packages/pusher/src/validation/schema.ts @@ -13,6 +13,8 @@ import { ethers } from 'ethers'; import { isNil, uniqWith, isEqual } from 'lodash'; import { z, type SuperRefinement } from 'zod'; +import packageJson from '../../package.json'; + export const limiterConfig = z.object({ minTime: z.number(), maxConcurrency: z.number() }); export const parameterSchema = z @@ -242,6 +244,10 @@ export const oisesSchema = z.array(oisSchema); export const apisCredentialsSchema = z.array(config.apiCredentialsSchema); +export const nodeSettingsSchema = z.object({ + nodeVersion: z.string().refine((version) => version === packageJson.version, 'Invalid node version'), +}); + export const configSchema = z .object({ airnodeWalletMnemonic: z.string().refine((mnemonic) => ethers.utils.isValidMnemonic(mnemonic), 'Invalid mnemonic'), @@ -250,6 +256,7 @@ export const configSchema = z chains: z.any(), endpoints: endpointsSchema, gateways: z.any(), + nodeSettings: nodeSettingsSchema, ois: oisesSchema, rateLimiting: rateLimitingSchema, signedApis: signedApisSchema, diff --git a/packages/pusher/test/fixtures.ts b/packages/pusher/test/fixtures.ts index 256f15fd..f22c11db 100644 --- a/packages/pusher/test/fixtures.ts +++ b/packages/pusher/test/fixtures.ts @@ -1,5 +1,6 @@ import type { AxiosResponse } from 'axios'; +import packageJson from '../package.json'; import type { SignedResponse, TemplateResponse } from '../src/sign-template-data'; import type { Config } from '../src/validation/schema'; @@ -99,6 +100,9 @@ export const config: Config = { securitySchemeValue: 'invalid-api-key', }, ], + nodeSettings: { + nodeVersion: packageJson.version, + }, }; export const nodaryTemplateRequestResponseData = { diff --git a/tsconfig.json b/tsconfig.json index 01b8a356..bfda79a6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "exactOptionalPropertyTypes": true, "noUncheckedIndexedAccess": true, "noImplicitReturns": true, + "resolveJsonModule": true, // Disabled because they are covered by Eslint rules "noUnusedLocals": false,