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
|
||||
|
||||
BuildEmbedUI:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
- uses: ./tools/github-actions/setup-deps
|
||||
|
@ -123,3 +123,27 @@ jobs:
|
|||
sudo atest service restart
|
||||
- name: Test
|
||||
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
|
||||
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:
|
||||
# runs-on: ubuntu-20.04
|
||||
# steps:
|
||||
|
|
|
@ -15,3 +15,5 @@ helm/*.tgz
|
|||
helm/api-testing/*.tgz
|
||||
oryxBuildBinary
|
||||
/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