From e7155a8cc6cfd8250ad286f34d5ac06339cda618 Mon Sep 17 00:00:00 2001 From: Revopush Date: Thu, 15 Oct 2026 14:03:46 +0300 Subject: [PATCH 1/2] Use async/await instead of promise chains No behaviour change: the same output, exit codes and server requests. - Command handlers, execute() and the release flows await instead of chaining .then(); release error handling uses try/catch - SDK request methods are async and still resolve null where they did - sign() uses jwt.sign synchronously and throws instead of returning rejected promises - new Promise(...) stays only around callback and event APIs Co-Authored-By: Claude Opus 5.5 --- script/binary-utils.ts | 2 +- script/command-executor.ts | 1189 ++++++++++++++++------------------ script/hash-utils.ts | 11 +- script/management-sdk.ts | 259 ++++---- script/sign.ts | 34 +- script/utils/gradle-utils.ts | 8 +- 6 files changed, 702 insertions(+), 801 deletions(-) diff --git a/script/binary-utils.ts b/script/binary-utils.ts index b54290f..ded5a54 100644 --- a/script/binary-utils.ts +++ b/script/binary-utils.ts @@ -116,7 +116,7 @@ async function openIPA(ipaPath: string): Promise<{ files: IpaFile[]; appPrefix: const files: IpaFile[] = []; for await (const entry of zipFile.eachEntry()) { if (entry.fileName.endsWith("/")) continue; - files.push({ path: entry.fileName, buffer: () => zipFile.openReadStreamPromise(entry).then(readStreamToBuffer) }); + files.push({ path: entry.fileName, buffer: async () => readStreamToBuffer(await zipFile.openReadStreamPromise(entry)) }); } const appPrefix = files.map((f) => f.path.match(/^(Payload\/[^/]+\.app)\//)?.[1]).find(Boolean); diff --git a/script/command-executor.ts b/script/command-executor.ts index d40a44c..11523d0 100644 --- a/script/command-executor.ts +++ b/script/command-executor.ts @@ -16,6 +16,7 @@ import sign from "./sign"; const ApkReader = require("@devicefarmer/adbkit-apkreader"); import { AccessKey, + Account, App, CodePushError, CollaboratorMap, @@ -131,14 +132,13 @@ export const confirm = (message: string = "Are you sure?"): Promise => }); }; -function accessKeyAdd(command: cli.IAccessKeyAddCommand): Promise { - return sdk.addAccessKey(command.name, command.ttl).then((accessKey: AccessKey) => { - log(`Successfully created the "${command.name}" access key: ${accessKey.key}`); - log("Make sure to save this key value somewhere safe, since you won't be able to view it from the CLI again!"); - }); +async function accessKeyAdd(command: cli.IAccessKeyAddCommand): Promise { + const accessKey: AccessKey = await sdk.addAccessKey(command.name, command.ttl); + log(`Successfully created the "${command.name}" access key: ${accessKey.key}`); + log("Make sure to save this key value somewhere safe, since you won't be able to view it from the CLI again!"); } -function accessKeyPatch(command: cli.IAccessKeyPatchCommand): Promise { +async function accessKeyPatch(command: cli.IAccessKeyPatchCommand): Promise { const willUpdateName: boolean = isCommandOptionSpecified(command.newName) && command.oldName !== command.newName; const willUpdateTtl: boolean = isCommandOptionSpecified(command.ttl); @@ -146,135 +146,115 @@ function accessKeyPatch(command: cli.IAccessKeyPatchCommand): Promise { throw new Error("A new name and/or TTL must be provided."); } - return sdk.patchAccessKey(command.oldName, command.newName, command.ttl).then((accessKey: AccessKey) => { - let logMessage: string = "Successfully "; - if (willUpdateName) { - logMessage += `renamed the access key "${command.oldName}" to "${command.newName}"`; - } + const accessKey: AccessKey = await sdk.patchAccessKey(command.oldName, command.newName, command.ttl); + let logMessage: string = "Successfully "; + if (willUpdateName) { + logMessage += `renamed the access key "${command.oldName}" to "${command.newName}"`; + } - if (willUpdateTtl) { - const expirationDate = moment(accessKey.expires).format("LLLL"); - if (willUpdateName) { - logMessage += ` and changed its expiration date to ${expirationDate}`; - } else { - logMessage += `changed the expiration date of the "${command.oldName}" access key to ${expirationDate}`; - } + if (willUpdateTtl) { + const expirationDate = moment(accessKey.expires).format("LLLL"); + if (willUpdateName) { + logMessage += ` and changed its expiration date to ${expirationDate}`; + } else { + logMessage += `changed the expiration date of the "${command.oldName}" access key to ${expirationDate}`; } + } - log(`${logMessage}.`); - }); + log(`${logMessage}.`); } -function accessKeyList(command: cli.IAccessKeyListCommand): Promise { +async function accessKeyList(command: cli.IAccessKeyListCommand): Promise { throwForInvalidOutputFormat(command.format); - return sdk.getAccessKeys().then((accessKeys: AccessKey[]): void => { - printAccessKeys(command.format, accessKeys); - }); + const accessKeys: AccessKey[] = await sdk.getAccessKeys(); + printAccessKeys(command.format, accessKeys); } -function accessKeyRemove(command: cli.IAccessKeyRemoveCommand): Promise { - return confirm().then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeAccessKey(command.accessKey).then((): void => { - log(`Successfully removed the "${command.accessKey}" access key.`); - }); - } +async function accessKeyRemove(command: cli.IAccessKeyRemoveCommand): Promise { + if (await confirm()) { + await sdk.removeAccessKey(command.accessKey); + log(`Successfully removed the "${command.accessKey}" access key.`); + return; + } - log("Access key removal cancelled."); - }); + log("Access key removal cancelled."); } -function appAdd(command: cli.IAppAddCommand): Promise { - return sdk.addApp(command.appName).then((app: App): Promise => { - log('Successfully added the "' + command.appName + '" app, along with the following default deployments:'); - const deploymentListCommand: cli.IDeploymentListCommand = { - type: cli.CommandType.deploymentList, - appName: app.name, - format: "table", - displayKeys: true, - }; - return deploymentList(deploymentListCommand, /*showPackage=*/ false); - }); +async function appAdd(command: cli.IAppAddCommand): Promise { + const app: App = await sdk.addApp(command.appName); + log('Successfully added the "' + command.appName + '" app, along with the following default deployments:'); + const deploymentListCommand: cli.IDeploymentListCommand = { + type: cli.CommandType.deploymentList, + appName: app.name, + format: "table", + displayKeys: true, + }; + await deploymentList(deploymentListCommand, /*showPackage=*/ false); } -function appList(command: cli.IAppListCommand): Promise { +async function appList(command: cli.IAppListCommand): Promise { throwForInvalidOutputFormat(command.format); - return sdk.getApps().then((retrievedApps: App[]): void => { - printAppList(command.format, retrievedApps); - }); + const retrievedApps: App[] = await sdk.getApps(); + printAppList(command.format, retrievedApps); } -function appRemove(command: cli.IAppRemoveCommand): Promise { - return confirm("Are you sure you want to remove this app? Note that its deployment keys will be PERMANENTLY unrecoverable.").then( - (wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeApp(command.appName).then((): void => { - log('Successfully removed the "' + command.appName + '" app.'); - }); - } +async function appRemove(command: cli.IAppRemoveCommand): Promise { + if (await confirm("Are you sure you want to remove this app? Note that its deployment keys will be PERMANENTLY unrecoverable.")) { + await sdk.removeApp(command.appName); + log('Successfully removed the "' + command.appName + '" app.'); + return; + } - log("App removal cancelled."); - } - ); + log("App removal cancelled."); } -function appRename(command: cli.IAppRenameCommand): Promise { - return sdk.renameApp(command.currentAppName, command.newAppName).then((): void => { - log('Successfully renamed the "' + command.currentAppName + '" app to "' + command.newAppName + '".'); - }); +async function appRename(command: cli.IAppRenameCommand): Promise { + await sdk.renameApp(command.currentAppName, command.newAppName); + log('Successfully renamed the "' + command.currentAppName + '" app to "' + command.newAppName + '".'); } -export const createEmptyTempReleaseFolder = (folderPath: string) => { - return deleteFolder(folderPath).then(() => { - fs.mkdirSync(folderPath); - }); +export const createEmptyTempReleaseFolder = async (folderPath: string): Promise => { + await deleteFolder(folderPath); + fs.mkdirSync(folderPath); }; -function appTransfer(command: cli.IAppTransferCommand): Promise { +async function appTransfer(command: cli.IAppTransferCommand): Promise { throwForInvalidEmail(command.email); - return confirm().then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.transferApp(command.appName, command.email).then((): void => { - log( - 'Successfully transferred the ownership of app "' + command.appName + '" to the account with email "' + command.email + '".' - ); - }); - } + if (await confirm()) { + await sdk.transferApp(command.appName, command.email); + log('Successfully transferred the ownership of app "' + command.appName + '" to the account with email "' + command.email + '".'); + return; + } - log("App transfer cancelled."); - }); + log("App transfer cancelled."); } -function addCollaborator(command: cli.ICollaboratorAddCommand): Promise { +async function addCollaborator(command: cli.ICollaboratorAddCommand): Promise { throwForInvalidEmail(command.email); - return sdk.addCollaborator(command.appName, command.email).then((): void => { - log('Successfully added "' + command.email + '" as a collaborator to the app "' + command.appName + '".'); - }); + await sdk.addCollaborator(command.appName, command.email); + log('Successfully added "' + command.email + '" as a collaborator to the app "' + command.appName + '".'); } -function listCollaborators(command: cli.ICollaboratorListCommand): Promise { +async function listCollaborators(command: cli.ICollaboratorListCommand): Promise { throwForInvalidOutputFormat(command.format); - return sdk.getCollaborators(command.appName).then((retrievedCollaborators: CollaboratorMap): void => { - printCollaboratorsList(command.format, retrievedCollaborators); - }); + const retrievedCollaborators: CollaboratorMap = await sdk.getCollaborators(command.appName); + printCollaboratorsList(command.format, retrievedCollaborators); } -function removeCollaborator(command: cli.ICollaboratorRemoveCommand): Promise { +async function removeCollaborator(command: cli.ICollaboratorRemoveCommand): Promise { throwForInvalidEmail(command.email); - return confirm().then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeCollaborator(command.appName, command.email).then((): void => { - log('Successfully removed "' + command.email + '" as a collaborator from the app "' + command.appName + '".'); - }); - } + if (await confirm()) { + await sdk.removeCollaborator(command.appName, command.email); + log('Successfully removed "' + command.email + '" as a collaborator from the app "' + command.appName + '".'); + return; + } - log("App collaborator removal cancelled."); - }); + log("App collaborator removal cancelled."); } function deleteConnectionInfoCache(printMessage: boolean = true): void { @@ -299,127 +279,115 @@ async function deleteReactTempFolders(): Promise { await Promise.all(reactFolders.map((name) => deleteFolder(path.join(tempFolder, name)))); } -function deploymentAdd(command: cli.IDeploymentAddCommand): Promise { - return sdk.addDeployment(command.appName, command.deploymentName, command.key).then((deployment: Deployment): void => { +async function deploymentAdd(command: cli.IDeploymentAddCommand): Promise { + const deployment: Deployment = await sdk.addDeployment(command.appName, command.deploymentName, command.key); + log( + 'Successfully added the "' + + command.deploymentName + + '" deployment with key "' + + deployment.key + + '" to the "' + + command.appName + + '" app.' + ); +} + +async function deploymentHistoryClear(command: cli.IDeploymentHistoryClearCommand): Promise { + if (await confirm()) { + await sdk.clearDeploymentHistory(command.appName, command.deploymentName); log( - 'Successfully added the "' + + 'Successfully cleared the release history associated with the "' + command.deploymentName + - '" deployment with key "' + - deployment.key + - '" to the "' + + '" deployment from the "' + command.appName + '" app.' ); - }); -} - -function deploymentHistoryClear(command: cli.IDeploymentHistoryClearCommand): Promise { - return confirm().then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.clearDeploymentHistory(command.appName, command.deploymentName).then((): void => { - log( - 'Successfully cleared the release history associated with the "' + - command.deploymentName + - '" deployment from the "' + - command.appName + - '" app.' - ); - }); - } + return; + } - log("Clear deployment cancelled."); - }); + log("Clear deployment cancelled."); } -export const deploymentList = (command: cli.IDeploymentListCommand, showPackage: boolean = true): Promise => { +export const deploymentList = async (command: cli.IDeploymentListCommand, showPackage: boolean = true): Promise => { throwForInvalidOutputFormat(command.format); - let deployments: Deployment[]; const DEPLOYMENTS_MAX_LENGTH = 10; // do not take metrics if number of deployment higher than this - return sdk - .getDeployments(command.appName) - .then((retrievedDeployments: Deployment[]) => { - deployments = retrievedDeployments; - if (showPackage && deployments.length < DEPLOYMENTS_MAX_LENGTH) { - const metricsPromises: Promise[] = deployments.map((deployment: Deployment) => { - if (deployment.package) { - return sdk.getDeploymentMetrics(command.appName, deployment.name).then((metrics: DeploymentMetrics): void => { - if (metrics[deployment.package.label]) { - const totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); - (deployment.package).metrics = { - active: metrics[deployment.package.label].active, - downloaded: metrics[deployment.package.label].downloaded, - failed: metrics[deployment.package.label].failed, - installed: metrics[deployment.package.label].installed, - totalActive: totalActive, - }; - } - }); - } else { - return Promise.resolve(); - } - }); + const deployments: Deployment[] = await sdk.getDeployments(command.appName); + if (showPackage && deployments.length < DEPLOYMENTS_MAX_LENGTH) { + await Promise.all( + deployments.map(async (deployment: Deployment): Promise => { + if (!deployment.package) { + return; + } - return Promise.all(metricsPromises); - } - }) - .then(() => { - printDeploymentList(command, deployments, showPackage); - }); + const metrics: DeploymentMetrics = await sdk.getDeploymentMetrics(command.appName, deployment.name); + if (metrics[deployment.package.label]) { + const totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); + (deployment.package).metrics = { + active: metrics[deployment.package.label].active, + downloaded: metrics[deployment.package.label].downloaded, + failed: metrics[deployment.package.label].failed, + installed: metrics[deployment.package.label].installed, + totalActive: totalActive, + }; + } + }) + ); + } + + printDeploymentList(command, deployments, showPackage); }; -function deploymentRemove(command: cli.IDeploymentRemoveCommand): Promise { - const confirmation = command.isForce - ? Promise.resolve(true) - : confirm("Are you sure you want to remove this deployment? Note that its deployment key will be PERMANENTLY unrecoverable."); +async function deploymentRemove(command: cli.IDeploymentRemoveCommand): Promise { + const wasConfirmed: boolean = + command.isForce || + (await confirm( + "Are you sure you want to remove this deployment? Note that its deployment key will be PERMANENTLY unrecoverable." + )); - return confirmation.then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeDeployment(command.appName, command.deploymentName).then((): void => { - log('Successfully removed the "' + command.deploymentName + '" deployment from the "' + command.appName + '" app.'); - }); - } + if (wasConfirmed) { + await sdk.removeDeployment(command.appName, command.deploymentName); + log('Successfully removed the "' + command.deploymentName + '" deployment from the "' + command.appName + '" app.'); + return; + } - log("Deployment removal cancelled."); - }); + log("Deployment removal cancelled."); } -function deploymentRename(command: cli.IDeploymentRenameCommand): Promise { - return sdk.renameDeployment(command.appName, command.currentDeploymentName, command.newDeploymentName).then((): void => { - log( - 'Successfully renamed the "' + - command.currentDeploymentName + - '" deployment to "' + - command.newDeploymentName + - '" for the "' + - command.appName + - '" app.' - ); - }); +async function deploymentRename(command: cli.IDeploymentRenameCommand): Promise { + await sdk.renameDeployment(command.appName, command.currentDeploymentName, command.newDeploymentName); + log( + 'Successfully renamed the "' + + command.currentDeploymentName + + '" deployment to "' + + command.newDeploymentName + + '" for the "' + + command.appName + + '" app.' + ); } -function deploymentHistory(command: cli.IDeploymentHistoryCommand): Promise { +async function deploymentHistory(command: cli.IDeploymentHistoryCommand): Promise { throwForInvalidOutputFormat(command.format); - return Promise.all([ + const [account, deploymentHistory, metrics] = await Promise.all([ sdk.getAccountInfo(), sdk.getDeploymentHistory(command.appName, command.deploymentName), sdk.getDeploymentMetrics(command.appName, command.deploymentName), - ]).then(([account, deploymentHistory, metrics]): void => { - const totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); - deploymentHistory.forEach((packageObject: Package) => { - if (metrics[packageObject.label]) { - (packageObject).metrics = { - active: metrics[packageObject.label].active, - downloaded: metrics[packageObject.label].downloaded, - failed: metrics[packageObject.label].failed, - installed: metrics[packageObject.label].installed, - totalActive: totalActive, - }; - } - }); - printDeploymentHistory(command, deploymentHistory, account.email); + ]); + const totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); + deploymentHistory.forEach((packageObject: Package) => { + if (metrics[packageObject.label]) { + (packageObject).metrics = { + active: metrics[packageObject.label].active, + downloaded: metrics[packageObject.label].downloaded, + failed: metrics[packageObject.label].failed, + installed: metrics[packageObject.label].installed, + totalActive: totalActive, + }; + } }); + printDeploymentHistory(command, deploymentHistory, account.email); } function deserializeConnectionInfo(): ILoginConnectionInfo { @@ -444,142 +412,138 @@ function deserializeConnectionInfo(): ILoginConnectionInfo { } } -export function execute(command: cli.ICommand) { +export async function execute(command: cli.ICommand): Promise { connectionInfo = deserializeConnectionInfo(); - return Promise.resolve().then(() => { - switch (command.type) { - // Must not be logged in - case cli.CommandType.login: - case cli.CommandType.register: - if (connectionInfo) { - throw new Error("You are already logged in from this machine."); - } - break; + switch (command.type) { + // Must not be logged in + case cli.CommandType.login: + case cli.CommandType.register: + if (connectionInfo) { + throw new Error("You are already logged in from this machine."); + } + break; - // It does not matter whether you are logged in or not - case cli.CommandType.link: - break; + // It does not matter whether you are logged in or not + case cli.CommandType.link: + break; - // Must be logged in - default: - if (!!sdk) break; // Used by unit tests to skip authentication + // Must be logged in + default: + if (!!sdk) break; // Used by unit tests to skip authentication - if (!connectionInfo) { - throw new Error( - "You are not currently logged in. Run the 'revopush login' command to authenticate with the CodePush server." - ); - } + if (!connectionInfo) { + throw new Error("You are not currently logged in. Run the 'revopush login' command to authenticate with the CodePush server."); + } - sdk = getSdk(connectionInfo.accessKey, CLI_HEADERS, connectionInfo.customServerUrl); - break; - } + sdk = getSdk(connectionInfo.accessKey, CLI_HEADERS, connectionInfo.customServerUrl); + break; + } - switch (command.type) { - case cli.CommandType.accessKeyAdd: - return accessKeyAdd(command); + switch (command.type) { + case cli.CommandType.accessKeyAdd: + return accessKeyAdd(command); - case cli.CommandType.accessKeyPatch: - return accessKeyPatch(command); + case cli.CommandType.accessKeyPatch: + return accessKeyPatch(command); - case cli.CommandType.accessKeyList: - return accessKeyList(command); + case cli.CommandType.accessKeyList: + return accessKeyList(command); - case cli.CommandType.accessKeyRemove: - return accessKeyRemove(command); + case cli.CommandType.accessKeyRemove: + return accessKeyRemove(command); - case cli.CommandType.appAdd: - return appAdd(command); + case cli.CommandType.appAdd: + return appAdd(command); - case cli.CommandType.appList: - return appList(command); + case cli.CommandType.appList: + return appList(command); - case cli.CommandType.appRemove: - return appRemove(command); + case cli.CommandType.appRemove: + return appRemove(command); - case cli.CommandType.appRename: - return appRename(command); + case cli.CommandType.appRename: + return appRename(command); - case cli.CommandType.appTransfer: - return appTransfer(command); + case cli.CommandType.appTransfer: + return appTransfer(command); - case cli.CommandType.collaboratorAdd: - return addCollaborator(command); + case cli.CommandType.collaboratorAdd: + return addCollaborator(command); - case cli.CommandType.collaboratorList: - return listCollaborators(command); + case cli.CommandType.collaboratorList: + return listCollaborators(command); - case cli.CommandType.collaboratorRemove: - return removeCollaborator(command); + case cli.CommandType.collaboratorRemove: + return removeCollaborator(command); - case cli.CommandType.debug: - return debugCommand(command); + case cli.CommandType.debug: + return debugCommand(command); - case cli.CommandType.deploymentAdd: - return deploymentAdd(command); + case cli.CommandType.deploymentAdd: + return deploymentAdd(command); - case cli.CommandType.deploymentHistoryClear: - return deploymentHistoryClear(command); + case cli.CommandType.deploymentHistoryClear: + return deploymentHistoryClear(command); - case cli.CommandType.deploymentHistory: - return deploymentHistory(command); + case cli.CommandType.deploymentHistory: + return deploymentHistory(command); - case cli.CommandType.deploymentList: - return deploymentList(command); + case cli.CommandType.deploymentList: + return deploymentList(command); - case cli.CommandType.deploymentRemove: - return deploymentRemove(command); + case cli.CommandType.deploymentRemove: + return deploymentRemove(command); - case cli.CommandType.deploymentRename: - return deploymentRename(command); + case cli.CommandType.deploymentRename: + return deploymentRename(command); - case cli.CommandType.link: - return link(command); + case cli.CommandType.link: + return link(command); - case cli.CommandType.login: - return login(command); + case cli.CommandType.login: + return login(command); - case cli.CommandType.logout: - return logout(command); + case cli.CommandType.logout: + return logout(command); - case cli.CommandType.patch: - return patch(command); + case cli.CommandType.patch: + return patch(command); - case cli.CommandType.promote: - return promote(command); + case cli.CommandType.promote: + return promote(command); - case cli.CommandType.register: - return register(command); + case cli.CommandType.register: + return register(command); - case cli.CommandType.release: - return release(command); + case cli.CommandType.release: + return release(command); - case cli.CommandType.releaseReact: - return releaseReact(command); + case cli.CommandType.releaseReact: + return releaseReact(command); - case cli.CommandType.releaseExpo: - return releaseExpo(command); + case cli.CommandType.releaseExpo: + return releaseExpo(command); - case cli.CommandType.releaseNative: - return releaseNative(command); + case cli.CommandType.releaseNative: + return releaseNative(command); - case cli.CommandType.rollback: - return rollback(command); + case cli.CommandType.rollback: + return rollback(command); - case cli.CommandType.sessionList: - return sessionList(command); + case cli.CommandType.sessionList: + return sessionList(command); - case cli.CommandType.sessionRemove: - return sessionRemove(command); + case cli.CommandType.sessionRemove: + return sessionRemove(command); - case cli.CommandType.whoami: - return whoami(command); + case cli.CommandType.whoami: + return whoami(command); - default: - // We should never see this message as invalid commands should be caught by the argument parser. - throw new Error("Invalid command: " + JSON.stringify(command)); - } - }); + default: + // We should never see this message as invalid commands should be caught by the argument parser. + throw new Error("Invalid command: " + JSON.stringify(command)); + } } function getTotalActiveFromDeploymentMetrics(metrics: DeploymentMetrics): number { @@ -599,66 +563,58 @@ function initiateExternalAuthenticationAsync(action: string, serverUrl?: string) opener(url); } -function link(command: cli.ILinkCommand): Promise { +async function link(command: cli.ILinkCommand): Promise { initiateExternalAuthenticationAsync("link", command.serverUrl); - return Promise.resolve(); } -function login(command: cli.ILoginCommand): Promise { +async function login(command: cli.ILoginCommand): Promise { // Check if one of the flags were provided. if (command.accessKey) { sdk = getSdk(command.accessKey, CLI_HEADERS, command.apiServerUrl); - return sdk.isAuthenticated().then((isAuthenticated: boolean): void => { - if (isAuthenticated) { - serializeConnectionInfo(command.accessKey, /*preserveAccessKeyOnLogout*/ true, command.apiServerUrl); - } else { - throw new Error("Invalid access key."); - } - }); + if (await sdk.isAuthenticated()) { + serializeConnectionInfo(command.accessKey, /*preserveAccessKeyOnLogout*/ true, command.apiServerUrl); + } else { + throw new Error("Invalid access key."); + } } else { - return loginWithExternalAuthentication("login", command.apiServerUrl, command.appServerUrl); + await loginWithExternalAuthentication("login", command.apiServerUrl, command.appServerUrl); } } -function loginWithExternalAuthentication(action: string, apiServerUrl?: string, appServerUrl?: string): Promise { +async function loginWithExternalAuthentication(action: string, apiServerUrl?: string, appServerUrl?: string): Promise { initiateExternalAuthenticationAsync(action, appServerUrl); log(""); // Insert newline - return requestAccessKey().then((accessKey: string): Promise => { - if (accessKey === null) { - // The user has aborted the synchronous prompt (e.g.: via [CTRL]+[C]). - return; - } + const accessKey: string = await requestAccessKey(); + if (accessKey === null) { + // The user has aborted the synchronous prompt (e.g.: via [CTRL]+[C]). + return; + } - sdk = getSdk(accessKey, CLI_HEADERS, apiServerUrl); + sdk = getSdk(accessKey, CLI_HEADERS, apiServerUrl); - return sdk.isAuthenticated().then((isAuthenticated: boolean): void => { - if (isAuthenticated) { - serializeConnectionInfo(accessKey, /*preserveAccessKeyOnLogout*/ false, apiServerUrl); - } else { - throw new Error("Invalid access key."); - } - }); - }); + if (await sdk.isAuthenticated()) { + serializeConnectionInfo(accessKey, /*preserveAccessKeyOnLogout*/ false, apiServerUrl); + } else { + throw new Error("Invalid access key."); + } } -function logout(_command: cli.ICommand): Promise { - return Promise.resolve() - .then((): Promise => { - if (!connectionInfo.preserveAccessKeyOnLogout) { - const machineName: string = os.hostname(); - return sdk.removeSession(machineName).catch((error: CodePushError) => { - // If we are not authenticated or the session doesn't exist anymore, just swallow the error instead of displaying it - if (error.statusCode !== AccountManager.ERROR_UNAUTHORIZED && error.statusCode !== AccountManager.ERROR_NOT_FOUND) { - throw error; - } - }); +async function logout(_command: cli.ICommand): Promise { + if (!connectionInfo.preserveAccessKeyOnLogout) { + const machineName: string = os.hostname(); + try { + await sdk.removeSession(machineName); + } catch (error) { + // If we are not authenticated or the session doesn't exist anymore, just swallow the error instead of displaying it + if (error.statusCode !== AccountManager.ERROR_UNAUTHORIZED && error.statusCode !== AccountManager.ERROR_NOT_FOUND) { + throw error; } - }) - .then((): void => { - sdk = null; - deleteConnectionInfoCache(); - }); + } + } + + sdk = null; + deleteConnectionInfoCache(); } function formatDate(unixOffset: number): string { @@ -878,7 +834,7 @@ interface ProjectVersionInfo { buildNumber?: string; } -function getReactNativeProjectVersionInfo(command: cli.IReleaseReactCommand, projectName: string): Promise { +async function getReactNativeProjectVersionInfo(command: cli.IReleaseReactCommand, projectName: string): Promise { log(chalk.cyan(`Detecting ${command.platform} app version:\n`)); if (command.platform === "ios") { @@ -937,13 +893,13 @@ function getReactNativeProjectVersionInfo(command: cli.IReleaseReactCommand, pro ); } - return Promise.resolve({ appVersion: rawShortVersion, buildNumber: rawBundleVersion }); + return { appVersion: rawShortVersion, buildNumber: rawBundleVersion }; } else if (command.platform === "android") { return getAndroidVersionInfo(command.gradleFile); } } -function getAppVersionFromXcodeProject( +async function getAppVersionFromXcodeProject( command: cli.IReleaseReactCommand, projectName: string ): Promise<{ appVersion: string; buildNumber: string }> { @@ -994,7 +950,7 @@ function getAppVersionFromXcodeProject( throw new Error(`The "CURRENT_PROJECT_VERSION" key doesn't exist in the "${resolvedPbxprojFile}" file.`); } - return Promise.resolve({ appVersion: marketingVersion, buildNumber: String(currentProjectVersion) }); + return { appVersion: marketingVersion, buildNumber: String(currentProjectVersion) }; } function printJson(object: any): void { @@ -1051,11 +1007,11 @@ function printTable(columnNames: string[], readData: (dataSource: any[]) => void log(table.toString()); } -function register(command: cli.IRegisterCommand): Promise { +async function register(command: cli.IRegisterCommand): Promise { return loginWithExternalAuthentication("register", command.serverUrl); } -function promote(command: cli.IPromoteCommand): Promise { +async function promote(command: cli.IPromoteCommand): Promise { const packageInfo: PackageInfo = { appVersion: command.appStoreVersion, description: command.description, @@ -1065,25 +1021,25 @@ function promote(command: cli.IPromoteCommand): Promise { rollout: command.rollout, }; - return sdk - .promote(command.appName, command.sourceDeploymentName, command.destDeploymentName, packageInfo) - .then((): void => { - log( - "Successfully promoted " + - (command.label !== null ? '"' + command.label + '" of ' : "") + - 'the "' + - command.sourceDeploymentName + - '" deployment of the "' + - command.appName + - '" app to the "' + - command.destDeploymentName + - '" deployment.' - ); - }) - .catch((err: CodePushError) => releaseErrorHandler(err, command)); + try { + await sdk.promote(command.appName, command.sourceDeploymentName, command.destDeploymentName, packageInfo); + log( + "Successfully promoted " + + (command.label !== null ? '"' + command.label + '" of ' : "") + + 'the "' + + command.sourceDeploymentName + + '" deployment of the "' + + command.appName + + '" app to the "' + + command.destDeploymentName + + '" deployment.' + ); + } catch (err) { + releaseErrorHandler(err, command); + } } -function patch(command: cli.IPatchCommand): Promise { +async function patch(command: cli.IPatchCommand): Promise { const packageInfo: PackageInfo = { appVersion: command.appStoreVersion, description: command.description, @@ -1102,16 +1058,15 @@ function patch(command: cli.IPatchCommand): Promise { throw new Error("At least one property must be specified to patch a release."); } - return sdk.patchRelease(command.appName, command.deploymentName, command.label, packageInfo).then((): void => { - log( - `Successfully updated the "${command.label ? command.label : `latest`}" release of "${command.appName}" app's "${ - command.deploymentName - }" deployment.` - ); - }); + await sdk.patchRelease(command.appName, command.deploymentName, command.label, packageInfo); + log( + `Successfully updated the "${command.label ? command.label : `latest`}" release of "${command.appName}" app's "${ + command.deploymentName + }" deployment.` + ); } -export const release = (command: cli.IReleaseCommand): Promise => { +export const release = async (command: cli.IReleaseCommand): Promise => { // for initial release we explicitly define release as optional, disabled, without rollout, with a special description const updateMetadata: PackageInfo = { description: command.initial ? `Zero release for v${command.appStoreVersion}` : command.description, @@ -1348,7 +1303,7 @@ function printReleaseSizeWarnings(analysis: ReleaseSizeAnalysis | null, force: b } } -export const releaseExpo = (command: cli.IReleaseReactCommand): Promise => { +export const releaseExpo = async (command: cli.IReleaseReactCommand): Promise => { const { platform, bundleName, projectName } = validateReactReleaseCommand(command, { resolveEntryFile: false, }); @@ -1364,72 +1319,55 @@ export const releaseExpo = (command: cli.IReleaseReactCommand): Promise => releaseCommand.package = outputFolder; releaseCommand.outputDir = outputFolder; releaseCommand.bundleName = bundleName; - let releaseSizeAnalysis: ReleaseSizeAnalysis | null = null; - - return sdk - .getDeployment(command.appName, command.deploymentName) - .then(async () => { - // For release-expo, buildNumber is NOT auto-detected — it must be passed explicitly - // via --buildNumber. Auto-detection only applies to release-native where the binary - // build number is the natural targeting key. - const versionInfo: ProjectVersionInfo = command.appStoreVersion - ? { appVersion: command.appStoreVersion, buildNumber: command.buildNumber } - : { appVersion: (await getReactNativeProjectVersionInfo(command, projectName)).appVersion, buildNumber: command.buildNumber }; - - if (!sourcemapOutputFolder.endsWith(".map") && !command.sourcemapOutput) { - await createEmptyTempReleaseFolder(sourcemapOutputFolder); - } + await sdk.getDeployment(command.appName, command.deploymentName); - return versionInfo; - }) - .then(({ appVersion, buildNumber }: ProjectVersionInfo) => { - throwForInvalidSemverRange(appVersion); - releaseCommand.appStoreVersion = appVersion; - releaseCommand.buildNumber = buildNumber; - - return createEmptyTempReleaseFolder(outputFolder); - }) - .then(() => deleteReactTempFolders()) - .then(async () => { - const { isHermes, sourceMaps, bundleOptions } = await planBundle(command, outputFolder, bundleName, sourcemapOutputFolder); - await runExpoExportEmbedCommand(bundleOptions); - - if (isHermes) { - await compileHermesBundle(command, outputFolder, bundleName, sourceMaps, baseReleaseTmpFolder); - } - }) - .then(() => { - releaseSizeAnalysis = validateGeneratedReleaseSizes(command, outputFolder, bundleName, platform); - }) - .then(async () => { - if (command.privateKeyPath) { - log(chalk.cyan("\nSigning the bundle:\n")); - await sign(command.privateKeyPath, outputFolder); - } else { - console.log("private key was not provided"); - } - }) - .then(() => { - log(chalk.cyan("\nReleasing update contents to CodePush:\n")); - return releaseReactNative(releaseCommand, () => printReleaseSizeWarnings(releaseSizeAnalysis, command.force || false)); - }) - .then(async () => { - if (isTempOutputFolder) { - await deleteFolder(outputFolder); - } + // For release-expo, buildNumber is NOT auto-detected — it must be passed explicitly + // via --buildNumber. Auto-detection only applies to release-native where the binary + // build number is the natural targeting key. + const appVersion: string = command.appStoreVersion || (await getReactNativeProjectVersionInfo(command, projectName)).appVersion; - if (!command.sourcemapOutput) { - await deleteFolder(sourcemapOutputFolder); - } + if (!sourcemapOutputFolder.endsWith(".map") && !command.sourcemapOutput) { + await createEmptyTempReleaseFolder(sourcemapOutputFolder); + } - await deleteFolder(baseReleaseTmpFolder); - }) - .catch(async (err: Error) => { - throw err; - }); + throwForInvalidSemverRange(appVersion); + releaseCommand.appStoreVersion = appVersion; + releaseCommand.buildNumber = command.buildNumber; + + await createEmptyTempReleaseFolder(outputFolder); + await deleteReactTempFolders(); + + const { isHermes, sourceMaps, bundleOptions } = await planBundle(command, outputFolder, bundleName, sourcemapOutputFolder); + await runExpoExportEmbedCommand(bundleOptions); + + if (isHermes) { + await compileHermesBundle(command, outputFolder, bundleName, sourceMaps, baseReleaseTmpFolder); + } + + const releaseSizeAnalysis: ReleaseSizeAnalysis = validateGeneratedReleaseSizes(command, outputFolder, bundleName, platform); + + if (command.privateKeyPath) { + log(chalk.cyan("\nSigning the bundle:\n")); + await sign(command.privateKeyPath, outputFolder); + } else { + console.log("private key was not provided"); + } + + log(chalk.cyan("\nReleasing update contents to CodePush:\n")); + await releaseReactNative(releaseCommand, () => printReleaseSizeWarnings(releaseSizeAnalysis, command.force || false)); + + if (isTempOutputFolder) { + await deleteFolder(outputFolder); + } + + if (!command.sourcemapOutput) { + await deleteFolder(sourcemapOutputFolder); + } + + await deleteFolder(baseReleaseTmpFolder); }; -export const releaseReact = (command: cli.IReleaseReactCommand): Promise => { +export const releaseReact = async (command: cli.IReleaseReactCommand): Promise => { const { platform, bundleName, entryFile, projectName } = validateReactReleaseCommand(command, { resolveEntryFile: true, }); @@ -1445,82 +1383,60 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise = releaseCommand.package = outputFolder; releaseCommand.outputDir = outputFolder; releaseCommand.bundleName = bundleName; - let releaseSizeAnalysis: ReleaseSizeAnalysis | null = null; - // Check that the app and deployment exist before releasing an update. // This validation helps to save about 1 minute or more in case user has typed wrong app or deployment name. - return ( - sdk - .getDeployment(command.appName, command.deploymentName) - .then(async () => { - // For release-react, buildNumber is NOT auto-detected — it must be passed explicitly - // via --buildNumber. Auto-detection only applies to release-native where the binary - // build number is the natural targeting key. - const versionInfo: ProjectVersionInfo = command.appStoreVersion - ? { appVersion: command.appStoreVersion, buildNumber: command.buildNumber } - : { - appVersion: (await getReactNativeProjectVersionInfo(command, projectName)).appVersion, - buildNumber: command.buildNumber, - }; - - if (!sourcemapOutputFolder.endsWith(".map") && !command.sourcemapOutput) { - // create tmp dir only if no dir was given by user. User must crete a directory if --sourcemapOutput is passes - await createEmptyTempReleaseFolder(sourcemapOutputFolder); - } + await sdk.getDeployment(command.appName, command.deploymentName); - return versionInfo; - }) - .then(({ appVersion, buildNumber }: ProjectVersionInfo) => { - throwForInvalidSemverRange(appVersion); - releaseCommand.appStoreVersion = appVersion; - releaseCommand.buildNumber = buildNumber; + // For release-react, buildNumber is NOT auto-detected — it must be passed explicitly + // via --buildNumber. Auto-detection only applies to release-native where the binary + // build number is the natural targeting key. + const appVersion: string = command.appStoreVersion || (await getReactNativeProjectVersionInfo(command, projectName)).appVersion; - return createEmptyTempReleaseFolder(outputFolder); - }) - // This is needed to clear the react native bundler cache: - // https://github.com/facebook/react-native/issues/4289 - .then(() => deleteReactTempFolders()) - .then(async () => { - const { isHermes, sourceMaps, bundleOptions } = await planBundle(command, outputFolder, bundleName, sourcemapOutputFolder); - await runReactNativeBundleCommand(bundleOptions, entryFile, isHermes); - - if (isHermes) { - await compileHermesBundle(command, outputFolder, bundleName, sourceMaps, baseReleaseTmpFolder); - } - }) - .then(() => { - releaseSizeAnalysis = validateGeneratedReleaseSizes(command, outputFolder, bundleName, platform); - }) - .then(async () => { - if (command.privateKeyPath) { - log(chalk.cyan("\nSigning the bundle:\n")); - await sign(command.privateKeyPath, outputFolder); - } else { - console.log("private key was not provided"); - } - }) - .then(() => { - log(chalk.cyan("\nReleasing update contents to CodePush:\n")); - return releaseReactNative(releaseCommand, () => printReleaseSizeWarnings(releaseSizeAnalysis, command.force || false)); - }) - .then(async () => { - if (isTempOutputFolder) { - await deleteFolder(outputFolder); - } + if (!sourcemapOutputFolder.endsWith(".map") && !command.sourcemapOutput) { + // create tmp dir only if no dir was given by user. User must crete a directory if --sourcemapOutput is passes + await createEmptyTempReleaseFolder(sourcemapOutputFolder); + } - if (!command.sourcemapOutput) { - await deleteFolder(sourcemapOutputFolder); - } + throwForInvalidSemverRange(appVersion); + releaseCommand.appStoreVersion = appVersion; + releaseCommand.buildNumber = command.buildNumber; - await deleteFolder(baseReleaseTmpFolder); - }) - .catch(async (err: Error) => { - throw err; - }) - ); + await createEmptyTempReleaseFolder(outputFolder); + // This is needed to clear the react native bundler cache: + // https://github.com/facebook/react-native/issues/4289 + await deleteReactTempFolders(); + + const { isHermes, sourceMaps, bundleOptions } = await planBundle(command, outputFolder, bundleName, sourcemapOutputFolder); + await runReactNativeBundleCommand(bundleOptions, entryFile, isHermes); + + if (isHermes) { + await compileHermesBundle(command, outputFolder, bundleName, sourceMaps, baseReleaseTmpFolder); + } + + const releaseSizeAnalysis: ReleaseSizeAnalysis = validateGeneratedReleaseSizes(command, outputFolder, bundleName, platform); + + if (command.privateKeyPath) { + log(chalk.cyan("\nSigning the bundle:\n")); + await sign(command.privateKeyPath, outputFolder); + } else { + console.log("private key was not provided"); + } + + log(chalk.cyan("\nReleasing update contents to CodePush:\n")); + await releaseReactNative(releaseCommand, () => printReleaseSizeWarnings(releaseSizeAnalysis, command.force || false)); + + if (isTempOutputFolder) { + await deleteFolder(outputFolder); + } + + if (!command.sourcemapOutput) { + await deleteFolder(sourcemapOutputFolder); + } + + await deleteFolder(baseReleaseTmpFolder); }; -export const releaseNative = (command: cli.IReleaseNativeCommand): Promise => { +export const releaseNative = async (command: cli.IReleaseNativeCommand): Promise => { const platform: string = command.platform.toLowerCase(); let bundleName: string = command.bundleName; const targetBinaryPath: string = command.targetBinary; @@ -1543,95 +1459,89 @@ export const releaseNative = (command: cli.IReleaseNativeCommand): Promise throw new Error("For Android platform, target binary must be an .apk or .aab file."); } - return sdk - .getDeployment(command.appName, command.deploymentName) - .then(async () => { - try { - await createEmptyTempReleaseFolder(outputFolder); - await createEmptyTempReleaseFolder(extractFolder); + await sdk.getDeployment(command.appName, command.deploymentName); - if (!bundleName) { - bundleName = platform === "ios" ? "main.jsbundle" : `index.android.bundle`; - } - let releaseCommandPartial: Partial; - - if (platform === "ios") { - log(chalk.cyan(`\nReading IPA file:\n`)); - const metadataZip = await extractMetadataFromIOS(targetBinaryPath, outputFolder); - const buildVersion = await getIosVersion(targetBinaryPath); - releaseCommandPartial = { - package: metadataZip, - appStoreVersion: buildVersion?.version, - buildNumber: buildVersion?.build, - }; - } else { - if (targetBinaryPathNormalised.endsWith(".apk")) { - log(chalk.cyan(`\nExtracting APK/ARR file:\n`)); - await extractArchive(targetBinaryPath, extractFolder); - - const reader = await ApkReader.open(targetBinaryPath); - const { versionName: appStoreVersion, versionCode } = await reader.readManifest(); - const metadataZip = await extractMetadataFromAndroid(extractFolder, outputFolder); - releaseCommandPartial = { - package: metadataZip, - appStoreVersion, - buildNumber: versionCode?.toString(), - }; - } else if (targetBinaryPathNormalised.endsWith(".aab")) { - log(chalk.cyan(`\nExtracting AAB file:\n`)); - await extractArchive(targetBinaryPath, extractFolder); - const { versionName: appStoreVersion, versionCode } = await parseAabManifest(targetBinaryPath); - - const metadataZip = await extractMetadataFromAndroid(`${extractFolder}/base`, outputFolder); // base folder is nested in AAB - releaseCommandPartial = { - package: metadataZip, - appStoreVersion, - buildNumber: versionCode?.toString(), - }; - } else { - throw new Error("For Android platform, target binary must be an .apk or .aab file."); - } - } + try { + await createEmptyTempReleaseFolder(outputFolder); + await createEmptyTempReleaseFolder(extractFolder); - const { package: metadataZip, appStoreVersion, buildNumber: detectedBuildNumber } = releaseCommandPartial; - // Use the zip file as package for release - const releaseCommand: cli.IReleaseReactCommand = { - type: cli.CommandType.release, - appName: command.appName, - deploymentName: command.deploymentName, - appStoreVersion: command.appStoreVersion || appStoreVersion, - buildNumber: command.buildNumber || detectedBuildNumber, - description: command.description, - disabled: command.disabled, - mandatory: command.mandatory, - rollout: command.rollout, - initial: command.initial, - noDuplicateReleaseError: command.noDuplicateReleaseError, - platform: platform, - outputDir: outputFolder, - bundleName: bundleName, + if (!bundleName) { + bundleName = platform === "ios" ? "main.jsbundle" : `index.android.bundle`; + } + let releaseCommandPartial: Partial; + + if (platform === "ios") { + log(chalk.cyan(`\nReading IPA file:\n`)); + const metadataZip = await extractMetadataFromIOS(targetBinaryPath, outputFolder); + const buildVersion = await getIosVersion(targetBinaryPath); + releaseCommandPartial = { + package: metadataZip, + appStoreVersion: buildVersion?.version, + buildNumber: buildVersion?.build, + }; + } else { + if (targetBinaryPathNormalised.endsWith(".apk")) { + log(chalk.cyan(`\nExtracting APK/ARR file:\n`)); + await extractArchive(targetBinaryPath, extractFolder); + + const reader = await ApkReader.open(targetBinaryPath); + const { versionName: appStoreVersion, versionCode } = await reader.readManifest(); + const metadataZip = await extractMetadataFromAndroid(extractFolder, outputFolder); + releaseCommandPartial = { package: metadataZip, + appStoreVersion, + buildNumber: versionCode?.toString(), }; + } else if (targetBinaryPathNormalised.endsWith(".aab")) { + log(chalk.cyan(`\nExtracting AAB file:\n`)); + await extractArchive(targetBinaryPath, extractFolder); + const { versionName: appStoreVersion, versionCode } = await parseAabManifest(targetBinaryPath); - return doNativeRelease(releaseCommand).then(async () => { - // Clean up zip file - if (fs.existsSync(releaseCommandPartial.package)) { - fs.unlinkSync(releaseCommandPartial.package); - } - }); - } finally { - try { - await deleteFolder(extractFolder); - await deleteFolder(outputFolder); - } catch (ignored) {} + const metadataZip = await extractMetadataFromAndroid(`${extractFolder}/base`, outputFolder); // base folder is nested in AAB + releaseCommandPartial = { + package: metadataZip, + appStoreVersion, + buildNumber: versionCode?.toString(), + }; + } else { + throw new Error("For Android platform, target binary must be an .apk or .aab file."); } - }) - .catch(async (err: Error) => { - throw err; - }); + } + + const { package: metadataZip, appStoreVersion, buildNumber: detectedBuildNumber } = releaseCommandPartial; + // Use the zip file as package for release + const releaseCommand: cli.IReleaseReactCommand = { + type: cli.CommandType.release, + appName: command.appName, + deploymentName: command.deploymentName, + appStoreVersion: command.appStoreVersion || appStoreVersion, + buildNumber: command.buildNumber || detectedBuildNumber, + description: command.description, + disabled: command.disabled, + mandatory: command.mandatory, + rollout: command.rollout, + initial: command.initial, + noDuplicateReleaseError: command.noDuplicateReleaseError, + platform: platform, + outputDir: outputFolder, + bundleName: bundleName, + package: metadataZip, + }; + + await doNativeRelease(releaseCommand); + // Clean up zip file + if (fs.existsSync(releaseCommandPartial.package)) { + fs.unlinkSync(releaseCommandPartial.package); + } + } finally { + try { + await deleteFolder(extractFolder); + await deleteFolder(outputFolder); + } catch (ignored) {} + } }; -const releaseReactNative = (command: cli.IReleaseReactCommand, onReleaseCompleted?: () => void): Promise => { +const releaseReactNative = async (command: cli.IReleaseReactCommand, onReleaseCompleted?: () => void): Promise => { // for initial release we explicitly define release as optional, disabled, without rollout, with a special description const updateMetadata: ReactNativePackageInfo = { description: command.initial ? `Zero release for v${command.appStoreVersion}` : command.description, @@ -1648,7 +1558,7 @@ const releaseReactNative = (command: cli.IReleaseReactCommand, onReleaseComplete return doRelease(command, updateMetadata, onReleaseCompleted); }; -const doRelease = ( +const doRelease = async ( command: cli.IReleaseCommand | cli.IReleaseReactCommand, updateMetadata: PackageInfo, onReleaseCompleted?: () => void @@ -1680,34 +1590,32 @@ const doRelease = ( lastTotalProgress = currentProgress; }; - return sdk - .isAuthenticated(true) - .then((): Promise => { - log("Release file path: " + filePath); - log("Metadata: " + JSON.stringify(updateMetadata)); - return sdk.release(command.appName, command.deploymentName, filePath, updateMetadata, uploadProgress); - }) - .then((): void => { - log( - 'Successfully released an update containing the "' + - command.package + - '" ' + - (isSingleFilePackage ? "file" : "directory") + - ' to the "' + - command.deploymentName + - '" deployment of the "' + - command.appName + - '" app.' - ); + try { + await sdk.isAuthenticated(true); + log("Release file path: " + filePath); + log("Metadata: " + JSON.stringify(updateMetadata)); + await sdk.release(command.appName, command.deploymentName, filePath, updateMetadata, uploadProgress); + log( + 'Successfully released an update containing the "' + + command.package + + '" ' + + (isSingleFilePackage ? "file" : "directory") + + ' to the "' + + command.deploymentName + + '" deployment of the "' + + command.appName + + '" app.' + ); - if (onReleaseCompleted) { - onReleaseCompleted(); - } - }) - .catch((err: CodePushError) => releaseErrorHandler(err, command)); + if (onReleaseCompleted) { + onReleaseCompleted(); + } + } catch (err) { + releaseErrorHandler(err, command); + } }; -const doNativeRelease = (releaseCommand: cli.IReleaseReactCommand): Promise => { +const doNativeRelease = async (releaseCommand: cli.IReleaseReactCommand): Promise => { throwForInvalidSemverRange(releaseCommand.appStoreVersion); const filePath: string = releaseCommand.package; @@ -1738,40 +1646,33 @@ const doNativeRelease = (releaseCommand: cli.IReleaseReactCommand): Promise => { - return sdk.releaseNative(releaseCommand.appName, releaseCommand.deploymentName, filePath, updateMetadata, uploadProgress); - }) - .then((): void => { - log( - 'Successfully released an update containing the "' + - releaseCommand.package + - '" ' + - "directory" + - ' to the "' + - releaseCommand.deploymentName + - '" deployment of the "' + - releaseCommand.appName + - '" app.' - ); - }) - .catch((err: CodePushError) => releaseErrorHandler(err, releaseCommand)); + try { + await sdk.isAuthenticated(true); + await sdk.releaseNative(releaseCommand.appName, releaseCommand.deploymentName, filePath, updateMetadata, uploadProgress); + log( + 'Successfully released an update containing the "' + + releaseCommand.package + + '" ' + + "directory" + + ' to the "' + + releaseCommand.deploymentName + + '" deployment of the "' + + releaseCommand.appName + + '" app.' + ); + } catch (err) { + releaseErrorHandler(err, releaseCommand); + } }; -function rollback(command: cli.IRollbackCommand): Promise { - return confirm().then((wasConfirmed: boolean) => { - if (!wasConfirmed) { - log("Rollback cancelled."); - return; - } +async function rollback(command: cli.IRollbackCommand): Promise { + if (!(await confirm())) { + log("Rollback cancelled."); + return; + } - return sdk.rollback(command.appName, command.deploymentName, command.targetRelease || undefined).then((): void => { - log( - 'Successfully performed a rollback on the "' + command.deploymentName + '" deployment of the "' + command.appName + '" app.' - ); - }); - }); + await sdk.rollback(command.appName, command.deploymentName, command.targetRelease || undefined); + log('Successfully performed a rollback on the "' + command.deploymentName + '" deployment of the "' + command.appName + '" app.'); } function requestAccessKey(): Promise { @@ -1882,28 +1783,25 @@ function serializeConnectionInfo(accessKey: string, preserveAccessKeyOnLogout: b ); } -function sessionList(command: cli.ISessionListCommand): Promise { +async function sessionList(command: cli.ISessionListCommand): Promise { throwForInvalidOutputFormat(command.format); - return sdk.getSessions().then((sessions: Session[]): void => { - printSessions(command.format, sessions); - }); + const sessions: Session[] = await sdk.getSessions(); + printSessions(command.format, sessions); } -function sessionRemove(command: cli.ISessionRemoveCommand): Promise { +async function sessionRemove(command: cli.ISessionRemoveCommand): Promise { if (os.hostname() === command.machineName) { throw new Error("Cannot remove the current login session via this command. Please run 'revopush logout' instead."); - } else { - return confirm().then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeSession(command.machineName).then((): void => { - log(`Successfully removed the login session for "${command.machineName}".`); - }); - } + } - log("Session removal cancelled."); - }); + if (await confirm()) { + await sdk.removeSession(command.machineName); + log(`Successfully removed the login session for "${command.machineName}".`); + return; } + + log("Session removal cancelled."); } function releaseErrorHandler(error: CodePushError, command: cli.ICommand): void { @@ -1940,12 +1838,11 @@ function throwForInvalidOutputFormat(format: string): void { } } -function whoami(_command: cli.ICommand): Promise { - return sdk.getAccountInfo().then((account): void => { - const accountInfo = `${account.email} (${account.linkedProviders.join(", ")})`; +async function whoami(_command: cli.ICommand): Promise { + const account: Account = await sdk.getAccountInfo(); + const accountInfo = `${account.email} (${account.linkedProviders.join(", ")})`; - log(accountInfo); - }); + log(accountInfo); } function isCommandOptionSpecified(option: any): boolean { diff --git a/script/hash-utils.ts b/script/hash-utils.ts index abc5778..4cb90f3 100644 --- a/script/hash-utils.ts +++ b/script/hash-utils.ts @@ -17,14 +17,13 @@ import { listFiles } from "./utils/file-utils"; const HASH_ALGORITHM = "sha256"; -export function generatePackageHashFromDirectory(directoryPath: string, basePath: string): Promise { +export async function generatePackageHashFromDirectory(directoryPath: string, basePath: string): Promise { if (!fs.lstatSync(directoryPath).isDirectory()) { throw new Error("Not a directory. Please either create a directory, or use hashFile()."); } - return generatePackageManifestFromDirectory(directoryPath, basePath).then((manifest: PackageManifest) => { - return manifest.computePackageHash(); - }); + const manifest: PackageManifest = await generatePackageManifestFromDirectory(directoryPath, basePath); + return manifest.computePackageHash(); } export function generatePackageManifestFromZip(filePath: string): Promise { @@ -143,7 +142,7 @@ export class PackageManifest { return this._map; } - public computePackageHash(): Promise { + public async computePackageHash(): Promise { let entries: string[] = []; this._map.forEach((hash: string, name: string): void => { entries.push(name + ":" + hash); @@ -153,7 +152,7 @@ export class PackageManifest { // can also compute this hash easily given the update contents. entries = entries.sort(); - return Promise.resolve(crypto.createHash(HASH_ALGORITHM).update(JSON.stringify(entries)).digest("hex")); + return crypto.createHash(HASH_ALGORITHM).update(JSON.stringify(entries)).digest("hex"); } public serialize(): string { diff --git a/script/management-sdk.ts b/script/management-sdk.ts index 77e1d1a..c8ae423 100644 --- a/script/management-sdk.ts +++ b/script/management-sdk.ts @@ -107,7 +107,7 @@ class AccountManager { }); } - public addAccessKey(friendlyName: string, ttl?: number): Promise { + public async addAccessKey(friendlyName: string, ttl?: number): Promise { if (!friendlyName) { throw new Error("A name must be specified when adding an access key."); } @@ -118,187 +118,195 @@ class AccountManager { ttl, }; - return this.post(urlEncode(["/accessKeys/"]), JSON.stringify(accessKeyRequest), /*expectResponseBody=*/ true).then( - (response: JsonResponse) => { - return { - createdTime: response.body.accessKey.createdTime, - expires: response.body.accessKey.expires, - key: response.body.accessKey.name, - name: response.body.accessKey.friendlyName, - }; - } + const res: JsonResponse = await this.post( + urlEncode(["/accessKeys/"]), + JSON.stringify(accessKeyRequest), + /*expectResponseBody=*/ true ); + return { + createdTime: res.body.accessKey.createdTime, + expires: res.body.accessKey.expires, + key: res.body.accessKey.name, + name: res.body.accessKey.friendlyName, + }; } - public getAccessKey(accessKeyName: string): Promise { - return this.get(urlEncode([`/accessKeys/${accessKeyName}`])).then((res: JsonResponse) => { - return { - createdTime: res.body.accessKey.createdTime, - expires: res.body.accessKey.expires, - name: res.body.accessKey.friendlyName, - }; - }); + public async getAccessKey(accessKeyName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/accessKeys/${accessKeyName}`])); + return { + createdTime: res.body.accessKey.createdTime, + expires: res.body.accessKey.expires, + name: res.body.accessKey.friendlyName, + }; } - public getAccessKeys(): Promise { - return this.get(urlEncode(["/accessKeys"])).then((res: JsonResponse) => { - const accessKeys: AccessKey[] = []; - - res.body.accessKeys.forEach((serverAccessKey: ServerAccessKey) => { - !serverAccessKey.isSession && - accessKeys.push({ - createdTime: serverAccessKey.createdTime, - expires: serverAccessKey.expires, - name: serverAccessKey.friendlyName, - }); - }); + public async getAccessKeys(): Promise { + const res: JsonResponse = await this.get(urlEncode(["/accessKeys"])); + const accessKeys: AccessKey[] = []; - return accessKeys; + res.body.accessKeys.forEach((serverAccessKey: ServerAccessKey) => { + !serverAccessKey.isSession && + accessKeys.push({ + createdTime: serverAccessKey.createdTime, + expires: serverAccessKey.expires, + name: serverAccessKey.friendlyName, + }); }); - } - - public getSessions(): Promise { - return this.get(urlEncode(["/accessKeys"])).then((res: JsonResponse) => { - // A machine name might be associated with multiple session keys, - // but we should only return one per machine name. - const sessionMap: { [machineName: string]: Session } = {}; - const now: number = new Date().getTime(); - res.body.accessKeys.forEach((serverAccessKey: ServerAccessKey) => { - if (serverAccessKey.isSession && serverAccessKey.expires > now) { - sessionMap[serverAccessKey.createdBy] = { - loggedInTime: serverAccessKey.createdTime, - machineName: serverAccessKey.createdBy, - }; - } - }); - const sessions: Session[] = Object.keys(sessionMap).map((machineName: string) => sessionMap[machineName]); + return accessKeys; + } - return sessions; + public async getSessions(): Promise { + const res: JsonResponse = await this.get(urlEncode(["/accessKeys"])); + // A machine name might be associated with multiple session keys, + // but we should only return one per machine name. + const sessionMap: { [machineName: string]: Session } = {}; + const now: number = new Date().getTime(); + res.body.accessKeys.forEach((serverAccessKey: ServerAccessKey) => { + if (serverAccessKey.isSession && serverAccessKey.expires > now) { + sessionMap[serverAccessKey.createdBy] = { + loggedInTime: serverAccessKey.createdTime, + machineName: serverAccessKey.createdBy, + }; + } }); + + return Object.keys(sessionMap).map((machineName: string) => sessionMap[machineName]); } - public patchAccessKey(oldName: string, newName?: string, ttl?: number): Promise { + public async patchAccessKey(oldName: string, newName?: string, ttl?: number): Promise { const accessKeyRequest: AccessKeyRequest = { friendlyName: newName, ttl, }; - return this.patch(urlEncode([`/accessKeys/${oldName}`]), JSON.stringify(accessKeyRequest)).then((res: JsonResponse) => { - return { - createdTime: res.body.accessKey.createdTime, - expires: res.body.accessKey.expires, - name: res.body.accessKey.friendlyName, - }; - }); + const res: JsonResponse = await this.patch(urlEncode([`/accessKeys/${oldName}`]), JSON.stringify(accessKeyRequest)); + return { + createdTime: res.body.accessKey.createdTime, + expires: res.body.accessKey.expires, + name: res.body.accessKey.friendlyName, + }; } - public removeAccessKey(name: string): Promise { - return this.del(urlEncode([`/accessKeys/${name}`])).then(() => null); + public async removeAccessKey(name: string): Promise { + await this.del(urlEncode([`/accessKeys/${name}`])); + return null; } - public removeSession(machineName: string): Promise { - return this.del(urlEncode([`/sessions/${machineName}`])).then(() => null); + public async removeSession(machineName: string): Promise { + await this.del(urlEncode([`/sessions/${machineName}`])); + return null; } // Account - public getAccountInfo(): Promise { - return this.get(urlEncode(["/account"])).then((res: JsonResponse) => res.body.account); + public async getAccountInfo(): Promise { + const res: JsonResponse = await this.get(urlEncode(["/account"])); + return res.body.account; } // Apps - public getApps(): Promise { - return this.get(urlEncode(["/apps"])).then((res: JsonResponse) => res.body.apps); + public async getApps(): Promise { + const res: JsonResponse = await this.get(urlEncode(["/apps"])); + return res.body.apps; } - public getApp(appName: string): Promise { - return this.get(urlEncode([`/apps/${appName}`])).then((res: JsonResponse) => res.body.app); + public async getApp(appName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}`])); + return res.body.app; } - public addApp(appName: string): Promise { + public async addApp(appName: string): Promise { const app: App = { name: appName }; - return this.post(urlEncode(["/apps/"]), JSON.stringify(app), /*expectResponseBody=*/ false).then(() => app); + await this.post(urlEncode(["/apps/"]), JSON.stringify(app), /*expectResponseBody=*/ false); + return app; } - public removeApp(appName: string): Promise { - return this.del(urlEncode([`/apps/${appName}`])).then(() => null); + public async removeApp(appName: string): Promise { + await this.del(urlEncode([`/apps/${appName}`])); + return null; } - public renameApp(oldAppName: string, newAppName: string): Promise { - return this.patch(urlEncode([`/apps/${oldAppName}`]), JSON.stringify({ name: newAppName })).then(() => null); + public async renameApp(oldAppName: string, newAppName: string): Promise { + await this.patch(urlEncode([`/apps/${oldAppName}`]), JSON.stringify({ name: newAppName })); + return null; } - public transferApp(appName: string, email: string): Promise { - return this.post(urlEncode([`/apps/${appName}/transfer/${email}`]), /*requestBody=*/ null, /*expectResponseBody=*/ false).then( - () => null - ); + public async transferApp(appName: string, email: string): Promise { + await this.post(urlEncode([`/apps/${appName}/transfer/${email}`]), /*requestBody=*/ null, /*expectResponseBody=*/ false); + return null; } // Collaborators - public getCollaborators(appName: string): Promise { - return this.get(urlEncode([`/apps/${appName}/collaborators`])).then((res: JsonResponse) => res.body.collaborators); + public async getCollaborators(appName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}/collaborators`])); + return res.body.collaborators; } - public addCollaborator(appName: string, email: string): Promise { - return this.post( - urlEncode([`/apps/${appName}/collaborators/${email}`]), - /*requestBody=*/ null, - /*expectResponseBody=*/ false - ).then(() => null); + public async addCollaborator(appName: string, email: string): Promise { + await this.post(urlEncode([`/apps/${appName}/collaborators/${email}`]), /*requestBody=*/ null, /*expectResponseBody=*/ false); + return null; } - public removeCollaborator(appName: string, email: string): Promise { - return this.del(urlEncode([`/apps/${appName}/collaborators/${email}`])).then(() => null); + public async removeCollaborator(appName: string, email: string): Promise { + await this.del(urlEncode([`/apps/${appName}/collaborators/${email}`])); + return null; } // Deployments - public addDeployment(appName: string, deploymentName: string, deploymentKey?: string): Promise { + public async addDeployment(appName: string, deploymentName: string, deploymentKey?: string): Promise { const deployment = { name: deploymentName, key: deploymentKey }; - return this.post(urlEncode([`/apps/${appName}/deployments/`]), JSON.stringify(deployment), /*expectResponseBody=*/ true).then( - (res: JsonResponse) => res.body.deployment + const res: JsonResponse = await this.post( + urlEncode([`/apps/${appName}/deployments/`]), + JSON.stringify(deployment), + /*expectResponseBody=*/ true ); + return res.body.deployment; } - public clearDeploymentHistory(appName: string, deploymentName: string): Promise { - return this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then(() => null); + public async clearDeploymentHistory(appName: string, deploymentName: string): Promise { + await this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])); + return null; } - public getDeployments(appName: string): Promise { - return this.get(urlEncode([`/apps/${appName}/deployments/`])).then((res: JsonResponse) => res.body.deployments); + public async getDeployments(appName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}/deployments/`])); + return res.body.deployments; } - public getDeployment(appName: string, deploymentName: string): Promise { - return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then((res: JsonResponse) => res.body.deployment); + public async getDeployment(appName: string, deploymentName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])); + return res.body.deployment; } - public getBaseRelease(appName: string, deploymentName: string, appVersion: string, buildNumber?: string): Promise { + public async getBaseRelease( + appName: string, + deploymentName: string, + appVersion: string, + buildNumber?: string + ): Promise { const qs = buildNumber ? `?appVersion=${appVersion}&buildNumber=${buildNumber}` : `?appVersion=${appVersion}`; - return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/basebundle${qs}`])).then( - (res: JsonResponse) => res.body.basebundle - ); + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/basebundle${qs}`])); + return res.body.basebundle; } - public renameDeployment(appName: string, oldDeploymentName: string, newDeploymentName: string): Promise { - return this.patch( - urlEncode([`/apps/${appName}/deployments/${oldDeploymentName}`]), - JSON.stringify({ name: newDeploymentName }) - ).then(() => null); + public async renameDeployment(appName: string, oldDeploymentName: string, newDeploymentName: string): Promise { + await this.patch(urlEncode([`/apps/${appName}/deployments/${oldDeploymentName}`]), JSON.stringify({ name: newDeploymentName })); + return null; } - public removeDeployment(appName: string, deploymentName: string): Promise { - return this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then(() => null); + public async removeDeployment(appName: string, deploymentName: string): Promise { + await this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])); + return null; } - public getDeploymentMetrics(appName: string, deploymentName: string): Promise { - return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/metrics`])).then( - (res: JsonResponse) => res.body.metrics - ); + public async getDeploymentMetrics(appName: string, deploymentName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/metrics`])); + return res.body.metrics; } - public getDeploymentHistory(appName: string, deploymentName: string): Promise { - return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then( - (res: JsonResponse) => res.body.history - ); + public async getDeploymentHistory(appName: string, deploymentName: string): Promise { + const res: JsonResponse = await this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])); + return res.body.history; } public async release( @@ -416,35 +424,38 @@ class AccountManager { }); } - public patchRelease(appName: string, deploymentName: string, label: string, updateMetadata: PackageInfo): Promise { + public async patchRelease(appName: string, deploymentName: string, label: string, updateMetadata: PackageInfo): Promise { const requestBody: string = JSON.stringify({ packageInfo: { ...updateMetadata, label } }); - return this.patch( + await this.patch( urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`]), requestBody, /*expectResponseBody=*/ false - ).then(() => null); + ); + return null; } - public promote( + public async promote( appName: string, sourceDeploymentName: string, destinationDeploymentName: string, updateMetadata: PackageInfo ): Promise { const requestBody: string = JSON.stringify({ packageInfo: updateMetadata }); - return this.post( + await this.post( urlEncode([`/apps/${appName}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`]), requestBody, /*expectResponseBody=*/ false - ).then(() => null); + ); + return null; } - public rollback(appName: string, deploymentName: string, targetRelease?: string): Promise { - return this.post( + public async rollback(appName: string, deploymentName: string, targetRelease?: string): Promise { + await this.post( urlEncode([`/apps/${appName}/deployments/${deploymentName}/rollback/${targetRelease || ``}`]), /*requestBody=*/ null, /*expectResponseBody=*/ false - ).then(() => null); + ); + return null; } private async packageFileFromPath(filePath: string): Promise { diff --git a/script/sign.ts b/script/sign.ts index e78743f..2e11542 100644 --- a/script/sign.ts +++ b/script/sign.ts @@ -14,7 +14,7 @@ interface CodeSigningClaims { export default async function sign(privateKeyPath: string, updateContentsPath: string): Promise { if (!privateKeyPath) { - return Promise.resolve(null); + return; } let privateKey: Buffer; @@ -22,7 +22,7 @@ export default async function sign(privateKeyPath: string, updateContentsPath: s try { privateKey = await fs.readFile(privateKeyPath); } catch (err) { - return Promise.reject(new Error(`The path specified for the signing key ("${privateKeyPath}") was not valid.`)); + throw new Error(`The path specified for the signing key ("${privateKeyPath}") was not valid.`); } // If releasing a single file, copy the file to a temporary 'CodePush' directory in which to publish the release @@ -31,7 +31,7 @@ export default async function sign(privateKeyPath: string, updateContentsPath: s updateContentsPath = copyFileToTmpDir(updateContentsPath); } } catch (error) { - return Promise.reject(error); + throw error; } const signatureFilePath: string = path.join(updateContentsPath, METADATA_FILE_NAME); @@ -42,11 +42,9 @@ export default async function sign(privateKeyPath: string, updateContentsPath: s if (err.code === "ENOENT") { prevSignatureExists = false; } else { - return Promise.reject( - new Error( - `Could not delete previous release signature at ${signatureFilePath}. + throw new Error( + `Could not delete previous release signature at ${signatureFilePath}. Please, check your access rights.` - ) ); } } @@ -62,19 +60,13 @@ export default async function sign(privateKeyPath: string, updateContentsPath: s contentHash: hash, }; - return new Promise((resolve, reject) => { - jwt.sign(claims, privateKey, { algorithm: "RS256" }, async (err: Error, signedJwt: string) => { - if (err) { - reject(new Error("The specified signing key file was not valid")); - } + let signedJwt: string; + try { + signedJwt = jwt.sign(claims, privateKey, { algorithm: "RS256" }); + } catch { + throw new Error("The specified signing key file was not valid"); + } - try { - await fs.writeFile(signatureFilePath, signedJwt); - console.log(`Generated a release signature and wrote it to ${signatureFilePath}`); - resolve(null); - } catch (error) { - reject(error); - } - }); - }); + await fs.writeFile(signatureFilePath, signedJwt); + console.log(`Generated a release signature and wrote it to ${signatureFilePath}`); } diff --git a/script/utils/gradle-utils.ts b/script/utils/gradle-utils.ts index cdc53d4..aa70e0c 100644 --- a/script/utils/gradle-utils.ts +++ b/script/utils/gradle-utils.ts @@ -123,10 +123,12 @@ function findGradleBuildFile(gradleFile?: string | null): string | undefined { ); } -function parseGroovyDslBuildFile(buildFile: string): Promise { - return g2js.parseFile(buildFile).catch(() => { +async function parseGroovyDslBuildFile(buildFile: string): Promise { + try { + return await g2js.parseFile(buildFile); + } catch { throw new Error(`Unable to parse the "${buildFile}" file. Please ensure it is a well-formed Gradle file.`); - }); + } } // RN < 0.71's `project.ext.react = [key: value]`. g2js prepends a trailing `// comment` to the next entry, so strip it From 98d83a2aa901c72f811988c75ccc59a9c0b3382f Mon Sep 17 00:00:00 2001 From: Revopush Date: Thu, 15 Oct 2026 14:09:29 +0300 Subject: [PATCH 2/2] Tidy async/await code after review - Type the release size result as nullable again - Drop a no-op buildNumber assignment and a redundant source map check - Remove a try/catch that only rethrew and comments that restate the code Co-Authored-By: Claude Opus 5.5 --- script/command-executor.ts | 16 +++++----------- script/sign.ts | 10 +++------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/script/command-executor.ts b/script/command-executor.ts index 11523d0..584185d 100644 --- a/script/command-executor.ts +++ b/script/command-executor.ts @@ -568,7 +568,6 @@ async function link(command: cli.ILinkCommand): Promise { } async function login(command: cli.ILoginCommand): Promise { - // Check if one of the flags were provided. if (command.accessKey) { sdk = getSdk(command.accessKey, CLI_HEADERS, command.apiServerUrl); if (await sdk.isAuthenticated()) { @@ -1169,7 +1168,7 @@ interface ReactReleaseInputs { // Up-front validation for the React-style release flows (release-react / release-expo). // Throws on the first violation and returns the derived inputs. The semver-range check -// lives in the chain since it needs the resolved app version. +// is done by the caller since it needs the resolved app version. function validateReactReleaseCommand( command: cli.IReleaseReactCommand, options: { resolveEntryFile: boolean } @@ -1326,13 +1325,12 @@ export const releaseExpo = async (command: cli.IReleaseReactCommand): Promise