@atomicsolutions/proton5-cli 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +590 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/config/add.d.ts +14 -0
- package/dist/commands/config/add.js +122 -0
- package/dist/commands/config/edit.d.ts +15 -0
- package/dist/commands/config/edit.js +63 -0
- package/dist/commands/config/list.d.ts +6 -0
- package/dist/commands/config/list.js +31 -0
- package/dist/commands/config/remove.d.ts +9 -0
- package/dist/commands/config/remove.js +35 -0
- package/dist/commands/connect.d.ts +9 -0
- package/dist/commands/connect.js +41 -0
- package/dist/commands/disconnect.d.ts +9 -0
- package/dist/commands/disconnect.js +30 -0
- package/dist/commands/runner/list-run.d.ts +6 -0
- package/dist/commands/runner/list-run.js +71 -0
- package/dist/commands/runner/status.d.ts +6 -0
- package/dist/commands/runner/status.js +24 -0
- package/dist/commands/runner/stop-run.d.ts +9 -0
- package/dist/commands/runner/stop-run.js +34 -0
- package/dist/commands/settings/index.d.ts +6 -0
- package/dist/commands/settings/index.js +25 -0
- package/dist/commands/settings/set.d.ts +10 -0
- package/dist/commands/settings/set.js +39 -0
- package/dist/commands/settings/unset.d.ts +10 -0
- package/dist/commands/settings/unset.js +35 -0
- package/dist/core/fileSystem/index.d.ts +10 -0
- package/dist/core/fileSystem/index.js +107 -0
- package/dist/core/manager/index.d.ts +31 -0
- package/dist/core/manager/index.js +287 -0
- package/dist/core/proton/api.d.ts +7 -0
- package/dist/core/proton/api.js +122 -0
- package/dist/core/proton/maven.d.ts +5 -0
- package/dist/core/proton/maven.js +74 -0
- package/dist/core/proton/npm.d.ts +1 -0
- package/dist/core/proton/npm.js +22 -0
- package/dist/core/proton/playwright.d.ts +5 -0
- package/dist/core/proton/playwright.js +47 -0
- package/dist/core/proton/pyTest.d.ts +5 -0
- package/dist/core/proton/pyTest.js +52 -0
- package/dist/core/proton/vbs.d.ts +5 -0
- package/dist/core/proton/vbs.js +46 -0
- package/dist/core/runner/index.d.ts +16 -0
- package/dist/core/runner/index.js +76 -0
- package/dist/core/services/api.d.ts +2 -0
- package/dist/core/services/api.js +20 -0
- package/dist/core/socket/index.d.ts +2 -0
- package/dist/core/socket/index.js +99 -0
- package/dist/core/store/index.d.ts +14 -0
- package/dist/core/store/index.js +212 -0
- package/dist/core/system/index.d.ts +12 -0
- package/dist/core/system/index.js +48 -0
- package/dist/daemon/index.d.ts +1 -0
- package/dist/daemon/index.js +5 -0
- package/dist/daemon/server.d.ts +1 -0
- package/dist/daemon/server.js +40 -0
- package/dist/daemon/state.d.ts +7 -0
- package/dist/daemon/state.js +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/ipc/client.d.ts +1 -0
- package/dist/ipc/client.js +16 -0
- package/dist/ipc/ensureDaemon.d.ts +1 -0
- package/dist/ipc/ensureDaemon.js +42 -0
- package/dist/ipc/protocol.d.ts +14 -0
- package/dist/ipc/protocol.js +1 -0
- package/dist/shared/constants.d.ts +4 -0
- package/dist/shared/constants.js +4 -0
- package/dist/shared/socketPath.d.ts +1 -0
- package/dist/shared/socketPath.js +5 -0
- package/dist/shared/types.d.ts +130 -0
- package/dist/shared/types.js +1 -0
- package/oclif.manifest.json +409 -0
- package/package.json +86 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { getStoreValue, setStoreValue } from '../../core/store/index.js';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
export default class SettingsUnset extends Command {
|
|
5
|
+
static description = 'Unset Proton server settings';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
];
|
|
9
|
+
static flags = {
|
|
10
|
+
mavenPath: Flags.boolean({
|
|
11
|
+
description: 'Caminho do Maven (ex: C:\\maven\\bin\\mvn.cmd)',
|
|
12
|
+
required: false
|
|
13
|
+
}),
|
|
14
|
+
parallelRuns: Flags.boolean({
|
|
15
|
+
description: 'Número de execuções paralelas',
|
|
16
|
+
required: false
|
|
17
|
+
})
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(SettingsUnset);
|
|
21
|
+
const spinner = ora('Updating settings...').start();
|
|
22
|
+
try {
|
|
23
|
+
const settings = getStoreValue('settings');
|
|
24
|
+
const newSettings = {
|
|
25
|
+
mavenPath: flags.mavenPath ? "" : settings.mavenPath,
|
|
26
|
+
parallelRuns: flags.parallelRuns ? 1 : settings.parallelRuns,
|
|
27
|
+
};
|
|
28
|
+
setStoreValue('settings', newSettings);
|
|
29
|
+
spinner.succeed('Settings redefined successfully');
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
spinner.fail('Failed to update settings');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function dirExists(dir: string): boolean;
|
|
2
|
+
export declare function cloneRepo(authorization: string, repo: string, branch: string, dir: string): Promise<void>;
|
|
3
|
+
/**
|
|
4
|
+
* Verifica se o arquivo TestProtonScript.java existe no diretório do repositório local.
|
|
5
|
+
* @param repoLocalDir
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export declare function checkIfTestProtonScriptExists(repoLocalDir: string): boolean;
|
|
9
|
+
export declare function getRepoRunMethod(dir: string): "" | "maven" | "sap" | "pyTest" | "playwright";
|
|
10
|
+
export declare function initializeFolders(): void;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { simpleGit } from 'simple-git';
|
|
2
|
+
import fs, { readdirSync, statSync } from "fs";
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
export function dirExists(dir) {
|
|
6
|
+
const homedir = os.homedir();
|
|
7
|
+
return fs.existsSync(homedir + '/.proton-runner-repo/' + dir);
|
|
8
|
+
}
|
|
9
|
+
export async function cloneRepo(authorization, repo, branch, dir) {
|
|
10
|
+
const homedir = os.homedir();
|
|
11
|
+
const git = simpleGit();
|
|
12
|
+
const url = `https://${authorization}@github.com/${repo}.git`;
|
|
13
|
+
await git.clone(url, homedir + '/.proton-runner-repo/' + dir, ['-b', branch, '-c', 'core.longpaths=true']);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Verifica se o arquivo TestProtonScript.java existe no diretório do repositório local.
|
|
17
|
+
* @param repoLocalDir
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
export function checkIfTestProtonScriptExists(repoLocalDir) {
|
|
21
|
+
const searchFile = 'TestProtonScript.java';
|
|
22
|
+
function findFile(dir) {
|
|
23
|
+
const files = readdirSync(dir);
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
const fullPath = join(dir, file);
|
|
26
|
+
if (statSync(fullPath).isDirectory()) {
|
|
27
|
+
if (findFile(fullPath)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (file === searchFile) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return findFile(repoLocalDir);
|
|
38
|
+
}
|
|
39
|
+
export function getRepoRunMethod(dir) {
|
|
40
|
+
const homedir = os.homedir();
|
|
41
|
+
const repoDir = homedir + '/.proton-runner-repo/' + dir;
|
|
42
|
+
const maven = fs.existsSync(repoDir + '/pom.xml');
|
|
43
|
+
if (maven) {
|
|
44
|
+
return 'maven';
|
|
45
|
+
}
|
|
46
|
+
const sap = fs.existsSync(repoDir + '/SAP_Runner.vbs');
|
|
47
|
+
if (sap) {
|
|
48
|
+
return 'sap';
|
|
49
|
+
}
|
|
50
|
+
const pyTest = fs.existsSync(repoDir + '/pytest.ini');
|
|
51
|
+
if (pyTest) {
|
|
52
|
+
return 'pyTest';
|
|
53
|
+
}
|
|
54
|
+
const pwTest = fs.existsSync(repoDir + '/playwright.config.ts');
|
|
55
|
+
if (pwTest) {
|
|
56
|
+
return 'playwright';
|
|
57
|
+
}
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
export function initializeFolders() {
|
|
61
|
+
const homedir = os.homedir();
|
|
62
|
+
const baseDirPath = homedir + '/.proton-runner';
|
|
63
|
+
const baseDir = fs.existsSync(baseDirPath);
|
|
64
|
+
if (!baseDir) {
|
|
65
|
+
fs.mkdirSync(baseDirPath);
|
|
66
|
+
}
|
|
67
|
+
const appDir = fs.existsSync(baseDirPath + '/app');
|
|
68
|
+
if (!appDir) {
|
|
69
|
+
fs.mkdirSync(baseDirPath + '/app');
|
|
70
|
+
}
|
|
71
|
+
const attachmentsDir = fs.existsSync(baseDirPath + '/attachments');
|
|
72
|
+
if (!attachmentsDir) {
|
|
73
|
+
fs.mkdirSync(baseDirPath + '/attachments');
|
|
74
|
+
}
|
|
75
|
+
const driversDir = fs.existsSync(baseDirPath + '/drivers');
|
|
76
|
+
if (!driversDir) {
|
|
77
|
+
fs.mkdirSync(baseDirPath + '/drivers');
|
|
78
|
+
}
|
|
79
|
+
const libraryDir = fs.existsSync(baseDirPath + '/library');
|
|
80
|
+
if (!libraryDir) {
|
|
81
|
+
fs.mkdirSync(baseDirPath + '/library');
|
|
82
|
+
}
|
|
83
|
+
const logDir = fs.existsSync(baseDirPath + '/log');
|
|
84
|
+
if (!logDir) {
|
|
85
|
+
fs.mkdirSync(baseDirPath + '/log');
|
|
86
|
+
}
|
|
87
|
+
const repositoryDir = fs.existsSync(baseDirPath + '/repository');
|
|
88
|
+
if (!repositoryDir) {
|
|
89
|
+
fs.mkdirSync(baseDirPath + '/repository');
|
|
90
|
+
}
|
|
91
|
+
const screenshotsDir = fs.existsSync(baseDirPath + '/screenshots');
|
|
92
|
+
if (!screenshotsDir) {
|
|
93
|
+
fs.mkdirSync(baseDirPath + '/screenshots');
|
|
94
|
+
}
|
|
95
|
+
const traceDir = fs.existsSync(baseDirPath + '/trace');
|
|
96
|
+
if (!traceDir) {
|
|
97
|
+
fs.mkdirSync(baseDirPath + '/trace');
|
|
98
|
+
}
|
|
99
|
+
const toolsDir = fs.existsSync(baseDirPath + '/tools');
|
|
100
|
+
if (!toolsDir) {
|
|
101
|
+
fs.mkdirSync(baseDirPath + '/tools');
|
|
102
|
+
}
|
|
103
|
+
const videoDir = fs.existsSync(baseDirPath + '/video');
|
|
104
|
+
if (!videoDir) {
|
|
105
|
+
fs.mkdirSync(baseDirPath + '/video');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ChildProcessWithoutNullStreams } from "child_process";
|
|
2
|
+
import type { IDatasetRun, ISystem, InitiateProcessOptions } from "#types";
|
|
3
|
+
export declare class Manager {
|
|
4
|
+
addToWaitList(run: IDatasetRun[]): Promise<void>;
|
|
5
|
+
addToRunList(run: IDatasetRun): IDatasetRun;
|
|
6
|
+
updateRun(idDatasetRun: number | string, updatedRun: Partial<IDatasetRun>): void;
|
|
7
|
+
removeRun(id: number): void;
|
|
8
|
+
stopRun(idDatasetRun: number): Promise<void>;
|
|
9
|
+
getRunList(): IDatasetRun[];
|
|
10
|
+
clearRunList(): void;
|
|
11
|
+
getWaitList(): IDatasetRun[];
|
|
12
|
+
clearWaitList(): void;
|
|
13
|
+
addProcess(idDatasetRun: number, processId: number): void;
|
|
14
|
+
removeProcess(idDatasetRun: number): void;
|
|
15
|
+
finishRun(idDatasetRun: number): Promise<void>;
|
|
16
|
+
addNextToRun(): Promise<void>;
|
|
17
|
+
addToRun(run: IDatasetRun): Promise<void>;
|
|
18
|
+
updateRepos(systemList: ISystem[]): Promise<{
|
|
19
|
+
success: boolean;
|
|
20
|
+
message: string;
|
|
21
|
+
updatedSystems?: undefined;
|
|
22
|
+
} | {
|
|
23
|
+
success: boolean;
|
|
24
|
+
message: string;
|
|
25
|
+
updatedSystems: ISystem[];
|
|
26
|
+
}>;
|
|
27
|
+
runAutomation(run: IDatasetRun): Promise<void>;
|
|
28
|
+
private processKiller;
|
|
29
|
+
initiateProcess(command: string, options: InitiateProcessOptions): ChildProcessWithoutNullStreams;
|
|
30
|
+
}
|
|
31
|
+
export declare const manager: Manager;
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { runner } from "../runner/index.js";
|
|
2
|
+
import { cloneRepo, dirExists, getRepoRunMethod } from "../fileSystem/index.js";
|
|
3
|
+
import { getCurrentComponentSystem, getRemoteHeaderHash, setLog, updateDatasetRunStatus } from "../proton/api.js";
|
|
4
|
+
import Maven from "../proton/maven.js";
|
|
5
|
+
import { runNpmInstall } from "../proton/npm.js";
|
|
6
|
+
import Playwright from "../proton/playwright.js";
|
|
7
|
+
import PyTest from "../proton/pyTest.js";
|
|
8
|
+
import VBScript from "../proton/vbs.js";
|
|
9
|
+
import { getStoreValue, setStoreValue } from "../store/index.js";
|
|
10
|
+
import { spawn } from "child_process";
|
|
11
|
+
import i18n from 'i18next';
|
|
12
|
+
export class Manager {
|
|
13
|
+
async addToWaitList(run) {
|
|
14
|
+
const waitList = this.getWaitList();
|
|
15
|
+
waitList.push(...run);
|
|
16
|
+
setStoreValue("waitList", waitList);
|
|
17
|
+
await runner.setStatus("Busy");
|
|
18
|
+
await this.addNextToRun();
|
|
19
|
+
}
|
|
20
|
+
addToRunList(run) {
|
|
21
|
+
const runList = this.getRunList();
|
|
22
|
+
runList.push(run);
|
|
23
|
+
setStoreValue("runList", runList);
|
|
24
|
+
return run;
|
|
25
|
+
}
|
|
26
|
+
updateRun(idDatasetRun, updatedRun) {
|
|
27
|
+
let currentRunList = getStoreValue("runList") || [];
|
|
28
|
+
currentRunList = currentRunList.map((run) => {
|
|
29
|
+
if (run.id === Number(idDatasetRun)) {
|
|
30
|
+
let updRun = {
|
|
31
|
+
...run,
|
|
32
|
+
...updatedRun
|
|
33
|
+
};
|
|
34
|
+
if (updatedRun.status) {
|
|
35
|
+
if (['Loading', 'Running'].includes(updatedRun.status)) {
|
|
36
|
+
updRun = {
|
|
37
|
+
...updRun,
|
|
38
|
+
isRunning: true
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
updRun = {
|
|
43
|
+
...updRun,
|
|
44
|
+
isRunning: false,
|
|
45
|
+
isUploadingResults: true
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return updRun;
|
|
50
|
+
}
|
|
51
|
+
return run;
|
|
52
|
+
});
|
|
53
|
+
setStoreValue("runList", currentRunList);
|
|
54
|
+
}
|
|
55
|
+
removeRun(id) {
|
|
56
|
+
let currentRunList = getStoreValue("runList") || [];
|
|
57
|
+
currentRunList = currentRunList.filter((run) => run.id !== id);
|
|
58
|
+
setStoreValue("runList", currentRunList);
|
|
59
|
+
let waitList = getStoreValue("waitList") || [];
|
|
60
|
+
if (currentRunList.length === 0 && waitList.length === 0) {
|
|
61
|
+
runner.setStatus("Idle");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async stopRun(idDatasetRun) {
|
|
65
|
+
await updateDatasetRunStatus(idDatasetRun, 'Stopped');
|
|
66
|
+
const processList = getStoreValue("process") || [];
|
|
67
|
+
const processItem = processList.find((item) => item.idDatasetRun === idDatasetRun);
|
|
68
|
+
if (processItem) {
|
|
69
|
+
try {
|
|
70
|
+
this.processKiller(processItem.processId);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
console.log(`Erro ao finalizar processo ${processItem.processId}: `, err);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
runner.setMsgPanel(`⏹️ **${i18n.t('msgPanel.automationRun')} #${idDatasetRun} ${i18n.t('msgPanel.wasStopped')}.**`);
|
|
77
|
+
}
|
|
78
|
+
getRunList() {
|
|
79
|
+
return getStoreValue("runList") || [];
|
|
80
|
+
}
|
|
81
|
+
clearRunList() {
|
|
82
|
+
setStoreValue("runList", []);
|
|
83
|
+
}
|
|
84
|
+
getWaitList() {
|
|
85
|
+
return getStoreValue("waitList") || [];
|
|
86
|
+
}
|
|
87
|
+
clearWaitList() {
|
|
88
|
+
setStoreValue("waitList", []);
|
|
89
|
+
}
|
|
90
|
+
addProcess(idDatasetRun, processId) {
|
|
91
|
+
const list = getStoreValue("process");
|
|
92
|
+
const newItem = { idDatasetRun, processId };
|
|
93
|
+
setStoreValue("process", [...list, newItem]);
|
|
94
|
+
}
|
|
95
|
+
removeProcess(idDatasetRun) {
|
|
96
|
+
let list = getStoreValue("process");
|
|
97
|
+
list = list.filter((item) => item.idDatasetRun !== idDatasetRun);
|
|
98
|
+
setStoreValue("process", list);
|
|
99
|
+
}
|
|
100
|
+
async finishRun(idDatasetRun) {
|
|
101
|
+
this.updateRun(idDatasetRun, { isUploadingResults: false });
|
|
102
|
+
runner.setMsgPanel(`🏁 **${i18n.t('msgPanel.automationRun')} #${idDatasetRun} ${i18n.t('msgPanel.finished')}**`);
|
|
103
|
+
this.removeRun(idDatasetRun);
|
|
104
|
+
await this.addNextToRun();
|
|
105
|
+
}
|
|
106
|
+
async addNextToRun() {
|
|
107
|
+
const parallelRuns = Number(getStoreValue("settings")?.parallelRuns || 1);
|
|
108
|
+
while (this.getRunList().length < parallelRuns) {
|
|
109
|
+
const waitList = this.getWaitList().sort((a, b) => b.idPriority - a.idPriority);
|
|
110
|
+
if (waitList.length === 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const nextRun = waitList.shift();
|
|
114
|
+
setStoreValue("waitList", waitList);
|
|
115
|
+
let addedRun = this.addToRunList(nextRun);
|
|
116
|
+
await updateDatasetRunStatus(addedRun.id, 'Loading');
|
|
117
|
+
this.addToRun(addedRun);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async addToRun(run) {
|
|
121
|
+
const updateRepoSuccess = await this.updateRepos(run.system);
|
|
122
|
+
if (updateRepoSuccess.success && updateRepoSuccess.updatedSystems) {
|
|
123
|
+
run = {
|
|
124
|
+
...run,
|
|
125
|
+
system: updateRepoSuccess.updatedSystems
|
|
126
|
+
};
|
|
127
|
+
await this.runAutomation(run);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
updateDatasetRunStatus(run.id, 'Failed');
|
|
131
|
+
await setLog(run.id, updateRepoSuccess.message);
|
|
132
|
+
this.removeRun(run.id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async updateRepos(systemList) {
|
|
136
|
+
const updatedSystems = [];
|
|
137
|
+
for (const system of systemList) {
|
|
138
|
+
const remoteHeaderHash = await getRemoteHeaderHash(system.gitRepository);
|
|
139
|
+
if (remoteHeaderHash) {
|
|
140
|
+
updatedSystems.push({
|
|
141
|
+
...system,
|
|
142
|
+
gitRepository: {
|
|
143
|
+
...system.gitRepository,
|
|
144
|
+
headerHash: remoteHeaderHash
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
const ownerRepo = `${system.gitRepository.owner}/${system.gitRepository.repository}`;
|
|
148
|
+
const dirRepo = ownerRepo + '/' + system.gitRepository.defaultBranch + '/' + remoteHeaderHash + '/' + system.gitRepository.repository;
|
|
149
|
+
const repoDirExists = dirExists(dirRepo);
|
|
150
|
+
if (repoDirExists) {
|
|
151
|
+
runner.setMsgPanel(`ℹ️ ${i18n.t('msgPanel.theProject')} ${system.gitRepository.repository} [${system.gitRepository.defaultBranch}] ${i18n.t('msgPanel.isUpToDate')}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
runner.setMsgPanel(`ℹ️ ${i18n.t('msgPanel.downloadingProject')} ${system.gitRepository.repository} [${system.gitRepository.defaultBranch}] ${i18n.t('msgPanel.from')} ${system.gitRepository.platform}...`);
|
|
155
|
+
await cloneRepo(system.gitRepository.remoteAuthorization, ownerRepo, system.gitRepository.defaultBranch, dirRepo);
|
|
156
|
+
const runMethod = getRepoRunMethod(dirRepo);
|
|
157
|
+
if (runMethod === 'playwright') {
|
|
158
|
+
await runNpmInstall(dirRepo);
|
|
159
|
+
}
|
|
160
|
+
runner.setMsgPanel(`ℹ️ ${i18n.t('msgPanel.downloadCompleted')} ${system.gitRepository.repository} [${system.gitRepository.defaultBranch}] ${i18n.t('msgPanel.projectUpdated')}`);
|
|
161
|
+
}
|
|
162
|
+
runner.setMsgPanel(`ℹ️ ${i18n.t('msgPanel.currentCommitHashFor')} ${system.gitRepository.platform} - ${system.gitRepository.repository} [${system.gitRepository.defaultBranch}]: ${remoteHeaderHash}`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const errorLog = `⛔ **${i18n.t('msgPanel.errorDownloadingProject')} ${system.gitRepository.repository} [${system.gitRepository.defaultBranch}]**`;
|
|
166
|
+
runner.setMsgPanel(errorLog);
|
|
167
|
+
return { success: false, message: errorLog };
|
|
168
|
+
// updateDatasetRunStatus(idDatasetRun, 'Failed');
|
|
169
|
+
// await setLog(idDatasetRun, errorLog);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { success: true, message: '', updatedSystems };
|
|
173
|
+
}
|
|
174
|
+
async runAutomation(run) {
|
|
175
|
+
let exitCode = 1;
|
|
176
|
+
let componentSystem = await getCurrentComponentSystem(run.id);
|
|
177
|
+
while (String(componentSystem).length > 0) {
|
|
178
|
+
const remote = run.system.find((sys) => { return sys.name === componentSystem; });
|
|
179
|
+
if (remote) {
|
|
180
|
+
const ownerRepo = `${remote.gitRepository.owner}/${remote.gitRepository.repository}`;
|
|
181
|
+
try {
|
|
182
|
+
const dir = ownerRepo + '/' + remote.gitRepository.defaultBranch + '/' + remote.gitRepository.headerHash + '/' + remote.gitRepository.repository;
|
|
183
|
+
const runMethod = getRepoRunMethod(dir);
|
|
184
|
+
const runConfig = { dir: dir, idDatasetRun: run.id, reference: run.externalId };
|
|
185
|
+
runner.setMsgPanel(`▶️ **${i18n.t('msgPanel.automationRun')} #${run.id} ${i18n.t('msgPanel.initiated')}**`);
|
|
186
|
+
switch (runMethod) {
|
|
187
|
+
case 'maven':
|
|
188
|
+
exitCode = await Maven.runScript(runConfig);
|
|
189
|
+
break;
|
|
190
|
+
case 'sap':
|
|
191
|
+
exitCode = await VBScript.runScript(runConfig);
|
|
192
|
+
break;
|
|
193
|
+
case 'pyTest':
|
|
194
|
+
exitCode = await PyTest.runScript(runConfig);
|
|
195
|
+
break;
|
|
196
|
+
case 'playwright':
|
|
197
|
+
exitCode = await Playwright.runScript(runConfig);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
await setLog(run.id, String(err));
|
|
203
|
+
}
|
|
204
|
+
this.removeProcess(run.id);
|
|
205
|
+
}
|
|
206
|
+
if (exitCode > 0) {
|
|
207
|
+
if (this.getRunList().find((r) => r.id === run.id)?.status !== 'Stopped') {
|
|
208
|
+
await updateDatasetRunStatus(run.id, 'Failed');
|
|
209
|
+
runner.setMsgPanel(`❌ **${i18n.t('msgPanel.errorRunningAutomation')} #${run.id}**`);
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
componentSystem = await getCurrentComponentSystem(run.id);
|
|
214
|
+
}
|
|
215
|
+
await this.finishRun(run.id);
|
|
216
|
+
await this.addNextToRun();
|
|
217
|
+
}
|
|
218
|
+
async processKiller(pid) {
|
|
219
|
+
return new Promise((resolve) => {
|
|
220
|
+
if (!pid || pid <= 0) {
|
|
221
|
+
resolve();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const platform = process.platform;
|
|
225
|
+
// =========================
|
|
226
|
+
// WINDOWS
|
|
227
|
+
// =========================
|
|
228
|
+
if (platform === 'win32') {
|
|
229
|
+
// taskkill mata o processo pai + todos os filhos
|
|
230
|
+
const killer = spawn('taskkill', ['/PID', pid.toString(), '/T', '/F'], {
|
|
231
|
+
windowsHide: true,
|
|
232
|
+
stdio: 'ignore'
|
|
233
|
+
});
|
|
234
|
+
killer.on('close', () => resolve());
|
|
235
|
+
killer.on('error', () => resolve());
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
// =========================
|
|
239
|
+
// LINUX / MACOS
|
|
240
|
+
// =========================
|
|
241
|
+
try {
|
|
242
|
+
/**
|
|
243
|
+
* Em sistemas Unix:
|
|
244
|
+
* - usamos PID negativo para matar o PROCESS GROUP
|
|
245
|
+
* - isso mata o processo pai e todos os filhos
|
|
246
|
+
*
|
|
247
|
+
* IMPORTANTE:
|
|
248
|
+
* o processo original deve ter sido criado com:
|
|
249
|
+
* detached: true
|
|
250
|
+
*/
|
|
251
|
+
process.kill(-pid, 'SIGKILL');
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
// Ignora erros (processo pode já ter morrido)
|
|
255
|
+
}
|
|
256
|
+
resolve();
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
initiateProcess(command, options) {
|
|
260
|
+
const { cwd, args } = options;
|
|
261
|
+
// =========================
|
|
262
|
+
// WINDOWS
|
|
263
|
+
// =========================
|
|
264
|
+
if (process.platform === 'win32') {
|
|
265
|
+
return spawn('cmd.exe', [
|
|
266
|
+
'/d',
|
|
267
|
+
'/s',
|
|
268
|
+
'/c',
|
|
269
|
+
`${command}`,
|
|
270
|
+
...args
|
|
271
|
+
], {
|
|
272
|
+
cwd,
|
|
273
|
+
stdio: 'pipe',
|
|
274
|
+
windowsHide: true
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// =========================
|
|
278
|
+
// LINUX / MACOS
|
|
279
|
+
// =========================
|
|
280
|
+
return spawn(command, args, {
|
|
281
|
+
cwd,
|
|
282
|
+
stdio: 'pipe',
|
|
283
|
+
detached: true
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
export const manager = new Manager();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IRepoData } from "#types";
|
|
2
|
+
export declare function getRemoteHeaderHash(remote: IRepoData): Promise<string | null>;
|
|
3
|
+
export declare function getCurrentComponentSystem(datasetRunId: number): Promise<string | null>;
|
|
4
|
+
export declare function updateDatasetRunStatus(datasetRunId: number, status: string): Promise<boolean>;
|
|
5
|
+
export declare function setLog(datasetRunId: number, msg: string): Promise<void>;
|
|
6
|
+
export declare function updateRunnerStatus(status: string): Promise<void>;
|
|
7
|
+
export declare function uploadLogFile(idDatasetRun: number, log: string): Promise<void>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import api from "../services/api.js";
|
|
2
|
+
import { getStoreValue } from "../store/index.js";
|
|
3
|
+
function getRequestBaseData() {
|
|
4
|
+
const activeConnection = getStoreValue("activeConnection");
|
|
5
|
+
if (!activeConnection || !activeConnection.token) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
headers: {
|
|
10
|
+
Authorization: `Bearer ${activeConnection.token}`,
|
|
11
|
+
database: activeConnection.server.includes("localhost") ? "dev5" : activeConnection.server.match(/\/\/([^.]+)\./)[1]
|
|
12
|
+
},
|
|
13
|
+
baseUrl: activeConnection.server.includes("localhost") ? "http://localhost:3333" : activeConnection.server
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function getRemoteHeaderHash(remote) {
|
|
17
|
+
const params = {
|
|
18
|
+
remoteAuthorization: remote.remoteAuthorization,
|
|
19
|
+
repository: remote.repository,
|
|
20
|
+
owner: remote.owner,
|
|
21
|
+
branch: remote.defaultBranch,
|
|
22
|
+
repoId: remote.repoId
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
const requestBaseData = getRequestBaseData();
|
|
26
|
+
if (!requestBaseData) {
|
|
27
|
+
console.log('No active connection');
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const response = await api.get(requestBaseData.baseUrl + '/git-repository/remote-header-hash', { params: params, headers: requestBaseData.headers });
|
|
31
|
+
if (response.status === 200) {
|
|
32
|
+
switch (remote.platform) {
|
|
33
|
+
case 'github':
|
|
34
|
+
return response.data['sha'];
|
|
35
|
+
case 'gitlab':
|
|
36
|
+
return response.data;
|
|
37
|
+
default:
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.log('erro ao obter remote repository');
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
console.log(err);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function getCurrentComponentSystem(datasetRunId) {
|
|
52
|
+
const requestBaseData = getRequestBaseData();
|
|
53
|
+
if (!requestBaseData) {
|
|
54
|
+
console.log('No active connection');
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const response = await api.get(requestBaseData.baseUrl + '/dataset-run/get-current-component-system/' + datasetRunId, { headers: requestBaseData.headers });
|
|
58
|
+
if (response.status === 200) {
|
|
59
|
+
return response.data['system'];
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log('Erro ao obter o sistema do componente atual');
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function updateDatasetRunStatus(datasetRunId, status) {
|
|
67
|
+
const requestBaseData = getRequestBaseData();
|
|
68
|
+
if (requestBaseData === null) {
|
|
69
|
+
console.log('No active connection');
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const body = {
|
|
73
|
+
status: status
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const response = await api.put(requestBaseData.baseUrl + '/dataset-run/update-run-status/' + datasetRunId, body, { headers: requestBaseData.headers });
|
|
77
|
+
if (response.status === 200) {
|
|
78
|
+
return true;
|
|
79
|
+
// console.log(`Run #${datasetRunId} updated to ${status}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return false;
|
|
83
|
+
// console.log('update dataset failed');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.log(err);
|
|
88
|
+
console.log('update dataset failed');
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export async function setLog(datasetRunId, msg) {
|
|
93
|
+
const requestBaseData = getRequestBaseData();
|
|
94
|
+
if (requestBaseData) {
|
|
95
|
+
const body = {
|
|
96
|
+
idDatasetRun: datasetRunId,
|
|
97
|
+
log: msg
|
|
98
|
+
};
|
|
99
|
+
await api.put(requestBaseData.baseUrl + '/dataset-run-log/update-log/' + datasetRunId, body, { headers: requestBaseData.headers });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export async function updateRunnerStatus(status) {
|
|
103
|
+
const requestBaseData = getRequestBaseData();
|
|
104
|
+
if (requestBaseData) {
|
|
105
|
+
const body = {
|
|
106
|
+
status: status
|
|
107
|
+
};
|
|
108
|
+
await api.put(requestBaseData.baseUrl + '/runner/update-runner-status', body, { headers: requestBaseData.headers });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function uploadLogFile(idDatasetRun, log) {
|
|
112
|
+
const requestBaseData = getRequestBaseData();
|
|
113
|
+
if (!requestBaseData) {
|
|
114
|
+
console.log('No active connection');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const params = { entity: 'dataset_run_resource', idEntity: String(idDatasetRun), description: '' };
|
|
118
|
+
const blobData = new Blob([log], { type: 'plain/text' });
|
|
119
|
+
let formData = new FormData();
|
|
120
|
+
formData.append('file', blobData, `${idDatasetRun}_RunLog.txt`);
|
|
121
|
+
await fetch(requestBaseData.baseUrl + '/file-system/upload-log-file?' + new URLSearchParams(params), { method: 'POST', body: formData, headers: requestBaseData.headers });
|
|
122
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import { checkIfTestProtonScriptExists } from '../fileSystem/index.js';
|
|
3
|
+
import { runner } from '../runner/index.js';
|
|
4
|
+
import { manager } from '../manager/index.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { uploadLogFile } from './api.js';
|
|
8
|
+
export default class Maven {
|
|
9
|
+
static async runScript(runConfig) {
|
|
10
|
+
const repoLocalDir = path.join(os.homedir(), '.proton-runner-repo', runConfig.dir);
|
|
11
|
+
const exitCode = await this.executeProcess(repoLocalDir, runConfig.idDatasetRun, runConfig.reference || '');
|
|
12
|
+
return exitCode;
|
|
13
|
+
}
|
|
14
|
+
static executeProcess(repoLocalDir, idDatasetRun, reference) {
|
|
15
|
+
if (!fs.existsSync(repoLocalDir)) {
|
|
16
|
+
throw new Error(`Diretório inválido: ${repoLocalDir}`);
|
|
17
|
+
}
|
|
18
|
+
const activeConnection = runner.getActiveConnection();
|
|
19
|
+
if (!activeConnection) {
|
|
20
|
+
return Promise.resolve(1);
|
|
21
|
+
}
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
try {
|
|
24
|
+
let mvnCmd = ['test', `-DidDatasetRun=${idDatasetRun}`];
|
|
25
|
+
if (reference.length > 0) {
|
|
26
|
+
let cucumberOption = '-Dcucumber.filter.tags="';
|
|
27
|
+
const tags = reference.split(' ');
|
|
28
|
+
for (let i = 0; i < tags.length; i++) {
|
|
29
|
+
cucumberOption = cucumberOption + `${tags[i]} `;
|
|
30
|
+
}
|
|
31
|
+
cucumberOption = cucumberOption + '"';
|
|
32
|
+
mvnCmd.push(cucumberOption);
|
|
33
|
+
}
|
|
34
|
+
if (checkIfTestProtonScriptExists(repoLocalDir)) {
|
|
35
|
+
mvnCmd.push('-Dtest=TestProtonScript');
|
|
36
|
+
}
|
|
37
|
+
const proc = manager.initiateProcess('mvn', {
|
|
38
|
+
cwd: repoLocalDir,
|
|
39
|
+
args: ['-B', ...mvnCmd]
|
|
40
|
+
});
|
|
41
|
+
if (!proc.pid) {
|
|
42
|
+
resolve(1);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
manager.addProcess(idDatasetRun, proc.pid);
|
|
46
|
+
let log = '';
|
|
47
|
+
proc.stdout.setEncoding('latin1');
|
|
48
|
+
proc.stdout.on('data', (data) => {
|
|
49
|
+
log += data;
|
|
50
|
+
});
|
|
51
|
+
proc.stderr.setEncoding('latin1');
|
|
52
|
+
proc.stderr.on('data', (data) => {
|
|
53
|
+
console.log('Maven STDERR:', data);
|
|
54
|
+
});
|
|
55
|
+
proc.on('close', async (code) => {
|
|
56
|
+
try {
|
|
57
|
+
await uploadLogFile(idDatasetRun, log);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.log(err);
|
|
61
|
+
console.log('Erro ao gerar arquivo de log');
|
|
62
|
+
}
|
|
63
|
+
resolve(code);
|
|
64
|
+
});
|
|
65
|
+
proc.on('error', err => {
|
|
66
|
+
console.error('Erro ao iniciar Maven:', err);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
resolve(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|