Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .eslintrc

This file was deleted.

23 changes: 21 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,38 @@ on:
branches:
- master

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false # don't cancel all jobs if some of them failed
matrix:
node: ["20"]
node: ["22", "24", "26"]
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: yarn install
- run: yarn install --frozen-lockfile
- run: yarn lint
- run: yarn build
- run: yarn test

# Live Docker / IPFS / Ethereum checks (previously part of `yarn test`).
# Kept visible but non-blocking, since they depend on external services.
integration:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: "24"
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test:integration
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
registry-url: https://registry.npmjs.org/

- name: Install dependencies
run: yarn
run: yarn install --frozen-lockfile

- name: Compare versions
run: |
Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/test-ga-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ on:
branches:
- master

permissions:
contents: read
# The build bot creates/updates its comment through the issues API; on a pull
# request GitHub requires pull-requests: write for that.
issues: write
pull-requests: write

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js 20.x
- name: Set up Node.js 24.x
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
node-version: 24.x
- run: yarn install --frozen-lockfile
- run: yarn build

# Simulate Github Actions run on an actual DAppNode package
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/test-ga-bump-upstream-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ env:
ENVIRONMENT: TEST
INITIAL_VERSION: "0.1.0"

permissions:
contents: write # bump branch
pull-requests: write # bump PR
issues: write # build bot comment

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js 20.x
- name: Set up Node.js 24.x
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
node-version: 24.x
- run: yarn install --frozen-lockfile
- run: yarn build

# Simulate Github Actions run on an actual DAppNode package
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/test-ga-bump-upstream.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ env:
ENVIRONMENT: TEST
INITIAL_VERSION: "0.1.0"

permissions:
contents: write # bump branch
pull-requests: write # bump PR
issues: write # build bot comment

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js 20.x
- name: Set up Node.js 24.x
uses: actions/setup-node@v4
with:
node-version: 20.x
- run: yarn install
node-version: 24.x
- run: yarn install --frozen-lockfile
- run: yarn build

