appcenter-cli
Version:
Command line tool for Visual Studio App Center
135 lines (113 loc) • 5.84 kB
text/typescript
import { AppCommand, CommandArgs, CommandResult, help, failure, ErrorCodes, success, shortName, longName, required, hasArg, position, name } from "../../util/commandline";
import { out } from "../../util/interaction";
import { inspect } from "util";
import { AppCenterClient, models, clientRequest } from "../../util/apis";
import * as chalk from "chalk";
import { isValidRollout, isValidRange } from "./lib/validation-utils";
import { DefaultApp } from "../../util/profile";
import { scriptName } from "../../util/misc";
const debug = require("debug")("appcenter-cli:commands:codepush:patch");
("Update the metadata for an existing CodePush release")
export default class PatchCommand extends AppCommand {
("Specifies one existing deployment name.")
("deployment-name")
(0)
public deploymentName: string;
("Specifies label of one existing release to update. (Defaults to the latest release within the specified deployment)")
("existing-release-label")
("l")
public releaseLabel: string;
("Specifies whether this release should be considered mandatory. (Putting -m flag means mandatory)")
("m")
("mandatory")
public isMandatory: boolean;
("Specifies whether this release should be immediately downloadable. (Putting -x flag means disabled)")
("x")
("disabled")
public isDisabled: boolean;
("Specifies binary app version(s) that specifies this release is targeting for. (The value must be a semver expression such as 1.1.0, ~1.2.3)")
("t")
("target-binary-version")
public targetBinaryRange: string;
("Specifies description of the changes made to the app with this release")
("d")
("description")
public description: string;
("Specifies percentage of users this release should be immediately available to. (The specified number must be an integer between 1 and 100)")
("r")
("rollout")
public rollout: string;
constructor(args: CommandArgs) {
super(args);
}
async run(client: AppCenterClient): Promise<CommandResult> {
const app = this.app;
let release: models.CodePushRelease;
if (this.targetBinaryRange === null && this.isDisabled === null && this.isMandatory === null && this.description === null && this.rollout === null) {
return failure(ErrorCodes.Exception, "At least one property must be specified to patch a release.");
}
const rollout = Number(this.rollout);
if (this.rollout != null && (!Number.isSafeInteger(rollout) || !isValidRollout(rollout))) {
return failure(ErrorCodes.Exception, `Rollout value should be integer value between ${chalk.bold("0")} or ${chalk.bold("100")}.`);
}
if (this.targetBinaryRange != null && !isValidRange(this.targetBinaryRange)) {
return failure(ErrorCodes.Exception, "Invalid binary version(s) for a release.");
}
const patch : models.CodePushReleaseModification = {
targetBinaryRange: this.targetBinaryRange,
isMandatory: this.isMandatory,
isDisabled: this.isDisabled,
description: this.description,
};
if (this.rollout != null) {
patch.rollout = rollout;
}
if (this.releaseLabel == null || this.releaseLabel === "") {
debug("Release label is not set, get latest...");
this.releaseLabel = await this.getLatestReleaseLabel(client, app);
}
try {
const httpRequest = await out.progress("Patching CodePush release...", clientRequest<models.CodePushRelease>(
(cb) => client.deploymentReleases.update(this.deploymentName, this.releaseLabel, patch, app.ownerName, app.appName, cb)));
release = httpRequest.result;
if (httpRequest.response.statusCode === 204) {
out.text(`No update for the ${chalk.bold(this.releaseLabel)} of ${this.identifier} app's ${chalk.bold(this.deploymentName)} deployment`);
} else {
out.text(`Successfully updated the ${chalk.bold(release.label)} of ${this.identifier} app's ${chalk.bold(this.deploymentName)} deployment`);
}
return success();
} catch (error) {
debug(`Failed to patch Codepush deployment - ${inspect(error)}`);
return failure(ErrorCodes.Exception, error.response.body);
}
}
private async getLatestReleaseLabel(client: AppCenterClient, app: DefaultApp): Promise<string> {
let releases: models.CodePushRelease[];
try {
const httpRequest = await out.progress("Fetching latest release label...", clientRequest<models.CodePushRelease[]>(
(cb) => client.codePushDeploymentReleases.get(this.deploymentName, app.ownerName, app.appName, cb)));
releases = httpRequest.result;
} catch (error) {
debug(`Failed to get list of CodePush deployments - ${inspect(error)}`);
if (error.statusCode === 404) {
const appNotFoundErrorMsg = `The app ${this.identifier} does not exist. Please double check the name, and provide it in the form owner/appname. \nRun the command ${chalk.bold(`${scriptName} apps list`)} to see what apps you have access to.`;
throw failure(ErrorCodes.NotFound, appNotFoundErrorMsg);
} else if (error.statusCode === 400) {
const deploymentNotExistErrorMsg = `The deployment ${chalk.bold(this.deploymentName)} does not exist.`;
throw failure(ErrorCodes.Exception, deploymentNotExistErrorMsg);
} else {
throw failure(ErrorCodes.Exception, error.response.body);
}
}
if (releases && releases.length > 0) {
return releases[releases.length - 1].label;
} else {
throw failure(ErrorCodes.NotFound, `Failed to find any release to patch for ${this.identifier} app's ${chalk.bold(this.deploymentName)} deployment`);
}
}
}