Skip to content

Commit 8e838b9

Browse files
author
Murat
committed
feat(task): added shell task support
fix #5
1 parent 45470f5 commit 8e838b9

File tree

10 files changed

+584
-15
lines changed

10 files changed

+584
-15
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
require('../../mocks/mockAll');
2+
const mockSpawn = jest.spyOn(require('child_process'), 'spawn');
3+
4+
import { shellTask } from '../../../tasks/shellTask';
5+
import { ShellTaskType } from '../../../types/mod.types';
6+
import { variables } from '../../../variables';
7+
import { mockPrompter } from '../../mocks/mockPrompter';
8+
9+
describe('shellTask', () => {
10+
it('should run command', async () => {
11+
mockSpawn.mockImplementationOnce(() => ({
12+
on: (_event: string, cb: CallableFunction) => {
13+
cb(0);
14+
},
15+
stdout: {
16+
on: (_event: string, cb: CallableFunction) => {
17+
cb('stdout');
18+
},
19+
},
20+
stderr: {
21+
on: (_event: string, cb: CallableFunction) => {
22+
cb('stderr');
23+
},
24+
},
25+
}));
26+
27+
const task: ShellTaskType = {
28+
type: 'shell',
29+
actions: [
30+
{
31+
name: 'test',
32+
command: 'test',
33+
},
34+
],
35+
};
36+
37+
await shellTask({
38+
configPath: 'path/to/config',
39+
task: task,
40+
packageName: 'test-package',
41+
});
42+
43+
expect(mockSpawn).toHaveBeenCalled();
44+
expect(variables.get('test.output')).toBe('stdoutstderr');
45+
46+
mockSpawn.mockReset();
47+
});
48+
it('should run command with args', async () => {
49+
mockSpawn.mockImplementationOnce(() => ({
50+
on: (_event: string, cb: CallableFunction) => {
51+
cb(0);
52+
},
53+
stdout: {
54+
on: (_event: string, cb: CallableFunction) => {
55+
cb('stdout');
56+
},
57+
},
58+
stderr: {
59+
on: (_event: string, cb: CallableFunction) => {
60+
cb('stderr');
61+
},
62+
},
63+
}));
64+
65+
const task: ShellTaskType = {
66+
type: 'shell',
67+
actions: [
68+
{
69+
name: 'test',
70+
command: 'test',
71+
args: ['arg1', 'arg2 arg3'],
72+
},
73+
],
74+
};
75+
76+
await shellTask({
77+
configPath: 'path/to/config',
78+
task: task,
79+
packageName: 'test-package',
80+
});
81+
82+
expect(mockSpawn).toHaveBeenCalledWith('test', ['arg1', 'arg2 arg3']);
83+
84+
mockSpawn.mockReset();
85+
});
86+
it('should handle unexpected error', async () => {
87+
mockSpawn.mockImplementationOnce(() => {
88+
throw new Error('unexpected error');
89+
});
90+
91+
const task: ShellTaskType = {
92+
type: 'shell',
93+
actions: [
94+
{
95+
name: 'test',
96+
command: 'test',
97+
},
98+
],
99+
};
100+
101+
await expect(
102+
shellTask({
103+
configPath: 'path/to/config',
104+
task: task,
105+
packageName: 'test-package',
106+
})
107+
).rejects.toThrowError('unexpected error');
108+
109+
expect(variables.get('test')).toEqual('error');
110+
111+
mockSpawn.mockReset();
112+
});
113+
it('should handle non zero exit code', async () => {
114+
mockSpawn.mockImplementationOnce(() => ({
115+
on: (_event: string, cb: CallableFunction) => {
116+
cb(1);
117+
},
118+
stdout: {
119+
on: (_event: string, cb: CallableFunction) => {
120+
cb('stdout');
121+
},
122+
},
123+
stderr: {
124+
on: (_event: string, cb: CallableFunction) => {
125+
cb('stderr');
126+
},
127+
},
128+
}));
129+
130+
const task: ShellTaskType = {
131+
type: 'shell',
132+
actions: [
133+
{
134+
name: 'test',
135+
command: 'test',
136+
},
137+
],
138+
};
139+
140+
await expect(
141+
shellTask({
142+
configPath: 'path/to/config',
143+
task: task,
144+
packageName: 'test-package',
145+
})
146+
).rejects.toThrowError('non zero exit code');
147+
148+
expect(variables.get('test')).toEqual('error');
149+
150+
mockSpawn.mockReset();
151+
});
152+
it('should skip when condition does not meet', async () => {
153+
const task: ShellTaskType = {
154+
type: 'shell',
155+
actions: [
156+
{
157+
when: { random: false },
158+
name: 'test',
159+
command: 'test',
160+
},
161+
],
162+
};
163+
164+
await shellTask({
165+
configPath: 'path/to/config',
166+
task: task,
167+
packageName: 'test-package',
168+
});
169+
170+
expect(mockSpawn).not.toHaveBeenCalled();
171+
172+
mockSpawn.mockReset();
173+
});
174+
it('should skip when execution not allowed', async () => {
175+
mockPrompter.confirm.mockClear();
176+
mockPrompter.confirm.mockReturnValueOnce(false);
177+
178+
const task: ShellTaskType = {
179+
type: 'shell',
180+
actions: [
181+
{
182+
name: 'test',
183+
command: 'test',
184+
},
185+
],
186+
};
187+
188+
await shellTask({
189+
configPath: 'path/to/config',
190+
task: task,
191+
packageName: 'test-package',
192+
});
193+
194+
expect(mockSpawn).not.toHaveBeenCalled();
195+
196+
mockSpawn.mockReset();
197+
mockPrompter.confirm.mockReset();
198+
});
199+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-call */
2+
3+
require('../../mocks/mockAll');
4+
5+
import { parseArgs } from '../../../utils/parseArgs';
6+
7+
describe('parseArgs', () => {
8+
it('should parse args correctly', () => {
9+
const args = parseArgs('arg1 "arg2 arg3" arg4');
10+
expect(args).toEqual(['arg1', 'arg2 arg3', 'arg4']);
11+
});
12+
it('should parse escaped args correctly', () => {
13+
const args = parseArgs('arg1 "arg\\"2 a\\"rg3"');
14+
expect(args).toEqual(['arg1', 'arg"2 a"rg3']);
15+
});
16+
});

src/schema/integrate.schema.json

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,9 @@
571571
{
572572
"$ref": "#/definitions/FsTaskType"
573573
},
574+
{
575+
"$ref": "#/definitions/ShellTaskType"
576+
},
574577
{
575578
"$ref": "#/definitions/PromptTaskType"
576579
}
@@ -1074,6 +1077,65 @@
10741077
],
10751078
"type": "object"
10761079
},
1080+
"ShellActionType": {
1081+
"additionalProperties": false,
1082+
"properties": {
1083+
"args": {
1084+
"items": {
1085+
"type": "string"
1086+
},
1087+
"type": "array"
1088+
},
1089+
"command": {
1090+
"type": "string"
1091+
},
1092+
"name": {
1093+
"type": "string"
1094+
},
1095+
"when": {
1096+
"$ref": "#/definitions/AnyObject"
1097+
}
1098+
},
1099+
"required": [
1100+
"command"
1101+
],
1102+
"type": "object"
1103+
},
1104+
"ShellTaskType": {
1105+
"additionalProperties": false,
1106+
"properties": {
1107+
"actions": {
1108+
"items": {
1109+
"$ref": "#/definitions/ShellActionType"
1110+
},
1111+
"type": "array"
1112+
},
1113+
"label": {
1114+
"type": "string"
1115+
},
1116+
"name": {
1117+
"type": "string"
1118+
},
1119+
"postInfo": {
1120+
"$ref": "#/definitions/TextOrTitleMessage"
1121+
},
1122+
"preInfo": {
1123+
"$ref": "#/definitions/TextOrTitleMessage"
1124+
},
1125+
"type": {
1126+
"const": "shell",
1127+
"type": "string"
1128+
},
1129+
"when": {
1130+
"$ref": "#/definitions/AnyObject"
1131+
}
1132+
},
1133+
"required": [
1134+
"actions",
1135+
"type"
1136+
],
1137+
"type": "object"
1138+
},
10771139
"StringsXmlTaskType": {
10781140
"additionalProperties": false,
10791141
"properties": {

src/schema/upgrade.schema.json

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,9 @@
571571
{
572572
"$ref": "#/definitions/FsTaskType"
573573
},
574+
{
575+
"$ref": "#/definitions/ShellTaskType"
576+
},
574577
{
575578
"$ref": "#/definitions/PromptTaskType"
576579
}
@@ -1074,6 +1077,65 @@
10741077
],
10751078
"type": "object"
10761079
},
1080+
"ShellActionType": {
1081+
"additionalProperties": false,
1082+
"properties": {
1083+
"args": {
1084+
"items": {
1085+
"type": "string"
1086+
},
1087+
"type": "array"
1088+
},
1089+
"command": {
1090+
"type": "string"
1091+
},
1092+
"name": {
1093+
"type": "string"
1094+
},
1095+
"when": {
1096+
"$ref": "#/definitions/AnyObject"
1097+
}
1098+
},
1099+
"required": [
1100+
"command"
1101+
],
1102+
"type": "object"
1103+
},
1104+
"ShellTaskType": {
1105+
"additionalProperties": false,
1106+
"properties": {
1107+
"actions": {
1108+
"items": {
1109+
"$ref": "#/definitions/ShellActionType"
1110+
},
1111+
"type": "array"
1112+
},
1113+
"label": {
1114+
"type": "string"
1115+
},
1116+
"name": {
1117+
"type": "string"
1118+
},
1119+
"postInfo": {
1120+
"$ref": "#/definitions/TextOrTitleMessage"
1121+
},
1122+
"preInfo": {
1123+
"$ref": "#/definitions/TextOrTitleMessage"
1124+
},
1125+
"type": {
1126+
"const": "shell",
1127+
"type": "string"
1128+
},
1129+
"when": {
1130+
"$ref": "#/definitions/AnyObject"
1131+
}
1132+
},
1133+
"required": [
1134+
"actions",
1135+
"type"
1136+
],
1137+
"type": "object"
1138+
},
10771139
"StringsXmlTaskType": {
10781140
"additionalProperties": false,
10791141
"properties": {

0 commit comments

Comments
 (0)