# Simulate Github Actions run on an actual DAppNode package
Expand Down
13 changes: 8 additions & 5 deletions .mocharc.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
colors: true
exit: true
extension: [ts]
import:
- tsx
require:
- dotenv/config
- ts-node/register
node-option:
- experimental-specifier-resolution=node
- loader=ts-node/esm
spec:
- test/**/*.test.ts
ignore:
- test/commands/build.test.ts
- test/tasks/generatePublishTx.test.ts
- test/utils/versions/getNextPackageVersion.test.ts
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ Only generates the IPFS Hash to be able to install it without needing to create
$ dappnodesdk build
```

### Root and variant files

In a multi-variant package (`--all_variants` or `--variants`), each release is assembled from the package root and the variant directory (`package_variants/<variant>` by default):

- `dappnode_package.json` and `docker-compose.yml` are deep-merged, with the variant overriding the root.
- `setup-wizard.*` fields are merged by `id`, and `notifications.yaml` endpoints by `correlationId`. Within a matched entry, the variant's values override the root's, and an array from the variant (for example `enum`, `conditions` or `alerts`) replaces the root's array. The release contains exactly what was validated.
- Single files (avatar, `prometheus-targets.*`, `disclaimer.md`, `getting-started.md`, `setup.schema.json`, `setup-target.json`, `setup-ui.json`, `signature.json`) are taken from the variant directory when it has one, and otherwise from the root.
- Grafana dashboards (`*grafana-dashboard.json`) are collected from both the root and the variant.

Schema validation coerces some values (for example `isBanner: "false"` becomes `false`). The release ships the coerced value, and the build prints a warning in the "Validate files" step so the source file can be fixed.

## Publish

It does the build of the image and shows the necessary transaction to be able to publish the package. The first time will create the repository but the rest will be updates of it.
Expand Down Expand Up @@ -126,11 +137,21 @@ services:
The dappnode SDK ueses the following internal dependencies to avoid code duplication across the `dappnodeSDK`, `DNP_DAPPMANAGER` and `sdk-publish` modules:

- `@dappnode/types`
- `@dappnode/toolkit`
- `@dappnode/schemas`

In order to have a better developing experience these modules lives inside the DNP_DAPPMANAGER repository

## Development

Use Node.js 22.13+ or Node.js 24+ (Node.js 20 reached end of life in April 2026), and install the exact locked dependencies with `yarn install --frozen-lockfile`.

- `yarn lint` checks source and tests without changing files; `yarn lint:fix` applies automatic fixes.
- `yarn build` checks and compiles the SDK.
- `yarn test` runs deterministic regressions using temporary files, mocked Docker commands, and localhost HTTP servers. It does not need external credentials.
- `yarn test:integration` runs the live Docker/IPFS/Ethereum tests and requires those services to be available.

Terminal builds show numbered package/architecture stages and recent complete log lines; uploads display a separate waiting-for-pinning phase after transmission.

## License

This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details
14 changes: 14 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import js from "@eslint/js";
import tseslint from "typescript-eslint";

export default tseslint.config(
{ ignores: ["dist/**", "node_modules/**", "test_files/**"] },
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ["**/*.ts"],
rules: {
"@typescript-eslint/no-unused-vars": ["error", { caughtErrors: "none" }]
}
}
);
65 changes: 32 additions & 33 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
"bin": "dist/dappnodesdk.js",
"scripts": {
"tag-and-publish": "npm version patch && git push --follow-tags",
"test": "mocha \"./{,!(node_modules)/**}/*.test.ts\"",
"lint": "eslint . --ext .ts --fix",
"build": "tsc",
"test": "mocha",
"lint": "eslint src test --max-warnings 0",
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc",
"prepublish": "npm run build",
"pre-commit": "npm run lint && npm run test"
"pre-commit": "npm run lint && npm run test",
"test:integration": "mocha --no-config --import=tsx --require=dotenv/config --timeout 120000 \"test/commands/build.test.ts\" \"test/tasks/generatePublishTx.test.ts\" \"test/utils/versions/getNextPackageVersion.test.ts\"",
"lint:fix": "npm run lint -- --fix"
},
"repository": {
"type": "git",
Expand All @@ -35,63 +37,60 @@
"homepage": "https://github.com/dappnode/DAppNodeSDK#readme",
"dependencies": {
"@dappnode/schemas": "^0.1.26",
"@dappnode/toolkit": "^0.1.21",
"@dappnode/types": "^0.1.41",
"@octokit/rest": "^20.1.1",
"@octokit/rest": "^22.0.1",
"async-retry": "^1.2.3",
"chalk": "^2.4.2",
"cli-progress": "^3.8.2",
"dockerode": "^3.3.5",
"dotenv": "^8.2.0",
"ethers": "6.3.0",
"dotenv": "^16.6.1",
"ethers": "^6.17.0",
"figlet": "^1.2.3",
"form-data": "^3.0.0",
"got": "^12.6.1",
"graphql": "^16.6.0",
"image-size": "^0.8.1",
"inquirer": "^6.2.1",
"js-yaml": "^4.1.0",
"form-data": "^4.0.6",
"got": "^14.6.6",
"image-size": "^2.0.4",
"inquirer": "^12.11.1",
"js-yaml": "^4.3.2",
"listr": "^0.14.3",
"lodash-es": "^4.17.21",
"lodash-es": "^4.18.1",
"log-update": "^6.1.0",
"mime-types": "^2.1.24",
"moment": "^2.27.0",
"prettier": "^2.1.2",
"request": "^2.88.2",
"rimraf": "^3.0.2",
"semver": "^7.6.0",
"source-map-support": "^0.5.19",
"tar-fs": "^2.0.0",
"tar-fs": "^3.1.3",
"timestring": "^5.0.1",
"yargs": "^17.7.1"
"yargs": "^17.7.1",
"wrap-ansi": "^9.0.2"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/async-retry": "^1.4.2",
"@types/chai": "^4.3.4",
"@types/cli-progress": "^3.7.0",
"@types/dockerode": "^3.3.16",
"@types/figlet": "^1.2.0",
"@types/inquirer": "^6.5.0",
"@types/js-yaml": "^3.12.5",
"@types/js-yaml": "^4.0.9",
"@types/listr": "^0.14.2",
"@types/lodash-es": "^4.17.7",
"@types/mime-types": "^2.1.0",
"@types/mocha": "^10.0.1",
"@types/node": "^22.7.5",
"@types/node": "^20.19.0",
"@types/prettier": "^2.1.5",
"@types/request": "^2.48.5",
"@types/rimraf": "^3.0.0",
"@types/semver": "^7.3.1",
"@types/tar-fs": "^2.0.0",
"@types/yargs": "^17.0.24",
"@typescript-eslint/eslint-plugin": "^3.5.0",
"@typescript-eslint/parser": "^3.5.0",
"chai": "^5.1.0",
"eslint": "^7.3.1",
"mocha": "^10.2.0",
"ts-node": "^10.9.1",
"typescript": "^5.6.3"
"eslint": "^10.14.0",
"mocha": "^12.0.3",
"tsx": "^4.23.15",
"typescript": "~5.9.3",
"typescript-eslint": "^8.72.1"
},
"engines": {
"node": ">=20.0.0"
"node": ">=22.13.0"
},
"resolutions": {
"minimatch": "^11.0.1",
"mocha/glob": "^14.0.2"
}
}
1 change: 0 additions & 1 deletion src/assets/defaultAvatar.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
/* eslint-disable max-len */
export default "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";
17 changes: 12 additions & 5 deletions src/commands/build/handler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getRendererOptions } from "../../utils/progress.js";
import Listr from "listr";
import { Architecture, architectures } from "@dappnode/types";
import { buildAndUpload } from "../../tasks/buildAndUpload/index.js";
Expand Down Expand Up @@ -51,7 +52,9 @@ export async function buildHandler({
for (const pkg of packagesToBuildProps) {
if (!pkg.architectures.includes(architecture))
throw Error(
`Architecture '${architecture}' is not supported by package ${pkg.manifest.name}. Supported: ${pkg.architectures.join(", ")}`
`Architecture '${architecture}' is not supported by package ${
pkg.manifest.name
}. Supported: ${pkg.architectures.join(", ")}`
);
pkg.architectures = [architecture];
}
Expand All @@ -66,15 +69,17 @@ export async function buildHandler({
skipUpload,
composeFileName,
requireGitData,
deleteOldPins,
variantsDirPath,
skipComposeValidation,
stripComposeVersion,
packagesToBuildProps
};

const verbosityOptions: VerbosityOptions = {
renderer: verbose ? "verbose" : silent ? "silent" : "default"
};
const verbosityOptions: VerbosityOptions = getRendererOptions({
verbose,
silent
});

const buildTasks = new Listr(buildAndUpload(buildOptions), verbosityOptions);

Expand All @@ -94,7 +99,9 @@ export function normalizeArchitecture(arch: string): Architecture {
const normalized = archShorthands[arch] ?? arch;
if (!architectures.includes(normalized as Architecture))
throw Error(
`Invalid architecture '${arch}', allowed values: ${architectures.join(", ")} (or shorthands: ${Object.keys(archShorthands).join(", ")})`
`Invalid architecture '${arch}', allowed values: ${architectures.join(
", "
)} (or shorthands: ${Object.keys(archShorthands).join(", ")})`
);
return normalized as Architecture;
}
10 changes: 7 additions & 3 deletions src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export const build: CommandModule<CliGlobalOptions, BuildCommandOptions> = {
handler: async (args): Promise<void> => {
const buildResults = await buildHandler(args);

printBuildResults(buildResults, Boolean(args.skipUpload));
printBuildResults(
buildResults,
Boolean(args.skip_upload || args.skip_save)
);
}
};

Expand All @@ -89,8 +92,9 @@ function printBuildResults(

for (const [dnpName, { releaseMultiHash }] of Object.entries(buildResults)) {
if (!releaseMultiHash) {
console.log(chalk.red(`Error building Dappnode Package (${dnpName})\n`));
return;
throw new Error(
`No uploaded release hash for Dappnode Package (${dnpName})`
);
}

console.log(`
Expand Down
Loading
Loading