@hiiretail/gcp-infra-generators 1.0.0 → 1.0.2

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.
Files changed (74) hide show
  1. package/dist/generators/clan-resources/clan-project/index.js +89 -189
  2. package/dist/generators/common-resources/bigquery/index.js +172 -267
  3. package/dist/generators/common-resources/budget/index.js +67 -153
  4. package/dist/generators/common-resources/cloud-armor/index.js +17 -167
  5. package/dist/generators/common-resources/cloud-storage/index.js +96 -205
  6. package/dist/generators/common-resources/cloudsql/index.js +71 -177
  7. package/dist/generators/common-resources/cloudsql-database/index.js +40 -287
  8. package/dist/generators/common-resources/confluent-cluster/index.js +23 -132
  9. package/dist/generators/common-resources/datastore/index.js +48 -194
  10. package/dist/generators/common-resources/elastic-cloud/index.js +22 -132
  11. package/dist/generators/common-resources/elastic-index-policy/handle-yaml.js +76 -0
  12. package/dist/generators/common-resources/elastic-index-policy/index.js +131 -286
  13. package/dist/generators/common-resources/elastic-template/index.js +52 -162
  14. package/dist/generators/common-resources/firestore/index.js +93 -233
  15. package/dist/generators/common-resources/iam/index.js +35 -157
  16. package/dist/generators/common-resources/iam/valid-prefix.js +8 -0
  17. package/dist/generators/common-resources/kafka-connect/index.js +35 -144
  18. package/dist/generators/common-resources/kafka-topics/index.js +20 -129
  19. package/dist/generators/common-resources/kms/index.js +31 -141
  20. package/dist/generators/common-resources/memorystore/index.js +42 -328
  21. package/dist/generators/common-resources/monitoring/handle-yaml.js +49 -0
  22. package/dist/generators/common-resources/monitoring/index.js +144 -322
  23. package/dist/generators/common-resources/monitoring/templates/alerts/generic-infra.yaml +37 -2
  24. package/dist/generators/common-resources/monitoring/validate.js +58 -0
  25. package/dist/generators/common-resources/pubsub/append.js +130 -0
  26. package/dist/generators/common-resources/pubsub/get-gcp-projects.js +34 -0
  27. package/dist/generators/common-resources/pubsub/handle-subscribers.js +68 -0
  28. package/dist/generators/common-resources/pubsub/index.js +194 -536
  29. package/dist/generators/common-resources/pubsub/validate.js +53 -0
  30. package/dist/generators/common-resources/scheduler/append.js +85 -0
  31. package/dist/generators/common-resources/scheduler/index.js +62 -249
  32. package/dist/generators/common-resources/spanner/append.js +31 -0
  33. package/dist/generators/common-resources/spanner/index.js +102 -269
  34. package/dist/generators/common-resources/spanner/validate.js +38 -0
  35. package/dist/generators/docs/rca/index.js +25 -135
  36. package/dist/generators/docs/runbook/index.js +16 -126
  37. package/dist/generators/docs/srb/index.js +33 -147
  38. package/dist/generators/docs/srb/run-docker.js +2 -0
  39. package/dist/generators/init/clan-infra/gcp-projects.js +47 -0
  40. package/dist/generators/init/clan-infra/index.js +95 -290
  41. package/dist/generators/init/clan-infra/tribe-clan-repo.js +38 -0
  42. package/dist/generators/init/clan-infra/validate.js +8 -0
  43. package/dist/generators/maintenance/manage-states/index.js +142 -219
  44. package/dist/generators/maintenance/update-modules/index.js +56 -155
  45. package/dist/generators/organization/clan-project/googlecloud.js +124 -0
  46. package/dist/generators/organization/clan-project/index.js +81 -303
  47. package/dist/node_modules/.package-lock.json +88 -31
  48. package/dist/node_modules/@google-cloud/storage/build/cjs/src/bucket.js +5 -5
  49. package/dist/node_modules/@google-cloud/storage/build/cjs/src/file.d.ts +1 -0
  50. package/dist/node_modules/@google-cloud/storage/build/cjs/src/file.js +10 -1
  51. package/dist/node_modules/@google-cloud/storage/build/cjs/src/storage.js +1 -1
  52. package/dist/node_modules/@google-cloud/storage/build/cjs/src/transfer-manager.d.ts +4 -4
  53. package/dist/node_modules/@google-cloud/storage/build/cjs/src/transfer-manager.js +4 -4
  54. package/dist/node_modules/@google-cloud/storage/build/cjs/src/util.d.ts +1 -1
  55. package/dist/node_modules/@google-cloud/storage/build/cjs/src/util.js +2 -2
  56. package/dist/node_modules/@google-cloud/storage/build/esm/src/bucket.js +5 -5
  57. package/dist/node_modules/@google-cloud/storage/build/esm/src/file.d.ts +1 -0
  58. package/dist/node_modules/@google-cloud/storage/build/esm/src/file.js +10 -1
  59. package/dist/node_modules/@google-cloud/storage/build/esm/src/storage.js +1 -1
  60. package/dist/node_modules/@google-cloud/storage/build/esm/src/transfer-manager.d.ts +4 -4
  61. package/dist/node_modules/@google-cloud/storage/build/esm/src/transfer-manager.js +4 -4
  62. package/dist/node_modules/@google-cloud/storage/build/esm/src/util.d.ts +1 -1
  63. package/dist/node_modules/@google-cloud/storage/build/esm/src/util.js +2 -2
  64. package/dist/node_modules/@google-cloud/storage/package.json +5 -5
  65. package/dist/package.json +45 -0
  66. package/dist/src/BaseGenerator.js +84 -0
  67. package/dist/src/SecretsGenerator.js +137 -0
  68. package/dist/src/cli.js +54 -255
  69. package/dist/src/dependency-check.js +48 -0
  70. package/dist/src/update-check.js +38 -0
  71. package/dist/src/validators.js +33 -0
  72. package/dist/src/yeoman.js +80 -0
  73. package/package.json +2 -3
  74. package/dist/node_modules/@google-cloud/storage/CHANGELOG.md +0 -1769
