Skip to content

Commit 6969bbc

Browse files
Merge pull request #154 from EduardKrieger/executeFile
ExecuteCommand
2 parents fd0a3be + 694b4fe commit 6969bbc

10 files changed

Lines changed: 166 additions & 23 deletions

File tree

assertions/serverIsReachable.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@ export class ServerIsReachable {
88
let interval = this.getValue(parameters, 'intervall', 5);
99
let startupTime = this.getValue(parameters, 'startupTime', 600);
1010
let command = this.getValue(parameters, 'command', "");
11-
1211
if(!port) {
1312
throw new Error("Missing arguments for the command " + command + ". You have to specify a port for the server. For further information read the function documentation.");
14-
1513
} else {
1614
let timeoutFlag = false;
1715
let reached = false;
@@ -45,4 +43,4 @@ export class ServerIsReachable {
4543
private static sleep(seconds: number) {
4644
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
4745
}
48-
}
46+
}

documentation/Functions.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ The following functions are already implemented:
44
* restoreDevonfwIde
55
* restoreWorkspace
66
* changeWorkspace
7+
* executeCommand
78
* installCobiGen
89
* cobiGenJava
910
* createDevon4jProject
@@ -114,6 +115,39 @@ will set the workspace directory to "[working directory]/devonfw/workspaces/proj
114115

115116
Learn more about the workspace directory and working directory on [Structure](https://github.com/devonfw-tutorials/tutorial-compiler/wiki/Structure)
116117

118+
### executeCommand
119+
#### parameter
120+
1. The command that will be executed on Windows
121+
2. The command that will be executed on Linux
122+
3. Json-object with optional fields
123+
* (Optional) Directory where the command will be executed, if not in current directory (relative to workspace){"dir": string}
124+
* (Optional) Synchronous or asynchronous process. Use asynchronous when starting a server. Default is synchronous. {"asynchronous": boolean}
125+
* (Optional) Array of arguments {"args": string[]}
126+
4. Assert information needed if you start a server to check server availability. Only required when you start a asynchronous server.
127+
128+
#### Commands
129+
It is needed to pass a command for Windows and also for Linux-based systems because both systems will always be tested.
130+
131+
##### Assertion information
132+
startupTime = Time in seconds to wait before checking if the server is running
133+
port: Port on which the server is running
134+
path: The URL path on which is checked if the server is running
135+
interval: The availability of the server is checked in the given interval
136+
* (Required) port: will throw error if no port is given.
137+
* (Optional) path: subpath which should be pinged, i.e: if localhost:8081/jumpthequeue should be checked path should be "jumpthequeue". DEFAULT: ""
138+
* (Optional) interval: interval in seconds in which the server should be pinged until it is available or timeouted. DEFAULT: 5 seconds
139+
* (Optional) startupTime: seconds until a timeout will occur and an error will be thrown. DEFAULT: 10 minutes
140+
141+
#### example
142+
143+
executeCommand("node", "node" ,{"args": ["-v"]})
144+
Will create a command for executing node -v .
145+
146+
executeCommand("somePollingScript.ps1","bash somePollingScript.sh", {"dir": "data/setup","asynchronous": true, "args": ["--params 5"]})
147+
Will create a command to execute the script in the directory with the parameter --params 5 and in a new terminal.
148+
149+
executeCommand("someServerScript.ps1","bash someServerScript.sh", {"asynchronous": true, "args":["-port 8080"] },{"port":8080 , "startupTime": 20, "path": "some/path/", "interval": 2})
150+
Starting a server in a new terminal. You have to specify the port for testing, the other parameters are optional. The startupTime can specify how long the runner will wait for a response from the server process and with interval you can set the frequenzy for the server testing. The path is the subpath from your server that should be reached.
117151

118152

119153
### installCobiGen
@@ -193,12 +227,14 @@ example:{...,"placeholder": "private int age;"}
193227
| --- | --- | --- |
194228
|<p>private int age;<br><br>public String getFirstname() {<br>return firstname;<br>}<br></p>|<p>private int age;<br><br>private String company;<br>public String getCompany() {<br>return firstname;<br>}<br>public void setCompany(String company) {<br>this.company = company;<br>}</p>|<p>private int age;<br><br>private String company;<br>public String getCompany() {<br>return firstname;<br>}<br>public void setCompany(String company) {<br>this.company = company;<br><br>public String getFirstname() {<br>return firstname;<br>}<br></p>|
195229

230+
196231
##### Prerequisite
197232
The usage of the line number function requires having VSCode installed on your System. Not having VSCode installed will not create any output for Katacoda.
198233

199234
##### Name of the placeholder
200235
If you want to insert content into your code between two existing lines, take the previous line as your placeholder or use the option to insert at a line number. Add your placeholder into the new file or string, otherwise it will be replaced entirely.
201236

237+
202238
A placeholder is optional. If you do not define a placeholder, the content in the existing file will be simply replaced by the new content.
203239

204240
Please try not to use custom placeholders. Keep in mind that you might want to build the project before changing them. Custom placeholders with a comment-syntax (e.g. "//PLACEHOLDER") will be removed by the console-environment and others might cause errors.

localBuildRun.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ Copy-Item -Force -Recurse -Path $PSScriptRoot\environments\ -Destination $PSScri
55
Copy-Item -Force -Recurse -Path $PSScriptRoot\runners\ -Destination $PSScriptRoot\build
66
npm test
77
if(-not $?) { throw 'tests failed' }
8-
node $PSScriptRoot\build\engine\run.js $args
8+
node $PSScriptRoot\build\engine\run.js $args
9+
if(Test-Path $PSScriptRoot\build\playbooks){
10+
Remove-Item .\build\playbooks -Recurse -Force
11+
}

localBuildRun.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ if [ $? -eq 1 ]; then
99
exit 1
1010
fi
1111

12-
node build/engine/run.js $*
12+
node build/engine/run.js $*
13+
14+
[ -d build/playbooks ] && rm -rf build/playbooks

runners/console/consoleInterfaces.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ export enum ConsolePlatform {
55

66
export interface AsyncProcess {
77
pid: number;
8-
name: string;
98
port: number;
109
}

runners/console/consoleUtils.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export class ConsoleUtils {
2525

2626
static executeCommandAsync(command: string, directory: string, result: RunResult, env: any): child_process.ChildProcess {
2727
if(result.returnCode != 0) return;
28-
2928
let process = child_process.spawn(command, [], { shell: true, cwd: directory, env: env });
3029

3130
let output = "";
@@ -66,4 +65,4 @@ export class ConsoleUtils {
6665
fs.renameSync(path.join(homedir, ".devon_backup"), path.join(homedir, ".devon"));
6766
}
6867
}
69-
}
68+
}

runners/console/index.ts

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import * as path from 'path';
88
import * as fs from "fs-extra";
99
import * as psList from "ps-list";
1010
import { ConsoleUtils } from "./consoleUtils";
11+
1112
const findProcess = require("find-process");
13+
1214
const os = require("os");
1315

1416
export class Console extends Runner {
@@ -285,7 +287,7 @@ export class Console extends Runner {
285287
}
286288
});
287289
if(process.pid) {
288-
this.asyncProcesses.push({ pid: process.pid, name: "dockerCompose", port: runCommand.command.parameters[1].port });
290+
this.asyncProcesses.push({ pid: process.pid, port: runCommand.command.parameters[1].port });
289291
}
290292
}else{
291293
result.returnCode = 1;
@@ -306,7 +308,7 @@ export class Console extends Runner {
306308
: ConsoleUtils.executeCommandAsync("mvn spring-boot:run", serverDir, result, this.env);
307309

308310
if(process.pid) {
309-
this.asyncProcesses.push({ pid: process.pid, name: "java", port: runCommand.command.parameters[1].port });
311+
this.asyncProcesses.push({ pid: process.pid, port: runCommand.command.parameters[1].port });
310312
}
311313
}else{
312314
result.returnCode = 1;
@@ -374,7 +376,7 @@ export class Console extends Runner {
374376
? ConsoleUtils.executeDevonCommandAsync("ng serve", projectDir, path.join(this.getWorkingDirectory(), "devonfw"), result, this.env)
375377
: ConsoleUtils.executeCommandAsync("ng serve", projectDir, result, this.env);
376378
if(process.pid) {
377-
this.asyncProcesses.push({ pid: process.pid, name: "node", port: runCommand.command.parameters[1].port });
379+
this.asyncProcesses.push({ pid: process.pid,port: runCommand.command.parameters[1].port });
378380
}
379381
}else{
380382
result.returnCode = 1;
@@ -439,7 +441,7 @@ export class Console extends Runner {
439441

440442
return result;
441443
}
442-
444+
443445
runChangeWorkspace(runCommand: RunCommand): RunResult {
444446
let result = new RunResult();
445447
result.returnCode = 0;
@@ -448,10 +450,70 @@ export class Console extends Runner {
448450
if(!fs.existsSync(workspacesDir))
449451
fs.mkdirSync(workspacesDir);
450452
this.setVariable(this.workspaceDirectory, workspacesDir);
453+
454+
return result;
455+
}
451456

452-
return result;
457+
runExecuteCommand(runCommand: RunCommand): RunResult {
458+
let result = new RunResult();
459+
result.returnCode = 0;
460+
let commandIndex;
461+
if(runCommand.command.parameters[0] && runCommand.command.parameters[1]){
462+
commandIndex = this.platform == ConsolePlatform.LINUX ? 1 : 0;
463+
}
464+
else{
465+
throw new Error("You have to pass a command for Windows and Linux based OS");
466+
}
467+
468+
let exeCommand = (runCommand.command.parameters.length > 1 && runCommand.command.parameters[2].args)
469+
? runCommand.command.parameters[commandIndex]+ " " +runCommand.command.parameters[2].args.join(" ")
470+
: runCommand.command.parameters[commandIndex];
471+
472+
let dirPath = (runCommand.command.parameters.length > 1 && runCommand.command.parameters[2].dir)
473+
? path.join(this.getVariable(this.workspaceDirectory), runCommand.command.parameters[2].dir)
474+
: this.getVariable(this.workspaceDirectory)
475+
476+
if(runCommand.command.parameters.length > 2 && runCommand.command.parameters[2].asynchronous){
477+
if(runCommand.command.parameters[3].port){
478+
let process = ConsoleUtils.executeCommandAsync(exeCommand, dirPath, result,this.env);
479+
if(process.pid) {
480+
this.asyncProcesses.push({ pid: process.pid, port: runCommand.command.parameters[3].port});
481+
}
482+
}
483+
else{
484+
throw new Error("Missing arguments for the command " + exeCommand + ". You have to specify a port for the server. For further information read the function documentation.");
485+
}
486+
}
487+
else ConsoleUtils.executeCommandSync(exeCommand, dirPath, result, this.env);
488+
489+
return result;
453490
}
454491

492+
493+
async assertExecuteCommand(runCommand: RunCommand, result: RunResult){
494+
try{
495+
let assert = new Assertions()
496+
.noErrorCode(result)
497+
.noException(result);
498+
if(runCommand.command.parameters.length > 3 && runCommand.command.parameters[2].asynchronous){
499+
await assert.serverIsReachable({
500+
path: runCommand.command.parameters[3].path,
501+
port: runCommand.command.parameters[3].port,
502+
interval: runCommand.command.parameters[3].interval,
503+
startupTime: runCommand.command.parameters[3].startupTime,
504+
command: this.platform == ConsolePlatform.WINDOWS
505+
? runCommand.command.parameters[0]
506+
: runCommand.command.parameters[1]
507+
});
508+
}
509+
} catch(error) {
510+
await this.cleanUp();
511+
throw error;
512+
}
513+
514+
}
515+
516+
455517
runAddSetupScript(runCommand: RunCommand): RunResult {
456518
let result = new RunResult();
457519
result.returnCode = 0;
@@ -464,14 +526,15 @@ export class Console extends Runner {
464526

465527
return result;
466528
}
467-
529+
468530
runOpenFile(runCommand: RunCommand): RunResult {
469531
let result = new RunResult();
470532
result.returnCode = 0;
471533
//Only needed for katacoda, wiki runner and the assertions
472534
return result;
473535
}
474536

537+
475538
async assertInstallDevonfwIde(runCommand: RunCommand, result: RunResult) {
476539
try {
477540
let installedTools = runCommand.command.parameters[0];
@@ -851,6 +914,7 @@ export class Console extends Runner {
851914
return process.ppid == processIdToKill;
852915
});
853916

917+
854918
if(childProcesses.length > 0) {
855919
for(let childProcess of childProcesses) {
856920
killProcessesRecursively(processes, childProcess.pid)
@@ -869,24 +933,21 @@ export class Console extends Runner {
869933
for(let asyncProcess of this.asyncProcesses) {
870934
killProcessesRecursively(processes, asyncProcess.pid);
871935
}
872-
873936
//Check if there are still running processes on the given ports
874-
for(let asyncProcess of this.asyncProcesses) {
937+
for(let asyncProcess of this.asyncProcesses.reverse()) {
875938
let processes: any[] = await findProcess("port", asyncProcess.port);
876939
if(processes.length > 0) {
877940
for(let proc of processes) {
878-
if(proc.name == asyncProcess.name || proc.name == asyncProcess.name + ".exe") {
879-
try {
880-
process.kill(proc.pid);
941+
try {
942+
process.kill(proc.pid);
881943
} catch(e) {
882944
console.error("Error killing id " + proc.pid, e);
883945
}
884946
}
885947
}
886-
}
887-
}
948+
}
949+
}
888950
}
889-
}
890951

891952
private async cleanUp(): Promise<void> {
892953
await this.killAsyncProcesses();

runners/katacoda/index.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,35 @@ export class Katacoda extends Runner {
400400
return null;
401401
}
402402

403+
runExecuteCommand(runCommand: RunCommand) : RunResult {
404+
let terminal = (runCommand.command.parameters.length > 2 && runCommand.command.parameters[2].asynchronous)
405+
? this.getTerminal("executeCommand"+runCommand.stepIndex)
406+
: undefined;
407+
408+
let filepath;
409+
let changeDir = false;
410+
if(runCommand.command.parameters.length > 2 && runCommand.command.parameters[2].dir){
411+
filepath = runCommand.command.parameters[2].asynchronous
412+
? path.join(this.getVariable(this.workspaceDirectory), runCommand.command.parameters[2].dir).replace(/\\/g, "/")
413+
: runCommand.command.parameters[2].dir;
414+
changeDir = true;
415+
this.currentDir = filepath;
416+
}
417+
418+
let bashCommand = {
419+
"name" : runCommand.command.parameters[1],
420+
"changeDir" : changeDir,
421+
"path" : filepath,
422+
"terminalId" : terminal ? terminal.terminalId : 1,
423+
"interrupt" : terminal ? terminal.isRunning : false,
424+
"args": (runCommand.command.parameters.length > 2 && runCommand.command.parameters[2].args) ? runCommand.command.parameters[1].args.join(" ") : undefined
425+
}
426+
427+
this.pushStep(runCommand, "Executing the command "+ runCommand.command.parameters[1] , "step"+ runCommand.stepIndex + ".md");
428+
this.renderTemplate("executeCommand.md", this.outputPathTutorial + "step" + (runCommand.stepIndex) + ".md", { text: runCommand.text, textAfter: runCommand.textAfter, bashCommand: bashCommand});
429+
return null;
430+
}
431+
403432
runChangeWorkspace(runCommand: RunCommand): RunResult {
404433
let workspacesDir = path.join('/root', runCommand.command.parameters[0]);
405434
this.setVariable(this.workspaceDirectory, workspacesDir);
@@ -419,6 +448,7 @@ export class Katacoda extends Runner {
419448

420449
this.pushStep(runCommand);
421450
return null;
451+
422452
}
423453

424454
runOpenFile(runCommand: RunCommand): RunResult {
@@ -489,7 +519,7 @@ export class Katacoda extends Runner {
489519
this.currentStepIndex++;
490520
}
491521
}
492-
522+
493523
supports(name: string, parameters: any[]): boolean {
494524
if(name == "changeFile" && parameters[1].lineNumber){
495525
if(this.showVsCodeIde){
@@ -505,3 +535,4 @@ export class Katacoda extends Runner {
505535
}
506536

507537
}
538+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<%= text; %>
2+
3+
<% if(bashCommand.interrupt){%><% if(bashCommand.changeDir){%>We want to execute the command in a different directory so you have to change your current directory. Some command is already running in terminal <%= bashCommand.terminalId; %> to stop the execution just use this command
4+
`cd <%= bashCommand.path; %> `{{execute T<%= bashCommand.terminalId; %> interrupt}} <% }} else{%>
5+
<% if(bashCommand.changeDir){%>We want to execute the command in a different directory so you have to change your current directory.
6+
With the next command we also open a new terminal, so you have to execute the command twice
7+
`cd <%= bashCommand.path; %> `{{execute T<%= bashCommand.terminalId; %>}}. <% } %><% } %>
8+
9+
<% if(bashCommand.interrupt){ %><% if(bashCommand.changeDir){%>Run <%= bashCommand.name; %> with this bash-command.`<%= bashCommand.name; %> <%= bashCommand.args; %>`{{execute T<%= bashCommand.terminalId; %>}} <% } else{%> Some command is already running in terminal <%= bashCommand.terminalId; %>. Rerun the command to stop and relaunch it automatically. `<%= bashCommand.name; %> <%= bashCommand.args; %>`{{execute T<%= bashCommand.terminalId; %> interrupt }} <%} }else{ %>Run <%= bashCommand.name; %> with this bash-command.
10+
`<%= bashCommand.name; %> <%= bashCommand.args; %>`{{execute T<%= bashCommand.terminalId; %>}} <%} %>
11+
12+
<%= textAfter; %>
13+

runners/wikiConsole/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export class WikiConsole extends WikiRunner {
102102
return null;
103103
}
104104

105+
105106
runDockerCompose(runCommand: RunCommand): RunResult {
106107
let dir = runCommand.command.parameters[0];
107108
this.renderWiki(path.join(this.getRunnerDirectory(), "templates", "dockerCompose.asciidoc"), { dir: dir, port: runCommand.command.parameters[1].port, app_path: runCommand.command.parameters[1].path })

0 commit comments

Comments
 (0)