mirror of
https://github.com/chatmail/core.git
synced 2026-04-22 16:06:30 +03:00
stdio jsonrpc server npm package (#5332)
- [x] figgure out how to build the packages (that it installs native optional package automatically) - [X] Make the gluecode - [x] expose both the lowerlevel api that desktop uses (~~send objects and receive objects~~, getting path of rpc-server is enough) - [X] and the higher level api needed for bots (jsonrpc client) - [X] typescript types - [x] automatically pick the right binary from npm or allow getting it from env var, or give out an error (throw error) - [x] find out how to dev locally (use local built core in dc desktop) - there is the question of how to link the typescript client and the task to add a search in the cargo target folder for a debug build or a different way, find out some good flow that we can use and document for dc desktop + locally built core development - [x] build the packages in ci - [x] fix that deltachat-rpc-server is not executable postponed: - [ ] publish from ci - [ ] add key/token to deploy to npm Closes #4694 ## Related prs - https://github.com/deltachat-bot/echo/pull/69 - https://github.com/deltachat/deltachat-desktop/pull/3567 --------- Co-authored-by: link2xt <link2xt@testrun.org>
This commit is contained in:
125
.github/workflows/deltachat-rpc-server.yml
vendored
125
.github/workflows/deltachat-rpc-server.yml
vendored
@@ -266,3 +266,128 @@ jobs:
|
|||||||
- name: Publish deltachat-rpc-client to PyPI
|
- name: Publish deltachat-rpc-client to PyPI
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
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
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,6 +33,7 @@ deltachat-ffi/xml
|
|||||||
|
|
||||||
coverage/
|
coverage/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.vscode
|
||||||
.vscode/launch.json
|
.vscode/launch.json
|
||||||
python/accounts.txt
|
python/accounts.txt
|
||||||
python/all-testaccounts.txt
|
python/all-testaccounts.txt
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"import": "./dist/deltachat.js",
|
"import": "./dist/deltachat.js",
|
||||||
"require": "./dist/deltachat.cjs"
|
"require": "./dist/deltachat.cjs",
|
||||||
|
"types": "./dist/deltachat.d.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
|
|||||||
2
deltachat-rpc-server/npm-package/.gitignore
vendored
Normal file
2
deltachat-rpc-server/npm-package/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
platform_package
|
||||||
|
*.tgz
|
||||||
3
deltachat-rpc-server/npm-package/.npmignore
Normal file
3
deltachat-rpc-server/npm-package/.npmignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
platform_package/*
|
||||||
|
scripts/
|
||||||
|
*.tgz
|
||||||
77
deltachat-rpc-server/npm-package/README.md
Normal file
77
deltachat-rpc-server/npm-package/README.md
Normal file
@@ -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
|
||||||
|
|
||||||
|
<!-- todo instructions, will uses an env var for pointing to `deltachat-rpc-server` binary -->
|
||||||
|
|
||||||
|
<!-- todo copy parts from https://github.com/deltachat/deltachat-desktop/blob/7045c6f549e4b9d5caa0709d5bd314bbd9fd53db/docs/UPDATE_CORE.md -->
|
||||||
|
|
||||||
|
## 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 <cargo-target>
|
||||||
|
# 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.
|
||||||
39
deltachat-rpc-server/npm-package/index.d.ts
vendored
Normal file
39
deltachat-rpc-server/npm-package/index.d.ts
vendored
Normal file
@@ -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<SearchOptions>
|
||||||
|
): Promise<string>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type DeltaChatOverJsonRpcServer = StdioDeltaChat & {
|
||||||
|
shutdown: () => Promise<void>;
|
||||||
|
readonly pathToServerBinary: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param directory directory for accounts folder
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export function startDeltaChat(directory: string, options?: Partial<SearchOptions> ): Promise<DeltaChatOverJsonRpcServer>
|
||||||
|
|
||||||
|
|
||||||
|
export namespace FnTypes {
|
||||||
|
export type getRPCServerPath = typeof getRPCServerPath
|
||||||
|
export type startDeltaChat = typeof startDeltaChat
|
||||||
|
}
|
||||||
143
deltachat-rpc-server/npm-package/index.js
Normal file
143
deltachat-rpc-server/npm-package/index.js
Normal file
@@ -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;
|
||||||
|
}
|
||||||
16
deltachat-rpc-server/npm-package/package.json
Normal file
16
deltachat-rpc-server/npm-package/package.json
Normal file
@@ -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": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
53
deltachat-rpc-server/npm-package/scripts/build_platform_package.py
Executable file
53
deltachat-rpc-server/npm-package/scripts/build_platform_package.py
Executable file
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
27
deltachat-rpc-server/npm-package/scripts/src/make_package.py
Normal file
27
deltachat-rpc-server/npm-package/scripts/src/make_package.py
Normal file
@@ -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))
|
||||||
|
|
||||||
@@ -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));
|
||||||
6
deltachat-rpc-server/npm-package/src/const.js
Normal file
6
deltachat-rpc-server/npm-package/src/const.js
Normal file
@@ -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"
|
||||||
41
deltachat-rpc-server/npm-package/src/errors.js
Normal file
41
deltachat-rpc-server/npm-package/src/errors.js
Normal file
@@ -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\``;
|
||||||
|
}
|
||||||
@@ -66,7 +66,11 @@ def main():
|
|||||||
parser = ArgumentParser(prog="set_core_version")
|
parser = ArgumentParser(prog="set_core_version")
|
||||||
parser.add_argument("newversion")
|
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 = [
|
toml_list = [
|
||||||
"Cargo.toml",
|
"Cargo.toml",
|
||||||
"deltachat-ffi/Cargo.toml",
|
"deltachat-ffi/Cargo.toml",
|
||||||
|
|||||||
Reference in New Issue
Block a user