diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 5e8183c737..2613cab367 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -13,19 +13,25 @@ on:
jobs:
publish:
- name: Build on node ${{ matrix.node_version }} and ${{ matrix.os }}
+ name: Publish on ${{ matrix.os }}, .Net ${{ matrix.dotnet_version }}, and node ${{ matrix.node_version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
- node_version: [16.x]
os: [macos-latest, ubuntu-latest]
+ dotnet_version: [7.0.x]
+ node_version: [16.x]
steps:
- name: Checkout git repo
uses: actions/checkout@v3
+ - name: Install .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: ${{matrix.dotnet_version}}
+
- name: Install Node and NPM
uses: actions/setup-node@v3
with:
@@ -37,6 +43,16 @@ jobs:
npm install
npm run build
+ - name: dotnet build - MacOS and Windows
+ if: ${{ matrix.os == 'macos-latest' }}
+ run: |
+ npm run build:data-release:windows
+ npm run build:data-release:macos
+
+ - name: dotnet build - Linux
+ if: ${{ matrix.os == 'ubuntu-latest' }}
+ run: npm run build:data-release:linux
+
- name: Publish releases - Windows and MacOS
# If the branch is labeled as a release version (e.g. "release/v1.2.3"),
if: ${{ matrix.os == 'macos-latest' && startsWith(github.ref, 'refs/heads/release/v') && contains(github.ref, '.') }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 08b67d870e..dfb1a1b733 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -16,26 +16,38 @@ on:
jobs:
test:
+ name: Test on ${{ matrix.os }}, .Net ${{ matrix.dotnet_version }}, and node ${{ matrix.node_version }}
+
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
+ dotnet_version: [7.0.x]
+ node_version: [16.x]
steps:
- name: Check out Git repository
uses: actions/checkout@v3
+ - name: Install .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: ${{matrix.dotnet_version}}
+
- name: Install Node.js and NPM
uses: actions/setup-node@v3
with:
- node-version: 16
+ node-version: ${{ matrix.node_version }}
cache: npm
- name: npm install
run: |
npm install
+ - name: dotnet build
+ run: npm run build:data-release
+
- name: npm test
env:
# no hardlinks so dependencies are copied
diff --git a/README.md b/README.md
index 98932a696e..762600a797 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,17 @@ Set up pre-requisites for building:
Add the system libraries needed for Electron, [Build Instructions (Linux)](https://www.electronjs.org/docs/latest/development/build-instructions-linux).
+### All Platforms Development Pre-requisites
+
+Install `dotnet` [.NET 7 SDK from here](https://learn.microsoft.com/en-us/dotnet/core/install/).
+
+To check if `dotnet` is installed run (ensure you have a v7 SDK):
+
+```bash
+dotnet --version
+dotnet --list-sdks
+```
+
### Cloning and installing dependencies (all platforms)
Clone the repo and install dependencies:
@@ -59,7 +70,7 @@ Start the app in the `dev` environment:
npm start
```
-After you run `npm start`, you can edit the Electron and frontend files, and they will hot reload. To edit C# files, you must stop the `npm start` process (or only close Paranext), run `npm run build:c-sharp`, and restart `npm start` (or if you only closed Paranext, make a trivial edit to `src/main/main.ts`, and save it to launch Paranext again).
+After you run `npm start`, you can edit the Electron and frontend files, and they will hot reload. To edit C# files, you must stop the `npm start` process (or only close Paranext), run `npm run build:data`, and restart `npm start` (or if you only closed Paranext, make a trivial edit to `src/main/main.ts`, and save it to launch Paranext again).
## Packaging for Production
diff --git a/c-sharp/ParanextDataProvider.csproj b/c-sharp/ParanextDataProvider.csproj
index 7386c62ff3..5191fc595c 100644
--- a/c-sharp/ParanextDataProvider.csproj
+++ b/c-sharp/ParanextDataProvider.csproj
@@ -6,6 +6,7 @@
enable
enable
Paranext.DataProvider
+ true
diff --git a/package-lock.json b/package-lock.json
index ceca18ad68..ab03f66288 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6023,9 +6023,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001449",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001449.tgz",
- "integrity": "sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==",
+ "version": "1.0.30001451",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz",
+ "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==",
"dev": true,
"funding": [
{
@@ -22558,9 +22558,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001449",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001449.tgz",
- "integrity": "sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==",
+ "version": "1.0.30001451",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz",
+ "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==",
"dev": true
},
"chalk": {
diff --git a/package.json b/package.json
index ead9753ce9..d02bd97f66 100644
--- a/package.json
+++ b/package.json
@@ -46,9 +46,9 @@
"build:renderer": "cross-env NODE_ENV=production TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.prod.ts",
"build:data": "dotnet build c-sharp/ParanextDataProvider.sln",
"build:data-release": "run-script-os",
- "build:data-release:windows": "dotnet publish c-sharp/ParanextDataProvider.csproj -p:PublishProfile=FolderProfile",
- "build:data-release:linux": "dotnet publish c-sharp/ParanextDataProvider.csproj -p:PublishProfile=FolderProfile -r linux-x64 -o ./c-sharp/bin/Release/net7.0/publish/linux-x64/",
- "build:data-release:macos": "dotnet publish c-sharp/ParanextDataProvider.csproj -p:PublishProfile=FolderProfile -r osx-x64 -o ./c-sharp/bin/Release/net7.0/publish/osx-x64/ && dotnet publish c-sharp/ParanextDataProvider.csproj -p:PublishProfile=FolderProfile -r osx-arm64 -o ./c-sharp/bin/Release/net7.0/publish/osx-arm64/",
+ "build:data-release:windows": "dotnet publish c-sharp/ParanextDataProvider.csproj -r win-x64 -o ./c-sharp/bin/Release/net7.0/publish/win-x64/",
+ "build:data-release:linux": "dotnet publish c-sharp/ParanextDataProvider.csproj -r linux-x64 -o ./c-sharp/bin/Release/net7.0/publish/linux-x64/",
+ "build:data-release:macos": "dotnet publish c-sharp/ParanextDataProvider.csproj -r osx-x64 -o ./c-sharp/bin/Release/net7.0/publish/osx-x64/ && dotnet publish c-sharp/ParanextDataProvider.csproj -p:PublishProfile=FolderProfile -r osx-arm64 -o ./c-sharp/bin/Release/net7.0/publish/osx-arm64/",
"postinstall": "ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts",
"lint": "cross-env NODE_ENV=development eslint . --ext .js,.jsx,.ts,.tsx",
"package": "ts-node ./.erb/scripts/clean.js dist && npm run build && npm run build:data-release && electron-builder build --publish never",
@@ -59,6 +59,7 @@
"start:main": "cross-env NODE_ENV=development electronmon -r ts-node/register/transpile-only -r tsconfig-paths/register .",
"start:preload": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.preload.dev.ts",
"start:renderer": "cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack serve --config ./.erb/configs/webpack.config.renderer.dev.ts",
+ "start:data": "dotnet run --project c-sharp/ParanextDataProvider.csproj",
"test": "jest"
},
"lint-staged": {
@@ -178,6 +179,12 @@
"x64"
]
},
+ "extraResources": [
+ {
+ "from": "./c-sharp/bin/Release/net7.0/publish/osx-${arch}/",
+ "to": "./dotnet/"
+ }
+ ],
"type": "distribution",
"hardenedRuntime": true,
"entitlements": "assets/entitlements.mac.plist",
@@ -203,12 +210,25 @@
"nsis",
"nsis-web",
"portable"
+ ],
+ "extraResources": [
+ {
+ "from": "./c-sharp/bin/Release/net7.0/publish/win-x64/",
+ "to": "./dotnet/"
+ }
]
},
"linux": {
"target": [
"AppImage"
],
+ "category": "Development",
+ "extraResources": [
+ {
+ "from": "./c-sharp/bin/Release/net7.0/publish/linux-x64/",
+ "to": "./dotnet/"
+ }
+ ],
"extraFiles": [
{
"from": "/usr/lib/x86_64-linux-gnu/libnss3.so",
@@ -466,8 +486,7 @@
"from": "/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0",
"to": "usr/lib/libgio-2.0.so.0"
}
- ],
- "category": "Development"
+ ]
},
"directories": {
"app": "release/app",
diff --git a/src/main/main.ts b/src/main/main.ts
index 4cf5c4764b..72eb8f42b7 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -12,10 +12,11 @@ import path from 'path';
import { app, BrowserWindow, shell, ipcMain } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
+import windowStateKeeper from 'electron-window-state';
+import dotnetDataProvider from '@main/services/dotnet-data-provider.service';
import * as NetworkService from '@shared/services/NetworkService';
import papi from '@shared/services/papi';
import { CommandHandler } from '@shared/util/PapiUtil';
-import windowStateKeeper from 'electron-window-state';
import MenuBuilder from './menu';
import { resolveHtmlPath } from './util';
@@ -134,10 +135,17 @@ app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
+ // TODO: cleanly stop the provider (close the ws or send command) - IJH 2022-02-23
+ dotnetDataProvider.kill();
app.quit();
}
});
+app.on('will-quit', () => {
+ // TODO: cleanly stop the provider (close the ws or send command) - IJH 2022-02-23
+ dotnetDataProvider.kill();
+});
+
// #endregion
// #region IPC HANDLING SETUP
@@ -190,20 +198,23 @@ const commandHandlers: { [commandName: string]: CommandHandler } = {
},
};
-NetworkService.initialize()
- .then(() => {
- // Set up test handlers
- Object.entries(ipcHandlers).forEach(([ipcHandle, handler]) => {
- NetworkService.registerRequestHandler(
- ipcHandle,
- async (...args: unknown[]) =>
- handler({} as Electron.IpcMainInvokeEvent, ...args),
- );
- });
- Object.entries(commandHandlers).forEach(([commandName, handler]) => {
- papi.commands.registerCommand(commandName, handler);
- });
- })
- .catch((e) => console.error(e));
+(async () => {
+ await NetworkService.initialize();
+ // Set up test handlers
+ Object.entries(ipcHandlers).forEach(([ipcHandle, handler]) => {
+ NetworkService.registerRequestHandler(
+ ipcHandle,
+ async (...args: unknown[]) =>
+ handler({} as Electron.IpcMainInvokeEvent, ...args),
+ );
+ });
+ Object.entries(commandHandlers).forEach(([commandName, handler]) => {
+ papi.commands.registerCommand(commandName, handler);
+ });
+
+ // Start the dotnet data provider early so its ready when needed once the
+ // WebSocket is up.
+ dotnetDataProvider.start();
+})().catch(console.error);
// #endregion
diff --git a/src/main/services/dotnet-data-provider.service.ts b/src/main/services/dotnet-data-provider.service.ts
new file mode 100644
index 0000000000..1b866d9684
--- /dev/null
+++ b/src/main/services/dotnet-data-provider.service.ts
@@ -0,0 +1,70 @@
+import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
+import path from 'path';
+
+let dotnet: ChildProcessWithoutNullStreams | undefined;
+
+function killDotnetDataProvider() {
+ if (!dotnet) return;
+
+ if (dotnet.kill()) {
+ console.log('[dotnet data provider] was killed');
+ } else {
+ console.error(
+ '[dotnet data provider] was not stopped! Investigate other .kill() options',
+ );
+ }
+ dotnet = undefined;
+}
+
+/**
+ * Starts the Dotnet Data Provider if it isn't already running.
+ */
+function startDotnetDataProvider() {
+ if (dotnet) return;
+
+ // default values for development
+ let command = process.platform.includes('win') ? 'npm.cmd' : 'npm';
+ let args: string[] = ['run', 'start:data'];
+
+ if (process.env.NODE_ENV === 'production') {
+ if (process.platform === 'win32') {
+ command = path.join(
+ process.resourcesPath,
+ 'dotnet',
+ 'ParanextDataProvider.exe',
+ );
+ args = [];
+ } else {
+ command = path.join(
+ process.resourcesPath,
+ 'dotnet',
+ 'ParanextDataProvider',
+ );
+ args = [];
+ }
+ }
+
+ dotnet = spawn(command, args);
+
+ dotnet.stdout.on('data', (data) => {
+ console.log(`[dotnet data provider] stdout: ${data}`);
+ });
+
+ dotnet.stderr.on('data', (data) => {
+ console.error(`[dotnet data provider] stderr: ${data}`);
+ });
+
+ dotnet.on('close', (code, signal) => {
+ if (signal) {
+ console.log(`[dotnet data provider] terminated with signal ${signal}`);
+ } else {
+ console.log(`[dotnet data provider] exited with code ${code}`);
+ }
+ });
+}
+
+const dotnetDataProvider = {
+ start: startDotnetDataProvider,
+ kill: killDotnetDataProvider,
+};
+export default dotnetDataProvider;
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index ef9113e579..c9572a35e1 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -68,8 +68,10 @@ const executeMany = async (fn: () => Promise) => {
}
};
+let addResult = 0;
+
const Hello = () => {
- const [promiseReturn, setPromiseReturn] = useState('');
+ const [promiseReturn, setPromiseReturn] = useState('Click a button.');
const [NODE_ENV] = usePromise(
useCallback(() => getVar('NODE_ENV'), []),
@@ -136,8 +138,10 @@ const Hello = () => {