/
index.ts
113 lines (108 loc) · 3.11 KB
/
index.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { logger } from '../../logger';
import * as globalCache from '../../util/cache/global';
import { Http } from '../../util/http';
import {
DatasourceError,
GetReleasesConfig,
Release,
ReleaseResult,
} from '../common';
export const id = 'crate';
const http = new Http(id);
export async function getReleases({
lookupName,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
const cacheNamespace = 'datasource-crate';
const cacheKey = lookupName;
const cachedResult = await globalCache.get<ReleaseResult>(
cacheNamespace,
cacheKey
);
// istanbul ignore if
if (cachedResult) {
return cachedResult;
}
const len = lookupName.length;
let path: string;
// Ignored because there is no way to test this without hitting up GitHub API
/* istanbul ignore next */
if (len === 1) {
path = '1/' + lookupName;
} else if (len === 2) {
path = '2/' + lookupName;
} else if (len === 3) {
path = '3/' + lookupName[0] + '/' + lookupName;
} else {
path =
lookupName.slice(0, 2) + '/' + lookupName.slice(2, 4) + '/' + lookupName;
}
const baseUrl =
'https://raw.githubusercontent.com/rust-lang/crates.io-index/master/';
const crateUrl = baseUrl + path;
try {
let res: any = await http.get(crateUrl);
if (!res || !res.body) {
logger.warn(
{ dependency: lookupName },
`Received invalid crate data from ${crateUrl}`
);
return null;
}
res = res.body;
res = res.split('\n');
res = res.map((line) => line.trim()).filter((line) => line.length !== 0);
if (res.length === 0) {
logger.warn(
{ dependency: lookupName },
`Received empty list from ${crateUrl}`
);
return null;
}
// Filter empty lines (takes care of trailing \n)
// eslint-disable-next-line @typescript-eslint/unbound-method
res = res.map(JSON.parse);
if (res[0].name !== lookupName) {
logger.warn(
{ dependency: lookupName },
`Received invalid crate name from ${crateUrl}`
);
return null;
}
if (!res[0].vers) {
logger.warn(
{ dependency: lookupName },
`Recieved invalid data (vers field doesn't exist) from ${crateUrl}`
);
return null;
}
const result: ReleaseResult = {
releases: [],
};
result.releases = res.map((version: { vers: string; yanked: boolean }) => {
const release: Release = {
version: version.vers,
};
if (version.yanked) {
release.isDeprecated = true;
}
return release;
});
const cacheMinutes = 10;
await globalCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
return result;
} catch (err) {
if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
logger.debug({ lookupName }, `Dependency lookup failure: not found`);
logger.debug({ err }, 'Crate lookup error');
return null;
}
if (
err.statusCode === 429 ||
(err.statusCode >= 500 && err.statusCode < 600)
) {
throw new DatasourceError(err);
}
logger.warn({ err, lookupName }, 'crates.io lookup failure: Unknown error');
return null;
}
}