@forge/cli
14.1.0-next.1-experimental-13c2f8d14.1.0-next.21-experimental-554d417
out/command-line/controller/global-install-controller.js+
out/command-line/controller/global-install-controller.jsNew file+209
Index: package/out/command-line/controller/global-install-controller.js
===================================================================
--- package/out/command-line/controller/global-install-controller.js
+++ package/out/command-line/controller/global-install-controller.js
@@ -0,0 +1,209 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.GlobalInstallController = void 0;
+const cli_shared_1 = require("@forge/cli-shared");
+const site_translation_1 = require("../../installations/site-translation");
+const LINKABLE_CONNECTIONS = [
+ { product: 'Jira', kind: 'site' },
+ { product: 'Confluence', kind: 'site' },
+ { product: 'Bitbucket', kind: 'workspace' }
+];
+class GlobalInstallController {
+ view;
+ identityClient;
+ installUnitClient;
+ installationService;
+ supportedProductsService;
+ constructor(view, identityClient, installUnitClient, installationService, supportedProductsService) {
+ this.view = view;
+ this.identityClient = identityClient;
+ this.installUnitClient = installUnitClient;
+ this.installationService = installationService;
+ this.supportedProductsService = supportedProductsService;
+ }
+ async run({ appId, environment, nonInteractive, unitId, product, site }) {
+ if ((product || site) && !unitId) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.connectRequiresUnit);
+ }
+ const environmentType = await this.assertDeployed(appId, environment);
+ const isNonInteractive = nonInteractive || Boolean(unitId);
+ const target = await this.resolveTarget(unitId, isNonInteractive);
+ if (isNonInteractive && (product || site)) {
+ await this.linkConnectionByFlags(appId, environment, target.unitId, product, site);
+ return;
+ }
+ const existingInstallationId = await this.installUnitClient.getUnitInstallationId(appId, environment, target.unitId);
+ if (existingInstallationId) {
+ this.view.showAlreadyInstalled(target);
+ const alreadyLinkedProducts = await this.installUnitClient.getLinkedConnectionProducts(appId, existingInstallationId);
+ await this.linkConnections({
+ environment,
+ installationId: existingInstallationId,
+ nonInteractive: isNonInteractive,
+ alreadyLinkedProducts
+ });
+ return;
+ }
+ this.view.showInstallConfirmation(target);
+ if (!isNonInteractive && !(await this.view.confirmContinueInstall())) {
+ return;
+ }
+ const { installationId } = await this.view.runInstall(() => this.installUnitClient.installAppIntoUnit({
+ appId,
+ environmentKey: environment,
+ unitId: target.unitId,
+ environmentType
+ }));
+ this.view.showInstallSuccess(environment, target);
+ await this.linkConnections({ environment, installationId, nonInteractive: isNonInteractive });
+ }
+ async runUpgrade({ appId, environment, nonInteractive, unitId, majorVersionId, isCodeUpgrade }) {
+ await this.assertDeployed(appId, environment);
+ const installations = await this.installUnitClient.getUnitInstallations(appId, environment);
+ if (!installations.length) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noUnitInstallations);
+ }
+ let selectedUnitId;
+ if (unitId) {
+ const match = installations.find((installation) => installation.unitId === unitId);
+ if (!match) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.notInstalledInUnit(unitId));
+ }
+ selectedUnitId = match.unitId;
+ }
+ else if (installations.length === 1) {
+ selectedUnitId = installations[0].unitId;
+ }
+ else if (nonInteractive) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.nonInteractiveUnitRequired);
+ }
+ else {
+ selectedUnitId = (await this.view.promptForUnitUpgrade(installations)).unitId;
+ }
+ await this.view.runUpgrade(() => this.installUnitClient.upgradeAppInUnit({
+ appId,
+ environmentKey: environment,
+ unitId: selectedUnitId,
+ majorVersionId,
+ isCodeUpgrade
+ }));
+ this.view.showUpgradeSuccess(environment, { unitId: selectedUnitId });
+ }
+ async assertDeployed(appId, environment) {
+ const permissions = await this.installationService.getAppEnvironmentPermissions(appId, environment);
+ if (!permissions?.hasDeployments) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.error.noDeploymentFound(environment));
+ }
+ return permissions.environmentType;
+ }
+ async resolveTarget(unitFlag, nonInteractive) {
+ if (unitFlag) {
+ return { unitId: unitFlag };
+ }
+ const org = await this.resolveOrg(nonInteractive);
+ const { unit, onlyUnit } = await this.resolveUnit(org, nonInteractive);
+ return { unitId: unit.id, unitName: unit.name, orgName: org.name, onlyUnit };
+ }
+ async resolveOrg(nonInteractive) {
+ const orgs = await this.view.fetchWithSpinner(() => this.identityClient.getAllOrgs());
+ if (!orgs.length) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noOrgs);
+ }
+ if (orgs.length === 1) {
+ return orgs[0];
+ }
+ if (nonInteractive) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.nonInteractiveOrgRequired);
+ }
+ return this.view.selectOrganization(orgs);
+ }
+ async resolveUnit(org, nonInteractive) {
+ const units = await this.view.fetchWithSpinner(() => this.identityClient.getAllUnits(org.id));
+ if (!units.length) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noUnits(org.name));
+ }
+ const onlyUnit = units.length === 1;
+ if (onlyUnit) {
+ return { unit: units[0], onlyUnit };
+ }
+ if (nonInteractive) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.nonInteractiveUnitRequired);
+ }
+ this.view.showSelectUnitOverview();
+ return { unit: await this.view.selectUnit(units), onlyUnit };
+ }
+ async linkConnections({ environment, installationId, nonInteractive, alreadyLinkedProducts = [] }) {
+ if (nonInteractive) {
+ return;
+ }
+ const linkedSet = new Set(alreadyLinkedProducts.map((product) => product.toLowerCase()));
+ const options = LINKABLE_CONNECTIONS.map((connection) => ({
+ ...connection,
+ linked: linkedSet.has(connection.product.toLowerCase())
+ }));
+ if (options.every((option) => option.linked)) {
+ return;
+ }
+ if (!(await this.view.confirmConnect(false))) {
+ return;
+ }
+ if (!installationId) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.noInstallationId);
+ }
+ for (;;) {
+ const selected = await this.view.selectConnection(options);
+ if (selected.linked) {
+ this.view.showAlreadyLinked(selected.product);
+ continue;
+ }
+ const kind = selected.kind;
+ const target = await this.view.promptForConnectionTarget(kind);
+ const url = this.supportedProductsService.validateSite(this.normalizeTarget(kind, target), selected.product);
+ this.view.showConnectConfirmation(selected.product, kind);
+ if (!(await this.view.confirmContinueConnect())) {
+ continue;
+ }
+ await this.performLink(environment, installationId, selected.product, kind, url);
+ selected.linked = true;
+ if (options.every((option) => option.linked)) {
+ break;
+ }
+ if (!(await this.view.confirmConnect(true))) {
+ break;
+ }
+ }
+ }
+ async linkConnectionByFlags(appId, environment, unitId, product, site) {
+ if (!product || !site) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.connectFlagsRequired);
+ }
+ const connection = LINKABLE_CONNECTIONS.find((c) => c.product.toLowerCase() === product.toLowerCase());
+ if (!connection) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.unsupportedConnection(product));
+ }
+ const installationId = await this.installUnitClient.getUnitInstallationId(appId, environment, unitId);
+ if (!installationId) {
+ throw new cli_shared_1.UserError(cli_shared_1.Text.install.global.error.notInstalledInUnit(unitId));
+ }
+ const url = this.supportedProductsService.validateSite(this.normalizeTarget(connection.kind, site.toString()), connection.product);
+ await this.performLink(environment, installationId, connection.product, connection.kind, url);
+ }
+ async performLink(environment, installationId, product, kind, url) {
+ await this.view.runConnect(() => this.installUnitClient.linkOptionalConnection(installationId, product, url));
+ this.view.showConnectionSuccess(environment, product, this.connectionDisplay(kind, url));
+ }
+ normalizeTarget(kind, input) {
+ if (kind === 'workspace') {
+ return site_translation_1.BitbucketTranslator.normalizeWorkspaceInput(input);
+ }
+ return input.trim();
+ }
+ connectionDisplay(kind, url) {
+ if (kind === 'workspace') {
+ return site_translation_1.BitbucketTranslator.extractWorkspaceName(url) || url.host;
+ }
+ return url.host;
+ }
+}
+exports.GlobalInstallController = GlobalInstallController;
+//# sourceMappingURL=global-install-controller.js.map
\ No newline at end of file