Skip to content

Commit a5c8a6d

Browse files
authored
Merge pull request #2 from pkgjs/detect
Detect node versions from .travis.yml
2 parents 56abd46 + 5dbe715 commit a5c8a6d

25 files changed

+1880
-23
lines changed

README.md

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ const result = await require('node-support').detect({ path });
3333
`path` should be a folder in the local file system. When the path is not a git repository - tries to read the git repository from `package.json` and tries to detect the versions listed in the repository as well.
3434

3535
```
36-
const result = await require('node-support').detect({ package });
36+
const result = await require('node-support').detect({ packageName });
3737
```
3838

39-
`package` is a string name for the package in the registry.
39+
`packageName` is a string name for the package in the registry.
4040

4141
```
4242
const result = await require('node-support').detect({ repository });
@@ -47,40 +47,47 @@ const result = await require('node-support').detect({ repository });
4747
### Result
4848

4949
- Throws if the `path` / `repository` does not have a `package.json`
50-
- Throws if `package` does not exist in the registry
50+
- Throws if `packageName` does not exist in the registry
51+
- Throws when unable to detect a git repository for the package
5152

5253
Otherwise returns an object with:
5354

5455
```javascript
5556
const result = {
5657

57-
// the "name" field of the `package.json`
58-
name: "package-name",
58+
// the `name` field of the `package.json`
59+
"name": "package-name",
5960

60-
// the "version" field of the `package.json` when used with `path` / `repository`,
61+
// the `version` field of the `package.json` when used with `path` / `repository`,
6162
// the `latest` dist-tag version when used with `package`
62-
version: "0.0.0",
63+
"version": "0.0.0",
6364

6465
// the current time when the result is returned
65-
timestamp: 1577115956099,
66+
"timestamp": 1577115956099,
6667

6768
// git commit hash of the repository HEAD at the time of scanning
68-
// will be left out when no git repository detected
69-
commit: "2de28c8c4ab8ac998d403509123736929131908c",
69+
"commit": "2de28c8c4ab8ac998d403509123736929131908c",
7070

7171
// will be left out when not present in the `package.json`
7272
// a copy of the `engines.node` field from the `package.json` if present
73-
engines: ">=x.y.z",
73+
"engines": ">=x.y.z",
7474

7575
// will be left out when `.travis.yml` file is not present
76-
travis: {
76+
"travis": {
7777
// the list of versions as detected by inspecting `node_js` / `matrix` configuration
7878
// will be an empty array when no versions are detected or the project is not a Node.js project
7979
// will contain "latest" when `language: node_js` specified, but no explicit versions detected
80-
raw: ["8", "10", "lts/*"],
81-
82-
// raw version specifiers and keywords resolved to exact Node.js versions
83-
resolved: ["8.17.0", "10.18.0", "12.14.0"]
80+
"raw": ["8", "10", "lts/*", "invalid-specifier"],
81+
82+
// raw version specifiers and keywords (as keys) resolved to exact Node.js versions (as values)
83+
// the value will be `false` when the specifier/keyword is unrecognized
84+
// will be an empty object when the `raw` array is empty
85+
"resolved": {
86+
"8": "8.17.0",
87+
"10": "10.18.0",
88+
"lts/*": "12.14.0",
89+
"invalid-specifier": false
90+
}
8491
}
8592
}
8693
```

bin/node-support

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
const Fs = require('fs');
6+
const NodeSupport = require('..');
7+
const { URL } = require('url');
8+
9+
10+
const internals = {};
11+
12+
13+
internals.autoDetect = (what) => {
14+
15+
if (!what) {
16+
return NodeSupport.detect({ path: '.' });
17+
}
18+
19+
try {
20+
var url = new URL(what);
21+
}
22+
catch (err) {
23+
if (err.code !== 'ERR_INVALID_URL') {
24+
throw err;
25+
}
26+
}
27+
28+
if (url) {
29+
return NodeSupport.detect({ repository: url.href });
30+
}
31+
32+
if (Fs.existsSync(what)) {
33+
return NodeSupport.detect({ path: what });
34+
}
35+
36+
if (what.includes('/') && !what.startsWith('@')) {
37+
return NodeSupport.detect({ repository: `https://github.com/${what}` });
38+
}
39+
40+
return NodeSupport.detect({ packageName: what });
41+
};
42+
43+
exports.main = async (nodeBin, thisBin, what) => {
44+
45+
const result = await internals.autoDetect(what);
46+
47+
console.log(result);
48+
};
49+
50+
exports.main(...process.argv)
51+
.catch((err) => {
52+
53+
console.error(err);
54+
process.exit(1);
55+
});

lib/engines.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
3+
exports.detect = ({ engines }) => {
4+
5+
if (engines) {
6+
7+
return {
8+
engines: engines.node
9+
};
10+
}
11+
};

lib/index.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
'use strict';
22

