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