diff --git a/.github/workflows/deltachat-rpc-server.yml b/.github/workflows/deltachat-rpc-server.yml index 78a3fbd331..2aedc2e897 100644 --- a/.github/workflows/deltachat-rpc-server.yml +++ b/.github/workflows/deltachat-rpc-server.yml @@ -266,3 +266,128 @@ jobs: - name: Publish deltachat-rpc-client to PyPI if: github.event_name == 'release' uses: pypa/gh-action-pypi-publish@release/v1 + + publish_npm_package: + name: Build & Publish npm prebuilds and deltachat-rpc-server + needs: ["build_linux", "build_windows", "build_macos"] + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + with: + show-progress: false + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Download Linux aarch64 binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-aarch64-linux + path: deltachat-rpc-server-aarch64-linux.d + + - name: Download Linux armv7l binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-armv7l-linux + path: deltachat-rpc-server-armv7l-linux.d + + - name: Download Linux armv6l binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-armv6l-linux + path: deltachat-rpc-server-armv6l-linux.d + + - name: Download Linux i686 binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-i686-linux + path: deltachat-rpc-server-i686-linux.d + + - name: Download Linux x86_64 binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-x86_64-linux + path: deltachat-rpc-server-x86_64-linux.d + + - name: Download Win32 binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-win32 + path: deltachat-rpc-server-win32.d + + - name: Download Win64 binary + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-win64 + path: deltachat-rpc-server-win64.d + + - name: Download macOS binary for x86_64 + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-x86_64-macos + path: deltachat-rpc-server-x86_64-macos.d + + - name: Download macOS binary for aarch64 + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-aarch64-macos + path: deltachat-rpc-server-aarch64-macos.d + + - name: Download Android binary for arm64-v8a + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-arm64-v8a-android + path: deltachat-rpc-server-arm64-v8a-android.d + + - name: Download Android binary for armeabi-v7a + uses: actions/download-artifact@v4 + with: + name: deltachat-rpc-server-armeabi-v7a-android + path: deltachat-rpc-server-armeabi-v7a-android.d + + - name: make npm packets for prebuilds and `@deltachat/stdio-rpc-server` + run: | + cd deltachat-rpc-server/npm-package + + python --version + + python scripts/pack_binary_for_platform.py aarch64-unknown-linux-musl ../../deltachat-rpc-server-aarch64-linux.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py armv7-unknown-linux-musleabihf ../../deltachat-rpc-server-armv7l-linux.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py arm-unknown-linux-musleabihf ../../deltachat-rpc-server-armv6l-linux.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py i686-unknown-linux-musl ../../deltachat-rpc-server-i686-linux.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py x86_64-unknown-linux-musl ../../deltachat-rpc-server-x86_64-linux.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py i686-pc-windows-gnu ../../deltachat-rpc-server-win32.d/deltachat-rpc-server.exe + python scripts/pack_binary_for_platform.py x86_64-pc-windows-gnu ../../deltachat-rpc-server-win64.d/deltachat-rpc-server.exe + python scripts/pack_binary_for_platform.py x86_64-apple-darwin ../../deltachat-rpc-server-x86_64-macos.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py aarch64-apple-darwin ../../deltachat-rpc-server-aarch64-macos.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py aarch64-linux-android ../../deltachat-rpc-server-arm64-v8a-android.d/deltachat-rpc-server + python scripts/pack_binary_for_platform.py armv7-linux-androideabi ../../deltachat-rpc-server-armeabi-v7a-android.d/deltachat-rpc-server + + ls -lah platform_package + + for platform in ./platform_package/*; do npm pack "$platform"; done + npm pack + ls -lah + + - name: Upload to artifacts + uses: actions/upload-artifact@v4 + with: + name: deltachat-rpc-server-npm-package + path: deltachat-rpc-server/npm-package/*.tgz + if-no-files-found: error + + - name: Upload npm packets to the GitHub release + if: github.event_name == 'release' + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + gh release upload ${{ github.ref_name }} \ + --repo ${{ github.repository }} \ + *.tgz + + - name: Publish npm packets for prebuilds and `@deltachat/stdio-rpc-server` + if: github.event_name == 'release' + run: | + ls -lah platform_package + + for platform in *.tgz; do npm publish "$platform"; done diff --git a/.gitignore b/.gitignore index c476805289..aab1ac6f08 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ deltachat-ffi/xml coverage/ .DS_Store +.vscode .vscode/launch.json python/accounts.txt python/all-testaccounts.txt diff --git a/deltachat-jsonrpc/typescript/package.json b/deltachat-jsonrpc/typescript/package.json index a1010d5e97..4f76e7fd94 100644 --- a/deltachat-jsonrpc/typescript/package.json +++ b/deltachat-jsonrpc/typescript/package.json @@ -25,7 +25,8 @@ "exports": { ".": { "import": "./dist/deltachat.js", - "require": "./dist/deltachat.cjs" + "require": "./dist/deltachat.cjs", + "types": "./dist/deltachat.d.ts" } }, "license": "MPL-2.0", diff --git a/deltachat-rpc-server/npm-package/.gitignore b/deltachat-rpc-server/npm-package/.gitignore new file mode 100644 index 0000000000..511a7e1fa0 --- /dev/null +++ b/deltachat-rpc-server/npm-package/.gitignore @@ -0,0 +1,2 @@ +platform_package +*.tgz \ No newline at end of file diff --git a/deltachat-rpc-server/npm-package/.npmignore b/deltachat-rpc-server/npm-package/.npmignore new file mode 100644 index 0000000000..473106442a --- /dev/null +++ b/deltachat-rpc-server/npm-package/.npmignore @@ -0,0 +1,3 @@ +platform_package/* +scripts/ +*.tgz \ No newline at end of file diff --git a/deltachat-rpc-server/npm-package/README.md b/deltachat-rpc-server/npm-package/README.md new file mode 100644 index 0000000000..97d17ac335 --- /dev/null +++ b/deltachat-rpc-server/npm-package/README.md @@ -0,0 +1,77 @@ +## npm package for deltachat-rpc-server + +This is the successor of `deltachat-node`, +it does not use NAPI bindings but instead uses stdio executables +to let you talk to core over jsonrpc over stdio. +This simplifies cross-compilation and even reduces binary size (no CFFI layer and no NAPI layer). + +## Usage + +> The **minimum** nodejs version for this package is `20.11` + +``` +npm i @deltachat/stdio-rpc-server @deltachat/jsonrpc-client +``` + +```js +import { startDeltaChat } from "@deltachat/stdio-rpc-server"; +import { C } from "@deltachat/jsonrpc-client"; + +async function main() { + const dc = await startDeltaChat("deltachat-data"); + console.log(await dc.rpc.getSystemInfo()); +} +``` + +For a more complete example refer to https://github.com/deltachat-bot/echo/pull/69/files (TODO change link when pr is merged). + +## How to use on an unsupported platform + + + + + +## How does it work when you install it + +NPM automatically installs platform dependent optional dependencies when `os` and `cpu` fields are set correctly. + +references: + +- https://napi.rs/docs/deep-dive/release#3-the-native-addon-for-different-platforms-is-distributed-through-different-npm-packages, [webarchive version](https://web.archive.org/web/20240309234250/https://napi.rs/docs/deep-dive/release#3-the-native-addon-for-different-platforms-is-distributed-through-different-npm-packages) +- https://docs.npmjs.com/cli/v6/configuring-npm/package-json#cpu +- https://docs.npmjs.com/cli/v6/configuring-npm/package-json#os + +When you import this package it searches for the rpc server in the following locations and order: + +1. `DELTA_CHAT_RPC_SERVER` environment variable +2. in PATH + - unless `DELTA_CHAT_SKIP_PATH=1` is specified + - searches in .cargo/bin directory first + - but there an additional version check is performed +3. prebuilds in npm packages + +## How do you built this package in CI + +- To build platform packages, run the `build_platform_package.py` script: + ``` + python3 build_platform_package.py + # example + python3 build_platform_package.py x86_64-apple-darwin + ``` +- Then pass it as an artifact to the last CI action that publishes the main package. +- upload all packages from `deltachat-rpc-server/npm-package/platform_package`. +- then publish `deltachat-rpc-server/npm-package`, + - this will run `update_optional_dependencies_and_version.js` (in the `prepack` script), + which puts all platform packages into `optionalDependencies` and updates the `version` in `package.json` + +## How to build a version you can use localy on your host machine for development + +You can not install the npm packet from the previous section locally, unless you have a local npm registry set up where you upload it too. This is why we have seperate scripts for making it work for local installation. + +- If you just need your host platform run `python scripts/make_local_dev_version.py` +- note: this clears the `platform_package` folder +- (advanced) If you need more than one platform for local install you can just run `node scripts/update_optional_dependencies_and_version.js` after building multiple plaftorms with `build_platform_package.py` + +## Thanks to nlnet + +The initial work on this package was funded by nlnet as part of the [Delta Tauri](https://nlnet.nl/project/DeltaTauri/) Project. diff --git a/deltachat-rpc-server/npm-package/index.d.ts b/deltachat-rpc-server/npm-package/index.d.ts new file mode 100644 index 0000000000..a3cd2c421a --- /dev/null +++ b/deltachat-rpc-server/npm-package/index.d.ts @@ -0,0 +1,39 @@ +import { StdioDeltaChat } from "@deltachat/jsonrpc-client"; + +export interface SearchOptions { + /** whether to disable looking for deltachat-rpc-server inside of $PATH */ + skipSearchInPath: boolean; + + /** whether to disable the DELTA_CHAT_RPC_SERVER environment variable */ + disableEnvPath: boolean; +} + +/** + * + * @returns absolute path to deltachat-rpc-server binary + * @throws when it is not found + */ +export function getRPCServerPath( + options?: Partial +): Promise; + + + +export type DeltaChatOverJsonRpcServer = StdioDeltaChat & { + shutdown: () => Promise; + readonly pathToServerBinary: string; +}; + + +/** + * + * @param directory directory for accounts folder + * @param options + */ +export function startDeltaChat(directory: string, options?: Partial ): Promise + + +export namespace FnTypes { + export type getRPCServerPath = typeof getRPCServerPath + export type startDeltaChat = typeof startDeltaChat +} \ No newline at end of file diff --git a/deltachat-rpc-server/npm-package/index.js b/deltachat-rpc-server/npm-package/index.js new file mode 100644 index 0000000000..d933a4618c --- /dev/null +++ b/deltachat-rpc-server/npm-package/index.js @@ -0,0 +1,143 @@ +//@ts-check +import { execFile, spawn } from "node:child_process"; +import { stat, readdir } from "node:fs/promises"; +import os from "node:os"; +import { join, basename } from "node:path"; +import process from "node:process"; +import { promisify } from "node:util"; +import { + ENV_VAR_NAME, + PATH_EXECUTABLE_NAME, + SKIP_SEARCH_IN_PATH, +} from "./src/const.js"; +import { + ENV_VAR_LOCATION_NOT_FOUND, + FAILED_TO_START_SERVER_EXECUTABLE, + NPM_NOT_FOUND_SUPPORTED_PLATFORM_ERROR, + NPM_NOT_FOUND_UNSUPPORTED_PLATFORM_ERROR, +} from "./src/errors.js"; + +// Because this is not compiled by typescript, esm needs this stuff (` with { type: "json" };`, +// nodejs still complains about it being experimental, but deno also uses it, so treefit bets taht it will become standard) +import package_json from "./package.json" with { type: "json" }; +import { createRequire } from "node:module"; + +// exports +// - [ ] a raw starter that has a stdin/out handle thingie like desktop uses +// - [X] a function that already wraps the stdio handle from above into the deltachat jsonrpc bindings + +function findRPCServerInNodeModules() { + const arch = os.arch(); + const operating_system = process.platform; + const package_name = `@deltachat/stdio-rpc-server-${operating_system}-${arch}`; + try { + const { resolve } = createRequire(import.meta.url); + return resolve(package_name); + } catch (error) { + console.debug("findRpcServerInNodeModules", error); + if (Object.keys(package_json.optionalDependencies).includes(package_name)) { + throw new Error(NPM_NOT_FOUND_SUPPORTED_PLATFORM_ERROR(package_name)); + } else { + throw new Error(NPM_NOT_FOUND_UNSUPPORTED_PLATFORM_ERROR()); + } + } +} + +/** @type {import("./index").FnTypes.getRPCServerPath} */ +export async function getRPCServerPath( + options = { skipSearchInPath: false, disableEnvPath: false } +) { + // @TODO: improve confusing naming of these options + const { skipSearchInPath, disableEnvPath } = options; + // 1. check if it is set as env var + if (process.env[ENV_VAR_NAME] && !disableEnvPath) { + try { + if (!(await stat(process.env[ENV_VAR_NAME])).isFile()) { + throw new Error( + `expected ${ENV_VAR_NAME} to point to the deltachat-rpc-server executable` + ); + } + } catch (error) { + throw new Error(ENV_VAR_LOCATION_NOT_FOUND()); + } + return process.env[ENV_VAR_NAME]; + } + + // 2. check if it can be found in PATH + if (!process.env[SKIP_SEARCH_IN_PATH] && !skipSearchInPath) { + const exec = promisify(execFile); + + const { stdout: executable } = + os.platform() !== "win32" + ? await exec("command", ["-v", PATH_EXECUTABLE_NAME]) + : await exec("where", [PATH_EXECUTABLE_NAME]); + + // by just trying to execute it and then use "command -v deltachat-rpc-server" (unix) or "where deltachat-rpc-server" (windows) to get the path to the executable + if (executable.length > 1) { + // test if it is the right version + try { + // for some unknown reason it is in stderr and not in stdout + const { stderr } = await promisify(execFile)(executable, ["--version"]); + const version = stderr.slice(0, stderr.indexOf("\n")); + if (package_json.version !== version) { + throw new Error( + `version mismatch: (npm package: ${package_json.version}) (installed ${PATH_EXECUTABLE_NAME} version: ${version})` + ); + } else { + return executable; + } + } catch (error) { + console.error( + "Found executable in PATH, but there was an error: " + error + ); + console.error("So falling back to using prebuild..."); + } + } + } + // 3. check for prebuilds + + return findRPCServerInNodeModules(); +} + +import { StdioDeltaChat } from "@deltachat/jsonrpc-client"; + +/** @type {import("./index").FnTypes.startDeltaChat} */ +export async function startDeltaChat(directory, options) { + const pathToServerBinary = await getRPCServerPath(options); + const server = spawn(pathToServerBinary, { + env: { + RUST_LOG: process.env.RUST_LOG || "info", + DC_ACCOUNTS_PATH: directory, + }, + }); + + server.on("error", (err) => { + throw new Error(FAILED_TO_START_SERVER_EXECUTABLE(pathToServerBinary, err)); + }); + let shouldClose = false; + + server.on("exit", () => { + if (shouldClose) { + return; + } + throw new Error("Server quit"); + }); + + server.stderr.pipe(process.stderr); + + /** @type {import('./index').DeltaChatOverJsonRpcServer} */ + //@ts-expect-error + const dc = new StdioDeltaChat(server.stdin, server.stdout, true); + + dc.shutdown = async () => { + shouldClose = true; + if (!server.kill()) { + console.log("server termination failed"); + } + }; + + //@ts-expect-error + dc.pathToServerBinary = pathToServerBinary; + + return dc; +} diff --git a/deltachat-rpc-server/npm-package/package.json b/deltachat-rpc-server/npm-package/package.json new file mode 100644 index 0000000000..92a38e8db3 --- /dev/null +++ b/deltachat-rpc-server/npm-package/package.json @@ -0,0 +1,16 @@ +{ + "type": "module", + "name": "@deltachat/stdio-rpc-server", + "version": "1.137.4", + "license": "MPL-2.0", + "main": "index.js", + "types": "index.d.ts", + "scripts": { + "prepack": "node scripts/update_optional_dependencies_and_version.js" + }, + "optionalDependencies": { + }, + "peerDependencies": { + "@deltachat/jsonrpc-client": "*" + } +} \ No newline at end of file diff --git a/deltachat-rpc-server/npm-package/scripts/build_platform_package.py b/deltachat-rpc-server/npm-package/scripts/build_platform_package.py new file mode 100755 index 0000000000..4ebc29b8dd --- /dev/null +++ b/deltachat-rpc-server/npm-package/scripts/build_platform_package.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +import subprocess +from sys import argv +from os import path, makedirs, chdir +from shutil import copy +from src.make_package import write_package_json + +# ensure correct working directory +chdir(path.join(path.dirname(path.abspath(__file__)), "../")) + +if len(argv) < 2: + print("First argument should be target architecture as required by cargo") + exit(1) + +target = argv[1].strip() + +subprocess.run( + ["cargo", "build", "--release", "-p", "deltachat-rpc-server", "--target", target], + check=True, +) + +newpath = "platform_package" +if not path.exists(newpath): + makedirs(newpath) + +# make new folder + +platform_path = "platform_package/" + target +if not path.exists(platform_path): + makedirs(platform_path) + +# copy binary it over + + +def binary_path(binary_name): + return "../../target/" + target + "/release/" + binary_name + + +my_binary_name = "deltachat-rpc-server" + +if not path.isfile(binary_path("deltachat-rpc-server")): + my_binary_name = "deltachat-rpc-server.exe" + if not path.isfile(binary_path("deltachat-rpc-server.exe")): + print("Did not find the build") + exit(1) + +my_binary_path = binary_path(my_binary_name) + +copy(my_binary_path, platform_path + "/" + my_binary_name) + +# make a package.json for it + +write_package_json(platform_path, target, my_binary_name) diff --git a/deltachat-rpc-server/npm-package/scripts/make_local_dev_version.py b/deltachat-rpc-server/npm-package/scripts/make_local_dev_version.py new file mode 100644 index 0000000000..635a6014ff --- /dev/null +++ b/deltachat-rpc-server/npm-package/scripts/make_local_dev_version.py @@ -0,0 +1,31 @@ +# This script is for making a version of the npm packet that you can install locally + +import subprocess +from sys import argv +from os import path, makedirs, chdir +import re +import json +import tomllib +from shutil import copy, rmtree + +# ensure correct working directory +chdir(path.join(path.dirname(path.abspath(__file__)), "../")) + +# get host target with "rustc -vV" +output = subprocess.run(["rustc", "-vV"], capture_output=True) +host_target = re.search('host: ([-\\w]*)', output.stdout.decode("utf-8")).group(1) +print("host target to build for is:", host_target) + +# clean platform_package folder +newpath = r'platform_package' +if not path.exists(newpath): + makedirs(newpath) +else: + rmtree(path.join(path.dirname(path.abspath(__file__)), "../platform_package/")) + makedirs(newpath) + +# run build_platform_package.py with the host's target to build it +subprocess.run(["python", "scripts/build_platform_package.py", host_target], capture_output=False, check=True) + +# run update_optional_dependencies_and_version.js to adjust the package / make it installable locally +subprocess.run(["node", "scripts/update_optional_dependencies_and_version.js", "--local"], capture_output=False, check=True) diff --git a/deltachat-rpc-server/npm-package/scripts/pack_binary_for_platform.py b/deltachat-rpc-server/npm-package/scripts/pack_binary_for_platform.py new file mode 100644 index 0000000000..8c58f39903 --- /dev/null +++ b/deltachat-rpc-server/npm-package/scripts/pack_binary_for_platform.py @@ -0,0 +1,46 @@ +import subprocess +from sys import argv +from os import path, makedirs, chdir, chmod, stat +import json +from shutil import copy +from src.make_package import write_package_json + +# ensure correct working directory +chdir(path.join(path.dirname(path.abspath(__file__)), "../")) + +if len(argv) < 3: + print("First argument should be target architecture as required by cargo") + print("Second argument should be the location of th built binary (binary_path)") + exit(1) + +target = argv[1].strip() +binary_path = argv[2].strip() + +output = subprocess.run(["rustc","--print","target-list"], capture_output=True, check=True) +available_targets = output.stdout.decode("utf-8") + +if available_targets.find(target) == -1: + print("target", target, "is not known / not valid") + exit(1) + + +newpath = r'platform_package' +if not path.exists(newpath): + makedirs(newpath) + +# make new folder + +platform_path = 'platform_package/' + target +if not path.exists(platform_path): + makedirs(platform_path) + +# copy binary it over + +my_binary_name = path.basename(binary_path) +new_binary_path = platform_path + "/" + my_binary_name +copy(binary_path, new_binary_path) +chmod(new_binary_path, 0o555) # everyone can read & execute, nobody can write + +# make a package.json for it + +write_package_json(platform_path, target, my_binary_name) diff --git a/deltachat-rpc-server/npm-package/scripts/src/convert_platform.py b/deltachat-rpc-server/npm-package/scripts/src/convert_platform.py new file mode 100644 index 0000000000..d7fe6ab516 --- /dev/null +++ b/deltachat-rpc-server/npm-package/scripts/src/convert_platform.py @@ -0,0 +1,21 @@ +def convert_cpu_arch_to_npm_cpu_arch(arch): + if arch == "x86_64": + return "x64" + if arch == "i686": + return "i32" + if arch == "aarch64": + return "arm64" + if arch == "armv7" or arch == "arm": + return "arm" + print("architecture might not be known by nodejs, please make sure it can be returned by 'process.arch':", arch) + return arch + +def convert_os_to_npm_os(os): + if os == "windows": + return "win32" + if os == "darwin" or os == "linux": + return os + if os.startswith("android"): + return "android" + print("architecture might not be known by nodejs, please make sure it can be returned by 'process.platform':", os) + return os \ No newline at end of file diff --git a/deltachat-rpc-server/npm-package/scripts/src/make_package.py b/deltachat-rpc-server/npm-package/scripts/src/make_package.py new file mode 100644 index 0000000000..253d0b4332 --- /dev/null +++ b/deltachat-rpc-server/npm-package/scripts/src/make_package.py @@ -0,0 +1,27 @@ +import tomllib +import json + +from .convert_platform import convert_cpu_arch_to_npm_cpu_arch, convert_os_to_npm_os + +def write_package_json(platform_path, rust_target, my_binary_name): + if len(rust_target.split("-")) == 3: + [cpu_arch, vendor, os] = rust_target.split("-") + else: + [cpu_arch, vendor, os, _env] = rust_target.split("-") + + # read version + tomlfile = open("../../Cargo.toml", 'rb') + version = tomllib.load(tomlfile)['package']['version'] + + package_json = dict({ + "name": "@deltachat/stdio-rpc-server-" + convert_os_to_npm_os(os) + "-" + convert_cpu_arch_to_npm_cpu_arch(cpu_arch), + "version": version, + "os": [convert_os_to_npm_os(os)], + "cpu": [convert_cpu_arch_to_npm_cpu_arch(cpu_arch)], + "main": my_binary_name, + "license": "MPL-2.0" + }) + + file = open(platform_path + "/package.json", 'w') + file.write(json.dumps(package_json, indent=4)) + diff --git a/deltachat-rpc-server/npm-package/scripts/update_optional_dependencies_and_version.js b/deltachat-rpc-server/npm-package/scripts/update_optional_dependencies_and_version.js new file mode 100644 index 0000000000..1c30d48063 --- /dev/null +++ b/deltachat-rpc-server/npm-package/scripts/update_optional_dependencies_and_version.js @@ -0,0 +1,57 @@ +import fs from "node:fs/promises"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const expected_cwd = join(dirname(fileURLToPath(import.meta.url)), ".."); + +if (process.cwd() !== expected_cwd) { + console.error( + "CWD missmatch: this script needs to be run from " + expected_cwd, + { actual: process.cwd(), expected: expected_cwd } + ); + process.exit(1); +} + +// whether to use local paths instead of npm registry version number for the prebuilds in optionalDependencies +// useful for local development +const is_local = process.argv.includes("--local"); + +const package_json = JSON.parse(await fs.readFile("./package.json", "utf8")); + +const cargo_toml = await fs.readFile("../Cargo.toml", "utf8"); +const version = cargo_toml + .split("\n") + .find((line) => line.includes("version")) + .split('"')[1]; + +const platform_packages_dir = "./platform_package"; + +const platform_package_names = await Promise.all( + (await fs.readdir(platform_packages_dir)).map(async (name) => { + const p = JSON.parse( + await fs.readFile( + join(platform_packages_dir, name, "package.json"), + "utf8" + ) + ); + if (p.version !== version) { + console.error( + name, + "has a different version than the version of the rpc server.", + { rpc_server: version, platform_package: p.version } + ); + throw new Error("version missmatch"); + } + return { folder_name: name, package_name: p.name }; + }) +); + +package_json.version = version; +package_json.optionalDependencies = {}; +for (const { folder_name, package_name } of platform_package_names) { + package_json.optionalDependencies[package_name] = is_local + ? `file:${expected_cwd}/platform_package/${folder_name}` // npm seems to work better with an absolute path here + : version; +} + +await fs.writeFile("./package.json", JSON.stringify(package_json, null, 4)); diff --git a/deltachat-rpc-server/npm-package/src/const.js b/deltachat-rpc-server/npm-package/src/const.js new file mode 100644 index 0000000000..fafed99a5c --- /dev/null +++ b/deltachat-rpc-server/npm-package/src/const.js @@ -0,0 +1,6 @@ +//@ts-check + +export const PATH_EXECUTABLE_NAME = 'deltachat-rpc-server' + +export const ENV_VAR_NAME = "DELTA_CHAT_RPC_SERVER" +export const SKIP_SEARCH_IN_PATH = "DELTA_CHAT_SKIP_PATH" \ No newline at end of file diff --git a/deltachat-rpc-server/npm-package/src/errors.js b/deltachat-rpc-server/npm-package/src/errors.js new file mode 100644 index 0000000000..52d8f115aa --- /dev/null +++ b/deltachat-rpc-server/npm-package/src/errors.js @@ -0,0 +1,41 @@ +//@ts-check +import { ENV_VAR_NAME } from "./const.js"; + +const cargoInstallCommand = + "cargo install --git https://github.com/deltachat/deltachat-core-rust deltachat-rpc-server"; + +export function NPM_NOT_FOUND_SUPPORTED_PLATFORM_ERROR(package_name) { + return `deltachat-rpc-server not found: + +- Install it with "npm i ${package_name}" +- or download/compile deltachat-rpc-server for your platform and + - either put it into your PATH (for example with "${cargoInstallCommand}") + - or set the "${ENV_VAR_NAME}" env var to the path to deltachat-rpc-server"`; +} + +export function NPM_NOT_FOUND_UNSUPPORTED_PLATFORM_ERROR() { + return `deltachat-rpc-server not found: + +Unfortunately no prebuild is available for your system, so you need to provide deltachat-rpc-server yourself. + +- Download or Compile deltachat-rpc-server for your platform and + - either put it into your PATH (for example with "${cargoInstallCommand}") + - or set the "${ENV_VAR_NAME}" env var to the path to deltachat-rpc-server"`; +} + +export function ENV_VAR_LOCATION_NOT_FOUND(error) { + return `deltachat-rpc-server not found in ${ENV_VAR_NAME}: + + Error: ${error} + + Content of ${ENV_VAR_NAME}: "${process.env[ENV_VAR_NAME]}"`; +} + +export function FAILED_TO_START_SERVER_EXECUTABLE(pathToServerBinary, error) { + return `Failed to start server executable at '${pathToServerBinary}', + + Error: ${error} + +Make sure the deltachat-rpc-server binary exists at this location +and you can start it with \`${pathToServerBinary} --version\``; +} diff --git a/scripts/set_core_version.py b/scripts/set_core_version.py index d83d564940..d2abbdb3b1 100755 --- a/scripts/set_core_version.py +++ b/scripts/set_core_version.py @@ -66,7 +66,11 @@ def main(): parser = ArgumentParser(prog="set_core_version") parser.add_argument("newversion") - json_list = ["package.json", "deltachat-jsonrpc/typescript/package.json"] + json_list = [ + "package.json", + "deltachat-jsonrpc/typescript/package.json", + "deltachat-rpc-server/npm-package/package.json", + ] toml_list = [ "Cargo.toml", "deltachat-ffi/Cargo.toml",