Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/package/src/common/archive-binary-dependencies.ts
6450 views
1
/*
2
* archive-binary-dependencies.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
import { join } from "../../../src/deno_ral/path.ts";
7
import { info } from "../../../src/deno_ral/log.ts";
8
import { logPandoc } from "../../../src/core/log.ts";
9
10
import { execProcess } from "../../../src/core/process.ts";
11
import { Configuration, withWorkingDir } from "./config.ts";
12
import {
13
ArchitectureDependency,
14
Dependency,
15
kDependencies,
16
PlatformDependency,
17
} from "./dependencies/dependencies.ts";
18
19
const kBucket = "s3://rstudio-buildtools/quarto";
20
const kBucketBaseUrl = "https://s3.amazonaws.com/rstudio-buildtools/quarto";
21
22
// Provides a URL in the archive for a dependency
23
export function archiveUrl(
24
dependency: Dependency,
25
platformDependency: PlatformDependency,
26
) {
27
// TODO: Deal w/archive bin deps for this
28
if (dependency.bucket === "deno") {
29
return platformDependency.url + platformDependency.filename;
30
} else {
31
return `${kBucketBaseUrl}/${dependency.bucket}/${dependency.version}/${platformDependency.filename}`;
32
}
33
}
34
35
// Archives dependencies (if they are not present in the archive already)
36
export async function archiveBinaryDependencies(_config: Configuration) {
37
await withWorkingDir(async (workingDir) => {
38
await logPandoc(`## Updating binary dependencies`);
39
40
for (const dependency of kDependencies) {
41
await archiveBinaryDependency(dependency, workingDir);
42
}
43
});
44
}
45
46
// Archives dependencies (if they are not present in the archive already)
47
export async function checkBinaryDependencies(_config: Configuration) {
48
await withWorkingDir(async (workingDir) => {
49
await logPandoc(`## Checking binary dependencies`);
50
51
for (const dependency of kDependencies) {
52
await checkBinaryDependency(dependency, workingDir);
53
}
54
});
55
}
56
57
export async function checkBinaryDependency(
58
dependency: Dependency,
59
workingDir: string,
60
) {
61
info(`** ${dependency.name} ${dependency.version} **`);
62
63
const dependencyBucketPath = `${dependency.bucket}/${dependency.version}`;
64
info("Checking archive status...\n");
65
66
const archive = async (
67
architectureDependency: ArchitectureDependency,
68
) => {
69
const platformDeps = [
70
architectureDependency.darwin,
71
architectureDependency.linux,
72
architectureDependency.windows,
73
];
74
for (const platformDep of platformDeps) {
75
if (platformDep) {
76
// This dependency doesn't exist, archive it
77
info(
78
`Checking ${dependencyBucketPath} - ${platformDep.filename}`,
79
);
80
81
// Download the file
82
await download(
83
workingDir,
84
platformDep,
85
);
86
}
87
}
88
};
89
90
for (const arch of Object.keys(dependency.architectureDependencies)) {
91
info(`Checking ${dependency.name}`);
92
const archDep = dependency.architectureDependencies[arch];
93
await archive(archDep);
94
}
95
96
info("");
97
}
98
99
export async function archiveBinaryDependency(
100
dependency: Dependency,
101
workingDir: string,
102
) {
103
await logPandoc(`## ${dependency.name} ${dependency.version}\n\nChecking archive status...`, "markdown");
104
105
const dependencyBucketPath = `${dependency.bucket}/${dependency.version}`;
106
107
const archive = async (
108
architectureDependency: ArchitectureDependency,
109
) => {
110
const platformDeps = [
111
architectureDependency.darwin,
112
architectureDependency.linux,
113
architectureDependency.windows,
114
];
115
for (const platformDep of platformDeps) {
116
if (platformDep) {
117
118
const dependencyAwsPath =
119
`${kBucket}/${dependencyBucketPath}/${platformDep.filename}`;
120
await logPandoc(`Checking \`${dependencyAwsPath}\``, "markdown");
121
const response = await s3cmd("ls", [dependencyAwsPath]);
122
if (response?.includes('Unable to locate credentials')) {
123
throw new Error("Unable to locate S3 credentials, please try again.");
124
}
125
126
127
if (!response) {
128
// This dependency doesn't exist, archive it
129
await logPandoc(
130
`Archiving \`${dependencyBucketPath}\` - ${platformDep.filename}`,
131
);
132
133
// Download the file
134
const localPath = await download(
135
workingDir,
136
platformDep,
137
);
138
139
// Sync to S3
140
info(`Copying to ${dependencyAwsPath}\n`);
141
const result = await s3cmd("cp", [
142
localPath,
143
dependencyAwsPath,
144
"--acl",
145
"public-read",
146
]);
147
info(` (Response): ${result}`);
148
} else {
149
info(` ${dependencyAwsPath.split("/").slice(-1)[0]} already archived.\n`);
150
}
151
}
152
}
153
};
154
155
for (const arch of Object.keys(dependency.architectureDependencies)) {
156
const archDep = dependency.architectureDependencies[arch];
157
await archive(archDep);
158
}
159
}
160
161
async function s3cmd(cmd: string, args: string[]) {
162
const s3Args = ["s3", cmd, ...args];
163
const p = await execProcess({
164
cmd: "aws",
165
args: s3Args,
166
stdout: "piped",
167
});
168
169
return p.stdout || p.stderr;
170
}
171
172
async function download(
173
workingDir: string,
174
dependency: PlatformDependency,
175
) {
176
info("Downloading " + dependency.url);
177
const response = await fetch(dependency.url);
178
if (response.status === 200) {
179
const blob = await response.blob();
180
181
const bytes = await blob.arrayBuffer();
182
const data = new Uint8Array(bytes);
183
184
const targetPath = join(workingDir, dependency.filename);
185
Deno.writeFileSync(
186
targetPath,
187
data,
188
);
189
return targetPath;
190
} else {
191
throw new Error(
192
`Failed to fetch dependency ${dependency.filename}.\nThe url ${dependency.url} returned a non 200 status code:\n${response.status} - ${response.statusText}`,
193
);
194
}
195
}
196
197