@@ -1,353 +1,158 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
4
- var __commonJS = (cb, mod) => function __require() {
5
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
6
- };
7
-
8
- // src/validators.js
9
- var require_validators = __commonJS({
10
- "src/validators.js"(exports2, module2) {
11
- var path2 = require("path");
12
- module2.exports = {
13
- chain: /* @__PURE__ */ __name((input, ...validators) => {
14
- let msg = "";
15
- validators.every((validator) => {
16
- msg = validator(input);
17
- return msg === true;
18
- });
19
- return msg === true ? true : msg;
20
- }, "chain"),
21
- filename: /* @__PURE__ */ __name((input) => {
22
- if (!input) {
23
- return true;
24
- }
25
- return path2.basename(input) === input ? true : "Invalid filename";
26
- }, "filename"),
27
- maxLength: /* @__PURE__ */ __name((input, maxLength) => !input || input.length <= maxLength ? true : `Exceeds max \
28
- length: ${maxLength}`, "maxLength"),
29
- required: /* @__PURE__ */ __name((input) => {
30
- const msg = "Required";
31
- if (Array.isArray(input)) {
32
- return input.length > 0 ? true : msg;
33
- }
34
- if (input) {
35
- return input.trim().length > 0 ? true : msg;
36
- }
37
- return msg;
38
- }, "required")
39
- };
40
- }
41
- });
42
-
43
- // src/BaseGenerator.js
44
- var require_BaseGenerator = __commonJS({
45
- "src/BaseGenerator.js"(exports2, module2) {
46
- var Generator = require("yeoman-generator");
47
- var path2 = require("path");
48
- var fs = require("fs");
49
- var inquirer = require("inquirer");
50
- var { chain: chain2, required: required2, filename } = require_validators();
51
- module2.exports = class extends Generator {
52
- constructor(args, opts) {
53
- super(args, opts);
54
- this.baseDir = path2.resolve(path2.join(__dirname, ".."));
55
- this.destinationRoot(process.cwd());
56
- const [command, generator] = opts.namespace.split(":").slice(-2);
57
- this.generatorId = path2.join(command, generator);
58
- this.sourceRoot(
59
- path2.join(this.baseDir, "generators", this.generatorId, "templates")
60
- );
61
- this.copyDir = (templateDir, targetDir, answers = this.answers, skipIfExists = false) => {
62
- if (skipIfExists && fs.existsSync(targetDir)) {
63
- return;
64
- }
65
- this.fs.copyTpl(
66
- this.templatePath(`${templateDir}/**/*`),
67
- this.destinationPath(targetDir),
68
- answers
69
- );
70
- };
71
- this.listSubDirectories = (parent) => fs.readdirSync(parent).filter((f) => !f.startsWith(".")).filter((f) => fs.
72
- lstatSync(path2.join(parent, f)).isDirectory()).sort((a, b) => a.localeCompare(b));
73
- this.kebabCase = (input) => input.replace(/\s|_/g, "-");
74
- this.chooseOrCreatePrompts = (name, getChoicesDirectory) => [
75
- {
76
- when: /* @__PURE__ */ __name((answers) => fs.existsSync(getChoicesDirectory(answers)), "when"),
77
- type: "list",
78
- name,
79
- message: `Choose ${name}`,
80
- store: true,
81
- choices: /* @__PURE__ */ __name((answers) => [
82
- ...this.listSubDirectories(getChoicesDirectory(answers)),
83
- new inquirer.Separator(),
84
- `Create new ${name}`
85
- ], "choices"),
86
- validate: required2,
87
- filter: this.kebabCase
88
- },
89
- {
90
- when: /* @__PURE__ */ __name((answers) => !fs.existsSync(getChoicesDirectory(answers)) || answers[name] === `\
91
- Create-new-${name}`, "when"),
92
- type: "input",
93
- name: `new-${name}`,
94
- message: `New ${name} name`,
95
- store: false,
96
- validate: /* @__PURE__ */ __name((input) => chain2(input, required2, filename), "validate"),
97
- filter: this.kebabCase
98
- },
99
- {
100
- when: /* @__PURE__ */ __name((answers) => !fs.existsSync(getChoicesDirectory(answers)) || answers[name] === "\
101
- Create-new-tribe", "when"),
102
- type: "input",
103
- name: "costCenter",
104
- message: "Please provide the Cost Center of the Tribe",
105
- store: false,
106
- validate: /* @__PURE__ */ __name((input) => chain2(input, required2, filename), "validate"),
107
- filter: this.kebabCase
108
- }
109
- ];
110
- }
111
- };
112
- }
113
- });
114
-
115
- // generators/init/clan-infra/gcp-projects.js
116
- var require_gcp_projects = __commonJS({
117
- "generators/init/clan-infra/gcp-projects.js"(exports2, module2) {
118
- var gcpProjects = {};
119
- var getGcpProjects2 = /* @__PURE__ */ __name(({ spawnCommandSync }, name) => {
120
- let projects = gcpProjects[name];
121
- if (!projects) {
122
- const result = spawnCommandSync(
123
- "gcloud",
124
- [
125
- "projects",
126
- "list",
127
- `--filter=NAME~${name}`,
128
- "--format=value(PROJECT_ID)"
129
- ],
130
- { encoding: "utf8", stdio: "pipe" }
131
- );
132
- if (result.status !== 0) {
133
- throw new Error(
134
- "gcloud error. Please run 'gcloud auth application-defaults login' and try again."
135
- );
136
- }
137
- projects = result.stdout.split(/\s+/).filter((line) => line.trim().length > 0).sort();
138
- gcpProjects[name] = projects;
139
- }
140
- return projects;
141
- }, "getGcpProjects");
142
- var validateGcpProjects2 = /* @__PURE__ */ __name(({ spawnCommandSync }, name) => {
143
- try {
144
- const projects = getGcpProjects2({ spawnCommandSync }, name);
145
- return projects.length > 0 ? true : "Invalid input. No matching GCP project(s) found.";
146
- } catch (err) {
147
- return err.message;
148
- }
149
- }, "validateGcpProjects");
150
- module2.exports = {
151
- getGcpProjects: getGcpProjects2,
152
- validateGcpProjects: validateGcpProjects2
153
- };
154
- }
155
- });
156
-
157
- // generators/init/clan-infra/tribe-clan-repo.js
158
- var require_tribe_clan_repo = __commonJS({
159
- "generators/init/clan-infra/tribe-clan-repo.js"(exports2, module2) {
160
- var path2 = require("path");
161
- var fs = require("fs");
162
- var getKeyValue = /* @__PURE__ */ __name((content, key) => {
163
- let value = "";
164
- content.split(/\r?\n/).forEach((line) => {
165
- if (line.includes(key)) {
166
- value = line.split("=")[1].replace('"', "").replace('"', "").trim();
167
- }
168
- });
169
- return value;
170
- }, "getKeyValue");
171
- var getTribeAndClanName2 = /* @__PURE__ */ __name(() => {
172
- const repoName = path2.basename(path2.resolve());
173
- const isClanInfraRepo = /^(?:\w+-){2}common(?:$|\W)$/.test(repoName);
174
- let tribe = "";
175
- let clan = "";
176
- if (isClanInfraRepo) {
177
- [tribe, clan] = repoName.split("-");
178
- } else {
179
- const commonHCL = path2.join("infra", "common.hcl");
180
- if (fs.existsSync(commonHCL)) {
181
- const commonHCLContent = fs.readFileSync(commonHCL, "utf8");
182
- tribe = getKeyValue(commonHCLContent, "tribe_name");
183
- clan = getKeyValue(commonHCLContent, "clan_name");
184
- }
185
- }
186
- return {
187
- tribe,
188
- clan
189
- };
190
- }, "getTribeAndClanName");
191
- module2.exports = getTribeAndClanName2;
192
- }
193
- });
1
+ const path = require('path');
2
+ const chalk = require('chalk');
3
+ const BaseGenerator = require('../../../src/BaseGenerator');
4
+ const { chain, required } = require('../../../src/validators');
5
+ const { getGcpProjects, validateGcpProjects } = require('./gcp-projects');
6
+ const getTribeAndClanName = require('./tribe-clan-repo');
7
+ const validate = require('./validate');
194
8
 
195
- // generators/init/clan-infra/validate.js
196
- var require_validate = __commonJS({
197
- "generators/init/clan-infra/validate.js"(exports2, module2) {
198
- var validate2 = {};
199
- validate2.jiraProjectKey = (input) => {
200
- if (!/\s/.test(input) && input === input.toLowerCase()) return true;
201
- return "Must be lowercase and not contain any whitepace";
202
- };
203
- module2.exports = validate2;
204
- }
205
- });
206
-
207
- // generators/init/clan-infra/index.js
208
- var path = require("path");
209
- var chalk = require("chalk");
210
- var BaseGenerator = require_BaseGenerator();
211
- var { chain, required } = require_validators();
212
- var { getGcpProjects, validateGcpProjects } = require_gcp_projects();
213
- var getTribeAndClanName = require_tribe_clan_repo();
214
- var validate = require_validate();
215
9
  module.exports = class extends BaseGenerator {
216
10
  prompting() {
217
11
  const { tribe: defaultTribe, clan: defaultClan } = getTribeAndClanName();
218
- const validateGcpInput = /* @__PURE__ */ __name((input) => validateGcpProjects(this, input), "validateGcpInput");
12
+ const validateGcpInput = (input) => validateGcpProjects(this, input);
219
13
  const prompts = [
220
14
  {
221
- type: "input",
222
- name: "tribe",
15
+ type: 'input',
16
+ name: 'tribe',
223
17
  default: defaultTribe,
224
- message: "Tribe name",
225
- validate: /* @__PURE__ */ __name((input) => chain(input, required, validateGcpInput), "validate")
18
+ message: 'Tribe name',
19
+ validate: (input) => chain(input, required, validateGcpInput),
226
20
  },
227
21
  {
228
- type: "input",
229
- name: "clan",
22
+ type: 'input',
23
+ name: 'clan',
230
24
  default: defaultClan,
231
- message: "Clan name",
232
- validate: /* @__PURE__ */ __name((input) => chain(input, required, validateGcpInput), "validate")
25
+ message: 'Clan name',
26
+ validate: (input) => chain(input, required, validateGcpInput),
233
27
  },
234
28
  {
235
- type: "input",
236
- name: "group",
237
- message: "Name of GitHub team that can apply changes",
238
- validate: required
29
+ type: 'input',
30
+ name: 'group',
31
+ message: 'Name of GitHub team that can apply changes',
32
+ validate: required,
239
33
  },
240
34
  {
241
- type: "input",
242
- name: "costCenter",
243
- message: "Provide the cost center your tribe belongs to",
244
- validate: required
35
+ type: 'input',
36
+ name: 'costCenter',
37
+ message: 'Provide the cost center your tribe belongs to',
38
+ validate: required,
245
39
  },
246
40
  {
247
- type: "input",
248
- name: "product",
249
- message: "Provide the product your resources belongs to. Can be modified for a specific resource later on",
250
- validate: required
41
+ type: 'input',
42
+ name: 'product',
43
+ message:
44
+ 'Provide the product your resources belongs to. Can be modified for a specific resource later on',
45
+ validate: required,
251
46
  },
252
47
  {
253
- type: "input",
254
- name: "component",
255
- message: "Provide the component your resources belongs to. Can be modified for a specific resource later on",
256
- validate: required
48
+ type: 'input',
49
+ name: 'component',
50
+ message:
51
+ 'Provide the component your resources belongs to. Can be modified for a specific resource later on',
52
+ validate: required,
257
53
  },
258
54
  {
259
- type: "input",
260
- name: "tenantAlias",
261
- message: "Provide the tenant alias for the resources or use 'multi-tenant'. Can be modified for a specific resou\
262
- rce later on.",
263
- validate: required
55
+ type: 'input',
56
+ name: 'tenantAlias',
57
+ message:
58
+ "Provide the tenant alias for the resources or use 'multi-tenant'. Can be modified for a specific resource later on.",
59
+ validate: required,
264
60
  },
265
61
  {
266
- type: "input",
267
- name: "jiraProjectKey",
268
- message: 'Provide the "jira project key" for your clan, which is the prefix of your task names (e.g. srt, hii, h\
269
- cc)',
270
- validate: required && validate.jiraProjectKey
62
+ type: 'input',
63
+ name: 'jiraProjectKey',
64
+ message:
65
+ 'Provide the "jira project key" for your clan, which is the prefix of your task names (e.g. srt, hii, hcc)',
66
+ validate: required && validate.jiraProjectKey,
271
67
  },
272
68
  {
273
- type: "input",
274
- name: "repoName",
69
+ type: 'input',
70
+ name: 'repoName',
275
71
  default: `${defaultTribe}-${defaultClan}-common`,
276
- message: "Provide the name of your common repository",
277
- validate: required
72
+ message: 'Provide the name of your common repository',
73
+ validate: required,
278
74
  },
279
75
  {
280
- type: "input",
281
- name: "foldersToSync",
282
- message: "Provide the name of docs subfolders that you want to sync to the Developer Portal (comma-seperated). L\
283
- eave empty to add it later."
76
+ type: 'input',
77
+ name: 'foldersToSync',
78
+ message:
79
+ 'Provide the name of docs subfolders that you want to sync to the Developer Portal (comma-seperated). Leave empty to add it later.',
284
80
  },
285
81
  {
286
- type: "list",
287
- name: "syncReleaseNotes",
288
- message: "Do you want to sync release notes to the Developer Portal?",
289
- default: "false",
290
- choices: ["true", "false"]
82
+ type: 'list',
83
+ name: 'syncReleaseNotes',
84
+ message: 'Do you want to sync release notes to the Developer Portal?',
85
+ default: 'false',
86
+ choices: ['true', 'false'],
291
87
  },
292
88
  {
293
- type: "input",
294
- name: "serviceName",
295
- message: "Provide the name of your service",
296
- validate: required
297
- }
89
+ type: 'input',
90
+ name: 'serviceName',
91
+ message: 'Provide the name of your service',
92
+ validate: required,
93
+ },
298
94
  ];
95
+
299
96
  return this.prompt(prompts).then((props) => {
300
97
  this.answers = props;
301
98
  });
302
99
  }
