diff --git a/README.md b/README.md index 911fa08..301a887 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,9 @@ Below is the list of commands currently supported by Appcircle CLI: | `appcircle login [--pat]` | Get an access token for the session | | `appcircle listBuildProfiles` | Get the list of build profiles | | `appcircle listDistributionProfiles` | Get the list of distribution profiles | -| `appcircle build [--profileId] [--branch]` | Start a new build | -| `appcircle download [--path] [--commitId] [--buildId]` | Download the artifact under the selected path | +| `appcircle listBuildProfileWorkflows [--profileId]` | Get the list of workflows for the profile | +| `appcircle build [--profileId] [--branch] [--workflow]` | Start a new build | +| `appcircle download [--path] [--commitId] [--buildId]` | Download the artifact under the selected path | | `appcircle upload [--app] [--message] [--profileId]` | Upload your app binary to selected distribution profile | | `appcircle createDistributionProfile [--name]` | Create a distribution profile | | `appcircle listEnvironmentVariableGroups` | Get list of environment variable groups | @@ -53,7 +54,8 @@ Below is the list of commands currently supported by Appcircle CLI: - Add a build profile and [connect a repository](https://docs.appcircle.io/build/adding-a-build-profile#connect-your-repository) - Get the build profile ID using `appcircle listBuildProfiles` -- Start a new build using `appcircle build --profileId="YOUR PROFILE ID" --branch="YOUR BRANCH"` +- Get the workflows of that build profile `appcircle listBuildProfileWorkflows --profileId="YOUR PROFILE ID"` +- Start a new build using `appcircle build --profileId="YOUR PROFILE ID" --branch="YOUR BRANCH" --workflow="YOUR WORKFLOW ID"` ## How to distribute an app via the Appcircle CLI? diff --git a/package.json b/package.json index 3ec03a3..2929d01 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@appcircle/cli", - "version": "1.0.1", + "version": "1.0.2", "description": "CLI tool for running Appcircle services from the command line", "main": "bin/appcircle", "bin": { @@ -29,7 +29,7 @@ }, "homepage": "https://github.com/appcircleio/appcircle-cli#readme", "dependencies": { - "axios": "^0.21.1", + "axios": "^0.25.0", "chalk": "^4.1.1", "commander": "^7.1.0", "enquirer": "^2.3.6", diff --git a/src/main.ts b/src/main.ts index 26aafb3..5f09292 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,7 @@ import { getEnvironmentVariables, createEnvironmentVariable, getBranches, + getWorkflows, getCommits } from './services'; @@ -34,6 +35,7 @@ const CommandTypes = { LOGIN: 'login', LIST_BUILD_PROFILES: 'listBuildProfiles', LIST_BUILD_PROFILE_BRANCHES: 'listBuildProfileBranches', + LIST_BUILD_PROFILE_WORKFLOWS: 'listBuildProfileWorkflows', LIST_BUILD_PROFILE_COMMITS: 'listBuildProfileCommits', LIST_DISTRIBUTION_PROFILES: 'listDistributionProfiles', BUILD: 'build', @@ -74,6 +76,17 @@ const Commands = [ } ] }, + { + command: CommandTypes.LIST_BUILD_PROFILE_WORKFLOWS, + description: 'Get list of workflows of a build profile', + params: [ + { + name: 'profileId', + description: 'Build profile ID', + type: CommandParameterTypes.STRING + } + ] + }, { command: CommandTypes.LIST_BUILD_PROFILE_COMMITS, description: 'Get list of commits of a branch', @@ -104,6 +117,12 @@ const Commands = [ description: 'Branch', type: CommandParameterTypes.SELECT, params: [] + }, + { + name: 'workflow', + description: 'Workflow', + type: CommandParameterTypes.SELECT, + params: [] } ] }, @@ -285,6 +304,18 @@ const Commands = [ spinner.text = 'Branches fetched'; spinner.succeed(); + } else if (param.name === 'workflow') { + const spinner = ora('Workflow fetching').start(); + const workflows = await getWorkflows({ profileId: params.profileId || '' }); + if (!workflows || workflows.length === 0) { + spinner.text = 'No workflows available'; + spinner.fail(); + return; + } + //@ts-ignore + param.params = workflows.map((workflow: any) => ({ name: workflow.workflowName, description: workflow.workflowName })); + spinner.text = 'Workflows fetched'; + spinner.succeed(); } else if (param.name === 'value' && params.isSecret) { param.type = CommandParameterTypes.PASSWORD; } @@ -351,6 +382,9 @@ const Commands = [ case CommandTypes.LIST_BUILD_PROFILE_BRANCHES: getBranches(params); break; + case CommandTypes.LIST_BUILD_PROFILE_WORKFLOWS: + getWorkflows(params); + break; case CommandTypes.LIST_BUILD_PROFILE_COMMITS: getCommits(params); break; diff --git a/src/services.ts b/src/services.ts index 8aaec99..5511aca 100644 --- a/src/services.ts +++ b/src/services.ts @@ -205,24 +205,24 @@ export async function getCommits(options: { branchId: string }) { } } -export async function startBuild(options: { profileId: string, branch: string }) { +export async function startBuild(options: { profileId: string, branch: string,workflow: string }) { + const spinner = ora(`Try to start a new build with ${options.workflow}`).start(); try { - const spinner = ora('Try to start a new build').start(); - - const accessToken = readVariable(EnvironmentVariables.AC_ACCESS_TOKEN); - const branches = await getBranches({ profileId: options.profileId || '' }); - const index = branches.findIndex((element: {[key:string]: any }) => element.name === options.branch); - const branchId = branches[index].id; + const branches = await getBranches({ profileId: options.profileId || '' }, false); + const workflows = await getWorkflows({ profileId: options.profileId || '' }, false); + const branchIndex = branches.findIndex((element: {[key:string]: any }) => element.name === options.branch); + const branchId = branches[branchIndex].id; + const workflowIndex = workflows.findIndex((element: {[key:string]: any }) => element.workflowName === options.workflow); + const workflowId = workflows[workflowIndex].id; const allCommitsByBranchId = await axios.get(`${API_HOSTNAME}/build/v2/commits?branchId=${branchId}`, { headers: getHeaders() }); const latestCommitId = allCommitsByBranchId.data[0].id; - - const buildResponse = await axios.post(`${API_HOSTNAME}/build/v2/commits/${latestCommitId}?purpose=1`, + const buildResponse = await axios.post(`${API_HOSTNAME}/build/v2/commits/${latestCommitId}?workflowId=${workflowId}`, qs.stringify({ sample: 'test' }), - { + { headers: { ...getHeaders(), "accept": "*/*", @@ -233,6 +233,7 @@ export async function startBuild(options: { profileId: string, branch: string }) spinner.text = `Build added to queue successfully.\n\nTaskId: ${buildResponse.data.taskId}\nQueueItemId: ${buildResponse.data.queueItemId}`; spinner.succeed(); } catch (error) { + spinner.fail('Build failed'); console.error(error); } } @@ -430,22 +431,23 @@ export async function createEnvironmentVariable(options: { type: keyof typeof En } } -export async function getBranches(options: { profileId: string }) { +export async function getBranches(options: { profileId: string }, showConsole: boolean = true) { try { const branchResponse = await axios.get(`${API_HOSTNAME}/build/v2/profiles/${options.profileId}`, { headers: getHeaders() }); - - console.table(branchResponse.data.branches - .map((branch: any) => ({ - 'Branch Id': branch.id, - 'Branch Name': branch.name, - 'Last Build': branch.lastBuildDate ? moment(branch.lastBuildDate).calendar() : 'No previous builds', - //@ts-ignore - 'Build Status': BuildStatus[String(branch.buildStatus)] || 'No previous builds' - })) - ); + if (showConsole) { + console.table(branchResponse.data.branches + .map((branch: any) => ({ + 'Branch Id': branch.id, + 'Branch Name': branch.name, + 'Last Build': branch.lastBuildDate ? moment(branch.lastBuildDate).calendar() : 'No previous builds', + //@ts-ignore + 'Build Status': BuildStatus[String(branch.buildStatus)] || 'No previous builds' + })) + ); + } return branchResponse.data.branches; } catch (error) { if (error.response && error.response.status === 404) { @@ -456,6 +458,31 @@ export async function getBranches(options: { profileId: string }) { } } +export async function getWorkflows(options: { profileId: string }, showConsole: boolean = true) { + try { + const workflowResponse = await axios.get(`${API_HOSTNAME}/build/v2/profiles/${options.profileId}/workflows`, + { + headers: getHeaders(), + }); + if (showConsole) { + console.table(workflowResponse.data + .map((workflow: any) => ({ + 'Workflow Id': workflow.id, + 'Workflow Name': workflow.workflowName, + 'Last Used': workflow.lastUsedTime ? moment(workflow.lastUsedTime).calendar() : 'No previous builds', + })) + ); + } + return workflowResponse.data; + } catch (error) { + if (error.response && error.response.status === 404) { + return []; + } else { + handleError(error); + } + } +} + export async function getBuildTaskStatus(options: { latestCommitId: string, taskId: string }) { try { const taskStatus = await axios.get(`${API_HOSTNAME}/build/v2/commits/${options.latestCommitId}/builds/${options.taskId}/status`, diff --git a/yarn.lock b/yarn.lock index 94d484f..f44d544 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,12 +41,12 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -axios@^0.21.1: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== dependencies: - follow-redirects "^1.10.0" + follow-redirects "^1.14.7" base64-js@^1.3.1: version "1.5.1" @@ -162,10 +162,10 @@ esm@^3.2.25: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -follow-redirects@^1.10.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43" - integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg== +follow-redirects@^1.14.7: + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== form-data@^4.0.0: version "4.0.0"