feat: add electron based desktop application (#428)

This commit is contained in:
Rick 2024-05-15 22:22:57 +08:00 committed by GitHub
parent ea033d94b6
commit f7fdbace6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 6952 additions and 1 deletions

View File

@ -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

View File

@ -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:

2
.gitignore vendored
View File

@ -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

View File

@ -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
```

View File

@ -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

View File

@ -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
}
}
]
};

View File

@ -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>

View File

@ -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.

6532
console/atest-desktop/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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])
}
})

View File

@ -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();
}
}