generated from actions/typescript-action
-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathutils.ts
98 lines (83 loc) · 3.43 KB
/
utils.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import * as tc from '@actions/tool-cache'
import * as httpm from 'typed-rest-client/HttpClient';
import { BearerCredentialHandler } from 'typed-rest-client/Handlers';
import { IHttpClientResponse } from 'typed-rest-client/Interfaces';
export async function get_response(url: string, token: string = '') {
const bearer = token ? [ new BearerCredentialHandler(token) ] : undefined;
const timeouts = [5000, 10000, 20000];
let retry = 0;
let res: IHttpClientResponse | undefined = undefined;
for (; retry < 3; retry++) {
const client = new httpm.HttpClient("dlang-community/setup-dlang", bearer);
res = await client.get(url);
// redirects are followed by the library, check for error codes here
const statusCode = res?.message?.statusCode ?? 500;
if (statusCode >= 400) {
await new Promise(resolve => setTimeout(resolve, timeouts[retry]));
continue;
}
return await res
}
throw new Error(`failed requesting ${url} - aborting after ${retry} tries\n${res?.message.statusCode} ${res?.message.statusMessage}:\n${res?.message.rawHeaders.join('\n')}\n\n${(await res?.readBody())?.trim()}`);
}
export async function body_as_text(url: string, token: string = ''): Promise<string> {
let response = await get_response(url, token)
let body = await response.readBody()
return body.trim()
}
export async function extract(format: string, archive: string, into?: string) {
if (format.endsWith(".7z"))
return await tc.extract7z(archive, into);
else if (format.endsWith(".zip"))
return await tc.extractZip(archive, into);
else if (/\.tar(\.\w+)?$/.test(format))
return await tc.extractTar(archive, into, 'x');
throw new Error("unsupported archive format: " + format);
}
/** Interface for a github tag as exposed by the rest api */
export interface IGithubTag {
name: string
zipball_url: string
tarball_url: string
commit: {
sha: string
urm: string
}
node_id: string
}
/** Visit all the tags of a repository
@param url - The github api url, for example:
https://api.github.com/repository/dlang/dmd
@param tagVisitor - A function that takes in an IGithubTag and
returns true to keep visiting or false to stop
@returns the number of pages fully visited. This can be used to
remember where the query left of if fetching tags repeatedly.
*/
export async function visitTags(url: string, token: string, tagVisitor: (tag: IGithubTag) => boolean): Promise<number> {
// "inspired" by https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api?apiVersion=2022-11-28#example-creating-a-pagination-method
let response = await get_response(url, token)
const nextPattern = /(?<=<)([\S]*)(?=>; rel="Next")/i;
let pagesRemaining = true;
let pagesVisited = 0;
while (pagesRemaining) {
const response = await get_response(url, token);
const parsedData = JSON.parse(await response.readBody())
let i: number;
for (i = 0; i < parsedData.length; ++ i) {
if (!tagVisitor(parsedData[i])) {
// Finish early
break
}
}
if (i != parsedData.length) {
break
}
++ pagesVisited
const linkHeader = <string>response.message.rawHeaders[response.message.rawHeaders.indexOf('Link') + 1]
pagesRemaining = linkHeader.length != 0 && linkHeader.includes(`rel=\"next\"`);
if (pagesRemaining) {
url = linkHeader.match(nextPattern)![0]
}
}
return pagesVisited
}