Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

Alorel / personal-build-tools / 367

23 Oct 2018 - 20:45 coverage increased (+5.02%) to 78.602%
367

Pull #33

travis-ci-com

9181eb84f9c35729a3bad740fb7f9d93?size=18&default=identiconweb-flow
fix travis typecheck script
Pull Request #33: Build

374 of 617 branches covered (60.62%)

Branch coverage included in aggregate %.

288 of 325 new or added lines in 9 files covered. (88.62%)

178 existing lines in 29 files now uncovered.

1470 of 1729 relevant lines covered (85.02%)

38.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.03
/src/lib/PromptableConfig.ts
1
import * as fs from 'fs-extra';
2
import {cloneDeep, isEmpty, memoize} from 'lodash';
8×
3
import * as rl$ from 'readline-sync';
8×
4
import {LazyGetter} from 'typescript-lazy-get-decorator';
5
import {IS_CI} from '../const/IS_CI';
8×
6
import {lastDirname} from '../fns/lastDirname';
8×
7
import {readJson} from '../fns/readJson';
8×
8
import {isLicense, License, LICENSE_VALUES} from '../inc/License';
8×
9
import {PACKAGE_MANAGERS, PackageManager} from '../inc/PackageManager';
8×
10
import {PackageJson} from '../interfaces/PackageJson';
8×
11
import {Colour} from './Colour';
8×
12
import {Git} from './Git';
8×
13
import {getGhRepoData} from './sync-request/gh-repo/gh-repo';
8×
14

8×
15
//tslint:disable:max-file-line-count
8×
16

8×
17
const enum Conf {
8×
18
  GH_TOK_URL = 'https://github.com/settings/tokens/new'
19
}
20

8×
21
let rl: typeof rl$;
6×
22

23
if (IS_CI) {
24
  rl = cloneDeep(rl$);
Branches [[1, 1]] missed. 8×
25

60×
26
  function throwError() {
27
    throw new Error('Unable to prompt in test environment; Please set full configuration.');
56×
28
  }
29

6×
30
  for (const k of Object.keys(rl)) {
Branches [[2, 1]] missed. 164×
31
    if (typeof rl[k] === 'function') {
204×
32
      rl[k] = throwError;
33
    }
34
  }
35
} else {
36
  rl = rl$;
168×
37
}
38

174×
39
interface GhMetadata {
40
  repo: string;
3,654×
41

42
  user: string;
43
}
44

4×
45
const memoisedFnNames: string[] = [];
4×
46

84×
47
function Memo(_target: any, prop: PropertyKey): void {
48
  memoisedFnNames.push(<string>prop);
49
}
50

