appcenter-cli
Version:
Command line tool for Visual Studio App Center
198 lines (167 loc) • 7.09 kB
text/typescript
// Base class for all command handlers
import * as Result from "./command-result";
import { shortName, longName, help, hasArg, getOptionsDescription, getPositionalOptionsDescription, common } from "./option-decorators";
import { parseOptions } from "./option-parser";
import { setDebug, setQuiet, OutputFormatSupport, setFormatJson, out } from "../interaction";
import { runHelp } from "./help";
import { scriptName } from "../misc";
import { getUser, environments, telemetryIsEnabled, getPortalUrlForEndpoint, getEnvFromEnvironmentVar, getTokenFromEnvironmentVar, appCenterAccessTokenEnvVar } from "../profile";
import { AppCenterClient, createAppCenterClient, AppCenterClientFactory } from "../apis";
import * as path from "path";
const debug = require("debug")("appcenter-cli:util:commandline:command");
export interface CommandArgs {
command: string[];
commandPath: string;
args: string[];
}
export class Command {
constructor(args: CommandArgs) {
const proto = Object.getPrototypeOf(this);
const flags = getOptionsDescription(proto);
const positionals = getPositionalOptionsDescription(proto);
parseOptions(flags, positionals, this, args.args);
this.commandPath = args.commandPath;
this.command = args.command;
debug(`Starting command with path ${args.commandPath}, command ${args.command}`);
}
// Used by help system to generate help messages
protected command: string[];
protected commandPath: string;
// Support for login command
protected clientFactory: AppCenterClientFactory;
// Additional output formats (except "list" which is used by default) which are supported by this command
protected readonly additionalSupportedOutputFormats: OutputFormatSupport = {
json: setFormatJson
};
// Default arguments supported by every command
public debug: boolean;
public format: string;
public token: string;
public environmentName: string;
public help: boolean;
public quiet: boolean;
public version: boolean;
public disableTelemetry: boolean;
// Entry point for runner. DO NOT override in command definition!
async execute(): Promise<Result.CommandResult> {
debug(`Initial execution of command`);
if (this.help) {
debug(`help switch detected, displaying help for command`);
runHelp(Object.getPrototypeOf(this), this);
return Result.success();
}
if (this.version) {
debug("Version switch detected, displaying version number");
return this.showVersion();
}
if (this.debug) {
setDebug();
const version = this.getVersion();
console.log(`Using appcenter-cli version: ${version}`);
}
if (this.quiet) {
setQuiet();
}
if (this.format) {
if (this.format in this.additionalSupportedOutputFormats) {
this.additionalSupportedOutputFormats[this.format]();
} else if (this.format != null && this.format !== "") {
return Promise.resolve(Result.failure(Result.ErrorCodes.InvalidParameter, `Unknown output format ${this.format}`));
}
}
this.clientFactory = createAppCenterClient(this.command, await telemetryIsEnabled(this.disableTelemetry));
return this.runNoClient();
}
// Entry point to load appcenter client.
// Override this if your command needs to do something special with login - typically just
// the login command
protected runNoClient(): Promise<Result.CommandResult> {
if (this.environmentName && !this.token) {
return Promise.resolve(Result.failure(Result.ErrorCodes.IllegalCommand, "Cannot specify environment without giving token"));
}
let client: AppCenterClient;
let endpoint: string;
if (this.token) {
debug(`Creating appcenter client for command from token for environment ${this.environmentName}`);
[client, endpoint] = this.getClientAndEndpointForToken(this.environmentName, this.token);
} else {
// creating client for either logged in user or environment variable token
const user = getUser();
const tokenFromEnvVar = getTokenFromEnvironmentVar();
const envFromEnvVar = getEnvFromEnvironmentVar();
const isLogoutCommand = this.command[0] === "logout";
if (user && tokenFromEnvVar && !isLogoutCommand) { // logout command should be executed even if both user and env token are set - it just logs out user
return Promise.resolve(Result.failure(Result.ErrorCodes.IllegalCommand, `logged in user and token in environment variable ${appCenterAccessTokenEnvVar} cannot be used together`));
} else if (user) {
debug(`Creating appcenter client for command for current logged in user`);
client = this.clientFactory.fromProfile(user);
endpoint = user.endpoint;
} else if (tokenFromEnvVar) {
debug(`Creating appcenter client from token specified in environment variable for environment ${this.environmentName}`);
[client, endpoint] = this.getClientAndEndpointForToken(envFromEnvVar, tokenFromEnvVar);
}
}
if (client && endpoint) {
return this.run(client, getPortalUrlForEndpoint(endpoint));
}
return Promise.resolve(Result.notLoggedIn(`${scriptName} ${this.command.join(" ")}`));
}
// Entry point for command author - override this!
protected run(client: AppCenterClient, portalBaseUrl: string): Promise<Result.CommandResult> {
throw new Error("Dev error, should be overridden!");
}
protected showVersion(): Result.CommandResult {
out.text((s) => s, `${scriptName} version ${this.getVersion()}`);
return Result.success();
}
protected getClientAndEndpointForToken(environmentString: string, token: string): [AppCenterClient, string] {
const environment = environments(environmentString);
if (!environment) {
throw Result.failure(Result.ErrorCodes.InvalidParameter, `${environmentString} is not valid environment name`);
}
return [this.clientFactory.fromToken(token, environment.endpoint), environment.endpoint];
}
protected getVersion(): string {
const packageJsonPath = path.join(__dirname, "../../../package.json");
/* tslint:disable-next-line:non-literal-require */
const packageJson: any = require(packageJsonPath);
return packageJson.version;
}
protected fixArrayParameter(input: any): string[] {
if (!input) {
return [];
} else if (typeof input === "string") {
return [ input ];
}
return input;
}
}