feat: add electron based desktop application (#428)
This commit is contained in:
parent
ea033d94b6
commit
f7fdbace6d
|
@ -106,7 +106,7 @@ jobs:
|
||||||
# run: cd operator && make docker-build
|
# run: cd operator && make docker-build
|
||||||
|
|
||||||
BuildEmbedUI:
|
BuildEmbedUI:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||||
- uses: ./tools/github-actions/setup-deps
|
- uses: ./tools/github-actions/setup-deps
|
||||||
|
@ -123,3 +123,27 @@ jobs:
|
||||||
sudo atest service restart
|
sudo atest service restart
|
||||||
- name: Test
|
- name: Test
|
||||||
run: make test-ui
|
run: make test-ui
|
||||||
|
|
||||||
|
BuildDesktop:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||||
|
- uses: ./tools/github-actions/setup-deps
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18.x
|
||||||
|
# for fixing Error: Cannot find module 'appdmg'
|
||||||
|
- name: Install Python 3.11.4
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.11.4'
|
||||||
|
- name: Build Desktop
|
||||||
|
run: |
|
||||||
|
cd console/atest-desktop
|
||||||
|
npm i
|
||||||
|
npm run package
|
||||||
|
npm run make
|
||||||
|
|
|
@ -117,6 +117,47 @@ jobs:
|
||||||
fi
|
fi
|
||||||
make helm-pkg helm-push
|
make helm-pkg helm-push
|
||||||
|
|
||||||
|
BuildDesktop:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||||
|
- uses: ./tools/github-actions/setup-deps
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18.x
|
||||||
|
# for fixing Error: Cannot find module 'appdmg'
|
||||||
|
- name: Install Python 3.11.4
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.11.4'
|
||||||
|
- name: Build Desktop
|
||||||
|
run: |
|
||||||
|
cd console/atest-desktop
|
||||||
|
npm i
|
||||||
|
npm run package
|
||||||
|
npm run make
|
||||||
|
tree out/make
|
||||||
|
- name: Upload to Draft
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GH_PUBLISH_SECRETS }}
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
run: |
|
||||||
|
export TAG=$(gh release list -L 1 | awk '{print $4}')
|
||||||
|
jq '.version = env.TAG' package.json > package.json.new && mv package.json.new package.json
|
||||||
|
npm run publish
|
||||||
|
- name: Upload
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GH_PUBLISH_SECRETS }}
|
||||||
|
if: github.ref != 'refs/heads/master'
|
||||||
|
run: |
|
||||||
|
export TAG=$(git describe --tags --abbrev=0)
|
||||||
|
jq '.version = env.TAG' package.json > package.json.new && mv package.json.new package.json
|
||||||
|
npm run publish
|
||||||
|
|
||||||
# image-operator:
|
# image-operator:
|
||||||
# runs-on: ubuntu-20.04
|
# runs-on: ubuntu-20.04
|
||||||
# steps:
|
# steps:
|
||||||
|
|
|
@ -15,3 +15,5 @@ helm/*.tgz
|
||||||
helm/api-testing/*.tgz
|
helm/api-testing/*.tgz
|
||||||
oryxBuildBinary
|
oryxBuildBinary
|
||||||
/helm/api-testing/charts/
|
/helm/api-testing/charts/
|
||||||
|
console/atest-desktop/out
|
||||||
|
console/atest-desktop/node_modules
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
```shell
|
||||||
|
npx electron-forge import
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm config set registry https://registry.npmmirror.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## Package
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm run package -- --platform=darwin
|
||||||
|
npm run package -- --platform=win32
|
||||||
|
npm run package -- --platform=linux
|
||||||
|
```
|
||||||
|
|
||||||
|
## For Linux
|
||||||
|
|
||||||
|
You need to install tools if you want to package Windows on Linux:
|
||||||
|
```shell
|
||||||
|
apt install wine64 zip -y
|
||||||
|
```
|
||||||
|
|
||||||
|
## Publish
|
||||||
|
|
||||||
|
export GITHUB_TOKEN=your-token
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm run publish -- --platform=darwin
|
||||||
|
npm run publish -- --platform=linux
|
||||||
|
```
|
|
@ -0,0 +1,16 @@
|
||||||
|
exports.control = function(okCallback, errorCallback) {
|
||||||
|
fetch('http://localhost:' + getPort() + '/healthz').
|
||||||
|
then(okCallback).catch(errorCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPort() {
|
||||||
|
// TODO support set this value
|
||||||
|
return 7788
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHomePage() {
|
||||||
|
return 'http://localhost:' + getPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getPort = getPort
|
||||||
|
exports.getHomePage = getHomePage
|
|
@ -0,0 +1,68 @@
|
||||||
|
const { FusesPlugin } = require('@electron-forge/plugin-fuses');
|
||||||
|
const { FuseV1Options, FuseVersion } = require('@electron/fuses');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
packagerConfig: {
|
||||||
|
asar: true,
|
||||||
|
},
|
||||||
|
rebuildConfig: {},
|
||||||
|
makers: [
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-squirrel',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-zip',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-deb',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-rpm',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-dmg',
|
||||||
|
config: {
|
||||||
|
format: 'ULFO'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '@electron-forge/maker-wix',
|
||||||
|
config: {
|
||||||
|
language: 1033,
|
||||||
|
manufacturer: 'API Testing Authors'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: '@electron-forge/plugin-auto-unpack-natives',
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
// Fuses are used to enable/disable various Electron functionality
|
||||||
|
// at package time, before code signing the application
|
||||||
|
new FusesPlugin({
|
||||||
|
version: FuseVersion.V1,
|
||||||
|
[FuseV1Options.RunAsNode]: false,
|
||||||
|
[FuseV1Options.EnableCookieEncryption]: true,
|
||||||
|
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
|
||||||
|
[FuseV1Options.EnableNodeCliInspectArguments]: false,
|
||||||
|
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
|
||||||
|
[FuseV1Options.OnlyLoadAppFromAsar]: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
publishers: [
|
||||||
|
{
|
||||||
|
name: '@electron-forge/publisher-github',
|
||||||
|
config: {
|
||||||
|
repository: {
|
||||||
|
owner: 'linuxsuren',
|
||||||
|
name: 'api-testing'
|
||||||
|
},
|
||||||
|
prerelease: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
<!--index.html-->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src * self blob: data: gap:; style-src * self 'unsafe-inline' blob: data: gap:; script-src * 'self' 'unsafe-eval' 'unsafe-inline' blob: data: gap:; object-src * 'self' blob: data: gap:; img-src * self 'unsafe-inline' blob: data: gap:; connect-src self * 'unsafe-inline' blob: data: gap:; frame-src * self blob: data: gap:;">
|
||||||
|
<title>API Testing</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<button type="button" id="action">Start</button>
|
||||||
|
<div>
|
||||||
|
Log output
|
||||||
|
</div>
|
||||||
|
<button type="button" id="open-server-page">Open Server Page</button>
|
||||||
|
|
||||||
|
<!-- You can also require other files to run in this process -->
|
||||||
|
<script src="./renderer.js"></script>
|
||||||
|
<script>
|
||||||
|
const actionBut = document.getElementById('action');
|
||||||
|
actionBut.addEventListener('click', (e) => {
|
||||||
|
const action = actionBut.innerHTML;
|
||||||
|
switch (action) {
|
||||||
|
case 'Stop':
|
||||||
|
stop();
|
||||||
|
actionBut.innerHTML = 'Start';
|
||||||
|
break;
|
||||||
|
case 'Start':
|
||||||
|
start();
|
||||||
|
actionBut.innerHTML= 'Stop';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const openServerBut = document.getElementById('open-server-page');
|
||||||
|
openServerBut.addEventListener('click', (e) => {
|
||||||
|
window.location = 'http://localhost:8080'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,105 @@
|
||||||
|
// main.js
|
||||||
|
|
||||||
|
// Modules to control application life and create native browser window
|
||||||
|
const { app, BrowserWindow, Menu, MenuItem } = require('electron')
|
||||||
|
const path = require('node:path')
|
||||||
|
const server = require('./api')
|
||||||
|
const spawn = require("child_process").spawn;
|
||||||
|
|
||||||
|
const createWindow = () => {
|
||||||
|
// Create the browser window.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
width: 1000,
|
||||||
|
height: 600,
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
enableRemoteModule: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server.control(() => {
|
||||||
|
mainWindow.loadURL(server.getHomePage())
|
||||||
|
}, () => {
|
||||||
|
// and load the index.html of the app.
|
||||||
|
mainWindow.loadFile('index.html')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const menu = new Menu()
|
||||||
|
menu.append(new MenuItem({
|
||||||
|
label: 'Window',
|
||||||
|
submenu: [{
|
||||||
|
label: 'Console',
|
||||||
|
accelerator: process.platform === 'darwin' ? 'Alt+Cmd+C' : 'Alt+Shift+C',
|
||||||
|
click: () => {
|
||||||
|
BrowserWindow.getFocusedWindow().loadFile('index.html');
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Server',
|
||||||
|
accelerator: process.platform === 'darwin' ? 'Alt+Cmd+S' : 'Alt+Shift+S',
|
||||||
|
click: () => {
|
||||||
|
BrowserWindow.getFocusedWindow().loadURL(server.getHomePage());
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Reload',
|
||||||
|
accelerator: process.platform === 'darwin' ? 'Cmd+R' : 'F5',
|
||||||
|
click: () => {
|
||||||
|
BrowserWindow.getFocusedWindow().reload()
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Developer Mode',
|
||||||
|
accelerator: process.platform === 'darwin' ? 'Alt+Cmd+D' : 'F12',
|
||||||
|
click: () => {
|
||||||
|
BrowserWindow.getFocusedWindow().webContents.openDevTools();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Quit',
|
||||||
|
accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Alt+Shift+Q',
|
||||||
|
click: () => {
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}))
|
||||||
|
|
||||||
|
Menu.setApplicationMenu(menu)
|
||||||
|
|
||||||
|
let serverProcess;
|
||||||
|
// This method will be called when Electron has finished
|
||||||
|
// initialization and is ready to create browser windows.
|
||||||
|
// Some APIs can only be used after this event occurs.
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
// const homedir = require('os').homedir();
|
||||||
|
|
||||||
|
serverProcess = spawn("atest", [
|
||||||
|
"server",
|
||||||
|
"--http-port", server.getPort(),
|
||||||
|
// TODO below setting is not working
|
||||||
|
// "--local-storage", path.join(homedir, ".atest", "data", "*.yaml")
|
||||||
|
]);
|
||||||
|
|
||||||
|
createWindow()
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
// On macOS it's common to re-create a window in the app when the
|
||||||
|
// dock icon is clicked and there are no other windows open.
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Quit when all windows are closed, except on macOS. There, it's common
|
||||||
|
// for applications and their menu bar to stay active until the user quits
|
||||||
|
// explicitly with Cmd + Q.
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit()
|
||||||
|
|
||||||
|
if (serverProcess) {
|
||||||
|
serverProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// In this file you can include the rest of your app's specific main process
|
||||||
|
// code. You can also put them in separate files and require them here.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"name": "atest-desktop",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "API Testing Desktop Application",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "electron-forge start",
|
||||||
|
"package": "electron-forge package",
|
||||||
|
"make": "electron-forge make",
|
||||||
|
"publish": "electron-forge publish"
|
||||||
|
},
|
||||||
|
"author": "linuxsuren",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@electron-forge/cli": "^7.4.0",
|
||||||
|
"@electron-forge/maker-deb": "^7.4.0",
|
||||||
|
"@electron-forge/maker-dmg": "^7.4.0",
|
||||||
|
"@electron-forge/maker-rpm": "^7.4.0",
|
||||||
|
"@electron-forge/maker-squirrel": "^7.4.0",
|
||||||
|
"@electron-forge/maker-wix": "^7.4.0",
|
||||||
|
"@electron-forge/maker-zip": "^7.4.0",
|
||||||
|
"@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
|
||||||
|
"@electron-forge/plugin-fuses": "^7.4.0",
|
||||||
|
"@electron-forge/publisher-github": "^7.4.0",
|
||||||
|
"@electron/fuses": "^1.8.0",
|
||||||
|
"electron": "^30.0.4",
|
||||||
|
"electron-wix-msi": "^5.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"child_process": "^1.0.2",
|
||||||
|
"electron-squirrel-startup": "^1.0.1"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"extraResources": [
|
||||||
|
"./assets/atest.exe"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"appdmg": "^0.6.6"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
// preload.js
|
||||||
|
|
||||||
|
// All the Node.js APIs are available in the preload process.
|
||||||
|
// It has the same sandbox as a Chrome extension.
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const replaceText = (selector, text) => {
|
||||||
|
const element = document.getElementById(selector)
|
||||||
|
if (element) element.innerText = text
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const dependency of ['chrome', 'node', 'electron']) {
|
||||||
|
replaceText(`${dependency}-version`, process.versions[dependency])
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,34 @@
|
||||||
|
let spawn = require("child_process").spawn;
|
||||||
|
let server = require("./api.js")
|
||||||
|
|
||||||
|
server.control(() => {
|
||||||
|
const actionBut = document.getElementById('action');
|
||||||
|
actionBut.innerHTML = 'Stop';
|
||||||
|
})
|
||||||
|
|
||||||
|
let process;
|
||||||
|
function start() {
|
||||||
|
process = spawn("atest", [
|
||||||
|
"server",
|
||||||
|
"--http-port",
|
||||||
|
server.getPort()
|
||||||
|
]);
|
||||||
|
|
||||||
|
process.stdout.on("data", (data) => {
|
||||||
|
console.log(data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stderr.on("data", (err) => {
|
||||||
|
console.log(err.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("exit", (code) => {
|
||||||
|
console.log(code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
if (process) {
|
||||||
|
process.kill();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue