@oneuptime/common 8.0.5466 → 8.0.5479

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.
@@ -2,6 +2,7 @@ import { EncryptionSecret, WorkflowHostname } from "../EnvironmentConfig";
2
2
  import PostgresAppInstance from "../Infrastructure/PostgresDatabase";
3
3
  import ClusterKeyAuthorization from "../Middleware/ClusterKeyAuthorization";
4
4
  import CountBy from "../Types/Database/CountBy";
5
+ import FindAllBy from "../Types/Database/FindAllBy";
5
6
  import CreateBy from "../Types/Database/CreateBy";
6
7
  import DeleteBy from "../Types/Database/DeleteBy";
7
8
  import DeleteById from "../Types/Database/DeleteById";
@@ -1168,6 +1169,75 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
1168
1169
  }
1169
1170
  }
1170
1171
 
1172
+ @CaptureSpan()
1173
+ public async findAllBy(
1174
+ findAllBy: FindAllBy<TBaseModel>,
1175
+ ): Promise<Array<TBaseModel>> {
1176
+ const { limit, skip, ...rest } = findAllBy;
1177
+
1178
+ let remaining: number | undefined = this.normalizePositiveNumber(limit);
1179
+ let currentSkip: number = this.normalizePositiveNumber(skip) || 0;
1180
+
1181
+ const results: Array<TBaseModel> = [];
1182
+
1183
+ while (true) {
1184
+ const currentBatchSize: number =
1185
+ remaining !== undefined
1186
+ ? Math.min(LIMIT_MAX, Math.max(remaining, 0))
1187
+ : LIMIT_MAX;
1188
+
1189
+ if (currentBatchSize <= 0) {
1190
+ break;
1191
+ }
1192
+
1193
+ const page: Array<TBaseModel> = await this.findBy({
1194
+ ...rest,
1195
+ skip: currentSkip,
1196
+ limit: currentBatchSize,
1197
+ });
1198
+
1199
+ if (page.length === 0) {
1200
+ break;
1201
+ }
1202
+
1203
+ results.push(...page);
1204
+
1205
+ currentSkip += page.length;
1206
+
1207
+ if (remaining !== undefined) {
1208
+ remaining -= page.length;
1209
+
1210
+ if (remaining <= 0) {
1211
+ break;
1212
+ }
1213
+ }
1214
+
1215
+ if (page.length < currentBatchSize) {
1216
+ break;
1217
+ }
1218
+ }
1219
+
1220
+ return results;
1221
+ }
1222
+
1223
+ private normalizePositiveNumber(
1224
+ value?: PositiveNumber | number,
1225
+ ): number | undefined {
1226
+ if (value === undefined || value === null) {
1227
+ return undefined;
1228
+ }
1229
+
1230
+ if (value instanceof PositiveNumber) {
1231
+ return value.toNumber();
1232
+ }
1233
+
1234
+ if (typeof value === "number") {
1235
+ return value;
1236
+ }
1237
+
1238
+ return undefined;
1239
+ }
1240
+
1171
1241
  @CaptureSpan()
1172
1242
  public async findBy(findBy: FindBy<TBaseModel>): Promise<Array<TBaseModel>> {
1173
1243
  return await this._findBy(findBy);
@@ -71,6 +71,8 @@ import URL from "../../Types/API/URL";
71
71
  import Exception from "../../Types/Exception/Exception";
72
72
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
73
73
  import DatabaseConfig from "../DatabaseConfig";
74
+ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
75
+ import PositiveNumber from "../../Types/PositiveNumber";
74
76
 
75
77
  export interface CurrentPlan {
76
78
  plan: PlanType | null;
@@ -1435,6 +1437,28 @@ export class ProjectService extends DatabaseService<Model> {
1435
1437
  };
1436
1438
  }
1437
1439
 
1440
+ @CaptureSpan()
1441
+ public async getAllActiveProjects(params?: {
1442
+ select?: Select<Model>;
1443
+ props?: DatabaseCommonInteractionProps;
1444
+ skip?: PositiveNumber | number;
1445
+ limit?: PositiveNumber | number;
1446
+ }): Promise<Array<Model>> {
1447
+ const select: Select<Model> | undefined =
1448
+ params?.select || ({ _id: true } as Select<Model>);
1449
+ const props: DatabaseCommonInteractionProps = params?.props || {
1450
+ isRoot: true,
1451
+ };
1452
+
1453
+ return await this.findAllBy({
1454
+ query: this.getActiveProjectStatusQuery(),
1455
+ select,
1456
+ props,
1457
+ skip: params?.skip,
1458
+ limit: params?.limit,
1459
+ });
1460
+ }
1461
+
1438
1462
  @CaptureSpan()
1439
1463
  public async getProjectLinkInDashboard(projectId: ObjectID): Promise<URL> {
1440
1464
  const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
@@ -0,0 +1,25 @@
1
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
2
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
3
+ import GroupBy from "./GroupBy";
4
+ import Query from "./Query";
5
+ import Select from "./Select";
6
+ import Sort from "./Sort";
7
+ import PositiveNumber from "../../../Types/PositiveNumber";
8
+
9
+ export default interface FindAllBy<TBaseModel extends BaseModel> {
10
+ query: Query<TBaseModel>;
11
+ select?: Select<TBaseModel> | undefined;
12
+ sort?: Sort<TBaseModel> | undefined;
13
+ groupBy?: GroupBy<TBaseModel> | undefined;
14
+ props: DatabaseCommonInteractionProps;
15
+ /**
16
+ * Optional number of documents to skip before fetching results.
17
+ * Acts the same way as `skip` in `findBy` but defaults to 0 when omitted.
18
+ */
19
+ skip?: PositiveNumber | number | undefined;
20
+ /**
21
+ * Optional total number of documents to return across all batches.
22
+ * When omitted, the method keeps fetching until no more data is returned.
23
+ */
24
+ limit?: PositiveNumber | number | undefined;
25
+ }
@@ -1,33 +1,26 @@
1
+ import path from "path";
2
+ import fs from "fs";
1
3
  import Execute from "../Execute";
2
4
  import LocalFile from "../LocalFile";
3
5
  import logger from "../Logger";
4
6
  import CaptureSpan from "../Telemetry/CaptureSpan";
5
7
  import CodeRepositoryFile from "./CodeRepositoryFile";
6
8
  import Dictionary from "../../../Types/Dictionary";
9
+ import BadDataException from "../../../Types/Exception/BadDataException";
7
10
 
8
11
  export default class CodeRepositoryUtil {
9
12
  @CaptureSpan()
10
13
  public static getCurrentCommitHash(data: {
11
14
  repoPath: string;
12
15
  }): Promise<string> {
13
- const command: string = `cd ${data.repoPath} && git rev-parse HEAD`;
14
-
15
- logger.debug("Executing command: " + command);
16
-
17
- return Execute.executeCommand(command);
16
+ return this.runGitCommand(data.repoPath, ["rev-parse", "HEAD"]);
18
17
  }
19
18
 
20
19
  @CaptureSpan()
21
20
  public static async addAllChangedFilesToGit(data: {
22
21
  repoPath: string;
23
22
  }): Promise<void> {
24
- const command: string = `cd ${data.repoPath} && git add -A`;
25
-
26
- logger.debug("Executing command: " + command);
27
-
28
- const stdout: string = await Execute.executeCommand(command);
29
-
30
- logger.debug(stdout);
23
+ await this.runGitCommand(data.repoPath, ["add", "-A"]);
31
24
  }
32
25
 
33
26
  @CaptureSpan()
@@ -36,26 +29,26 @@ export default class CodeRepositoryUtil {
36
29
  authorName: string;
37
30
  authorEmail: string;
38
31
  }): Promise<void> {
39
- const command: string = `cd ${data.repoPath} && git config --global user.name "${data.authorName}" && git config --global user.email "${data.authorEmail}"`;
40
-
41
- logger.debug("Executing command: " + command);
42
-
43
- const stdout: string = await Execute.executeCommand(command);
44
-
45
- logger.debug(stdout);
32
+ await this.runGitCommand(data.repoPath, [
33
+ "config",
34
+ "--global",
35
+ "user.name",
36
+ data.authorName,
37
+ ]);
38
+
39
+ await this.runGitCommand(data.repoPath, [
40
+ "config",
41
+ "--global",
42
+ "user.email",
43
+ data.authorEmail,
44
+ ]);
46
45
  }
47
46
 
48
47
  @CaptureSpan()
49
48
  public static async discardAllChangesOnCurrentBranch(data: {
50
49
  repoPath: string;
51
50
  }): Promise<void> {
52
- const command: string = `cd ${data.repoPath} && git checkout .`;
53
-
54
- logger.debug("Executing command: " + command);
55
-
56
- const stdout: string = await Execute.executeCommand(command);
57
-
58
- logger.debug(stdout);
51
+ await this.runGitCommand(data.repoPath, ["checkout", "."]);
59
52
  }
60
53
 
61
54
  // returns the folder name of the cloned repository
@@ -64,33 +57,25 @@ export default class CodeRepositoryUtil {
64
57
  repoPath: string;
65
58
  repoUrl: string;
66
59
  }): Promise<string> {
67
- const command: string = `cd ${data.repoPath} && git clone ${data.repoUrl}`;
68
-
69
- logger.debug("Executing command: " + command);
70
-
71
- const stdout: string = await Execute.executeCommand(command);
60
+ await this.runGitCommand(data.repoPath, ["clone", data.repoUrl]);
72
61
 
73
- logger.debug(stdout);
74
-
75
- // get the folder name of the repository from the disk.
76
-
77
- const getFolderNameCommand: string = `cd ${data.repoPath} && ls`;
62
+ const normalizedUrl: string = data.repoUrl.trim().replace(/\/+$/g, "");
63
+ const lastSegment: string =
64
+ normalizedUrl.split("/").pop() || normalizedUrl.split(":").pop() || "";
65
+ const folderName: string = lastSegment.replace(/\.git$/i, "");
78
66
 
79
- const folderName: string =
80
- await Execute.executeCommand(getFolderNameCommand);
67
+ if (!folderName) {
68
+ throw new BadDataException(
69
+ "Unable to determine repository folder name after cloning.",
70
+ );
71
+ }
81
72
 
82
73
  return folderName.trim();
83
74
  }
84
75
 
85
76
  @CaptureSpan()
86
77
  public static async pullChanges(data: { repoPath: string }): Promise<void> {
87
- const command: string = `cd ${data.repoPath} && git pull`;
88
-
89
- logger.debug("Executing command: " + command);
90
-
91
- const stdout: string = await Execute.executeCommand(command);
92
-
93
- logger.debug(stdout);
78
+ await this.runGitCommand(data.repoPath, ["pull"]);
94
79
  }
95
80
 
96
81
  @CaptureSpan()
@@ -98,13 +83,26 @@ export default class CodeRepositoryUtil {
98
83
  repoPath: string;
99
84
  branchName: string;
100
85
  }): Promise<void> {
101
- const command: string = `cd ${data.repoPath} && git checkout ${data.branchName} || git checkout -b ${data.branchName}`;
102
-
103
- logger.debug("Executing command: " + command);
86
+ try {
87
+ await this.runGitCommand(data.repoPath, [
88
+ "rev-parse",
89
+ "--verify",
90
+ data.branchName,
91
+ ]);
92
+ await this.runGitCommand(data.repoPath, ["checkout", data.branchName]);
93
+ } catch (error) {
94
+ logger.debug(
95
+ `Branch ${data.branchName} not found. Creating a new branch instead.`,
96
+ );
104
97
 
105
- const stdout: string = await Execute.executeCommand(command);
98
+ logger.debug(error);
106
99
 
107
- logger.debug(stdout);
100
+ await this.runGitCommand(data.repoPath, [
101
+ "checkout",
102
+ "-b",
103
+ data.branchName,
104
+ ]);
105
+ }
108
106
  }
109
107
 
110
108
  @CaptureSpan()
@@ -112,15 +110,12 @@ export default class CodeRepositoryUtil {
112
110
  repoPath: string;
113
111
  filePath: string;
114
112
  }): Promise<string> {
115
- const path: string = LocalFile.sanitizeFilePath(
116
- `${data.repoPath}/${data.filePath}`,
113
+ const absolutePath: string = this.resolvePathWithinRepo(
114
+ data.repoPath,
115
+ data.filePath,
117
116
  );
118
117
 
119
- const command: string = `cat ${path}`;
120
-
121
- logger.debug("Executing command: " + command);
122
-
123
- return Execute.executeCommand(`${command}`);
118
+ return LocalFile.read(absolutePath);
124
119
  }
125
120
 
126
121
  // discard all changes in the working directory
@@ -128,13 +123,7 @@ export default class CodeRepositoryUtil {
128
123
  public static async discardChanges(data: {
129
124
  repoPath: string;
130
125
  }): Promise<void> {
131
- const command: string = `cd ${data.repoPath} && git checkout .`;
132
-
133
- logger.debug("Executing command: " + command);
134
-
135
- const stdout: string = await Execute.executeCommand(command);
136
-
137
- logger.debug(stdout);
126
+ await this.runGitCommand(data.repoPath, ["checkout", "."]);
138
127
  }
139
128
 
140
129
  @CaptureSpan()
@@ -183,13 +172,9 @@ export default class CodeRepositoryUtil {
183
172
  `${data.repoPath}/${data.directoryPath}`,
184
173
  );
185
174
 
186
- const command: string = `rm -rf ${totalPath}`;
175
+ logger.debug("Deleting directory: " + totalPath);
187
176
 
188
- logger.debug("Executing command: " + command);
189
-
190
- const stdout: string = await Execute.executeCommand(command);
191
-
192
- logger.debug(stdout);
177
+ await LocalFile.deleteDirectory(totalPath);
193
178
  }
194
179
 
195
180
  @CaptureSpan()
@@ -197,11 +182,15 @@ export default class CodeRepositoryUtil {
197
182
  repoPath: string;
198
183
  branchName: string;
199
184
  }): Promise<void> {
200
- const command: string = `cd ${data.repoPath} && git checkout -b ${data.branchName}`;
201
-
202
- logger.debug("Executing command: " + command);
185
+ logger.debug(
186
+ `Creating git branch '${data.branchName}' in ${path.resolve(data.repoPath)}`,
187
+ );
203
188
 
204
- const stdout: string = await Execute.executeCommand(command);
189
+ const stdout: string = await this.runGitCommand(data.repoPath, [
190
+ "checkout",
191
+ "-b",
192
+ data.branchName,
193
+ ]);
205
194
 
206
195
  logger.debug(stdout);
207
196
  }
