autoforce 0.1.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.
@@ -0,0 +1,488 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { executeShell, getOrganizationObject, getCurrentOrganization, getBranchName, getTargetOrg } from "./taskFunctions.js";
11
+ import { convertNameToKey, convertKeyToName, getFiles, filterDirectory, addNewItems } from "./util.js";
12
+ import { GitHubApi } from "./github-graphql.js";
13
+ import { GitLabApi } from "./gitlab-graphql.js";
14
+ import prompts from "prompts";
15
+ import matter from 'gray-matter';
16
+ import fs from "fs";
17
+ import { logError } from "./color.js";
18
+ const filterProcesses = (fullPath) => fullPath.endsWith(".md"); // && !fullPath.endsWith("intro.md")
19
+ const ISSUES_TYPES = [{ value: 'feature', title: 'feature' }, { value: 'bug', title: 'bug' }, { value: 'documentation', title: 'documentation' }, { value: 'automation', title: 'automation' }];
20
+ const CONFIG_FILE = process.cwd() + '/.autoforce.json';
21
+ export function createConfigurationFile() {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ console.log('Preguntar por GitHub o GitLab');
24
+ console.log('Chequear las variables de entorno');
25
+ console.log('Tema proyecto guardar la referencia');
26
+ console.log('Genera documentacion');
27
+ console.log('Direccion de las carpetas');
28
+ const config = { projectNumber: 1 };
29
+ try {
30
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
31
+ }
32
+ catch (_a) {
33
+ throw new Error(`No se pudo guardar la configuracion en ${CONFIG_FILE}`);
34
+ }
35
+ return true;
36
+ });
37
+ }
38
+ class Context {
39
+ constructor() {
40
+ this.isGitApi = false;
41
+ this.sfInstalled = true;
42
+ this.sfToken = true;
43
+ this.defaultDias = 7;
44
+ this.isVerbose = false;
45
+ this.projectPath = process.cwd();
46
+ this.existNewBranch = false;
47
+ // Ultima salida del shell
48
+ this.salida = '';
49
+ }
50
+ loadGitApi() {
51
+ if (!this.repositoryOwner || !this.repositoryRepo || !this.repositoryUrl) {
52
+ throw new Error("Falta agregue repository en el package.json para obtener el Owner or Repo");
53
+ }
54
+ const isGithub = this.repositoryUrl.indexOf('github') > 0;
55
+ const isGitlab = this.repositoryUrl.indexOf('gitlab') > 0;
56
+ if (isGithub && process.env.GITHUB_TOKEN) {
57
+ const token = process.env.GITHUB_TOKEN;
58
+ this.gitApi = new GitHubApi(token, this.repositoryOwner, this.repositoryRepo, this.projectNumber);
59
+ this.isGitApi = true;
60
+ }
61
+ if (isGitlab && process.env.GITLAB_TOKEN) {
62
+ const token = process.env.GITLAB_TOKEN;
63
+ this.gitApi = new GitLabApi(token, this.repositoryOwner, this.repositoryRepo, this.projectNumber);
64
+ this.isGitApi = true;
65
+ }
66
+ }
67
+ loadPackage() {
68
+ try {
69
+ const filename = this.projectPath + "/package.json";
70
+ const content = fs.readFileSync(filename, "utf8");
71
+ const packageJson = JSON.parse(content);
72
+ if (packageJson.repository) {
73
+ if (packageJson.repository.url) {
74
+ this.repositoryUrl = packageJson.repository.url;
75
+ this.repositoryType = packageJson.repository.type;
76
+ // Ver de sacar repo y owner
77
+ if (this.repositoryUrl && this.repositoryUrl.includes("github.com")) {
78
+ const repositoryArray = this.repositoryUrl.split('github.com/');
79
+ [this.repositoryOwner, this.repositoryRepo] = repositoryArray[1].split('/');
80
+ }
81
+ }
82
+ else if (typeof packageJson.repository === 'string') {
83
+ this.repositoryUrl = packageJson.repository;
84
+ const repositoryArray = this.repositoryUrl.split(':');
85
+ this.repositoryType = repositoryArray[0];
86
+ [this.repositoryOwner, this.repositoryRepo] = repositoryArray[1].split('/');
87
+ }
88
+ if (this.repositoryRepo && this.repositoryRepo.endsWith('.git')) {
89
+ this.repositoryRepo = this.repositoryRepo.replace('.git', '');
90
+ }
91
+ }
92
+ }
93
+ catch (error) {
94
+ console.log(error);
95
+ throw new Error(`Verifique que exista y sea valido el package.json`);
96
+ }
97
+ }
98
+ loadConfig() {
99
+ if (!fs.existsSync(CONFIG_FILE)) {
100
+ logError('Aun no ha configurado autoforce, lo puede hacer mas tarde manualmente creando .autoforce.json en el root del proyecto o asisitido corriendo yarn init autoforce. O bien puede hacerlo ahora mismo :) ');
101
+ createConfigurationFile();
102
+ return;
103
+ }
104
+ const content = fs.readFileSync(CONFIG_FILE, "utf8");
105
+ try {
106
+ const config = JSON.parse(content);
107
+ for (const key in config) {
108
+ this.set(key, config[key]);
109
+ }
110
+ }
111
+ catch (_a) {
112
+ throw new Error(`Verifique que el ${CONFIG_FILE} sea json valido`);
113
+ }
114
+ }
115
+ init() {
116
+ // Busca variables de entorno
117
+ this.loadPackage();
118
+ this.loadConfig();
119
+ this.loadGitApi();
120
+ //
121
+ this.branchName = getBranchName();
122
+ if (typeof this.branchName === 'string') {
123
+ this.issueFromBranch(this.branchName);
124
+ }
125
+ }
126
+ get targetOrg() {
127
+ if (!this._targetOrg) {
128
+ this._targetOrg = getTargetOrg();
129
+ }
130
+ return this._targetOrg;
131
+ }
132
+ get existBranchScratch() {
133
+ return typeof this._branchScratch !== 'undefined';
134
+ }
135
+ get branchScratch() {
136
+ if (!this._branchScratch && this.branchName) {
137
+ this._branchScratch = getOrganizationObject(this.branchName);
138
+ }
139
+ return this._branchScratch;
140
+ }
141
+ getProcessHeader(fullpath) {
142
+ const fileContents = fs.readFileSync(fullpath, 'utf8');
143
+ const { data } = matter(fileContents);
144
+ return data;
145
+ }
146
+ addProcessMetadata(component, items) {
147
+ if (!this.process) {
148
+ throw new Error(`No hay proceso configurado`);
149
+ }
150
+ const content = fs.readFileSync(CONFIG_FILE, "utf8");
151
+ try {
152
+ const config = JSON.parse(content);
153
+ const processes = config.processes || {};
154
+ if (!processes[this.process]) {
155
+ processes[this.process] = {};
156
+ }
157
+ if (!processes[this.process][component]) {
158
+ processes[this.process][component] = [];
159
+ }
160
+ addNewItems(processes[this.process][component], items);
161
+ config.processes = processes;
162
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
163
+ }
164
+ catch (_a) {
165
+ throw new Error(`No se pudo guardar la metadata`);
166
+ }
167
+ }
168
+ get processesHeader() {
169
+ if (!this._processesHeader) {
170
+ this._processesHeader = {};
171
+ const folders = getFiles(process.cwd() + "/docs", filterDirectory, true, ['diccionarios']);
172
+ for (const folder of folders) {
173
+ const fullpath = `${process.cwd()}/docs/${folder}`;
174
+ const filenames = getFiles(fullpath, filterProcesses);
175
+ for (const filename of filenames) {
176
+ const header = this.getProcessHeader(fullpath + "/" + filename);
177
+ if (header.process) {
178
+ this._processesHeader[header.process] = Object.assign(Object.assign({}, header), { folder: fullpath, filename });
179
+ }
180
+ }
181
+ }
182
+ }
183
+ return this._processesHeader;
184
+ }
185
+ // TODO: merge con getProcessFromDocs
186
+ getProcessMetadata() {
187
+ const folders = getFiles(process.cwd() + "/docs", filterDirectory, true, ['diccionarios']);
188
+ const retArray = [];
189
+ for (const folder of folders) {
190
+ const fullpath = `${process.cwd()}/docs/${folder}`;
191
+ const processes = getFiles(fullpath, filterProcesses);
192
+ for (const process of processes) {
193
+ const header = this.getProcessHeader(fullpath + "/" + process);
194
+ const processKey = convertNameToKey(header.slug || header.title || process);
195
+ if (this.processes && this.processes[processKey]) {
196
+ retArray.push(Object.assign({ folder, name: convertKeyToName(processKey) }, this.processes[processKey]));
197
+ }
198
+ }
199
+ }
200
+ return retArray;
201
+ }
202
+ getModules() {
203
+ return getFiles(process.cwd() + "/docs", filterDirectory, false, ['diccionarios']);
204
+ }
205
+ get modules() {
206
+ return this.getModules().map(module => { return { value: module, title: module }; });
207
+ }
208
+ get existScratch() {
209
+ return typeof this.scratch !== 'undefined';
210
+ }
211
+ get scratch() {
212
+ if (!this._scratch) {
213
+ this._scratch = getCurrentOrganization();
214
+ }
215
+ return this._scratch;
216
+ }
217
+ validate(guards) {
218
+ return __awaiter(this, void 0, void 0, function* () {
219
+ for (const guard of guards) {
220
+ const value = yield this.get(guard);
221
+ if (!value) {
222
+ throw new Error(`No se encontro la variable ${guard} en el contexto. Ejecute yarn auto config o lea el index.md para mas informacion.`);
223
+ }
224
+ }
225
+ });
226
+ }
227
+ issueFromBranch(branchName) {
228
+ const branchSplit = branchName.split("/");
229
+ if (branchSplit.length > 1) {
230
+ this.issueType = branchSplit[0];
231
+ if (!Number.isNaN(Number(branchSplit[1]))) {
232
+ this.issueNumber = parseInt(branchSplit[1]);
233
+ }
234
+ else {
235
+ // [this.issueNumber, this.issueTitle] = branchSplit[1].split() // /^([^ -]+)[ -](.*)$/.exec( branchSplit[1]).slice(1);
236
+ }
237
+ }
238
+ }
239
+ branchNameFromIssue(issueType, issueNumber, title) {
240
+ let baseName = issueType + '/' + issueNumber;
241
+ if (title) {
242
+ baseName += ' - ' + title.replaceAll(' ', '-');
243
+ }
244
+ return baseName;
245
+ }
246
+ get isDevelopment() {
247
+ return this.issueType === 'feature' || this.issueType === 'fix';
248
+ }
249
+ get isNewDevelopment() {
250
+ return this.newIssueType === 'feature' || this.newIssueType === 'fix';
251
+ }
252
+ get newIssueNumber() {
253
+ return this._newIssueNumber;
254
+ }
255
+ set newIssueNumber(value) {
256
+ this._newIssueNumber = value;
257
+ if (this.newIssueType) {
258
+ this.setNewBranchName();
259
+ }
260
+ }
261
+ get newIssueType() {
262
+ return this._newIssueType;
263
+ }
264
+ set newIssueType(value) {
265
+ this._newIssueType = value;
266
+ if (this.newIssueNumber) {
267
+ this.setNewBranchName();
268
+ }
269
+ }
270
+ setNewBranchName() {
271
+ if (this.newIssueType && this.newIssueNumber) {
272
+ this.newBranchName = this.branchNameFromIssue(this.newIssueType, this.newIssueNumber);
273
+ const salida = executeShell(`git show-ref refs/heads/${this.newBranchName}`);
274
+ this.existNewBranch = typeof salida === 'string' && (salida.includes(this.newBranchName));
275
+ }
276
+ }
277
+ askFornewBranchName() {
278
+ return __awaiter(this, void 0, void 0, function* () {
279
+ if (!this.newBranchName) {
280
+ if (!this.newIssueType) {
281
+ this.newIssueType = yield this.askFornewIssueType();
282
+ }
283
+ if (!this.newIssueNumber) {
284
+ this.newIssueNumber = yield this.askFornewIssueNumber();
285
+ }
286
+ this.setNewBranchName();
287
+ }
288
+ return this.newBranchName;
289
+ });
290
+ }
291
+ askFornewIssueNumber() {
292
+ return __awaiter(this, void 0, void 0, function* () {
293
+ const answer = yield prompts([
294
+ {
295
+ type: "text",
296
+ name: "newIssueNumber",
297
+ message: "Por favor ingrese el nuevo issueNumber?"
298
+ }
299
+ ]);
300
+ return answer.newIssueNumber;
301
+ });
302
+ }
303
+ set process(value) {
304
+ this._process = value;
305
+ }
306
+ getProcessFromTitle(title) {
307
+ const desde = title.indexOf('[');
308
+ const hasta = title.indexOf(']', desde);
309
+ if (desde !== -1 && hasta !== -1) {
310
+ return title.substring(desde + 1, hasta);
311
+ }
312
+ return;
313
+ }
314
+ get process() {
315
+ if (!this._process && this.issueTitle) {
316
+ const process = this.getProcessFromTitle(this.issueTitle);
317
+ if (process) {
318
+ this._process = process;
319
+ }
320
+ }
321
+ return this._process;
322
+ }
323
+ askForprocess() {
324
+ return __awaiter(this, void 0, void 0, function* () {
325
+ if (this.projectApi && !this.issueTitle && this.issueNumber) {
326
+ const issue = yield this.projectApi.getIssueObject(this.issueNumber);
327
+ this.issueTitle = issue.title;
328
+ }
329
+ if (this.issueTitle) {
330
+ const process = this.getProcessFromTitle(this.issueTitle);
331
+ if (process && this.processesHeader[process]) {
332
+ return process;
333
+ }
334
+ }
335
+ const choices = Object.values(this.processesHeader).map(header => {
336
+ return { value: header.process, title: header.title };
337
+ });
338
+ const answer = yield prompts([{
339
+ type: "select",
340
+ name: "process",
341
+ message: "Por favor seleccione el proceso",
342
+ choices
343
+ }]);
344
+ return answer.process;
345
+ });
346
+ }
347
+ askFornewIssueType() {
348
+ return __awaiter(this, void 0, void 0, function* () {
349
+ const answer = yield prompts([
350
+ {
351
+ type: "list",
352
+ name: "newIssueType",
353
+ initial: "feature",
354
+ message: "Por favor ingrese el type del issue?",
355
+ choices: ISSUES_TYPES
356
+ }
357
+ ]);
358
+ return answer.newIssueType;
359
+ });
360
+ }
361
+ convertToArrayOfInputs(inputs) {
362
+ let inputsArray = [];
363
+ if (Array.isArray(inputs)) {
364
+ // Si viene los args como ['name1', 'names] lo convierte a [{name: 'name1'}, {name: 'name2'}]
365
+ inputsArray = inputs.map(input => { return { name: input, type: 'text', message: `Por favor ingrese ${input}?` }; });
366
+ }
367
+ else {
368
+ // Si viene args como objeto { name1: {...}, name2: {...}} lo convierte a [{name: name1...}, {name: name2...}]
369
+ for (const key in inputs) {
370
+ let initial = typeof inputs[key].default == 'string' ? inputs[key].default : undefined;
371
+ if (initial !== undefined) {
372
+ initial = this.merge(initial);
373
+ }
374
+ inputsArray.push(Object.assign({ name: key, type: 'text', initial, message: `Por favor ingrese ${key}?` }, inputs[key]));
375
+ }
376
+ }
377
+ return inputsArray;
378
+ }
379
+ askForExit() {
380
+ return __awaiter(this, void 0, void 0, function* () {
381
+ const answer = yield prompts([
382
+ {
383
+ type: "confirm",
384
+ name: "exit",
385
+ initial: true,
386
+ message: "Desea salir?"
387
+ }
388
+ ]);
389
+ if (answer.exit) {
390
+ process.exit(-1);
391
+ }
392
+ });
393
+ }
394
+ mergeArgs(args) {
395
+ if (Array.isArray(args)) {
396
+ const argsArray = [];
397
+ for (const argName of args) {
398
+ if (typeof argName === 'string') {
399
+ argsArray.push(this.merge(argName));
400
+ }
401
+ }
402
+ return argsArray;
403
+ }
404
+ else if (typeof args === 'object') {
405
+ const argsObject = {};
406
+ for (const argName in args) {
407
+ argsObject[argName] = this.merge(args[argName]);
408
+ }
409
+ return argsObject;
410
+ }
411
+ throw new Error(`Los argumentos ${args} son incompatibles para el merge`);
412
+ }
413
+ askForArguments(inputs) {
414
+ return __awaiter(this, void 0, void 0, function* () {
415
+ // unifica los dos tipos de inputs (array y objeto) en un array de inputs
416
+ const inputsArray = this.convertToArrayOfInputs(inputs);
417
+ for (const input of inputsArray) {
418
+ const hasValue = yield this.get(input.name);
419
+ if (!hasValue) {
420
+ const answer = yield prompts([input], { onCancel: this.askForExit });
421
+ this[input.name] = answer[input.name];
422
+ }
423
+ }
424
+ });
425
+ }
426
+ setObject(obj) {
427
+ for (const field in obj) {
428
+ Object.defineProperty(this, field, obj[field]);
429
+ }
430
+ }
431
+ set(key, value) {
432
+ try {
433
+ this[key] = value;
434
+ }
435
+ catch (_a) {
436
+ throw new Error(`No se puede setear el ${key} con el valor ${value} en context`);
437
+ }
438
+ }
439
+ // Devuelve el valor o hace un askFor si esta vacio
440
+ get(key) {
441
+ return __awaiter(this, void 0, void 0, function* () {
442
+ try {
443
+ const value = this[key];
444
+ if (!value) {
445
+ const askForMethod = 'askFor' + key;
446
+ if (this[askForMethod] && typeof this[askForMethod] == 'function') {
447
+ this[key] = yield this[askForMethod]();
448
+ }
449
+ }
450
+ return this[key];
451
+ }
452
+ catch (_a) {
453
+ throw new Error(`No se puedo obtener la propiedad ${key} en context`);
454
+ }
455
+ });
456
+ }
457
+ merge(text) {
458
+ if (typeof text != 'string' || text.indexOf('${') === -1) {
459
+ return text;
460
+ }
461
+ const matches = text.matchAll(/\$\{([^}]+)}/g);
462
+ // si no tiene para merge
463
+ if (matches === null) {
464
+ return text;
465
+ }
466
+ // si es un texto con merges
467
+ for (const match of matches) {
468
+ const mergedValue = this[match[1]];
469
+ // si es una sola variable
470
+ if (match.index == 0 && text === match[0]) {
471
+ return mergedValue;
472
+ }
473
+ if (typeof mergedValue === 'string') {
474
+ text = text.replace(match[0], mergedValue);
475
+ }
476
+ else if (typeof mergedValue === 'number' || typeof mergedValue === 'boolean') {
477
+ text = text.replace(match[0], mergedValue.toString());
478
+ }
479
+ else {
480
+ throw new Error(`La propiedad '${match[1]}' del objeto context no es mergeable`);
481
+ }
482
+ }
483
+ return text;
484
+ }
485
+ }
486
+ const context = new Context();
487
+ context.init();
488
+ export default context;
@@ -0,0 +1,76 @@
1
+ export declare class GitHubApi implements IGitApi, IProjectApi {
2
+ repoVar: {
3
+ owner: string;
4
+ repo: string;
5
+ };
6
+ projectNumber: number | undefined;
7
+ graphqlAuth: import("@octokit/graphql/types").graphql;
8
+ constructor(token: string, owner: string, repo: string, projectNumber?: number);
9
+ getUser(): Promise<{
10
+ login: string;
11
+ id: number;
12
+ }>;
13
+ getRepository(label?: string): Promise<{
14
+ id: string;
15
+ label?: {
16
+ id: string;
17
+ } | undefined;
18
+ projectV2: {
19
+ id: string;
20
+ field: {
21
+ id: string;
22
+ name: string;
23
+ options: {
24
+ name: string;
25
+ id: string;
26
+ }[];
27
+ };
28
+ };
29
+ }>;
30
+ createPullRequest(branchName: string, title: string, body: string): Promise<boolean>;
31
+ getColumnValueMap(): Promise<Record<string, string>>;
32
+ createIssue(title: string, state?: string, label?: string, milestone?: string, body?: string): Promise<number>;
33
+ moveIssue(issueNumber: number, state: string): Promise<boolean>;
34
+ assignIssueToMe(issueNumber: number): Promise<boolean>;
35
+ getCommit(commitSha: string): Promise<{
36
+ id: string;
37
+ oid: string;
38
+ }>;
39
+ assignBranchToIssue(issueNumber: number, branchName: string, commitSha: string): Promise<boolean>;
40
+ getIssueState(issueNumber: number): Promise<string>;
41
+ getIssueName(title: string): string;
42
+ getIssueObject(issueNumber: number): Promise<IIssueObject>;
43
+ getIssue(issueNumber: number): Promise<{
44
+ id: string;
45
+ title: string;
46
+ labels: {
47
+ nodes: {
48
+ name: string;
49
+ color: string;
50
+ }[];
51
+ };
52
+ projectItems: {
53
+ nodes: {
54
+ id: string;
55
+ project: {
56
+ id: string;
57
+ };
58
+ fieldValueByName: {
59
+ name: string;
60
+ id: string;
61
+ field: {
62
+ id: string;
63
+ };
64
+ };
65
+ }[];
66
+ };
67
+ linkedBranches: {
68
+ nodes: {
69
+ ref: {
70
+ id: string;
71
+ name: string;
72
+ };
73
+ }[];
74
+ };
75
+ }>;
76
+ }