100
+
303
101
  writing() {
304
102
  const { clan, tribe, group, serviceName } = this.answers;
305
- const groups = group.startsWith("@") ? group : `@extenda/${group}`;
103
+ const groups = group.startsWith('@') ? group : `@extenda/${group}`;
306
104
  const [tribeProd, tribeStaging] = getGcpProjects(this, tribe);
307
105
  const [clanProd, clanStaging] = getGcpProjects(this, clan);
106
+
308
107
  this.fs.copy(
309
- this.templatePath("infra", ".terragrunt-version"),
310
- this.destinationPath("infra", ".terragrunt-version")
108
+ this.templatePath('infra', '.terragrunt-version'),
109
+ this.destinationPath('infra', '.terragrunt-version'),
311
110
  );
312
111
  this.fs.copy(
313
- this.templatePath("infra", ".terraform-version"),
314
- this.destinationPath("infra", ".terraform-version")
112
+ this.templatePath('infra', '.terraform-version'),
113
+ this.destinationPath('infra', '.terraform-version'),
315
114
  );
316
- this.copyDir("infra", this.destinationPath("infra"), this.answers);
317
- const createEnv = /* @__PURE__ */ __name((env, tribeProject, clanProject) => {
318
- this.copyDir("env", this.destinationPath(path.join("infra", env)), {
115
+ this.copyDir('infra', this.destinationPath('infra'), this.answers);
116
+
117
+ const createEnv = (env, tribeProject, clanProject) => {
118
+ this.copyDir('env', this.destinationPath(path.join('infra', env)), {
319
119
  ...this.answers,
320
120
  env,
321
121
  clanProject,
322
- tribeProject
122
+ tribeProject,
323
123
  });
324
- }, "createEnv");
325
- createEnv("prod", tribeProd, clanProd);
326
- createEnv("staging", tribeStaging, clanStaging);
124
+ };
125
+
126
+ createEnv('prod', tribeProd, clanProd);
127
+ createEnv('staging', tribeStaging, clanStaging);
128
+
129
+ // Init notification channels for clan
327
130
  this.copyDir(
328
- "notification-channels",
329
- path.join("infra", "prod", "monitoring", "notification-channels"),
330
- this.answers
131
+ 'notification-channels',
132
+ path.join('infra', 'prod', 'monitoring', 'notification-channels'),
133
+ this.answers,
331
134
  );
332
- this.copyDir("github", this.destinationPath(path.join(".github")), {
135
+
136
+ this.copyDir('github', this.destinationPath(path.join('.github')), {
333
137
  ...this.answers,
334
- groups
138
+ groups,
335
139
  });
336
140
  this.copyDir(
337
- "release-notes",
141
+ 'release-notes',
338
142
  this.destinationPath(`docs/${serviceName}/release-notes`),
339
- this.answers
143
+ this.answers,
340
144
  );
341
145
  this.copyDir(
342
- "release-notes-schema",
343
- this.destinationPath("docs/"),
344
- this.answers
146
+ 'release-notes-schema',
147
+ this.destinationPath('docs/'),
148
+ this.answers,
345
149
  );
346
150
  this.fs.copy(
347
- this.templatePath(".yamllint"),
348
- this.destinationPath(".yamllint")
151
+ this.templatePath('.yamllint'),
152
+ this.destinationPath('.yamllint'),
349
153
  );
350
154
  }
155
+
351
156
  end() {
352
157
  this.log(`
353
158
  ${chalk.green(`Your repository has been initialized. To finalize your configuration, please continue
@@ -0,0 +1,38 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ const getKeyValue = (content, key) => {
5
+ let value = '';
6
+ content.split(/\r?\n/).forEach((line) => {
7
+ if (line.includes(key)) {
8
+ value = line.split('=')[1].replace('"', '').replace('"', '').trim();
9
+ }
10
+ });
11
+ return value;
12
+ };
13
+
14
+ const getTribeAndClanName = () => {
15
+ const repoName = path.basename(path.resolve());
16
+ const isClanInfraRepo = /^(?:\w+-){2}common(?:$|\W)$/.test(repoName);
17
+
18
+ let tribe = '';
19
+ let clan = '';
20
+
21
+ if (isClanInfraRepo) {
22
+ [tribe, clan] = repoName.split('-');
23
+ } else {
24
+ const commonHCL = path.join('infra', 'common.hcl');
25
+ if (fs.existsSync(commonHCL)) {
26
+ const commonHCLContent = fs.readFileSync(commonHCL, 'utf8');
27
+ tribe = getKeyValue(commonHCLContent, 'tribe_name');
28
+ clan = getKeyValue(commonHCLContent, 'clan_name');
29
+ }
30
+ }
31
+
32
+ return {
33
+ tribe,
34
+ clan,
35
+ };
36
+ };
37
+
38
+ module.exports = getTribeAndClanName;
@@ -0,0 +1,8 @@
1
+ const validate = {};
2
+
3
+ validate.jiraProjectKey = (input) => {
4
+ if (!/\s/.test(input) && input === input.toLowerCase()) return true;
5
+ return 'Must be lowercase and not contain any whitepace';
6
+ };
7
+
8
+ module.exports = validate;