contensis-cli 1.1.1 → 1.1.2-beta.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.
- package/dist/factories/RequestHandlerFactory.js +203 -0
- package/dist/factories/RequestHandlerFactory.js.map +7 -0
- package/dist/localisation/en-GB.js +28 -5
- package/dist/localisation/en-GB.js.map +2 -2
- package/dist/mappers/DevRequests-to-RequestHanderCliArgs.js +119 -0
- package/dist/mappers/DevRequests-to-RequestHanderCliArgs.js.map +7 -0
- package/dist/providers/GitHubCliModuleProvider.js +107 -0
- package/dist/providers/GitHubCliModuleProvider.js.map +7 -0
- package/dist/providers/HttpProvider.js +72 -0
- package/dist/providers/HttpProvider.js.map +7 -0
- package/dist/providers/ManifestProvider.js +53 -0
- package/dist/providers/ManifestProvider.js.map +7 -0
- package/dist/providers/file-provider.js +11 -0
- package/dist/providers/file-provider.js.map +2 -2
- package/dist/services/ContensisAuthService.js +19 -11
- package/dist/services/ContensisAuthService.js.map +2 -2
- package/dist/services/ContensisCliService.js +27 -14
- package/dist/services/ContensisCliService.js.map +2 -2
- package/dist/services/ContensisDevService.js +10 -59
- package/dist/services/ContensisDevService.js.map +3 -3
- package/dist/util/debug.js +29 -0
- package/dist/util/debug.js.map +7 -0
- package/dist/util/fetch.js +65 -0
- package/dist/util/fetch.js.map +7 -0
- package/dist/util/index.js.map +1 -1
- package/dist/util/logger.js.map +2 -2
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +4 -1
- package/src/factories/RequestHandlerFactory.ts +225 -0
- package/src/localisation/en-GB.ts +54 -12
- package/src/mappers/DevRequests-to-RequestHanderCliArgs.ts +145 -0
- package/src/providers/GitHubCliModuleProvider.ts +114 -0
- package/src/providers/HttpProvider.ts +50 -0
- package/src/providers/ManifestProvider.ts +43 -0
- package/src/providers/file-provider.ts +9 -0
- package/src/services/ContensisAuthService.ts +23 -14
- package/src/services/ContensisCliService.ts +30 -15
- package/src/services/ContensisDevService.ts +19 -85
- package/src/util/debug.ts +1 -0
- package/src/util/fetch.ts +74 -0
- package/src/util/index.ts +1 -1
- package/src/util/logger.ts +0 -1
- package/src/version.ts +1 -1
- package/dist/mappers/DevRequests-to-RequestHanderSiteConfigYaml.js +0 -56
- package/dist/mappers/DevRequests-to-RequestHanderSiteConfigYaml.js.map +0 -7
- package/src/mappers/DevRequests-to-RequestHanderSiteConfigYaml.ts +0 -44
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { createSpinner } from 'nanospinner';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { LogMessages } from '~/localisation/en-GB';
|
|
6
|
+
import GitHubCliModuleProvider from '~/providers/GitHubCliModuleProvider';
|
|
7
|
+
|
|
8
|
+
import ManifestProvider from '~/providers/ManifestProvider';
|
|
9
|
+
import { appRootDir, joinPath } from '~/providers/file-provider';
|
|
10
|
+
import { isDebug } from '~/util/debug';
|
|
11
|
+
import { Logger } from '~/util/logger';
|
|
12
|
+
|
|
13
|
+
export class RequestHandlerFactory {
|
|
14
|
+
debug = isDebug();
|
|
15
|
+
log = Logger;
|
|
16
|
+
messages = LogMessages;
|
|
17
|
+
manifest = new ManifestProvider(); // Load cli-manifest.json
|
|
18
|
+
|
|
19
|
+
basePath = path.join(appRootDir);
|
|
20
|
+
name = 'request-handler-localdevelopment';
|
|
21
|
+
cmd = 'Zengenti.Contensis.RequestHandler.LocalDevelopment';
|
|
22
|
+
|
|
23
|
+
prerelease;
|
|
24
|
+
|
|
25
|
+
get exePath() {
|
|
26
|
+
return path.join(this.basePath, `${this.name}-${this.moduleInfo.version}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get moduleInfo() {
|
|
30
|
+
return (
|
|
31
|
+
this.manifest.getModule(this.name) || {
|
|
32
|
+
github: 'contensis/request-handler-localdevelopment',
|
|
33
|
+
version: '*',
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
constructor(prerelease = false) {
|
|
39
|
+
this.prerelease = prerelease;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Use the factory to create a request handler instance
|
|
43
|
+
// handling the download and updating of the external binary
|
|
44
|
+
async Create() {
|
|
45
|
+
const { moduleInfo } = this;
|
|
46
|
+
const firstUse = !moduleInfo?.version || moduleInfo?.version === '*';
|
|
47
|
+
|
|
48
|
+
if (firstUse) {
|
|
49
|
+
// Create cli-manifest.json
|
|
50
|
+
this.manifest.writeModule(this.name, this.moduleInfo);
|
|
51
|
+
|
|
52
|
+
// Download for first time use (await)
|
|
53
|
+
await this.CheckUpdate({ verbose: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Apply any downloaded/pending update so we launch that version
|
|
57
|
+
await this.ApplyUpdate();
|
|
58
|
+
|
|
59
|
+
// Fire an async update check and continue working in the background (do not await)
|
|
60
|
+
if (!firstUse) this.CheckUpdate();
|
|
61
|
+
|
|
62
|
+
// Return a RequestHandler ready to invoke
|
|
63
|
+
return this.CreateInvoke(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
CreateInvoke(self = this) {
|
|
67
|
+
// Hoist the vars we need from `this` as we lose scope
|
|
68
|
+
// when the function is returned from the Create() method
|
|
69
|
+
const { debug, log, messages, cmd, exePath } = self;
|
|
70
|
+
|
|
71
|
+
// Invoke request handler method
|
|
72
|
+
return async (args: string[]) => {
|
|
73
|
+
const child = spawn(joinPath(exePath, cmd), args, { stdio: 'inherit' });
|
|
74
|
+
|
|
75
|
+
if (args?.length && debug)
|
|
76
|
+
log.warning(
|
|
77
|
+
`Spawning process with supplied args: ${JSON.stringify(
|
|
78
|
+
child.spawnargs,
|
|
79
|
+
null,
|
|
80
|
+
2
|
|
81
|
+
)}`
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
let isRunning = false;
|
|
85
|
+
|
|
86
|
+
// Log child output through event listeners
|
|
87
|
+
child?.stdout?.on('data', data => {
|
|
88
|
+
isRunning = true;
|
|
89
|
+
log.raw(data);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
child?.stderr?.on('data', data => {
|
|
93
|
+
log.error(data);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
child.on('spawn', () => {
|
|
97
|
+
isRunning = true;
|
|
98
|
+
log.help(messages.devrequests.spawn());
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
child.on('exit', code => {
|
|
102
|
+
isRunning = false;
|
|
103
|
+
|
|
104
|
+
log[code === 0 ? 'success' : 'warning'](
|
|
105
|
+
messages.devrequests.exited(code)
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
child.on('error', error => {
|
|
110
|
+
isRunning = false;
|
|
111
|
+
log.error(messages.devrequests.errored(error));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
115
|
+
|
|
116
|
+
// keep the method running until we can return
|
|
117
|
+
while (true === true) {
|
|
118
|
+
if (!isRunning) return;
|
|
119
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async CheckUpdate({ verbose = false }: { verbose?: boolean } = {}) {
|
|
125
|
+
const { debug, log, manifest, messages, moduleInfo } = this;
|
|
126
|
+
const github = new GitHubCliModuleProvider(moduleInfo.github);
|
|
127
|
+
// Find latest version
|
|
128
|
+
const release = await github.FindLatestRelease();
|
|
129
|
+
if (verbose || debug)
|
|
130
|
+
if (release)
|
|
131
|
+
log.info(
|
|
132
|
+
`${messages.devrequests.install.download(
|
|
133
|
+
moduleInfo.github,
|
|
134
|
+
release.tag_name
|
|
135
|
+
)}\n${release.html_url}`
|
|
136
|
+
);
|
|
137
|
+
else
|
|
138
|
+
log.warning(messages.devrequests.install.notFound(moduleInfo.github));
|
|
139
|
+
|
|
140
|
+
// Should we download an update?
|
|
141
|
+
if (
|
|
142
|
+
release?.tag_name &&
|
|
143
|
+
![moduleInfo.version, moduleInfo.install].includes(release.tag_name)
|
|
144
|
+
) {
|
|
145
|
+
// Download platform-specific release asset
|
|
146
|
+
const downloadPath = path.join(
|
|
147
|
+
this.basePath,
|
|
148
|
+
`${this.name}-${release.tag_name}`
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// add spinner while downloading
|
|
152
|
+
const spinner = createSpinner(
|
|
153
|
+
messages.devrequests.install.downloading(
|
|
154
|
+
moduleInfo.github,
|
|
155
|
+
release.tag_name
|
|
156
|
+
)
|
|
157
|
+
);
|
|
158
|
+
if (verbose || debug) {
|
|
159
|
+
spinner.start();
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
await github.DownloadRelease(release, {
|
|
163
|
+
path: downloadPath,
|
|
164
|
+
// Map NodeJS os platform to release asset name
|
|
165
|
+
platforms: [
|
|
166
|
+
['win32', 'win-x64'],
|
|
167
|
+
['darwin', 'osx-x64'],
|
|
168
|
+
['linux', 'linux-x64'],
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
} catch (ex: any) {
|
|
172
|
+
spinner.error();
|
|
173
|
+
log.error(
|
|
174
|
+
messages.devrequests.install.downloadFail(
|
|
175
|
+
moduleInfo.github,
|
|
176
|
+
release.tag_name
|
|
177
|
+
),
|
|
178
|
+
ex
|
|
179
|
+
);
|
|
180
|
+
} finally {
|
|
181
|
+
if (verbose || debug)
|
|
182
|
+
spinner.success({
|
|
183
|
+
text: messages.devrequests.install.downloaded(
|
|
184
|
+
moduleInfo.github,
|
|
185
|
+
release.tag_name
|
|
186
|
+
),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Update module info with downloaded release
|
|
190
|
+
this.moduleInfo.install = release.tag_name;
|
|
191
|
+
// Write module info update to manifest so it installs on next invoke
|
|
192
|
+
manifest.writeModule(this.name, this.moduleInfo);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async ApplyUpdate() {
|
|
198
|
+
const { manifest, messages, moduleInfo } = this;
|
|
199
|
+
|
|
200
|
+
if (moduleInfo.install && moduleInfo.version !== moduleInfo.install) {
|
|
201
|
+
let { apply } =
|
|
202
|
+
moduleInfo.version === '*'
|
|
203
|
+
? { apply: true }
|
|
204
|
+
: await inquirer.prompt({
|
|
205
|
+
name: 'apply',
|
|
206
|
+
type: 'confirm',
|
|
207
|
+
message: messages.devrequests.install.applyUpdate(
|
|
208
|
+
moduleInfo.install,
|
|
209
|
+
moduleInfo.version
|
|
210
|
+
),
|
|
211
|
+
default: 'Y',
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
if (apply) {
|
|
215
|
+
moduleInfo.version = moduleInfo.install;
|
|
216
|
+
delete moduleInfo.install;
|
|
217
|
+
manifest.writeModule(this.name, this.moduleInfo);
|
|
218
|
+
|
|
219
|
+
// TODO: clean up user folder by deleting old version(s)}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const createRequestHandler = () => new RequestHandlerFactory().Create();
|
|
@@ -341,10 +341,36 @@ export const LogMessages = {
|
|
|
341
341
|
return Logger.infoText(status);
|
|
342
342
|
}
|
|
343
343
|
},
|
|
344
|
-
get: (
|
|
345
|
-
|
|
344
|
+
get: (
|
|
345
|
+
id: string,
|
|
346
|
+
branch: string,
|
|
347
|
+
version: string,
|
|
348
|
+
env: string,
|
|
349
|
+
projectId?: string
|
|
350
|
+
) =>
|
|
351
|
+
`[${env}] ${
|
|
352
|
+
version && version !== 'latest'
|
|
353
|
+
? `Found v${version}`
|
|
354
|
+
: 'Latest block versions'
|
|
355
|
+
} for ${Logger.infoText(`${branch}/`)}${Logger.highlightText(
|
|
356
|
+
id
|
|
357
|
+
)} in project ${projectId}\n`,
|
|
358
|
+
noGet: (
|
|
359
|
+
id: string,
|
|
360
|
+
branch: string,
|
|
361
|
+
version: string,
|
|
362
|
+
env: string,
|
|
363
|
+
projectId?: string
|
|
364
|
+
) =>
|
|
365
|
+
`[${env}] Did not find ${
|
|
366
|
+
version ? `v${version}` : 'any block versions'
|
|
367
|
+
} for ${Logger.highlightText(id)} in branch ${Logger.infoText(
|
|
368
|
+
branch
|
|
369
|
+
)} in ${Logger.infoText(projectId)} project`,
|
|
370
|
+
noGetTip: () =>
|
|
371
|
+
`Check the available blocks and branches in this project by running "list blocks"`,
|
|
346
372
|
list: (env: string, projectId?: string) =>
|
|
347
|
-
`[${env}] Blocks in project ${projectId}
|
|
373
|
+
`[${env}] Blocks in project ${projectId}\n`,
|
|
348
374
|
noList: (env: string, projectId?: string) =>
|
|
349
375
|
`[${env}] Cannot retrieve blocks in project ${projectId}`,
|
|
350
376
|
getLogs: (id: string, branch: string, env: string, projectId?: string) =>
|
|
@@ -367,15 +393,6 @@ export const LogMessages = {
|
|
|
367
393
|
`[${env}] Unable to push block ${Logger.highlightText(
|
|
368
394
|
id
|
|
369
395
|
)} in project ${projectId}`,
|
|
370
|
-
latestVersion: (
|
|
371
|
-
version: string,
|
|
372
|
-
id: string,
|
|
373
|
-
env: string,
|
|
374
|
-
projectId?: string
|
|
375
|
-
) =>
|
|
376
|
-
`[${env}] Found latest block version ${Logger.highlightText(
|
|
377
|
-
id
|
|
378
|
-
)} in project ${projectId} ${Logger.highlightText(version)}`,
|
|
379
396
|
failedParsingVersion: () =>
|
|
380
397
|
`Did not find a "version.versionNo" in response`,
|
|
381
398
|
actionComplete: (
|
|
@@ -586,4 +603,29 @@ export const LogMessages = {
|
|
|
586
603
|
'my-awesome-website'
|
|
587
604
|
)}`,
|
|
588
605
|
},
|
|
606
|
+
devrequests: {
|
|
607
|
+
install: {
|
|
608
|
+
notFound: (repo: string) =>
|
|
609
|
+
`Could not find a release in ${repo} repo - please check github`,
|
|
610
|
+
download: (repo: string, version: string) =>
|
|
611
|
+
`Found release ${repo} ${version}`,
|
|
612
|
+
downloading: (repo: string, version: string) =>
|
|
613
|
+
`Downloading ${repo} ${version}`,
|
|
614
|
+
downloadFail: (repo: string, version: string) =>
|
|
615
|
+
`Problem downloading release ${version} from ${repo}`,
|
|
616
|
+
downloaded: (repo: string, version: string) =>
|
|
617
|
+
`Downloaded ${repo} ${version}`,
|
|
618
|
+
applyUpdate: (version: string, existing: string) =>
|
|
619
|
+
`Use updated version ${version} ${Logger.infoText(
|
|
620
|
+
`(replaces ${existing})`
|
|
621
|
+
)}?`,
|
|
622
|
+
},
|
|
623
|
+
launch: () => `Launching request handler for local development`,
|
|
624
|
+
spawn: () =>
|
|
625
|
+
`If you see a firewall popup requesting network access, it is safe to approve`,
|
|
626
|
+
exited: (code: number | null) =>
|
|
627
|
+
`Request handler exited with code ${code}\n`,
|
|
628
|
+
errored: (error: Error) =>
|
|
629
|
+
`Could not launch request handler due to error \n${error}`,
|
|
630
|
+
},
|
|
589
631
|
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { ContensisMigrationService } from 'migratortron';
|
|
2
|
+
import ContensisCli from '~/services/ContensisCliService';
|
|
3
|
+
|
|
4
|
+
type EndpointJson = {
|
|
5
|
+
id: string;
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type BlockJson = {
|
|
10
|
+
id: string;
|
|
11
|
+
baseUri: string;
|
|
12
|
+
staticPaths: string[];
|
|
13
|
+
endpoints: EndpointJson[];
|
|
14
|
+
versionNo: number;
|
|
15
|
+
branch: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type RendererJson = {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
rules: RendererRuleJson[];
|
|
22
|
+
assignedContentTypes: string[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type RendererRuleJson = {
|
|
26
|
+
return?: {
|
|
27
|
+
blockId?: string;
|
|
28
|
+
endpointId?: string | null;
|
|
29
|
+
version?: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
interface ISiteConfigYaml {
|
|
33
|
+
alias: string;
|
|
34
|
+
projectId: string;
|
|
35
|
+
accessToken: string;
|
|
36
|
+
clientId: string;
|
|
37
|
+
sharedSecret: string;
|
|
38
|
+
blocks: BlockJson[];
|
|
39
|
+
renderers: RendererJson[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const buildSiteConfig = async (cli: ContensisCli) => {
|
|
43
|
+
const { currentEnv, env, log, messages } = cli;
|
|
44
|
+
const siteConfig: ISiteConfigYaml = {
|
|
45
|
+
alias: cli.currentEnv,
|
|
46
|
+
projectId: cli.currentProject,
|
|
47
|
+
accessToken: '',
|
|
48
|
+
clientId: '',
|
|
49
|
+
sharedSecret: '',
|
|
50
|
+
blocks: [],
|
|
51
|
+
renderers: [],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getBlocks = async (contensis: ContensisMigrationService) => {
|
|
55
|
+
const [err, blocksRaw] = await contensis.blocks.GetBlocks();
|
|
56
|
+
if (err) log.error(messages.blocks.noList(currentEnv, env.currentProject));
|
|
57
|
+
|
|
58
|
+
// const blocksRaw = await cli.PrintBlocks();
|
|
59
|
+
|
|
60
|
+
const blocks: BlockJson[] = [];
|
|
61
|
+
for (const block of blocksRaw || []) {
|
|
62
|
+
// Retrieve block version
|
|
63
|
+
const [err, versions] = await contensis.blocks.GetBlockVersions(
|
|
64
|
+
block.id,
|
|
65
|
+
'default',
|
|
66
|
+
'latest'
|
|
67
|
+
);
|
|
68
|
+
if (err || versions?.length === 0)
|
|
69
|
+
log.warning(
|
|
70
|
+
messages.blocks.noGet(
|
|
71
|
+
block.id,
|
|
72
|
+
'default',
|
|
73
|
+
'latest',
|
|
74
|
+
currentEnv,
|
|
75
|
+
env.currentProject
|
|
76
|
+
)
|
|
77
|
+
);
|
|
78
|
+
if (versions?.[0]) {
|
|
79
|
+
const v = versions[0];
|
|
80
|
+
blocks.push({
|
|
81
|
+
id: v.id,
|
|
82
|
+
baseUri: v.previewUrl,
|
|
83
|
+
staticPaths: v.staticPaths,
|
|
84
|
+
endpoints: v.endpoints,
|
|
85
|
+
versionNo: v.version.versionNo,
|
|
86
|
+
branch: v.source.branch,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return blocks;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const contensis = await cli.ConnectContensis();
|
|
94
|
+
if (contensis) {
|
|
95
|
+
const [blocks, renderers] = await Promise.all([
|
|
96
|
+
getBlocks(contensis),
|
|
97
|
+
contensis.renderers.GetRenderers(),
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
siteConfig.blocks = blocks;
|
|
101
|
+
siteConfig.renderers = renderers?.[1]?.map(r => ({
|
|
102
|
+
id: r.id,
|
|
103
|
+
name: r.name,
|
|
104
|
+
assignedContentTypes: r.assignedContentTypes,
|
|
105
|
+
rules: r.rules,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return siteConfig;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const requestHandlerCliArgs = async (
|
|
112
|
+
cli: ContensisCli,
|
|
113
|
+
overrideArgs: string[] = []
|
|
114
|
+
) => {
|
|
115
|
+
const args = overrideArgs
|
|
116
|
+
? typeof overrideArgs?.[0] === 'string' && overrideArgs[0].includes(' ', 2)
|
|
117
|
+
? overrideArgs[0].split(' ')
|
|
118
|
+
: overrideArgs
|
|
119
|
+
: []; // args could be [ '-c .\\site_config.yaml' ] or [ '-c', '.\\site_config.yaml' ]
|
|
120
|
+
|
|
121
|
+
const siteConfig = await buildSiteConfig(cli);
|
|
122
|
+
// Add required args
|
|
123
|
+
if (!args.find(a => a === '--alias')) args.push('--alias', cli.currentEnv);
|
|
124
|
+
if (!args.find(a => a === '--project-api-id'))
|
|
125
|
+
args.push('--project-api-id', cli.currentProject);
|
|
126
|
+
if (!args.find(a => a === '--blocks-json'))
|
|
127
|
+
args.push('--blocks-json', JSON.stringify(siteConfig.blocks));
|
|
128
|
+
if (!args.find(a => a === '--renderers-json'))
|
|
129
|
+
args.push('--renderers-json', JSON.stringify(siteConfig.renderers));
|
|
130
|
+
|
|
131
|
+
await cli.Login(cli.env.lastUserId, { silent: true }); // to hydrate the auth service
|
|
132
|
+
const client = cli.auth?.clientDetails;
|
|
133
|
+
if (client) {
|
|
134
|
+
if (!args.find(a => a === '--client-id') && 'clientId' in client)
|
|
135
|
+
args.push('--client-id', client.clientId);
|
|
136
|
+
if (!args.find(a => a === '--client-secret') && 'clientSecret' in client)
|
|
137
|
+
args.push('--client-secret', client.clientSecret);
|
|
138
|
+
if (!args.find(a => a === '--username') && 'username' in client)
|
|
139
|
+
args.push('--username', client.username);
|
|
140
|
+
if (!args.find(a => a === '--password') && 'password' in client)
|
|
141
|
+
args.push('--password', client.password);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return args;
|
|
145
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import Zip from 'adm-zip';
|
|
3
|
+
import type { Endpoints } from '@octokit/types';
|
|
4
|
+
import HttpProvider from '~/providers/HttpProvider';
|
|
5
|
+
import {
|
|
6
|
+
checkDir,
|
|
7
|
+
joinPath,
|
|
8
|
+
removeDirectory,
|
|
9
|
+
removeFile,
|
|
10
|
+
} from './file-provider';
|
|
11
|
+
import { doRetry } from '~/util/fetch';
|
|
12
|
+
|
|
13
|
+
type GitHubApiRelease =
|
|
14
|
+
Endpoints['GET /repos/{owner}/{repo}/releases/latest']['response']['data'];
|
|
15
|
+
|
|
16
|
+
class GitHubCliModuleProvider {
|
|
17
|
+
http: HttpProvider;
|
|
18
|
+
repo: string;
|
|
19
|
+
baseUrl = 'https://api.github.com/repos';
|
|
20
|
+
|
|
21
|
+
get releases_url() {
|
|
22
|
+
return `${this.baseUrl}/${this.repo}/releases`;
|
|
23
|
+
}
|
|
24
|
+
get latest_release_url() {
|
|
25
|
+
return `${this.baseUrl}/${this.repo}/releases/latest`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
download?: {
|
|
29
|
+
tag: string;
|
|
30
|
+
name: string;
|
|
31
|
+
url: string;
|
|
32
|
+
browser_url: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
constructor(repo: string) {
|
|
36
|
+
this.http = new HttpProvider();
|
|
37
|
+
this.repo = repo;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async FindLatestRelease() {
|
|
41
|
+
const { http, latest_release_url, releases_url } = this;
|
|
42
|
+
// return latest tag version is:
|
|
43
|
+
|
|
44
|
+
const responses = await Promise.all([
|
|
45
|
+
http.get<GitHubApiRelease>(latest_release_url, {
|
|
46
|
+
doRetry: doRetry({ silent: true }),
|
|
47
|
+
}),
|
|
48
|
+
http.get<GitHubApiRelease[]>(releases_url),
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const [latestErr, latest, latestResponse] = responses[0];
|
|
52
|
+
const [releasesErr, releases] = responses[1];
|
|
53
|
+
|
|
54
|
+
if (releasesErr) {
|
|
55
|
+
throw new Error(`Unable to get releases`, { cause: releasesErr });
|
|
56
|
+
} else if (!releases || releases.length === 0)
|
|
57
|
+
throw new Error(`No releases available`);
|
|
58
|
+
else if (latestErr && !latest) {
|
|
59
|
+
if (latestResponse?.status === 404 && releases?.length) {
|
|
60
|
+
// No latest release, check releases for prerelease version, fallback to last release
|
|
61
|
+
const release = releases.find(r => r.prerelease) || releases[0];
|
|
62
|
+
|
|
63
|
+
if (release) {
|
|
64
|
+
return release;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
return latest;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async DownloadRelease(
|
|
73
|
+
release: GitHubApiRelease,
|
|
74
|
+
{
|
|
75
|
+
path,
|
|
76
|
+
platforms,
|
|
77
|
+
unzip = true,
|
|
78
|
+
}: { path: string; unzip?: boolean; platforms: [NodeJS.Platform, string][] }
|
|
79
|
+
) {
|
|
80
|
+
// find os-specific asset
|
|
81
|
+
const platform = platforms.find(p => p[0] === os.platform()) || [
|
|
82
|
+
os.platform(),
|
|
83
|
+
os.platform(),
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const asset = release.assets.find(r =>
|
|
87
|
+
r.name.toLowerCase().includes(platform[1])
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// download asset
|
|
91
|
+
if (asset) {
|
|
92
|
+
const filePath = joinPath(path, asset.name);
|
|
93
|
+
removeDirectory(path);
|
|
94
|
+
checkDir(filePath);
|
|
95
|
+
await this.http.downloadFile(asset.browser_download_url, filePath);
|
|
96
|
+
|
|
97
|
+
if (unzip && asset.name.endsWith('.zip')) {
|
|
98
|
+
// unzip the downloaded file
|
|
99
|
+
const zipFile = new Zip(filePath);
|
|
100
|
+
zipFile.extractAllTo(path);
|
|
101
|
+
|
|
102
|
+
// delete the downloaded zip file
|
|
103
|
+
removeFile(filePath);
|
|
104
|
+
}
|
|
105
|
+
} else
|
|
106
|
+
throw new Error(
|
|
107
|
+
`no asset found in release ${
|
|
108
|
+
release.tag_name
|
|
109
|
+
} for platform ${os.platform()}\n${release.html_url}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default GitHubCliModuleProvider;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import to from 'await-to-js';
|
|
2
|
+
import { FetchInit } from 'enterprise-fetch';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { Readable } from 'stream';
|
|
5
|
+
import { finished } from 'stream/promises';
|
|
6
|
+
|
|
7
|
+
import { isJson, tryParse } from '~/util';
|
|
8
|
+
import { enhancedFetch } from '~/util/fetch';
|
|
9
|
+
class HttpProvider {
|
|
10
|
+
constructor() {}
|
|
11
|
+
|
|
12
|
+
async get<T = any>(url: string, init: FetchInit = {}) {
|
|
13
|
+
return this.fetch<T>(url, { method: 'GET', ...init });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async fetch<T = any>(
|
|
17
|
+
uri: string,
|
|
18
|
+
init: FetchInit = {}
|
|
19
|
+
): Promise<[Error | null, T | undefined, Response | undefined]> {
|
|
20
|
+
const [error, response] = await to(enhancedFetch(uri, init));
|
|
21
|
+
|
|
22
|
+
if (response && !error) {
|
|
23
|
+
const [bodyError, text] = await to(response.text());
|
|
24
|
+
if (bodyError) return [bodyError, undefined, response];
|
|
25
|
+
if (isJson(text)) {
|
|
26
|
+
const err =
|
|
27
|
+
!response.status || !response.ok ? tryParse(text) : undefined;
|
|
28
|
+
const payload =
|
|
29
|
+
response.status && response.ok ? tryParse(text) : undefined;
|
|
30
|
+
return [err, payload, response];
|
|
31
|
+
}
|
|
32
|
+
return [
|
|
33
|
+
response.ok ? null : new Error(text),
|
|
34
|
+
response.ok ? (text as unknown as T) : undefined,
|
|
35
|
+
response,
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
return [error, undefined, response];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async downloadFile(url: string, destination: string) {
|
|
42
|
+
const res = await fetch(url);
|
|
43
|
+
if (res.ok && res.body !== null) {
|
|
44
|
+
const fileStream = fs.createWriteStream(destination, { flags: 'wx' });
|
|
45
|
+
await finished(Readable.fromWeb(res.body as any).pipe(fileStream));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default HttpProvider;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { tryParse } from '~/util';
|
|
2
|
+
import { appPath, readFile, writeFile } from './file-provider';
|
|
3
|
+
|
|
4
|
+
export type CliModule = {
|
|
5
|
+
github: string;
|
|
6
|
+
version: string;
|
|
7
|
+
install?: string;
|
|
8
|
+
cmd?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type CliManifest = {
|
|
12
|
+
[moduleName: string]: CliModule;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const MANIFEST_PATH = appPath('cli-manifest.json');
|
|
16
|
+
|
|
17
|
+
class ManifestProvider {
|
|
18
|
+
private manifest: CliManifest;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
const manifest = tryParse(readFile(MANIFEST_PATH));
|
|
22
|
+
this.manifest = manifest || {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get() {
|
|
26
|
+
return this.manifest;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getModule(name: string) {
|
|
30
|
+
return this.manifest?.[name];
|
|
31
|
+
}
|
|
32
|
+
writeModule(name: string, moduleInfo: CliModule) {
|
|
33
|
+
if (this.manifest) this.manifest[name] = moduleInfo;
|
|
34
|
+
else
|
|
35
|
+
this.manifest = {
|
|
36
|
+
[name]: moduleInfo,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
writeFile(MANIFEST_PATH, JSON.stringify(this.manifest, null, 2));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default ManifestProvider;
|
|
@@ -50,6 +50,13 @@ export const removeFile = (filePath: string) => {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
export const removeDirectory = (filePath: string) => {
|
|
54
|
+
const directoryPath = appPath(filePath);
|
|
55
|
+
if (fs.existsSync(directoryPath)) {
|
|
56
|
+
fs.rmSync(directoryPath, { force: true, recursive: true });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
53
60
|
export const moveFile = (file: string, fromPath: string, toPath: string) => {
|
|
54
61
|
const from = path.join(appRootDir, `${fromPath}${file}`);
|
|
55
62
|
const to = path.join(appRootDir, `${toPath}${file}`);
|
|
@@ -84,6 +91,8 @@ export const appPath = (filePath: string) =>
|
|
|
84
91
|
export const cwdPath = (filePath: string) =>
|
|
85
92
|
path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath);
|
|
86
93
|
|
|
94
|
+
export const joinPath = path.join;
|
|
95
|
+
|
|
87
96
|
type DetectedFileType =
|
|
88
97
|
| { type: 'json'; contents: any }
|
|
89
98
|
| { type: 'xml' | 'csv'; contents: string };
|