-
Notifications
You must be signed in to change notification settings - Fork 249
/
Copy pathtasks.ts
221 lines (208 loc) · 6.07 KB
/
tasks.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import { join } from 'path';
import execa, { Options } from 'execa';
import githubUsername from 'github-username';
import {
Runner,
TypescriptStarterInferredOptions,
TypescriptStarterOptions,
TypescriptStarterUserOptions,
} from './utils';
export enum Placeholders {
email = 'YOUR_EMAIL',
name = 'YOUR_NAME',
username = 'YOUR_GITHUB_USER_NAME',
}
// We implement these as function factories to make unit testing easier.
export const cloneRepo = (
spawner: typeof execa,
suppressOutput = false
) => async (
repoInfo: {
readonly branch: string;
readonly repo: string;
},
workingDirectory: string,
dir: string
) => {
const projectDir = join(workingDirectory, dir);
const gitHistoryDir = join(projectDir, '.git');
const args =
repoInfo.branch === '.'
? ['clone', '--depth=1', repoInfo.repo, dir]
: [
'clone',
'--depth=1',
`--branch=${repoInfo.branch}`,
repoInfo.repo,
dir,
];
try {
await spawner('git', args, {
cwd: workingDirectory,
stdio: suppressOutput ? 'pipe' : 'inherit',
});
} catch (err) {
if (err.exitCodeName === 'ENOENT') {
// eslint-disable-next-line functional/no-throw-statement
throw new Error(`
Git is not installed on your PATH. Please install Git and try again.
For more information, visit: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
`);
} else {
// eslint-disable-next-line functional/no-throw-statement
throw new Error(`Git clone failed.`);
}
}
try {
const revParseResult = await spawner('git', ['rev-parse', 'HEAD'], {
cwd: projectDir,
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'inherit'],
});
const commitHash = revParseResult.stdout;
return { commitHash, gitHistoryDir };
} catch (err) {
// eslint-disable-next-line functional/no-throw-statement
throw new Error(`Git rev-parse failed.`);
}
};
export const getGithubUsername = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fetcher: any
) => async (email: string | undefined): Promise<string> => {
if (email === Placeholders.email) {
return Placeholders.username;
}
return fetcher(email).catch(() => {
return Placeholders.username;
});
};
export const getUserInfo = (spawner: typeof execa) => async () => {
const opts: Options = {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'inherit'],
};
try {
const nameResult = await spawner('git', ['config', 'user.name'], opts);
const emailResult = await spawner('git', ['config', 'user.email'], opts);
return {
gitEmail: emailResult.stdout,
gitName: nameResult.stdout,
};
} catch (err) {
return {
gitEmail: Placeholders.email,
gitName: Placeholders.name,
};
}
};
export const initialCommit = (spawner: typeof execa) => async (
hash: string,
projectDir: string
): Promise<void> => {
const opts: Options = {
cwd: projectDir,
encoding: 'utf8',
stdio: 'pipe',
};
await spawner('git', ['init'], opts);
await spawner('git', ['add', '-A'], opts);
await spawner(
'git',
[
'commit',
'-m',
`Initial commit\n\nCreated with bitjson/typescript-starter@${hash}`,
],
opts
);
};
export const install = (spawner: typeof execa) => async (
runner: Runner,
projectDir: string
) => {
const opts: Options = {
cwd: projectDir,
encoding: 'utf8',
stdio: 'inherit',
};
try {
runner === Runner.Npm
? spawner('npm', ['install'], opts)
: spawner('yarn', opts);
} catch (err) {
// eslint-disable-next-line functional/no-throw-statement
throw new Error(`Installation failed. You'll need to install manually.`);
}
};
/**
* Returns the URL and branch to clone. We clone the branch (tag) at the current
* release rather than `master`. This ensures we get the exact files expected by
* this version of the CLI. (If we cloned master, changes merged to master, but
* not yet released, may cause unexpected results.)
* @param starterVersion the current version of this CLI
*/
export const getRepoInfo = (starterVersion: string) => {
return process.env.TYPESCRIPT_STARTER_REPO_URL
? {
branch: process.env.TYPESCRIPT_STARTER_REPO_BRANCH
? process.env.TYPESCRIPT_STARTER_REPO_BRANCH
: 'master',
repo: process.env.TYPESCRIPT_STARTER_REPO_URL,
}
: {
branch: `v${starterVersion}`,
repo: 'https://github.com/bitjson/typescript-starter.git',
};
};
export type Tasks = {
readonly cloneRepo: (
repoInfo: {
readonly branch: string;
readonly repo: string;
},
workingDirectory: string,
dir: string
) => Promise<{ readonly commitHash: string; readonly gitHistoryDir: string }>;
readonly initialCommit: (
hash: string,
projectDir: string,
name: string
) => Promise<void>;
readonly install: (runner: Runner, projectDir: string) => Promise<void>;
};
export const LiveTasks: Tasks = {
cloneRepo: cloneRepo(execa),
initialCommit: initialCommit(execa),
install: install(execa),
};
export const addInferredOptions = async (
userOptions: TypescriptStarterUserOptions
): Promise<TypescriptStarterOptions> => {
const { gitName, gitEmail } = await getUserInfo(execa)();
const username = await getGithubUsername(githubUsername)(gitEmail);
const inferredOptions: TypescriptStarterInferredOptions = {
email: gitEmail,
fullName: gitName,
githubUsername: username,
repoInfo: getRepoInfo(userOptions.starterVersion),
workingDirectory: process.cwd(),
};
return {
...inferredOptions,
appveyor: userOptions.appveyor,
circleci: userOptions.circleci,
cspell: userOptions.cspell,
description: userOptions.description,
domDefinitions: userOptions.domDefinitions,
editorconfig: userOptions.editorconfig,
functional: userOptions.functional,
install: userOptions.install,
nodeDefinitions: userOptions.nodeDefinitions,
projectName: userOptions.projectName,
runner: userOptions.runner,
strict: userOptions.strict,
travis: userOptions.travis,
vscode: userOptions.vscode,
};
};