3-
module.exports = () => {
3+
const Engines = require('./engines');
4+
const Package = require('./package');
5+
const Travis = require('./travis');
46

5-
throw new Error('Not implemented');
7+
exports.detect = async ({ path, repository, packageName }) => {
8+
9+
const packageInfo = await Package.detect({ path, repository, packageName });
10+
11+
const result = {};
12+
13+
result.name = packageInfo.name;
14+
result.version = packageInfo.version;
15+
result.commit = await packageInfo.getCommit();
16+
result.timestamp = Date.now();
17+
18+
const travis = await Travis.detect(packageInfo);
19+
const engines = await Engines.detect(packageInfo);
20+
21+
Object.assign(result, travis, engines);
22+
23+
return result;
624
};

lib/loader.js

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
'use strict';
2+
3+
const Fs = require('fs');
4+
const GitUrlParse = require('git-url-parse');
5+
const Package = require('../package.json');
6+
const Pacote = require('pacote');
7+
const Path = require('path');
8+
const Wreck = require('@hapi/wreck');
9+
10+
const Utils = require('./utils');
11+
12+
const internals = {};
13+
14+
15+
internals.parseRepository = (packument) => {
16+
17+
if (typeof packument.repository === 'string') {
18+
return packument.repository;
19+
}
20+
21+
if (!packument.repository || !packument.repository.url) {
22+
throw new Error(`Unable to determine the git repository for ${packument.name}`);
23+
}
24+
25+
return packument.repository.url;
26+
};
27+
28+
29+
internals.createPackageLoader = async (packageName) => {
30+
31+
try {
32+
const packument = await Pacote.packument(packageName + '@latest', {
33+
'fullMetadata': true,
34+
'user-agent': `${Package.name}@${Package.version}, see ${Package.homepage}`
35+
});
36+
37+
const repository = internals.parseRepository(packument);
38+
39+
const repositoryLoader = internals.createRepositoryLoader(repository);
40+
41+
return {
42+
...repositoryLoader,
43+
loadFile: async (filename, options) => {
44+
45+
const result = await repositoryLoader.loadFile(filename, options);
46+
47+
if (filename === 'package.json' && result.name !== packageName) {
48+
throw new Error(`${repository} does not contain ${packageName}`);
49+
}
50+
51+
return result;
52+
}
53+
};
54+
}
55+
catch (err) {
56+
57+
if (err.statusCode === 404) {
58+
throw new Error(`Package ${packageName} does not exist`);
59+
}
60+
61+
throw err;
62+
63+
}
64+
};
65+
66+
67+
internals.createRepositoryLoader = (repository) => {
68+
69+
const parsedRepository = GitUrlParse(repository);
70+
71+
return {
72+
getCommit: async () => {
73+
74+
const simpleGit = Utils.simpleGit();
75+
const httpRepository = GitUrlParse.stringify(parsedRepository, 'http');
76+
const result = await simpleGit.listRemote([httpRepository, 'HEAD']);
77+
const [head] = result.split(/\s+/);
78+
79+
return head;
80+
},
81+
loadFile: async (filename, options) => {
82+
83+
if (parsedRepository.source !== 'github.com') {
84+
throw new Error('Only github.com paths supported, feel free to PR at https://github.com/pkgjs/node-support');
85+
}
86+
87+
const url = `https://raw.githubusercontent.com/${parsedRepository.full_name}/HEAD/${filename}`;
88+
89+
try {
90+
const { payload } = await Wreck.get(url, options);
91+
92+
return payload;
93+
}
94+
catch (err) {
95+
96+
if (err.output && err.output.statusCode === 404) {
97+
throw new Error(`${repository} does not contain a ${filename}`);
98+
}
99+
100+
throw err;
101+
}
102+
}
103+
};
104+
};
105+
106+
107+
internals.createPathLoader = async (path) => {
108+
109+
const simpleGit = Utils.simpleGit(path);
110+
const isRepo = await simpleGit.checkIsRepo();
111+
112+
if (!isRepo) {
113+
throw new Error(`${path} is not a git repository`);
114+
}
115+
116+
if (!Fs.existsSync(Path.join(path, 'package.json'))) {
117+
throw new Error(`${path} does not contain a package.json`);
118+
}
119+
120+
return {
121+
getCommit: () => {
122+
123+
return simpleGit.revparse(['HEAD']);
124+
},
125+
loadFile: (filename, options = {}) => {
126+
127+
const fullPath = Path.join(path, filename);
128+
129+
if (!Fs.existsSync(fullPath)) {
130+
return;
131+
}
132+
133+
const buffer = Fs.readFileSync(fullPath);
134+
135+
if (options.json) {
136+
return JSON.parse(buffer.toString());
137+
}
138+
139+
return buffer;
140+
}
141+
};
142+
};
143+
144+
145+
exports.create = ({ path, repository, packageName }) => {
146+
147+
if (repository) {
148+
return internals.createRepositoryLoader(repository);
149+
}
150+
151+
if (packageName) {
152+
return internals.createPackageLoader(packageName);
153+
}
154+
155+
return internals.createPathLoader(path);
156+
};

lib/package.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const Loader = require('./loader');
4+
5+
6+
exports.detect = async ({ path, repository, packageName }) => {
7+
8+
const { loadFile, getCommit } = await Loader.create({ path, repository, packageName });
9+
10+
const packageJson = await loadFile('package.json', { json: 'force' });
11+
12+
const { name, version, engines } = packageJson;
13+
14+
return {
15+
name,
16+
version,
17+
engines,
18+
19+
getCommit,
20+
loadFile
21+
};
22+
};

0 commit comments

Comments
 (0)