@@ -211,11 +200,14 @@ export default class CodeRepositoryUtil {
211
200
  repoPath: string;
212
201
  branchName: string;
213
202
  }): Promise<void> {
214
- const command: string = `cd ${data.repoPath} && git checkout ${data.branchName}`;
215
-
216
- logger.debug("Executing command: " + command);
203
+ logger.debug(
204
+ `Checking out git branch '${data.branchName}' in ${path.resolve(data.repoPath)}`,
205
+ );
217
206
 
218
- const stdout: string = await Execute.executeCommand(command);
207
+ const stdout: string = await this.runGitCommand(data.repoPath, [
208
+ "checkout",
209
+ data.branchName,
210
+ ]);
219
211
 
220
212
  logger.debug(stdout);
221
213
  }
@@ -225,22 +217,51 @@ export default class CodeRepositoryUtil {
225
217
  repoPath: string;
226
218
  filePaths: Array<string>;
227
219
  }): Promise<void> {
228
- const filePaths: Array<string> = data.filePaths.map((filePath: string) => {
229
- if (filePath.startsWith("/")) {
230
- // remove the leading slash and return
231
- return filePath.substring(1);
220
+ const repoRoot: string = path.resolve(data.repoPath);
221
+
222
+ const sanitizedRelativeFilePaths: Array<string> = [];
223
+
224
+ for (const inputFilePath of data.filePaths) {
225
+ const normalizedPath: string = inputFilePath.startsWith("/")
226
+ ? inputFilePath.substring(1)
227
+ : inputFilePath;
228
+
229
+ if (normalizedPath.trim() === "") {
230
+ continue;
232
231
  }
233
232
 
234
- return filePath;
235
- });
233
+ const absoluteFilePath: string = this.resolvePathWithinRepo(
234
+ data.repoPath,
235
+ normalizedPath,
236
+ );
237
+
238
+ const relativeFilePath: string = path.relative(
239
+ repoRoot,
240
+ absoluteFilePath,
241
+ );
236
242
 
237
- const command: string = `cd ${
238
- data.repoPath
239
- } && git add ${filePaths.join(" ")}`;
243
+ if (relativeFilePath.trim() === "") {
244
+ continue;
245
+ }
246
+
247
+ sanitizedRelativeFilePaths.push(
248
+ LocalFile.sanitizeFilePath(relativeFilePath),
249
+ );
250
+ }
240
251
 
241
- logger.debug("Executing command: " + command);
252
+ if (sanitizedRelativeFilePaths.length === 0) {
253
+ logger.debug("git add skipped because no file paths were provided");
254
+ return;
255
+ }
256
+
257
+ logger.debug(
258
+ `Adding ${sanitizedRelativeFilePaths.length} file(s) to git in ${path.resolve(data.repoPath)}`,
259
+ );
242
260
 
243
- const stdout: string = await Execute.executeCommand(command);
261
+ const stdout: string = await this.runGitCommand(data.repoPath, [
262
+ "add",
263
+ ...sanitizedRelativeFilePaths,
264
+ ]);
244
265
 
245
266
  logger.debug(stdout);
246
267
  }
@@ -250,11 +271,13 @@ export default class CodeRepositoryUtil {
250
271
  repoPath: string;
251
272
  username: string;
252
273
  }): Promise<void> {
253
- const command: string = `cd ${data.repoPath} && git config user.name "${data.username}"`;
274
+ logger.debug(`Setting git user.name in ${path.resolve(data.repoPath)}`);
254
275
 
255
- logger.debug("Executing command: " + command);
256
-
257
- const stdout: string = await Execute.executeCommand(command);
276
+ const stdout: string = await this.runGitCommand(data.repoPath, [
277
+ "config",
278
+ "user.name",
279
+ data.username,
280
+ ]);
258
281
 
259
282
  logger.debug(stdout);
260
283
  }
@@ -264,11 +287,13 @@ export default class CodeRepositoryUtil {
264
287
  repoPath: string;
265
288
  message: string;
266
289
  }): Promise<void> {
267
- const command: string = `cd ${data.repoPath} && git commit -m "${data.message}"`;
268
-
269
- logger.debug("Executing command: " + command);
290
+ logger.debug("Executing git commit");
270
291
 
271
- const stdout: string = await Execute.executeCommand(command);
292
+ const stdout: string = await Execute.executeCommandFile({
293
+ command: "git",
294
+ args: ["commit", "-m", data.message],
295
+ cwd: data.repoPath,
296
+ });
272
297
 
273
298
  logger.debug(stdout);
274
299
  }
@@ -288,15 +313,28 @@ export default class CodeRepositoryUtil {
288
313
 
289
314
  const { repoPath, filePath } = data;
290
315
 
291
- const command: string = `cd ${repoPath} && git log -1 --pretty=format:"%H" ".${filePath}"`;
316
+ const repoRoot: string = path.resolve(repoPath);
317
+ const absoluteTarget: string = this.resolvePathWithinRepo(
318
+ repoPath,
319
+ filePath,
320
+ );
321
+ const relativeTarget: string = path.relative(repoRoot, absoluteTarget);
322
+ const gitArgument: string = LocalFile.sanitizeFilePath(
323
+ `./${relativeTarget}`,
324
+ );
292
325
 
293
- logger.debug("Executing command: " + command);
326
+ logger.debug(`Getting last commit hash for ${gitArgument} in ${repoRoot}`);
294
327
 
295
- const hash: string = await Execute.executeCommand(command);
328
+ const hash: string = await this.runGitCommand(repoRoot, [
329
+ "log",
330
+ "-1",
331
+ "--pretty=format:%H",
332
+ gitArgument,
333
+ ]);
296
334
 
297
335
  logger.debug(hash);
298
336
 
299
- return hash;
337
+ return hash.trim();
300
338
  }
301
339
 
302
340
  @CaptureSpan()
@@ -318,11 +356,11 @@ export default class CodeRepositoryUtil {
318
356
 
319
357
  totalPath = LocalFile.sanitizeFilePath(totalPath); // clean up the path
320
358
 
321
- const output: string = await Execute.executeCommand(`ls ${totalPath}`);
359
+ const entries: Array<fs.Dirent> = await LocalFile.readDirectory(totalPath);
322
360
 
323
- const fileNames: Array<string> = output.split("\n");
324
-
325
- return fileNames;
361
+ return entries.map((entry: fs.Dirent) => {
362
+ return entry.name;
363
+ });
326
364
  }
327
365
 
328
366
  @CaptureSpan()