UNCOV
51
export class PromptableConfig<T extends { [k: string]: any }> {
!
52

53
  public constructor(private readonly data: T) {
UNCOV
54
    for (const fn of memoisedFnNames) {
Branches [[3, 0], [3, 1]] missed. !
UNCOV
55
      this[fn] = memoize(this[fn]);
!
56
    }
UNCOV
57
  }
!
58

59
  /** @internal */
UNCOV
60
  public static get readline(): typeof rl {
Branches [[4, 0], [4, 1]] missed. !
UNCOV
61
    return rl;
!
62
  }
UNCOV
63

!
64
  @LazyGetter(true)
65
  public get ghRepoFromMetadata(): string | null {
UNCOV
66
    if (this.ghMetadata) {
!
UNCOV
67
      return this.ghMetadata.repo;
Branches [[5, 0], [5, 1]] missed. !
UNCOV
68
    }
!
UNCOV
69

Branches [[6, 1]] missed. !
70
    return null;
612×
71
  }
72

73
  @LazyGetter(true)
74
  public get ghUserFromMetadata(): string | null {
75
    if (this.ghMetadata) {
76
      return this.ghMetadata.user;
274×
77
    }
78

79
    return null;
2×
80
  }
81

82
  @LazyGetter(true)
83
  private get ghMetadata(): GhMetadata | null {
2×
84
    try {
85
      if (Git.originUrl) {
86
        const match = /([a-z0-9-_.]+)\/([a-z0-9-_.]+)\.git/i.exec(Git.originUrl);
Branches [[8, 1]] missed. 2×
87
        if (match) {
88
          return {
89
            repo: match[2], //tslint:disable-line:no-magic-numbers
48×
90
            user: match[1]
91
          };
92
        }
50×
93
      }
94

95
      return null;
48×
96
    } catch {
97
      return null;
UNCOV
98
    }
!
UNCOV
99
  }
Branches [[13, 0], [13, 1]] missed. !
UNCOV
100

!
101
  public get<K extends keyof T>(k: K): T[K] {
UNCOV
102
    return this.data[k];
Branches [[14, 0], [14, 1], [15, 0], [15, 1]] missed. !
UNCOV
103
  }
Branches [[16, 0], [16, 1]] missed. !
UNCOV
104

!
UNCOV
105
  public has<K = T>(k: keyof K, strict = true): boolean {
!
106
    return strict ? this.data[<any>k] !== undefined : !!this.data[<any>k];
107
  }
108

50×
109
  @Memo
110
  public promptedEmail(prop = 'email'): string {
111
    return this.getPromptEmail(prop, 'What\'s your email? ');
50×
112
  }
113

114
  @Memo
50×
115
  public promptedEncryptionPassword(prop = 'password'): string {
116
    return this.getPromptHidden(prop, 'Encryption password: ');
117
  }
118

119
  @Memo
UNCOV
120
  public promptedGhEmail(prop = 'ghEmail'): string {
!
UNCOV
121
    return this.getPromptEmail(prop, 'What\'s GitHub your email? ');
Branches [[19, 0], [19, 1]] missed. !
UNCOV
122
  }
!
123

UNCOV
124
  @Memo
Branches [[20, 0], [20, 1], [21, 0], [21, 1]] missed. !
UNCOV
125
  public promptedGhRepo(prop = 'ghRepo'): string {
Branches [[22, 0], [22, 1]] missed. !
UNCOV
126
    let msg = 'What is your GitHub repo';
!
UNCOV
127

!
128
    if (this.has(prop)) {
129
      return this.get<any>(prop);
UNCOV
130
    } else if (!IS_CI && this.ghRepoFromMetadata) {
!
131
      if (rl.keyInYNStrict(`Is your GitHub repo ${Colour.cyan(this.ghRepoFromMetadata)}? `)) {
132
        this.data[prop] = this.ghRepoFromMetadata;
UNCOV
133

!
134
        return this.ghRepoFromMetadata;
135
      } else {
Branches [[23, 0]] missed.
UNCOV
136
        return this.getPrompt(prop, `${msg} then? `);
!
137
      }
138
    }
Branches [[24, 0]] missed.
139

6×
140
    return this.getPrompt(prop, `${msg}? `);
141
  }
Branches [[25, 0]] missed.
142

6×
143
  @Memo
144
  public promptedGhToken(prop = 'ghToken'): string {
UNCOV
145
    return this.getPromptHidden(prop, [
!
146
      'What\'s your global GitHub token used only by this CLI tool?',
147
      `You can create one here: ${Conf.GH_TOK_URL} `
148
    ].join(' '));
UNCOV
149
  }
Branches [[28, 0], [28, 1]] missed. !
UNCOV
150

!
151
  @Memo
UNCOV
152
  public promptedGhUser(prop = 'ghUser'): string {
Branches [[29, 0], [29, 1], [30, 1]] missed. !
153
    let msg = 'What is your GitHub username';
6×
154

58×
155
    if (this.has(prop)) {
156
      return this.get<any>(prop);
58×
157
    } else if (!IS_CI && this.ghUserFromMetadata) {
158
      if (rl.keyInYNStrict(`Is your GitHub username ${Colour.cyan(this.ghUserFromMetadata)}? `)) {
UNCOV
159
        this.data[prop] = this.ghUserFromMetadata;
!
160

161
        return this.ghUserFromMetadata;
UNCOV
162
      } else {
Branches [[33, 0], [33, 1]] missed. !
UNCOV
163
        return this.getPrompt(prop, `${msg} then? `);
!
164
      }
165
    }
!
UNCOV
166

Branches [[34, 0], [34, 1]] missed. !
167
    return this.getPrompt(prop, `${msg}? `);
!
168
  }
169

Branches [[35, 1]] missed. 50×
170
  @Memo
50×
171
  public promptedGpgKeyId(prop = 'gpgKeyId'): string {
172
    return this.getPromptHidden(prop, 'What\'s GPG key ID? ');
173
  }
!
174

175
  @Memo
176
  public promptedGpgKeyPwd(prop = 'gpgKeyPwd'): string {
UNCOV
177
    return this.getPromptHidden(prop, 'What\'s GPG key password? ');
Branches [[37, 0], [37, 1]] missed. !
178
  }
!
179

UNCOV
180
  @Memo
!
UNCOV
181
  public promptedGpgPrivkey(prop = 'gpgPrivkey'): string {
!
UNCOV
182
    return this.getPromptHidden(prop, 'Paste your GPG private key contents: ');
!
183
  }
!
UNCOV
184

Branches [[38, 0], [38, 1], [39, 1]] missed. !
185
  @Memo
50×
186
  public promptedGpgPubkey(prop = 'gpgPubkey'): string {
50×
187
    return this.getPrompt(prop, 'Paste your GPG public key contents: ');
UNCOV
188
  }
!
189

190
  @Memo
191
  public promptedLicense(prop = 'license'): License {
50×
192
    let pjson: null | PackageJson;
193
    if (this.has(prop)) {
194
      return this.get(prop);
50×
195
    } else if ((pjson = readJson()) && isLicense(pjson.license)) {
UNCOV
196
      this.data[prop] = pjson.license;
Branches [[42, 0], [42, 1]] missed. !
UNCOV
197

!
198
      return pjson.license;
UNCOV
199
    }
Branches [[43, 0], [43, 1], [44, 0], [44, 1]] missed. !
UNCOV
200

!
201
    return this.getPromptSelect(prop, 'What license do you with to use? ', LICENSE_VALUES);
!
202
  }
UNCOV
203

Branches [[45, 0], [45, 1]] missed. !
UNCOV
204
  @Memo
!
UNCOV
205
  public promptedName(prop = 'name'): string {
!
206
    return this.getPrompt(prop, 'What\'s your name? ');
207
  }
UNCOV
208

!
209
  @Memo
210
  public promptedPkgMgr(prop = 'pkgMgr'): PackageManager {
211
    if (this.has(prop)) {
Branches [[46, 0]] missed.
UNCOV
212
      return this.get(prop);
!
213
    }
214

215
    const files = fs.readdirSync(process.cwd(), 'utf8');
216
    if (files.includes('yarn.lock')) {
217
      return PackageManager.YARN;
Branches [[47, 0]] missed.
218
    } else if (files.includes('package-lock.json')) {
2×
219
      return PackageManager.NPM;
220
    } else {
Branches [[48, 0]] missed.
221
      return this.getPromptSelect(prop, 'What package manager do you want to use? ', PACKAGE_MANAGERS);
6×
222
    }
223
  }
UNCOV
224

!
225
  @Memo
226
  public promptedProjectDescription(prop = 'projectDesc'): string {
227
    if (this.has(prop)) {
50×
228
      return this.get(prop);
229
    }
230
    const tok = this.promptedGhToken();
50×
231
    const repo = this.promptedGhRepo();
232
    const user = this.promptedGhUser();
UNCOV
233
    const ghProjRemote = getGhRepoData(tok, user, repo);
Branches [[54, 0], [54, 1]] missed. !
UNCOV
234

!
235
    if (ghProjRemote && ghProjRemote.description) {
236
      this.data[prop] = ghProjRemote.description;
!
UNCOV
237

!
UNCOV
238
      return this.data[prop];
!
239
    }
!
240

241
    return this.getPrompt(prop, 'What\'s your project description? ');
!
242
  }
!
UNCOV
243

Branches [[55, 0], [55, 1]] missed. !
UNCOV
244
  @Memo
!
245
  public promptedProjectKeywords(prop = 'projectKeywords'): string {
246
    return this.getPromptArray(prop, 'What are your project keywords?');
UNCOV
247
  }
Branches [[56, 0], [56, 1], [57, 1]] missed. !
UNCOV
248

!
249
  @Memo
250
  public promptedProjectName(prop = 'projectName'): string {
251
    const ask = (opt: string) => rl.keyInYNStrict(`Is your project name ${Colour.cyan(opt)}? `);
4×
252
    let dir: string;
!
253

2×
254
    if (this.has(prop)) {
255
      return this.get(prop);
UNCOV
256
    } else if (this.ghRepoFromMetadata && ask(this.ghRepoFromMetadata)) {
!
257
      this.data[prop] = this.ghRepoFromMetadata;
258

259
      return this.ghRepoFromMetadata;
2×
260
    } else if (ask((dir = lastDirname()))) {
261
      this.data[prop] = dir;
Branches [[62, 0]] missed.
UNCOV
262

Branches [[63, 1]] missed. !
UNCOV
263
      return dir;
!
264
    } else {
265
      return this.getPrompt(prop, 'What is your project name then? ');
266
    }
12×
267
  }
12×
UNCOV
268

!
269
  @Memo
270
  public promptedReleaseGhToken(prop = 'releaseGhToken'): string { //tslint:disable-line:max-line-length no-identical-functions
271
    return this.getPromptHidden(prop, [
272
      'What\'s your release GitHub token?',
Branches [[64, 0]] missed. 2×
UNCOV
273
      `You can create one here: ${Conf.GH_TOK_URL} `
!
274
    ].join(' '));
275
  }
Branches [[65, 1]] missed. 2×
276

277
  @Memo
2×
278
  public promptedReleaseNpmToken(prop = 'releaseNpmToken'): string {
2×
279
    return this.getPromptHidden(prop, 'What\'s your release NPM token? ');
280
  }
!
281

2×
282
  @Memo
283
  public promptedTravisTokenOrg(prop = 'travisTokenOrg'): string {
284
    return this.getPromptHidden(prop, 'What\'s your travis-ci token? ');
2×
285
  }
2×
286

287
  @Memo
288
  public promptedTravisTokenPro(prop = 'travisTokenPro'): string {
289
    return this.getPromptHidden(prop, 'What\'s your travis-ci token? ');
8×
290
  }
291

292
  @Memo
293
  public promptedUserWebsite(prop = 'userWebsite'): string {
294
    return this.getPromptEmail(prop, 'What\'s your name? ');
8×
295
  }
296

297
  private getPrompt<K extends keyof T>(k: K, question: string, forbidEmpty = true, strict = true): T[K] {
298
    return this.promptCommon(k, () => rl.question(question), forbidEmpty, strict);
299
  }
8×
300

301
  private getPromptArray<K extends keyof T>(k: K, question: string, forbidEmpty = true): T[K] {
302
    if (!isEmpty(this.get(k))) {
303
      return this.get(k);
304
    }
8×
305
    const out: string[] = [];
306

307
    const run = () => {
308
      console.log(question);
309
      console.log('Enter an empty line when done');
310
      let response: string;
8×
311
      do {
312
        response = rl.question('').trim();
313
        if (response) {
314
          out.push(response);
315
        }
316
      } while (response);
8×
317

318
      if (forbidEmpty && !out.length) {
319
        run();
320
      }
321
    };
322
    run();
8×
323
    this.data[k] = out;
324

325
    return out;
326
  }
327

328
  private getPromptEmail<K extends keyof T>(k: K, question: string, forbidEmpty = true, strict = true): T[K] {
8×
329
    return this.promptCommon(k, () => rl.questionEMail(question), forbidEmpty, strict);
330
  }
331

332
  private getPromptHidden<K extends keyof T>(k: K, question: string, forbidEmpty = true, strict = true): string {
333
    return this.promptCommon(
334
      k,
8×
335
      () => rl.question(question, {hideEchoBack: true, cancel: true}),
336
      forbidEmpty,
337
      strict
338
    );
339
  }
340

8×
341
  private getPromptSelect<K extends keyof T>(k: K, question: string, opts: string[], strict = true): T[K] {
342
    if (this.has(<any>k, strict)) {
343
      return this.data[k];
344
    } else {
345
      const idx = rl.keyInSelect(opts, question, {cancel: false});
346
      this.data[k] = opts[idx];
8×
347

348
      return this.data[k];
349
    }
350
  }
351

352
  private promptCommon<K extends keyof T>(k: K, askFn: () => string, forbidEmpty: boolean, strict: boolean): T[K] {
8×
353
    if (this.has(<any>k, strict)) {
354
      return this.data[k];
355
    } else if (forbidEmpty) {
356
      let v: string;
357
      do {
358
        v = askFn();
8×
359
      } while (!v);
360

361
      this.data[k] = v;
362

363
      return this.data[k];
364
    } else {
8×
365
      this.data[k] = askFn();
366

367
      return this.data[k];
368
    }
Branches [[66, 0]] missed.
369
  }
370
}
8×
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc