Skip to content

Commit

Permalink
feat: command deps to view dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
favoyang committed Jan 8, 2020
1 parent 44d178e commit 4c866d0
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The tool is designed to work with [the OpenUPM registry](https://openupm.com), b
- [Remove packages](#remove-packages)
- [Search packages](#search-packages)
- [View package information](#view-package-information)
- [View package dependencies](#view-package-dependencies)
- [Command options](#command-options)
- [Work with unity official (upstream) registry](#work-with-unity-official-upstream-registry)

Expand Down Expand Up @@ -80,6 +81,17 @@ However the search behavior may still performance various for different registri
openupm view <pkg>
```

### View package dependencies
```
open deps <pkg>
```

Using option `--deep` to view dependencies recursively

```
open deps <pkg> --deep
```

### Command options

The cli assumes current working directory (cwd) is the root of an Unity project (the parent of `Assets` folder). However you can specify the cwd.
Expand Down
15 changes: 15 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const add = require("./cmd-add");
const remove = require("./cmd-remove");
const search = require("./cmd-search");
const view = require("./cmd-view");
const deps = require("./cmd-deps");
const log = require("./logger");
require("pkginfo")(module);

Expand Down Expand Up @@ -55,6 +56,20 @@ program
if (retCode) process.exit(retCode);
});

program
.command("deps <pkg>")
.alias("dep")
.option("-d, --deep", "view package dependencies recursively")
.description(
`view package dependencies
openupm deps <pkg>
openupm deps <pkg>@<version>`
)
.action(async function(pkg, options) {
const retCode = await deps(pkg, options);
if (retCode) process.exit(retCode);
});

// prompt for invalid command
program.on("command:*", function() {
log.error(`invalid command: ${program.args.join(" ")}
Expand Down
21 changes: 21 additions & 0 deletions lib/cmd-deps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const log = require("./logger");
const { fetchPackageDependencies, parseEnv, parseName } = require("./core");

const deps = async function(pkg, options) {
// parse env
if (!parseEnv(options, { checkPath: false })) return 1;
// parse name
let { name, version } = parseName(pkg);
// deps
await _deps({ name, version, deep: options.deep });
return 0;
};

const _deps = async function({ name, version, deep }) {
const results = await fetchPackageDependencies({ name, version, deep });
results
.filter(x => !x.self)
.forEach(x => log.info(`- ${x.name}@${x.version}`));
};

module.exports = deps;
87 changes: 87 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const _ = require("lodash");
const fs = require("fs");
const keyFileStorage = require("key-file-storage").default;
const isIp = require("is-ip");
Expand Down Expand Up @@ -88,6 +89,91 @@ const fetchPackageInfo = async function(name, registry) {
}
};

/* Fetch package dependencies
[
{
name,
version,
upstream, // whether belongs to upstream registry
self, // whether is the source package
module, // whether is an unity module
}, ...
]
*/
const fetchPackageDependencies = async function({ name, version, deep }) {
// a list of pending dependency {name, version}
const pendingList = [{ name, version }];
// a list of processed dependency {name, version}
const processedList = [];
// a list of dependency entry exists on the registry
const validDeps = [];
// cached dict: {pkg-name: pkgInfo}
const cachedPacakgeInfoDict = {};
while (pendingList.length > 0) {
const entry = pendingList.shift();
if (processedList.find(x => _.isEqual(x, entry)) === undefined) {
// add entry to processed list
processedList.push(entry);
// create valid depedenency structure
const validDep = {
...entry,
module: /com.unity.modules/i.test(entry.name),
upstream: false,
self: entry.name == name
};
if (!validDep.module) {
// fetch package info
let pkgInfo = cachedPacakgeInfoDict[entry.name];
if (!pkgInfo) {
pkgInfo = await fetchPackageInfo(entry.name);
if (pkgInfo) cachedPacakgeInfoDict[entry.name] = pkgInfo;
}
if (!pkgInfo) {
pkgInfo = await fetchPackageInfo(entry.name, env.upstreamRegistry);
if (pkgInfo) {
validDep.upstream = true;
cachedPacakgeInfoDict[entry.name] = pkgInfo;
}
}
// handle package not exist
if (!pkgInfo) {
log.warn(`package not found: ${entry.name}`);
continue;
}
// verify version
const versions = Object.keys(pkgInfo.versions);
if (!entry.version || entry.version == "latest") {
// eslint-disable-next-line require-atomic-updates
validDep.version = entry.version = getLatestVersion(pkgInfo);
}
// handle version not exist
if (!versions.find(x => x == entry.version)) {
log.warn(
`[WARN] package ${entry.name}@${
entry.version
} is not a valid choice of ${versions.reverse().join(", ")}`
);
// eslint-disable-next-line require-atomic-updates
validDep.version = entry.version = getLatestVersion(pkgInfo);
log.warn(`[WARN] fall back to ${entry.name}@${entry.version}`);
}
// add dependencies to pending list
if (validDep.self || deep) {
const deps = _.toPairs(
pkgInfo.versions[entry.version]["dependencies"]
).map(x => {
return { name: x[0], version: x[1] };
});
deps.forEach(x => pendingList.push(x));
}
}
validDeps.push(validDep);
log.debug(`- ${entry.name}@${entry.version}`);
}
}
return validDeps;
};

// Get latest version from package info
const getLatestVersion = function(pkgInfo) {
if (pkgInfo["dist-tags"] && pkgInfo["dist-tags"]["latest"])
Expand Down Expand Up @@ -154,6 +240,7 @@ module.exports = {
getCache,
cleanCache,
fetchPackageInfo,
fetchPackageDependencies,
getLatestVersion,
loadManifest,
parseEnv,
Expand Down
153 changes: 153 additions & 0 deletions test/test-cmd-deps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-undef */
const assert = require("assert");
const nock = require("nock");
const should = require("should");
const { parseEnv, loadManifest } = require("../lib/core");
const deps = require("../lib/cmd-deps");
const {
getWorkDir,
createWorkDir,
removeWorkDir,
captureStream,
nockUp,
nockDown
} = require("./utils");

describe("cmd-deps.js", function() {
const options = {
parent: {
registry: "http://example.com",
chdir: getWorkDir("test-openupm-cli")
}
};
describe("deps", function() {
let stdout;
let stderr;
const remotePkgInfoA = {
name: "com.example.package-a",
versions: {
"1.0.0": {
name: "com.example.package-a",
version: "1.0.0",
dependencies: {
"com.example.package-b": "1.0.0"
}
}
},
"dist-tags": {
latest: "1.0.0"
}
};
const remotePkgInfoB = {
name: "com.example.package-b",
versions: {
"1.0.0": {
name: "com.example.package-b",
version: "1.0.0",
dependencies: {
"com.example.package-up": "1.0.0"
}
}
},
"dist-tags": {
latest: "1.0.0"
}
};
const remotePkgInfoUp = {
name: "com.example.package-up",
versions: {
"1.0.0": {
name: "com.example.package-up",
version: "1.0.0",
dependencies: {}
}
},
"dist-tags": {
latest: "1.0.0"
}
};
beforeEach(function() {
removeWorkDir("test-openupm-cli");
createWorkDir("test-openupm-cli", { manifest: true });
nockUp();
nock("http://example.com")
.get("/com.example.package-a")
.reply(200, remotePkgInfoA, { "Content-Type": "application/json" });
nock("http://example.com")
.get("/com.example.package-b")
.reply(200, remotePkgInfoB, { "Content-Type": "application/json" });
nock("http://example.com")
.get("/pkg-not-exist")
.reply(404);
nock("http://example.com")
.get("/com.example.package-up")
.reply(404);
nock("https://api.bintray.com")
.get("/npm/unity/unity/com.example.package-up")
.reply(200, remotePkgInfoUp, {
"Content-Type": "application/json"
});
nock("https://api.bintray.com")
.get("/npm/unity/unity/pkg-not-exist")
.reply(404);
stdout = captureStream(process.stdout);
stderr = captureStream(process.stderr);
});
afterEach(function() {
removeWorkDir("test-openupm-cli");
nockDown();
stdout.unhook();
stderr.unhook();
});
it("deps pkg", async function() {
const retCode = await deps("com.example.package-a", options);
retCode.should.equal(0);
const stdoutContent = stdout.captured();
stdoutContent.includes("com.example.package-b").should.be.ok();
});
it("deps pkg --deep", async function() {
const retCode = await deps("com.example.package-a", {
...options,
deep: true
});
retCode.should.equal(0);
const stdoutContent = stdout.captured();
stdoutContent.includes("com.example.package-b").should.be.ok();
stdoutContent.includes("com.example.package-up").should.be.ok();
});
it("deps pkg@latest", async function() {
const retCode = await deps("com.example.package-a@latest", options);
retCode.should.equal(0);
const stdoutContent = stdout.captured();
stdoutContent.includes("com.example.package-b").should.be.ok();
});
it("deps pkg@1.0.0", async function() {
const retCode = await deps("com.example.package-a@1.0.0", options);
retCode.should.equal(0);
const stdoutContent = stdout.captured();
stdoutContent.includes("com.example.package-b").should.be.ok();
});
it("deps pkg@not-exist-version", async function() {
const retCode = await deps("com.example.package-a@2.0.0", options);
retCode.should.equal(0);
stderr
.captured()
.includes("is not a valid choice")
.should.be.ok();
console.log(stdout.captured());
});
it("deps pkg-not-exist", async function() {
const retCode = await deps("pkg-not-exist", options);
retCode.should.equal(0);
stderr
.captured()
.includes("package not found")
.should.be.ok();
});
it("deps pkg upstream", async function() {
const retCode = await deps("com.example.package-up", options);
retCode.should.equal(0);
});
});
});

0 comments on commit 4c866d0

Please sign in to comment.