-
Notifications
You must be signed in to change notification settings - Fork 0
/
software-heritage.ts
78 lines (65 loc) · 2.46 KB
/
software-heritage.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
import { basename } from "path";
import { exit } from "process";
import { softwareHeritageToken } from "./env";
import { error, info, log, sleep, success, warning } from "./util";
/** archive repos at software heritage */
const archiveRepos = async (urls: string[]) => {
info(`Archiving repos at SoftwareHeritage.org`);
/** archive repos one at a time */
for (const [index, url] of Object.entries(urls)) {
const name = basename(url);
info(`Archiving repo ${name}, ${Number(index) + 1} of ${urls.length}`);
await archiveRepo(url);
}
};
/** standard headers */
const headers = new Headers();
if (softwareHeritageToken)
headers.set("Authorization", `Bearer ${softwareHeritageToken}`);
/** submit a repo url to be archived at software heritage */
const archiveRepo = async (repo: string) => {
/** retries, with hard limit */
for (let retry = 0; retry < 5; retry++) {
if (retry) log(`Retry ${retry}`);
try {
{
/** submit to api to be archived */
const url = `https://archive.softwareheritage.org/api/1/origin/save/git/url/${repo}/`;
const options = { method: "POST", headers };
const response = await (await fetch(url, options)).json();
/** handle rate limit exception as warning */
if (response.exception === "Throttled") {
warning(response.reason);
/** expected format: "Request was throttled. Expected available in 120 seconds." */
let wait = Number(response.reason.match(/(\d+) seconds/)?.[1]) || 60;
/** add a bit of extra wait time to account for estimate and sleep timer inaccuracy */
wait += 10;
wait *= 1.05;
log(
"Trying again in " +
(wait >= 60
? `~${Math.round(wait / 60)} min(s)`
: `${wait} seconds(s)`),
);
await sleep(wait * 1000);
continue;
} else if (response.exception) {
/** for all other exceptions, throw critical error */
throw Error(`${response.exception}: ${response.reason}`);
}
/** if accepted, finish */
if (response.save_request_status === "accepted") {
success("Successfully submitted for archiving");
return;
}
/** catch all other errors */
throw Error(JSON.stringify(response, null, 2));
}
} catch (message) {
/** catch critical errors and exit */
error(message);
exit(1);
}
}
};
export { archiveRepos };