diff --git a/README.md b/README.md index 468cc03..50151cd 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Below is the list of commands currently supported by Appcircle CLI: | `appcircle config [argument] [options]` | View and edit Appcircle CLI properties | | `appcircle login [--pat]` | Get an access token for the session | | `appcircle organization [action] [options]` | Manage organization users, roles, and details | + `appcircle publish [options]` | Manage publish module actions | | `appcircle listBuildProfiles` | Get the list of build profiles | | `appcircle listDistributionProfiles` | Get the list of distribution profiles | | `appcircle listBuildProfileWorkflows [--profileId]` | Get the list of workflows for the profile | diff --git a/package.json b/package.json index b1fee47..d42848a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@appcircle/cli", - "version": "1.2.0", + "version": "1.3.0-beta.1", "description": "CLI tool for running Appcircle services from the command line", "main": "bin/appcircle", "bin": { diff --git a/src/constant.ts b/src/constant.ts index ab49ab1..67f9a62 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -49,3 +49,5 @@ export const PROGRAM_NAME = "appcircle"; export const UNKNOWN_PARAM_VALUE = '-' export const CURRENT_PARAM_VALUE = 'current' +export let globalVariables : {[key:string]: string} = {} + diff --git a/src/core/command-runner.ts b/src/core/command-runner.ts index cbf0312..10c21a8 100644 --- a/src/core/command-runner.ts +++ b/src/core/command-runner.ts @@ -54,6 +54,22 @@ import { getOrganizationUserinfo, assignRolesToUserInOrganitaion, getOrganizationUsersWithRoles, + createPublishProfile, + getPublishProfiles, + uploadAppVersion, + deleteAppVersion, + getAppVersionDownloadLink, + getPublishByAppVersion, + startExistingPublishFlow, + setAppVersionReleaseCandidateStatus, + switchPublishProfileAutoPublishSettings, + getPublishProfileDetailById, + getPublishVariableGroups, + getPublishVariableListByGroupId, + deletePublishProfile, + renamePublishProfile, + getAppVersions, + downloadAppVersion, } from '../services'; import { commandWriter, configWriter } from './writer'; import { trustAppcircleCertificate } from '../security/trust-url-certificate'; @@ -201,6 +217,124 @@ const handleOrganizationCommand = async (command: ProgramCommand, params: any) = } }; +const handlePublishCommand = async (command: ProgramCommand, params: any) => { + if(params.platform && !['ios','android'].includes(params.platform)){ + throw new ProgramError(`Invalid platform(${params.platform}). Supported platforms: ios, android`); + } + if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-create`) { + const profileRes = await createPublishProfile({ platform: params.platform, name: params.name }); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: profileRes, + }); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-list`) { + const profiles = await getPublishProfiles({ platform: params.platform }); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: profiles, + }); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-delete`) { + const response = await deletePublishProfile(params); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: response, + }); + } + else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-rename`) { + const response = await renamePublishProfile(params); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: response, + }); + } + else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-version-upload`) { + const spinner = createOra('Try to upload the app version').start(); + try { + const responseData = await uploadAppVersion(params); + commandWriter(CommandTypes.PUBLISH, responseData); + spinner.text = `App version uploaded successfully.\n\nTaskId: ${responseData.taskId}`; + spinner.succeed(); + } catch (e: any) { + spinner.fail('Upload failed'); + throw e; + } + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-version-delete`) { + const spinner = createOra('Try to remove the app version').start(); + try { + const responseData = await deleteAppVersion(params); + commandWriter(CommandTypes.PUBLISH, responseData); + spinner.text = `App version removed successfully.\n\nTaskId: ${responseData.taskId}`; + spinner.succeed(); + } catch (e: any) { + spinner.fail('Remove failed'); + throw e; + } + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-start`) { + const spinner = createOra('Publish flow starting').start(); + const publish = await getPublishByAppVersion(params); + const firstStep = publish.steps[0]; + const startResponse = await startExistingPublishFlow({ ...params, publishId: firstStep.publishId }); + commandWriter(CommandTypes.PUBLISH, startResponse); + spinner.text = `Publish started successfully.`; + spinner.succeed(); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-version-download`) { + let spinner = createOra('Fetching app version download link').start(); + try { + let downloadPath = path.resolve((params.path || '').replace('~', `${os.homedir}`)); + const responseData = await getAppVersionDownloadLink(params); + const appVersions = await getAppVersions(params); + const appVersion = appVersions.find((appVersion: any) => appVersion.id === params.appVersionId); + if (!appVersion) { + spinner.fail(); + throw new Error('App version not found'); + } + spinner.text = `App version download link fetched successfully.`; + spinner.text = `Try to download the app version.`; + downloadPath = path.join(downloadPath, appVersion.fileName); + await downloadAppVersion({ url: responseData, path:downloadPath }); + spinner.text = `App version downloaded successfully.\n\nDownload Path: ${downloadPath}`; + spinner.succeed(); + } catch (e: any) { + spinner.fail('Process failed'); + throw e; + } + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-version-markAsRC`) { + const response = await setAppVersionReleaseCandidateStatus({...params, releaseCandidate: true }); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: response, + }); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-version-unmarkAsRC`) { + const response = await setAppVersionReleaseCandidateStatus({...params, releaseCandidate: false }); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: response, + }); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-profile-settings-autopublish`) { + const publishProfileDetails = await getPublishProfileDetailById(params); + const response = await switchPublishProfileAutoPublishSettings({ ...params, currentProfileSettings: publishProfileDetails.profileSettings }); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: response, + }); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-variable-group-list`) { + const variableGroups = await getPublishVariableGroups(); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: variableGroups, + }); + } else if (command.fullCommandName === `${PROGRAM_NAME}-publish-variable-group-view`) { + const variables = await getPublishVariableListByGroupId(params); + commandWriter(CommandTypes.PUBLISH, { + fullCommandName: command.fullCommandName, + data: variables.variables, + }); + } else { + const beutufiyCommandName = command.fullCommandName.split('-').join(' '); + console.error(`"${beutufiyCommandName} ..." command not found \nRun "${beutufiyCommandName} --help" for more information`); + } +}; + export const runCommand = async (command: ProgramCommand) => { const params = command.opts() as any; const commandName = command.name(); @@ -209,7 +343,7 @@ export const runCommand = async (command: ProgramCommand) => { //console.log('Full-Command-Name: ', command.fullCommandName, params); //In interactive mode, if any parameters have errors, we can't continue execution. - if(params.isError){ + if (params.isError) { process.exit(1); } @@ -222,6 +356,10 @@ export const runCommand = async (command: ProgramCommand) => { return handleOrganizationCommand(command, params); } + if (command.isGroupCommand(CommandTypes.PUBLISH)) { + return handlePublishCommand(command, params); + } + switch (commandName) { case CommandTypes.LOGIN: { responseData = await getToken(params); diff --git a/src/core/commands.ts b/src/core/commands.ts index cbef18b..bd63d9c 100644 --- a/src/core/commands.ts +++ b/src/core/commands.ts @@ -13,6 +13,7 @@ export enum CommandTypes { CONFIG = 'config', LOGIN = 'login', ORGANIZATION = 'organization', + PUBLISH = 'publish', LIST_BUILD_PROFILES = 'listBuildProfiles', LIST_BUILD_PROFILE_BRANCHES = 'listBuildProfileBranches', LIST_BUILD_PROFILE_WORKFLOWS = 'listBuildProfileWorkflows', @@ -63,6 +64,16 @@ export type CommandType = { params: ParamType[]; }; +const platformParam = { + name: 'platform', + description: 'Platform (ios/android)', + type: CommandParameterTypes.SELECT, + params: [{ name: 'ios' , message: 'iOS' }, { name: 'android', message: 'Android' }], + defaultValue: 'ios', + valueType: 'string', + required: true, +}; + export const Commands: CommandType[] = [ { command: CommandTypes.CONFIG, @@ -374,6 +385,306 @@ export const Commands: CommandType[] = [ ], params: [], }, + { + command: CommandTypes.PUBLISH, + description: 'Publish', + longDescription: 'Manage publish module actions.', + subCommands: [ + { + command: 'start', + description: 'Start a publish', + longDescription: 'Starts a publish', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'appVersionId', + description: 'App version', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + } + ], + }, + { + command: 'profile', + description: 'Publish profile actions', + longDescription: 'Publish profile actions', + params: [], + subCommands: [ + { + command: 'list', + description: 'Publish profile list', + longDescription: 'Get list of publish profile', + params: [platformParam], + }, + { + command: 'create', + description: 'Create a publish profile', + longDescription: 'Create a new publish profile', + params: [platformParam, + { + name: 'name', + description: 'Profile name', + type: CommandParameterTypes.STRING, + defaultValue: undefined, + valueType: 'string', + required: true, + } + ], + }, + { + command: 'rename', + description: 'Raname publish profile', + longDescription: 'Rename publish profile', + params: [platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'name', + description: 'New profile name', + type: CommandParameterTypes.STRING, + defaultValue: undefined, + valueType: 'string', + required: true, + } + ], + }, + { + command: 'delete', + description: 'Remove publish profile', + longDescription: 'Remove publish profile', + params: [platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + } + ], + }, + { + command: 'version', + description: 'App version actions', + longDescription: 'App version actions', + params: [], + subCommands: [ + { + command: 'upload', + description: 'Upload a new app version', + longDescription: 'Upload a new version to given publish profile', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'app', + description: 'App path', + type: CommandParameterTypes.STRING, + valueType: 'path', + required: true + } + ], + }, + { + command: 'download', + description: 'Download app version', + longDescription: 'Download app version from selected publish profile', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'appVersionId', + description: 'App version', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'path', + description: '[OPTIONAL] The path for artifacts to be downloaded:', + longDescription:'[OPTIONAL] The path for artifacts to be downloaded: (Defaults to the current directory)', + type: CommandParameterTypes.STRING, + valueType: 'path', + required: false + } + ], + }, + { + command: 'delete', + description: 'Remove app version', + longDescription: 'Remove app version from selected publish profile', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'appVersionId', + description: 'App version', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + } + ], + }, + { + command: 'markAsRC', + description: 'Mark as Release Candidate', + longDescription: 'Mark an app version as Release Candidate', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'appVersionId', + description: 'App version', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + } + ], + }, + { + command: 'unmarkAsRC', + description: 'Unmark as Release Candidate', + longDescription: 'Unmark an app version as Release Candidate', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'appVersionId', + description: 'App version', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + } + ], + }, + ] + }, + { + command: 'settings', + description: 'Publish profile settings', + longDescription: 'Publish profile settings', + params: [], + subCommands: [ + { + command: 'autopublish', + description: 'Set Publish Profile as Auto Publish', + longDescription: 'Start a publish process when a new version is received.', + params: [ + platformParam, + { + name: 'publishProfileId', + description: 'Publish Profile ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + }, + { + name: 'enable', + description: 'Enable Auto Publish', + type: CommandParameterTypes.BOOLEAN, + valueType: 'boolean', + required: true + } + ], + }, + ] + } + ] + }, + { + command: 'variable', + description: 'Publish Variables', + longDescription: 'Publish Variables', + params: [], + subCommands: [ + { + command: 'group', + description: 'Group Actions', + longDescription: 'Publish variable group actions', + params: [], + subCommands:[ + { + command: "list", + description: 'List groups', + longDescription: 'Publish variable group actions', + params: [], + }, + // { + // command: "create", + // description: 'Create new group', + // longDescription: 'Create a new publish variable group', + // params: [{ + // name: 'name', + // description: 'Group name', + // type: CommandParameterTypes.STRING, + // defaultValue: undefined, + // valueType: 'string', + // required: true, + // }], + // }, + { + command: "view", + description: 'View items of group', + longDescription: 'View items of publish variable group', + params: [ + { + name: 'publishVariableGroupId', + description: 'Variable Group ID', + type: CommandParameterTypes.SELECT, + valueType: 'uuid', + required: true + } + ], + } + ] + } + ] + } + ], + params: [], + }, { command: CommandTypes.LIST_BUILD_PROFILES, description: 'Get list of build profiles', diff --git a/src/core/interactive-runner.ts b/src/core/interactive-runner.ts index 35dd840..f0cd22a 100644 --- a/src/core/interactive-runner.ts +++ b/src/core/interactive-runner.ts @@ -6,7 +6,7 @@ import moment from 'moment'; import { prompt, Select, AutoComplete, BooleanPrompt, MultiSelect } from 'enquirer'; import { runCommand } from './command-runner'; import { Commands, CommandParameterTypes, CommandTypes, CommandType } from './commands'; -import { APPCIRCLE_COLOR, UNKNOWN_PARAM_VALUE } from '../constant'; +import { APPCIRCLE_COLOR, OperatingSystems, UNKNOWN_PARAM_VALUE } from '../constant'; import { createOra } from '../utils/orahelper'; import { getBranches, @@ -25,6 +25,9 @@ import { getOrganizationInvitations, getOrganizationUsers, getOrganizationUserinfo, + getPublishProfiles, + getAppVersions, + getPublishVariableGroups, } from '../services'; import { DefaultEnvironmentVariables, getConfigStore } from '../config'; import { ProgramCommand, createCommandActionCallback } from '../program'; @@ -307,7 +310,41 @@ const handleInteractiveParamsOrArguments = async ( param.params = userList.map((user: any) => ({ name: user.id, message: user._message || ` ${user.id} (${user.email})` })); spinner.text = 'Users fetched'; spinner.succeed(); - } else if (param.name === 'email' && param.type === CommandParameterTypes.SELECT) { + }else if(param.name === 'publishProfileId' && param.type === CommandParameterTypes.SELECT){ + const spinner = ora('Publish Profiles Fetching').start(); + const selectedPlatform = params["platform"]; + const publishProfiles = await getPublishProfiles({ platform: selectedPlatform }); + param.params = publishProfiles.map((profile:any) => ({name:profile.id, message: ` ${profile.id} (${profile.name}) - ${(OperatingSystems as any)[profile.platformType]}`})); + spinner.text = 'Publish Profiles Fetched'; + spinner.succeed(); + }else if(param.name === 'appVersionId' && param.type === CommandParameterTypes.SELECT){ + const spinner = ora('App Versions Fetching').start(); + const selectedPlatform = params["platform"]; + const selectedPublishProfileId = params["publishProfileId"]; + const appVersions = await getAppVersions({ platform: selectedPlatform, publishProfileId: selectedPublishProfileId }); + if (!appVersions || appVersions.length === 0) { + spinner.text = 'No app versions available'; + spinner.fail(); + return { isError: true }; + }else { + param.params = appVersions.map((appVersion:any) => ({name:appVersion.id, message: ` ${appVersion.id} - ${appVersion.name}(${appVersion.version}) ${appVersion.releaseCandidate ? '(Release Candidate)' : ''}`})); + spinner.text = 'App Versions Fetched'; + spinner.succeed(); + } + }else if(param.name === 'publishVariableGroupId' && param.type === CommandParameterTypes.SELECT){ + const spinner = ora('Publish Variable Groups Fetching').start(); + const groups = await getPublishVariableGroups(); + if (!groups || groups.length === 0) { + spinner.text = 'No groups available'; + spinner.fail(); + return { isError: true }; + }else { + param.params = groups.map((group:any) => ({name:group.id, message: ` ${group.id} (${group.name})`})); + spinner.text = 'Publish Variable Groups Fetched'; + spinner.succeed(); + } + } + else if (param.name === 'email' && param.type === CommandParameterTypes.SELECT) { const spinner = ora('Invitations fetching').start(); const invitationsList = await getOrganizationInvitations({ organizationId: params.organizationId || params.currentOrganizationId || '' }); if (param.required !== false && (!invitationsList || invitationsList.length === 0)) { @@ -321,7 +358,7 @@ const handleInteractiveParamsOrArguments = async ( param.params = invitationsList.map((invitation: any) => ({ name: invitation.userEmail, message: invitation._message || invitation.userEmail })); spinner.text = 'Invitations fetched'; spinner.succeed(); - } else if (param.name === 'value' && params.isSecret) { + }else if (param.name === 'value' && params.isSecret) { param.type = CommandParameterTypes.PASSWORD; } @@ -346,6 +383,7 @@ const handleInteractiveParamsOrArguments = async ( }, ]); (params as any)[param.name] = (stringPrompt as any)[Object.keys(stringPrompt)[0]]; + } else if (param.type === CommandParameterTypes.BOOLEAN) { const booleanPrompt = new BooleanPrompt({ name: param.name, @@ -365,6 +403,7 @@ const handleInteractiveParamsOrArguments = async ( ], }); (params as any)[param.name] = await selectPrompt.run(); + } else if (param.type === CommandParameterTypes.MULTIPLE_SELECT && param.params) { const selectPrompt = new AutoComplete({ name: param.name, @@ -387,7 +426,6 @@ const handleInteractiveParamsOrArguments = async ( const handleCommandParamsAndArguments = async (selectedCommand: CommandType, parentCommand: any): Promise => { const params = (await handleInteractiveParamsOrArguments(selectedCommand.params || [])) || {}; const args = (await handleInteractiveParamsOrArguments(selectedCommand.arguments || [])) || {}; - return createCommandActionCallback({ parent: parentCommand || null, name: () => selectedCommand.command, @@ -398,7 +436,6 @@ const handleCommandParamsAndArguments = async (selectedCommand: CommandType, par const handleSelectedCommand = async (command: CommandType, __parentCommand?: any): Promise => { const preparedCommand = await handleCommandParamsAndArguments(command, __parentCommand); - if (command.subCommands?.length) { const commandSelect = new AutoComplete({ name: 'action', diff --git a/src/core/writer.ts b/src/core/writer.ts index 455e538..c6e51b4 100644 --- a/src/core/writer.ts +++ b/src/core/writer.ts @@ -287,7 +287,80 @@ const writersMap: { [key in CommandTypes]: (data: any) => void } = { } else { console.log(data.data); } - }, + }, + [CommandTypes.PUBLISH]: (data: any) => { + if(data.fullCommandName === `${PROGRAM_NAME}-publish-profile-create`){ + console.table( + [{ 'Id:': data.data.id, + 'Name:': data.data.name, + 'Created:': data.data.createDate ? moment(data.data.createDate).calendar() : '-', + }] + ) + }else if(data.fullCommandName === `${PROGRAM_NAME}-publish-profile-rename`){ + console.table( + [{ 'Id:': data.data.id, + 'Name:': data.data.name, + 'Created:': data.data.createDate ? moment(data.data.createDate).calendar() : '-', + 'Updated:': data.data.updateDate ? moment(data.data.updateDate).calendar() : '-', + }] + ) + } + else if(data.fullCommandName === `${PROGRAM_NAME}-publish-profile-list`){ + data.data.length > 0 ? + console.table( + data.data.map((publishProfile: any) => ({ + 'Id': publishProfile.id, + 'Name': publishProfile.name, + 'Last Version': publishProfile.lastUploadVersion, + 'Last Version Code': publishProfile.lastUploadVersionCode, + 'Version Code': publishProfile.version, + 'App Unique Id': publishProfile.appUniqueId , + 'Latest Publish': publishProfile.latestPublishDate ? moment(publishProfile.publishDate).calendar() : '-', + 'Platform': (OperatingSystems as any)[publishProfile.platformType], + Created: publishProfile.createDate ? moment(publishProfile.createDate).calendar() : '-', + Updated: publishProfile.updateDate ? moment(publishProfile.updateDate).calendar() : '-', + })) + ) : console.log(' No publish profile found'); + }else if(data.fullCommandName === `${PROGRAM_NAME}-publish-variable-group-list`){ + data.data.length > 0 ? console.table( + data.data.map((group: any) => ({ + 'Group Id': group.id, + 'Group Name': group.name, + Created: group.createDate ? moment(group.createDate).calendar() : '-', + Updated: group.updateDate ? moment(group.updateDate).calendar() : '-', + })) + ) : console.log(' No publish variable group found'); + }else if(data.fullCommandName === `${PROGRAM_NAME}-publish-variable-group-view`){ + data.data.length > 0 ? console.table( + data.data.map((variable: any) => ({ + 'Key Name': variable.key, + 'Key Value': variable.isSecret ? '********' : variable.value, + })) + ) : console.log(' No publish variable found'); + }else if(data.fullCommandName ===`${PROGRAM_NAME}-publish-profile-settings-autopublish`){ + console.table( + [{ 'Id:': data.data.id, + 'Name:': data.data.name, + 'Created:': data.data.createDate ? moment(data.data.createDate).calendar() : '-', + 'Updated:': data.data.updateDate ? moment(data.data.updateDate).calendar() : '-', + 'Auto Publish': data.data.profileSettings.whenNewVersionRecieved ? 'Yes' : 'No', + }] + ) + }else if(data.fullCommandName ===`${PROGRAM_NAME}-publish-profile-version-markAsRC` || data.fullCommandName ===`${PROGRAM_NAME}-publish-profile-version-unmarkAsRC`){ + console.table( + [{ 'Id:': data.data.id, + 'Name:': data.data.name, + 'Unique Name': data.data.uniqueName, + 'Created:': data.data.createDate ? moment(data.data.createDate).calendar() : '-', + 'Updated:': data.data.updateDate ? moment(data.data.updateDate).calendar() : '-', + 'Release Candidate': data.data.releaseCandidate ? 'Yes' : 'No', + }] + ) + } + else { + console.log(data.data) + } + } }; export const commandWriter = (command: CommandTypes, data: any) => { diff --git a/src/main.ts b/src/main.ts index 831f559..23f7d7d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,9 +7,10 @@ import { getConsoleOutputType, setConsoleOutputType } from './config'; import { error } from 'console'; import { ProgramError } from './core/ProgramError'; import { PROGRAM_NAME } from './constant'; +import chalk from 'chalk'; const collectErrorMessageFromData = (data: any) => { - return data ? '\n↳ ' + Object.keys(data).map(key => data[key]).join('\n↳ '): ''; + return data ? '\n↳ ' + Object.keys(data).filter(k => k !== 'stackTrace').map(key => ' -' +key +': ' + data[key]).join('\n↳ '): ''; } const handleError = (error: any) => { @@ -22,9 +23,9 @@ const handleError = (error: any) => { } else { if (axios.isAxiosError(error)) { const data = error.response?.data as any; - console.error(`\n${error.message} ${error.response?.statusText}${collectErrorMessageFromData(data)}`); + console.error(`\n${chalk.red('✖')} ${error.message} ${chalk.red(error.response?.statusText)}${collectErrorMessageFromData(data)}`); if(error.response?.status === 401) { - console.error(`Run "${PROGRAM_NAME} login --help" command for more information.`); + console.error(`Run ${chalk.cyan(`"${PROGRAM_NAME} login --help"`)} command for more information.`); } } else if (error instanceof ProgramError) { console.error(error.message); diff --git a/src/services/index.ts b/src/services/index.ts index b21e9f1..8992fcc 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -395,3 +395,4 @@ export const getUserInfo = async () => { }; export * from './organization'; +export * from './publish'; diff --git a/src/services/publish.ts b/src/services/publish.ts new file mode 100644 index 0000000..e699534 --- /dev/null +++ b/src/services/publish.ts @@ -0,0 +1,170 @@ + +import fs from 'fs'; +import FormData from 'form-data'; +import { ProgramError } from '../core/ProgramError'; +import { OptionsType, appcircleApi, getHeaders } from './api'; +import axios from 'axios'; + +export async function createPublishProfile(options: OptionsType<{ platform: string, name: string }>) { + const response = await appcircleApi.post( + `publish/v2/profiles/${options.platform}`, + { name: options.name }, + { headers: getHeaders() } + ); + return response.data; + } + +export async function deletePublishProfile(options: OptionsType<{ platform: string, publishProfileId: string }>) { + const response = await appcircleApi.delete( + `publish/v2/profiles/${options.platform}/${options.publishProfileId}`, + { headers: getHeaders() } + ); + return response.data; + } + export async function renamePublishProfile(options: OptionsType<{ platform: string, publishProfileId: string, name: string }>) { + const response = await appcircleApi.patch( + `publish/v2/profiles/${options.platform}/${options.publishProfileId}`, + { name : options.name}, + { headers: getHeaders() } + ); + return response.data; + } + + export async function getPublishProfiles(options: OptionsType<{ platform: string }>) { + const response = await appcircleApi.get( + `publish/v2/profiles/${options.platform}`, + { headers: getHeaders() } + ); + return response.data; + } + + export async function startExistingPublishFlow(options: OptionsType<{ publishProfileId: string, platform:string, publishId: string }>) { + const startResponse = await appcircleApi.post(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/publish/${options.publishId}?action=restart`,"{}", + { + headers: getHeaders() + } + ); + return startResponse.data; + } + + export async function getPublishVariableGroups() { + + const response = await appcircleApi.get(`publish/v2/variable-groups`, + { + headers: getHeaders() + } + ); + + return response.data; + } + export async function getPublishVariableListByGroupId(options: OptionsType<{ publishVariableGroupId: string }>) { + + const response = await appcircleApi.get(`publish/v2/variable-groups/${options.publishVariableGroupId}`, + { + headers: getHeaders() + } + ); + + return response.data; + } + export async function setAppVersionReleaseCandidateStatus(options: OptionsType<{ publishProfileId: string, platform:string, appVersionId: string, releaseCandidate: boolean }>) { + + const response = await appcircleApi.patch(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/app-versions/${options.appVersionId}?action=releaseCandidate`,{ + ReleaseCandidate: options.releaseCandidate + }, + { + headers: getHeaders() + } + ); + + return response.data; + } + + export async function switchPublishProfileAutoPublishSettings(options: OptionsType<{ publishProfileId: string, platform:string, appVersionId: string, enable: boolean, currentProfileSettings: any }>) { + const response = await appcircleApi.patch(`publish/v2/profiles/${options.platform}/${options.publishProfileId}`,{ + profileSettings: {...options.currentProfileSettings , whenNewVersionRecieved: options.enable } + }, + { + headers: getHeaders() + } + ); + + return response.data; + } + + export async function uploadAppVersion(options: OptionsType<{ app: string; publishProfileId: string, platform:string }>) { + const data = new FormData(); + data.append('File', fs.createReadStream(options.app)); + + const uploadResponse = await appcircleApi.post(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/app-versions`, data, { + maxContentLength: Infinity, + maxBodyLength: Infinity, + headers: { + ...getHeaders(), + ...data.getHeaders(), + 'Content-Type': 'multipart/form-data;boundary=' + data.getBoundary(), + }, + }); + return uploadResponse.data; + } + export async function getAppVersions(options: OptionsType<{ publishProfileId: string, platform:string }>) { + const appVersions = await appcircleApi.get(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/app-versions`, { + headers: getHeaders(), + }); + return appVersions.data; + } + export async function getPublishByAppVersion(options: OptionsType<{ publishProfileId: string, platform:string,appVersionId: string }>) { + const publish = await appcircleApi.get(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/app-versions/${options.appVersionId}/publish`, { + headers: getHeaders(), + }); + return publish.data; + } + export async function deleteAppVersion(options: OptionsType<{ publishProfileId: string, platform:string, appVersionId: string }>) { + const appVersions = await appcircleApi.delete(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/app-versions/${options.appVersionId}`, { + headers: getHeaders(), + }); + return appVersions.data; + } + export async function getAppVersionDownloadLink(options: OptionsType<{ publishProfileId: string, platform:string, appVersionId: string }>) { + + const downloadResponse = await appcircleApi.get(`publish/v2/profiles/${options.platform}/${options.publishProfileId}/app-versions/${options.appVersionId}?action=download`, + { + headers: getHeaders() + } + ); + + return downloadResponse.data; + } + export async function getPublishProfileDetailById(options: OptionsType<{ publishProfileId: string, platform:string }>) { + const response = await appcircleApi.get(`publish/v2/profiles/${options.platform}/${options.publishProfileId}`, + { + headers: getHeaders() + } + ); + return response.data; + } + + export async function downloadAppVersion(options: OptionsType<{ url: string, path: string }>) { + + + const downloadResponse = await axios.get(options.url, { + responseType: 'stream', + }); + return new Promise((resolve, reject) => { + const writer = fs.createWriteStream(options.path); + downloadResponse.data.pipe(writer); + let error: any = null; + writer.on('error', (err) => { + error = err; + writer.close(); + reject(err); + }); + writer.on('close', () => { + if (!error) { + resolve(true); + } + //no need to call the reject here, as it will have been called in the + //'error' stream; + }); + }); + } \ No newline at end of file