@@ -350,16 +388,12 @@ export default class CodeRepositoryUtil {
350
388
  totalPath = LocalFile.sanitizeFilePath(totalPath); // clean up the path
351
389
 
352
390
  const files: Dictionary<CodeRepositoryFile> = {};
353
- const output: string = await Execute.executeCommand(`ls ${totalPath}`);
354
-
355
- const fileNames: Array<string> = output.split("\n");
356
-
357
391
  const subDirectories: Array<string> = [];
358
392
 
359
- for (const fileName of fileNames) {
360
- if (fileName === "") {
361
- continue;
362
- }
393
+ const entries: Array<fs.Dirent> = await LocalFile.readDirectory(totalPath);
394
+
395
+ for (const entry of entries) {
396
+ const fileName: string = entry.name;
363
397
 
364
398
  const filePath: string = LocalFile.sanitizeFilePath(
365
399
  `${directoryPath}/${fileName}`,
@@ -369,13 +403,7 @@ export default class CodeRepositoryUtil {
369
403
  continue;
370
404
  }
371
405
 
372
- const isDirectory: boolean = (
373
- await Execute.executeCommand(
374
- `file "${LocalFile.sanitizeFilePath(`${totalPath}/${fileName}`)}"`,
375
- )
376
- ).includes("directory");
377
-
378
- if (isDirectory) {
406
+ if (entry.isDirectory()) {
379
407
  subDirectories.push(
380
408
  LocalFile.sanitizeFilePath(`${directoryPath}/${fileName}`),
381
409
  );
@@ -449,4 +477,45 @@ export default class CodeRepositoryUtil {
449
477
 
450
478
  return files;
451
479
  }
480
+
481
+ private static runGitCommand(
482
+ repoPath: string,
483
+ args: Array<string>,
484
+ ): Promise<string> {
485
+ const cwd: string = path.resolve(repoPath);
486
+
487
+ logger.debug(
488
+ `Executing git command in ${cwd}: git ${args
489
+ .map((arg: string) => {
490
+ return arg.includes(" ") ? `"${arg}"` : arg;
491
+ })
492
+ .join(" ")}`,
493
+ );
494
+
495
+ return Execute.executeCommandFile({
496
+ command: "git",
497
+ args,
498
+ cwd,
499
+ });
500
+ }
501
+
502
+ private static resolvePathWithinRepo(
503
+ repoPath: string,
504
+ targetPath: string,
505
+ ): string {
506
+ const root: string = path.resolve(repoPath);
507
+ const sanitizedTarget: string = LocalFile.sanitizeFilePath(
508
+ targetPath,
509
+ ).replace(/^\/+/, "");
510
+ const absoluteTarget: string = path.resolve(root, sanitizedTarget);
511
+
512
+ if (
513
+ absoluteTarget !== root &&
514
+ !absoluteTarget.startsWith(root + path.sep)
515
+ ) {
516
+ throw new BadDataException("File path is outside the repository");
517
+ }
518
+
519
+ return absoluteTarget;
520
+ }
452
521
  }
@@ -170,13 +170,15 @@ export default class GitHubUtil extends HostedCodeRepository {
170
170
  `https://github.com/${data.organizationName}/${data.repositoryName}.git`,
171
171
  );
172
172
 
173
- const command: string = `git remote add ${
174
- data.remoteName
175
- } ${url.toString()}`;
176
-
177
- logger.debug("Executing command: " + command);
173
+ logger.debug(
174
+ `Adding remote '${data.remoteName}' for ${data.organizationName}/${data.repositoryName}`,
175
+ );
178
176
 
179
- const result: string = await Execute.executeCommand(command);
177
+ const result: string = await Execute.executeCommandFile({
178
+ command: "git",
179
+ args: ["remote", "add", data.remoteName, url.toString()],
180
+ cwd: process.cwd(),
181
+ });
180
182
 
181
183
  logger.debug(result);
182
184
  }
@@ -197,10 +199,19 @@ export default class GitHubUtil extends HostedCodeRepository {
197
199
  "Pushing changes to remote repository with username: " + username,
198
200
  );
199
201
 
200
- const command: string = `cd ${data.repoPath} && git push -u https://${username}:${password}@github.com/${data.organizationName}/${data.repositoryName}.git ${branchName}`;
201
- logger.debug("Executing command: " + command);
202
+ const encodedUsername: string = encodeURIComponent(username);
203
+ const encodedPassword: string = encodeURIComponent(password);
204
+ const remoteUrl: string = `https://${encodedUsername}:${encodedPassword}@github.com/${data.organizationName}/${data.repositoryName}.git`;
202
205
 
203
- const result: string = await Execute.executeCommand(command);
206
+ logger.debug(
207
+ `Pushing branch '${branchName}' to ${data.organizationName}/${data.repositoryName}`,
208
+ );
209
+
210
+ const result: string = await Execute.executeCommandFile({
211
+ command: "git",
212
+ args: ["push", "-u", remoteUrl, branchName],
213
+ cwd: data.repoPath,
214
+ });
204
215
 
205
216
  logger.debug(result);
206
217
  }