From 92801c2fce68957bd3bdb672bbb7c92efe6321a4 Mon Sep 17 00:00:00 2001 From: dappnodedev <144998261+dappnodedev@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:23:41 +0200 Subject: [PATCH] Improve upstream repo schema (#1932) * Improve upstream repo schema * Add upstream field to types * Set upstream fields as arrays * Add tests to upstream settings --- .../src/autoUpdates/formatNotificationBody.ts | 7 +- .../schemas/src/schemas/manifest.schema.json | 46 ++++++++++ .../schemas/test/unit/validateSchema.test.ts | 84 +++++++++++++++++++ packages/types/src/calls.ts | 8 +- packages/types/src/manifest.ts | 25 +++--- 5 files changed, 152 insertions(+), 18 deletions(-) diff --git a/packages/daemons/src/autoUpdates/formatNotificationBody.ts b/packages/daemons/src/autoUpdates/formatNotificationBody.ts index 7cdff9ea6..3a54c8407 100644 --- a/packages/daemons/src/autoUpdates/formatNotificationBody.ts +++ b/packages/daemons/src/autoUpdates/formatNotificationBody.ts @@ -15,7 +15,7 @@ export function formatPackageUpdateNotification({ dnpName: string; currentVersion: string; newVersion: string; - upstreamVersion?: string; + upstreamVersion?: string | string[]; autoUpdatesEnabled: boolean; }): string { const prettyName = prettyDnpName(dnpName); @@ -25,7 +25,7 @@ export function formatPackageUpdateNotification({ `New version ready to install for ${prettyName} (current version ${currentVersion})`, upstreamVersion ? ` - package version: ${newVersion}\n` + - ` - upstream version: ${upstreamVersion}` + ` - upstream version: ${upstreamVersion}` : ` - version: ${newVersion}`, `Connect to your DAppNode to install this new version [install / ${prettyName}](${installUrl}).`, @@ -46,8 +46,7 @@ export function formatSystemUpdateNotification({ "New system version ready to install", packages.map( (p) => - ` - ${prettyDnpName(p.name)}: ${p.to} ${ - p.from ? `(current: ${p.from})` : "" + ` - ${prettyDnpName(p.name)}: ${p.to} ${p.from ? `(current: ${p.from})` : "" }` ), diff --git a/packages/schemas/src/schemas/manifest.schema.json b/packages/schemas/src/schemas/manifest.schema.json index 0db3e184a..ce6506398 100644 --- a/packages/schemas/src/schemas/manifest.schema.json +++ b/packages/schemas/src/schemas/manifest.schema.json @@ -7,6 +7,28 @@ "title": "DAppNode Package (DNP) manifest", "required": ["name", "version", "description", "type", "license"], "description": "The DAppNode Package manifest defines all the necessary information for a DAppNode to understand this package:\n - IPFS of BZZ hashes to download its docker image \n - Docker related data to configure and run its container \n - Metadata to control how the package is shown in the admin UI.", + "dependencies": { + "upstream": { + "not": { + "required": ["upstreamRepo", "upstreamVersion", "upstreamArg"] + } + }, + "upstreamRepo": { + "not": { + "required": ["upstream"] + } + }, + "upstreamVersion": { + "not": { + "required": ["upstream"] + } + }, + "upstreamArg": { + "not": { + "required": ["upstream"] + } + } + }, "properties": { "name": { "type": "string", @@ -71,6 +93,30 @@ } ] }, + "upstream": { + "type": "array", + "items": { + "type": "object", + "required": ["repo", "version", "arg"], + "properties": { + "repo": { + "type": "string", + "description": "Repository of the upstream software.", + "examples": ["ethereum/go-ethereum", "NethermindEth/nethermind"] + }, + "version": { + "type": "string", + "description": "Version of the upstream software.", + "examples": ["2.6.0", "v1.2.1"] + }, + "arg": { + "type": "string", + "description": "Environment variable name for specifying the version.", + "examples": ["GETH_VERSION", "NETHERMIND_VERSION"] + } + } + } + }, "shortDescription": { "type": "string", "description": "Short DAppNode Package description, 6-8 words sentence briefly describing the purpose of this DAppNode Package. The purpose is to quickly grab users' attention and clearly define its purpose. Markdown is discouraged as it will NOT be rendered on the DAppNode Package store view.", diff --git a/packages/schemas/test/unit/validateSchema.test.ts b/packages/schemas/test/unit/validateSchema.test.ts index b0d0fd043..d0faa7136 100644 --- a/packages/schemas/test/unit/validateSchema.test.ts +++ b/packages/schemas/test/unit/validateSchema.test.ts @@ -330,5 +330,89 @@ volumes: expect(() => validateSetupWizardSchema(invalidSetupWizard)).to.throw(); }); + + it("should allow a manifest with upstream settings defined as an array of objects", () => { + const manifest: Manifest = { + name: "example.dnp.dappnode.eth", + version: "1.0.0", + description: "A sample DAppNode package", + type: "service", + license: "MIT", + upstream: [ + { + repo: "ethereum/go-ethereum", + version: "1.9.24", + arg: "GETH_VERSION" + } + ] + }; + + expect(() => validateManifestSchema(manifest)).to.not.throw(); + }); + + it("should allow a manifest with the upstream settings defined as separate strings", () => { + const manifest: Manifest = { + name: "example.dnp.dappnode.eth", + version: "1.0.0", + description: "A sample DAppNode package", + type: "service", + license: "MIT", + upstreamRepo: "ethereum/go-ethereum", + upstreamVersion: "1.9.24", + upstreamArg: "GETH_VERSION" + }; + + expect(() => validateManifestSchema(manifest)).to.not.throw(); + }); + + it("should allow a manifest with the upstream settings defined as separate arrays", () => { + const manifest: Manifest = { + name: "example.dnp.dappnode.eth", + version: "1.0.0", + description: "A sample DAppNode package", + type: "service", + license: "MIT", + upstreamRepo: ["ethereum/go-ethereum", "NethermindEth/nethermind"], + upstreamVersion: ["1.9.24", "1.10.0"], + upstreamArg: ["GETH_VERSION", "NETHERMIND_VERSION"] + }; + + expect(() => validateManifestSchema(manifest)).to.not.throw(); + }); + + it("should not allow a manifest with upstream settings defined in both possible ways", () => { + const manifest: Manifest = { // Using 'any' to bypass TypeScript checks for invalid schema + name: "example.dnp.dappnode.eth", + version: "1.0.0", + description: "A sample DAppNode package", + type: "service", + license: "MIT", + upstream: [ + { + repo: "ethereum/go-ethereum", + version: "1.9.24", + arg: "GETH_VERSION" + } + ], + upstreamRepo: "ethereum/go-ethereum", + upstreamVersion: "1.9.24", + upstreamArg: "GETH_VERSION" + }; + + expect(() => validateManifestSchema(manifest)).to.throw(); + }); + + it("should allow a manifest without any upstream definitions", () => { + const manifest: Manifest = { + name: "example.dnp.dappnode.eth", + version: "1.0.0", + description: "A sample DAppNode package", + type: "service", + license: "MIT" + }; + + expect(() => validateManifestSchema(manifest)).to.not.throw(); + }); + }); }); diff --git a/packages/types/src/calls.ts b/packages/types/src/calls.ts index 55df0b366..b393cfd7c 100644 --- a/packages/types/src/calls.ts +++ b/packages/types/src/calls.ts @@ -546,7 +546,7 @@ export type InstalledPackageData = Pick< export interface UpdateAvailable { newVersion: string; - upstreamVersion?: string; + upstreamVersion?: string | string[]; } export interface InstalledPackageDetailData extends InstalledPackageData { @@ -1034,9 +1034,9 @@ export type EthClientSyncedNotificationStatus = { export type Eth2ClientTarget = | { - execClient: ExecutionClientMainnet; - consClient: ConsensusClientMainnet; - } + execClient: ExecutionClientMainnet; + consClient: ConsensusClientMainnet; + } | "remote"; /** diff --git a/packages/types/src/manifest.ts b/packages/types/src/manifest.ts index 97874e75a..820363cce 100644 --- a/packages/types/src/manifest.ts +++ b/packages/types/src/manifest.ts @@ -9,9 +9,14 @@ export interface Manifest { // Package metadata name: string; version: string; - upstreamVersion?: string; - upstreamRepo?: string; - upstreamArg?: string; + upstreamVersion?: string | string[]; + upstreamRepo?: string | string[]; + upstreamArg?: string | string[]; + upstream?: { + repo: string; + version: string; + arg: string; + }[]; shortDescription?: string; description?: string; author?: string; @@ -49,13 +54,13 @@ export interface Manifest { minimumDockerVersion?: string; }; globalEnvs?: - | { - all?: boolean; - } - | { - envs: string[]; - services: string[]; - }[]; + | { + all?: boolean; + } + | { + envs: string[]; + services: string[]; + }[]; architectures?: Architecture[]; // Safety properties to solve problematic updates