Skip to content

Commit

Permalink
Add dependency management command
Browse files Browse the repository at this point in the history
  • Loading branch information
shamsartem authored Jul 12, 2022
1 parent 4fea293 commit ce156fb
Show file tree
Hide file tree
Showing 16 changed files with 453 additions and 72 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ USAGE

<!-- commands -->
* [`fluence autocomplete [SHELL]`](#fluence-autocomplete-shell)
* [`fluence dependency [NAME] [-v] [--use <version | recommended>] [--no-input]`](#fluence-dependency-name--v---use-version--recommended---no-input)
* [`fluence deploy [--on <peer_id>] [--relay <multiaddr>] [--force] [--timeout <milliseconds>] [-k <name>] [--no-input]`](#fluence-deploy---on-peer_id---relay-multiaddr---force---timeout-milliseconds--k-name---no-input)
* [`fluence help [COMMAND]`](#fluence-help-command)
* [`fluence init [PATH] [--no-input]`](#fluence-init-path---no-input)
Expand Down Expand Up @@ -158,6 +159,32 @@ EXAMPLES
_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v1.3.0/src/commands/autocomplete/index.ts)_
## `fluence dependency [NAME] [-v] [--use <version | recommended>] [--no-input]`
Manage dependencies stored inside .fluence directory of the current user
```
USAGE
$ fluence dependency [NAME] [-v] [--use <version | recommended>] [--no-input]

ARGUMENTS
NAME Dependency name. Currently the only dependency is aqua

FLAGS
-v, --version Show current version of the dependency
--no-input Don't interactively ask for any input from the user
--use=<version | recommended> Set version of the dependency that you want to use. Use recommended keyword if you want
to use recommended version

DESCRIPTION
Manage dependencies stored inside .fluence directory of the current user

EXAMPLES
$ fluence dependency
```
_See code: [dist/commands/dependency.ts](https://github.com/fluencelabs/fluence-cli/blob/v0.0.0/dist/commands/dependency.ts)_
## `fluence deploy [--on <peer_id>] [--relay <multiaddr>] [--force] [--timeout <milliseconds>] [-k <name>] [--no-input]`
Deploy service to the remote peer
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"camelcase": "^5.2.0",
"inquirer": "^8.2.4",
"platform": "^1.3.6",
"replace-homedir": "^2.0.0",
"yaml": "^2.1.1",
"yaml-diff-patch": "^1.2.0"
},
Expand Down Expand Up @@ -87,7 +88,6 @@
"@oclif/plugin-autocomplete"
],
"topicSeparator": " ",
"topics": {},
"hooks": {
"init": "./src/hooks/init/init"
}
Expand Down
173 changes: 173 additions & 0 deletions src/commands/dependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/**
* Copyright 2022 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import color from "@oclif/color";
import { Command, Flags } from "@oclif/core";

import { initAquaCli } from "../lib/aquaCli";
import {
AQUA_NPM_DEPENDENCY,
dependencyList,
initDependencyConfig,
initReadonlyDependencyConfig,
isDependency,
} from "../lib/configs/user/dependency";
import { FLUENCE_DIR_NAME, NO_INPUT_FLAG } from "../lib/const";
import { getIsInteractive } from "../lib/helpers/getIsInteractive";
import { usage } from "../lib/helpers/usage";
import { npmDependencies } from "../lib/npm";
import { confirm, input, list } from "../lib/prompt";

const NAME = "NAME";
const RECOMMENDED = "recommended";
const VERSION_FLAG_NAME = "version";
const USE_FLAG_NAME = "use";

export default class Dependency extends Command {
static override description = `Manage dependencies stored inside ${FLUENCE_DIR_NAME} directory of the current user`;
static override examples = ["<%= config.bin %> <%= command.id %>"];
static override flags = {
version: Flags.boolean({
char: "v",
description: "Show current version of the dependency",
exclusive: [USE_FLAG_NAME],
}),
use: Flags.string({
description: `Set version of the dependency that you want to use. Use ${color.yellow(
RECOMMENDED
)} keyword if you want to use ${RECOMMENDED} version`,
helpValue: `<version | ${RECOMMENDED}>`,
exclusive: [VERSION_FLAG_NAME],
}),
...NO_INPUT_FLAG,
};
static override args = [
{
name: NAME,
description: `Dependency name. Currently the only dependency is ${color.yellow(
AQUA_NPM_DEPENDENCY
)}`,
},
];
static override usage: string = usage(this);
async run(): Promise<void> {
const { args, flags } = await this.parse(Dependency);
const isInteractive = getIsInteractive(flags);
let name: unknown = args[NAME];

if (name === undefined) {
if (flags.use === RECOMMENDED) {
const resetAllDependencies = await confirm({
isInteractive,
message: `Do you want to reset all dependencies to their ${color.yellow(
RECOMMENDED
)} versions`,
});

if (resetAllDependencies) {
const dependencyConfig = await initDependencyConfig(this);
dependencyConfig.dependency = {};
await dependencyConfig.$commit();
this.log(
`Successfully reset all dependencies to their ${color.yellow(
RECOMMENDED
)} versions`
);
return;
}
}

name = await list({
isInteractive,
message: "Select dependency",
oneChoiceMessage: (name): string =>
`Do you want to manage ${color.yellow(name)}`,
onNoChoices: (): void =>
this.error("You have to select dependency to manage"),
options: [...dependencyList],
});
}

if (!isDependency(name)) {
this.error(`Unknown dependency ${color.yellow(name)}`);
}

const dependencyName = name;
const { recommendedVersion, packageName } = npmDependencies[dependencyName];

const handleVersion = async (): Promise<void> => {
const result =
(await initReadonlyDependencyConfig(this)).dependency[dependencyName] ??
recommendedVersion;

this.log(
`Using version ${color.yellow(result)}${
result.includes(recommendedVersion) ? ` (${RECOMMENDED})` : ""
} of ${packageName}`
);
};

if (flags.version === true) {
return handleVersion();
}

const handleUse = async (version: string): Promise<void> => {
const dependencyConfig = await initDependencyConfig(this);
if (version === RECOMMENDED) {
delete dependencyConfig.dependency[dependencyName];
} else {
dependencyConfig.dependency[dependencyName] = version;
}

await dependencyConfig.$commit();
await initAquaCli(this);
await handleVersion();
};

if (typeof flags.use === "string") {
return handleUse(flags.use);
}

return (
await list({
isInteractive,
message: "Select action",
oneChoiceMessage: (): never => {
this.error("Unreachable");
},
onNoChoices: (): never => this.error("Unreachable"),
options: [
{
value: handleVersion,
name: `Print version of ${color.yellow(name)}`,
},
{
value: async (): Promise<void> =>
handleUse(
await input({
isInteractive,
message: `Enter version of ${color.yellow(
name
)} that you want to use`,
})
),
name: `Set version of ${color.yellow(name)} that you want to use`,
},
],
})
)();
}
}
2 changes: 1 addition & 1 deletion src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export default class Deploy extends Command {
this.log(`Random peer ${color.yellow(peerId)} selected for deployment`);
}

const aquaCli = await initAquaCli(this, isInteractive);
const aquaCli = await initAquaCli(this);
const successfullyDeployedServices: Services = {};
for (const { name, count = 1 } of fluenceConfig.services) {
process.chdir(path.join(artifactsPath, name));
Expand Down
6 changes: 5 additions & 1 deletion src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import path from "node:path";
import { color } from "@oclif/color";
import { Command } from "@oclif/core";
import type { JSONSchemaType } from "ajv";
import replaceHomedir from "replace-homedir";

import { ajv } from "../lib/ajv";
import { initReadonlyFluenceConfig } from "../lib/configs/project/fluence";
Expand Down Expand Up @@ -264,7 +265,10 @@ export const init = async (options: InitOptions): Promise<void> => {

commandObj.log(
color.magentaBright(
`\nFluence project successfully initialized at ${projectPath}\n`
`\nFluence project successfully initialized at ${replaceHomedir(
projectPath,
"~"
)}\n`
)
);
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const removeApp = async ({
return;
}

const aquaCli = await initAquaCli(commandObj, isInteractive);
const aquaCli = await initAquaCli(commandObj);
const notRemovedServices: Services = {};
const addr = getRandomRelayAddr();

Expand Down
2 changes: 1 addition & 1 deletion src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export default class Run extends Command {
);
}
let result: string;
const aquaCli = await initAquaCli(this, isInteractive);
const aquaCli = await initAquaCli(this);
try {
result = await aquaCli(
{
Expand Down
14 changes: 4 additions & 10 deletions src/lib/aquaCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* limitations under the License.
*/

import { CommandObj, DEPENDENCIES } from "./const";
import { AQUA_NPM_DEPENDENCY } from "./configs/user/dependency";
import type { CommandObj } from "./const";
import { execPromise } from "./execPromise";
import { getMessageWithKeyValuePairs } from "./helpers/getMessageWithKeyValuePairs";
import { unparseFlags } from "./helpers/unparseFlags";
Expand Down Expand Up @@ -61,17 +62,10 @@ export type AquaCLI = {
): Promise<string>;
};

export const initAquaCli = async (
commandObj: CommandObj,
isInteractive: boolean
): Promise<AquaCLI> => {
export const initAquaCli = async (commandObj: CommandObj): Promise<AquaCLI> => {
const aquaCliPath = await ensureNpmDependency({
dependency: DEPENDENCIES.aqua,
name: AQUA_NPM_DEPENDENCY,
commandObj,
confirmMessage:
"Aqua CLI dependency needs to be updated. Do you want to continue?",
installMessage: "Downloading Aqua CLI, may take a while",
isInteractive,
});

return (aquaCliInput, message, keyValuePairs): Promise<string> => {
Expand Down
6 changes: 5 additions & 1 deletion src/lib/configs/initConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import path from "node:path";

import color from "@oclif/color";
import type { AnySchema, JSONSchemaType, ValidateFunction } from "ajv";
import replaceHomedir from "replace-homedir";
import { parse } from "yaml";
import { yamlDiffPatch } from "yaml-diff-patch";

Expand Down Expand Up @@ -379,7 +380,10 @@ export function getConfigInitFunction<

if (initializedConfigs.has(configPath)) {
throw new Error(
`Config ${configPath} was already initialized. Please initialize readonly config instead or use previously initialized mutable config`
`Mutable config ${replaceHomedir(
configPath,
"~"
)} was already initialized. Please initialize readonly config instead or use previously initialized mutable config`
);
}
initializedConfigs.add(configPath);
Expand Down
Loading

0 comments on commit ce156fb

Please sign in to comment.