Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/tools/tools-console.ts
6438 views
1
/*
2
* cmd.ts
3
*
4
* Copyright (C) 2021-2022 Posit Software, PBC
5
*/
6
import * as colors from "fmt/colors";
7
import { Confirm, prompt, Select } from "cliffy/prompt/mod.ts";
8
import { Table } from "cliffy/table/mod.ts";
9
import { info, warning } from "../deno_ral/log.ts";
10
11
import {
12
allTools,
13
installTool,
14
toolSummary,
15
uninstallTool,
16
updateTool,
17
} from "../tools/tools.ts";
18
19
import { withSpinner } from "../core/console.ts";
20
import {
21
InstallableTool,
22
RemotePackageInfo,
23
ToolSummaryData,
24
} from "../tools/types.ts";
25
26
interface ToolInfo {
27
tool: InstallableTool;
28
installed: boolean;
29
version?: string;
30
latest: RemotePackageInfo;
31
}
32
33
export async function outputTools() {
34
const toolRows: string[][] = [];
35
const statusMsgs: string[] = [];
36
37
// Reads the status
38
const installStatus = (summary: ToolSummaryData): string => {
39
if (summary.installed) {
40
if (summary.installedVersion) {
41
if (summary.installedVersion === summary.latestRelease.version) {
42
return "Up to date";
43
} else {
44
return "Update available";
45
}
46
} else {
47
return "External Installation";
48
}
49
} else {
50
return "Not installed";
51
}
52
};
53
54
// The column widths for output (in chars)
55
const tools = await loadTools();
56
for (const tool of tools) {
57
const summary = await toolSummary(tool.tool.name);
58
if (summary) {
59
const toolDetails = [
60
tool.tool.name.toLowerCase(),
61
installStatus(summary),
62
summary.installedVersion || "---",
63
summary.latestRelease.version,
64
];
65
toolRows.push(toolDetails);
66
67
if (summary.configuration.status !== "ok") {
68
statusMsgs.push(
69
`${summary.configuration.message}`,
70
);
71
}
72
}
73
}
74
75
info("");
76
// Write the output
77
const table = new Table().header([
78
colors.bold("Tool"),
79
colors.bold("Status"),
80
colors.bold("Installed"),
81
colors.bold("Latest"),
82
]).body(
83
toolRows,
84
).padding(5);
85
info(table.toString());
86
statusMsgs.forEach((msg) => {
87
warning(msg);
88
});
89
}
90
91
export async function loadTools(): Promise<ToolInfo[]> {
92
let sorted: ToolInfo[] = [];
93
await withSpinner({ message: "Inspecting tools" }, async () => {
94
const all = await allTools();
95
const toolsWithInstall = [{
96
tools: all.installed,
97
installed: true,
98
}, {
99
tools: all.notInstalled,
100
installed: false,
101
}];
102
103
const toolInfos = [];
104
for (const toolWithInstall of toolsWithInstall) {
105
for (const tool of toolWithInstall.tools) {
106
const version = await tool.installedVersion();
107
const latest = await tool.latestRelease();
108
toolInfos.push({
109
tool,
110
version,
111
installed: toolWithInstall.installed,
112
latest,
113
});
114
}
115
}
116
117
sorted = toolInfos.sort((tool1, tool2) => {
118
return tool1.tool.name.localeCompare(tool2.tool.name);
119
});
120
});
121
return sorted;
122
}
123
124
export async function afterConfirm(
125
message: string,
126
action: () => Promise<void>,
127
prompt?: boolean,
128
) {
129
if (prompt !== false) {
130
const confirmed: boolean = await Confirm.prompt(
131
{
132
message,
133
default: true,
134
},
135
);
136
if (confirmed) {
137
info("");
138
return action();
139
} else {
140
return Promise.resolve();
141
}
142
} else {
143
return action();
144
}
145
}
146
147
export const removeTool = (
148
toolname: string,
149
prompt?: boolean,
150
updatePath?: boolean,
151
) => {
152
return afterConfirm(
153
`Are you sure you'd like to remove ${toolname}?`,
154
() => {
155
return uninstallTool(toolname, updatePath);
156
},
157
prompt,
158
);
159
};
160
161
export async function updateOrInstallTool(
162
tool: string,
163
action: "update" | "install",
164
prompt?: boolean,
165
updatePath?: boolean,
166
) {
167
const summary = await toolSummary(tool);
168
169
if (action === "update") {
170
if (!summary?.installed) {
171
return afterConfirm(
172
`${tool} is not installed. Do you want to install it now?`,
173
() => {
174
return installTool(tool, updatePath);
175
},
176
prompt,
177
);
178
} else {
179
if (summary.installedVersion === undefined) {
180
info(
181
`${tool} was not installed using Quarto. Please use the tool that you used to install ${tool} instead.`,
182
);
183
} else if (summary.installedVersion === summary.latestRelease.version) {
184
info(`${tool} is already up to date.`);
185
} else {
186
return afterConfirm(
187
`Do you want to update ${tool} from ${summary.installedVersion} to ${summary.latestRelease.version}?`,
188
() => {
189
return updateTool(tool);
190
},
191
prompt,
192
);
193
}
194
}
195
} else {
196
if (summary && summary.installed) {
197
if (summary.installedVersion === summary.latestRelease.version) {
198
info(`${tool} is already installed and up to date.`);
199
} else {
200
return afterConfirm(
201
`${tool} is already installed. Do you want to update to ${summary.latestRelease.version}?`,
202
() => {
203
return updateTool(tool);
204
},
205
prompt,
206
);
207
}
208
} else {
209
return installTool(tool, updatePath);
210
}
211
}
212
}
213
214
export async function selectTool(
215
toolsInfo: ToolInfo[],
216
action: "install" | "update" | "remove",
217
) {
218
const name = (toolInfo: ToolInfo) => {
219
if (action === "install" || action == "update") {
220
if (toolInfo.installed) {
221
return `${toolInfo.tool.name}${
222
toolInfo.version ? " (" + toolInfo.version + " installed)" : ""
223
}`;
224
} else {
225
return `${toolInfo.tool.name}${
226
toolInfo.latest.version ? " (" + toolInfo.latest.version + ")" : ""
227
}`;
228
}
229
} else {
230
if (!toolInfo.installed) {
231
return `${toolInfo.tool.name} (not installed)`;
232
} else {
233
return `${toolInfo.tool.name}${
234
toolInfo.version ? " (" + toolInfo.version + " installed)" : ""
235
}`;
236
}
237
}
238
};
239
240
const result = await prompt([{
241
name: "tool",
242
message: `Select a tool to ${action}`,
243
options: toolsInfo.map((toolInfo) => {
244
return {
245
name: name(toolInfo),
246
value: toolInfo.tool.name.toLowerCase(),
247
disabled: action === "install"
248
? toolInfo.installed
249
: !toolInfo.installed,
250
};
251
}),
252
type: Select,
253
}]);
254
return result.tool;
255
}
256
257