Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add build status to project upload #542

Merged
merged 7 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions packages/cli-lib/api/dfs.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const http = require('../http');
const fs = require('fs');

const DEVELOPER_FILE_SYSTEM_API_PATH = 'dfs/v1/projects';
const PROJECTS_API_PATH = 'dfs/v1/projects';
const BUILD_STATUS_PATH = 'dfs-status/v1/project';

/**
* Fetch projects
Expand All @@ -11,7 +12,7 @@ const DEVELOPER_FILE_SYSTEM_API_PATH = 'dfs/v1/projects';
*/
async function fetchProjects(portalId) {
return http.get(portalId, {
uri: DEVELOPER_FILE_SYSTEM_API_PATH,
uri: PROJECTS_API_PATH,
});
}

Expand All @@ -24,7 +25,7 @@ async function fetchProjects(portalId) {
*/
async function createProject(portalId, name) {
return http.post(portalId, {
uri: DEVELOPER_FILE_SYSTEM_API_PATH,
uri: PROJECTS_API_PATH,
body: {
name,
},
Expand All @@ -41,7 +42,7 @@ async function createProject(portalId, name) {
*/
async function uploadProject(accountId, projectName, projectFile) {
return http.post(accountId, {
uri: `${DEVELOPER_FILE_SYSTEM_API_PATH}/upload/${projectName}`,
uri: `${PROJECTS_API_PATH}/upload/${projectName}`,
timeout: 60000,
formData: {
file: fs.createReadStream(projectFile),
Expand All @@ -58,7 +59,7 @@ async function uploadProject(accountId, projectName, projectFile) {
*/
async function fetchProject(portalId, name) {
return http.get(portalId, {
uri: `${DEVELOPER_FILE_SYSTEM_API_PATH}/${name}`,
uri: `${PROJECTS_API_PATH}/${name}`,
});
}

Expand All @@ -71,7 +72,20 @@ async function fetchProject(portalId, name) {
*/
async function deleteProject(portalId, name) {
return http.delete(portalId, {
uri: `${DEVELOPER_FILE_SYSTEM_API_PATH}/${name}`,
uri: `${PROJECTS_API_PATH}/${name}`,
});
}

/**
* Get project build status
*
* @async
* @param {string} name
* @returns {Promise}
*/
async function getBuildStatus(portalId, name, buildId) {
return http.get(portalId, {
uri: `${BUILD_STATUS_PATH}/${name}/build/${buildId}`,
});
}

Expand All @@ -81,4 +95,5 @@ module.exports = {
uploadProject,
fetchProject,
deleteProject,
getBuildStatus,
};
18 changes: 18 additions & 0 deletions packages/cli-lib/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ const PROJECT_TEMPLATE_TYPES = {
},
};

const PROJECT_BUILD_STATUS = {
BUILDING: 'BUILDING',
ENQUEUED: 'ENQUEUED',
FAILURE: 'FAILURE',
PENDING: 'PENDING',
SUCCESS: 'SUCCESS',
};

const PROJECT_BUILD_STATUS_TEXT = {
[PROJECT_BUILD_STATUS.BUILDING]: 'is building',
[PROJECT_BUILD_STATUS.ENQUEUED]: 'is queued',
[PROJECT_BUILD_STATUS.FAILURE]: 'failed to build',
[PROJECT_BUILD_STATUS.PENDING]: 'is pending',
[PROJECT_BUILD_STATUS.SUCCESS]: 'built successfully',
};

module.exports = {
ConfigFlags,
Mode,
Expand All @@ -142,6 +158,8 @@ module.exports = {
OAUTH_AUTH_METHOD,
PERSONAL_ACCESS_KEY_AUTH_METHOD,
POLLING_DELAY,
PROJECT_BUILD_STATUS,
PROJECT_BUILD_STATUS_TEXT,
PROJECT_TEMPLATE_TYPES,
SCOPE_GROUPS,
};
4 changes: 2 additions & 2 deletions packages/cli/bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const functionsCommand = require('../commands/functions');
const listCommand = require('../commands/list');
const openCommand = require('../commands/open');
const mvCommand = require('../commands/mv');
const projectsCommand = require('../commands/projects');
const projectCommands = require('../commands/project');
const themeCommand = require('../commands/theme');
const configCommand = require('../commands/config');
const accountsCommand = require('../commands/accounts');
Expand Down Expand Up @@ -94,7 +94,7 @@ const argv = yargs
})
.command(openCommand)
.command(mvCommand)
.command(projectsCommand)
.command(projectCommands)
.command(themeCommand)
.command(configCommand)
.command(accountsCommand)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ const {
addAccountOptions,
addOverwriteOptions,
} = require('../lib/commonOpts');
const deploy = require('./projects/deploy');
const init = require('./projects/init');
const upload = require('./projects/upload');
const deploy = require('./project/deploy');
const init = require('./project/init');
const upload = require('./project/upload');

exports.command = 'project';
exports.describe = false; //'Commands for working with projects';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ exports.handler = async options => {
const { path: projectPath } = options;
const accountId = getAccountId(options);

trackCommandUsage('projects-deploy', { projectPath }, accountId);
trackCommandUsage('project-deploy', { projectPath }, accountId);

logger.debug(`Deploying project at path: ${projectPath}`);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ exports.handler = async options => {
const { path: projectPath } = options;
const accountId = getAccountId(options);

trackCommandUsage('projects-init', { projectPath }, accountId);
trackCommandUsage('project-init', { projectPath }, accountId);

const cwd = projectPath ? path.resolve(getCwd(), projectPath) : getCwd();
const projectConfig = await getOrCreateProjectConfig(cwd);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const tmp = require('tmp');
const {
getProjectConfig,
validateProjectConfig,
pollBuildStatus,
} = require('../../lib/projects');
const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');

Expand All @@ -49,7 +50,11 @@ const uploadProjectFiles = async (accountId, projectName, filePath) => {
logger.log(`Uploading project '${projectName}'...`);
try {
const upload = await uploadProject(accountId, projectName, filePath);
logger.log(`Project uploaded and build #${upload.buildId} created`);

logger.log(
`Project "${projectName}" uploaded and build #${upload.buildId} created`
);
await pollBuildStatus(accountId, projectName, upload.buildId);
} catch (err) {
if (err.statusCode === 404) {
return logger.error(
Expand All @@ -71,7 +76,7 @@ exports.handler = async options => {
const { path: projectPath } = options;
const accountId = getAccountId(options);

trackCommandUsage('projects-upload', { projectPath }, accountId);
trackCommandUsage('project-upload', { projectPath }, accountId);

const cwd = projectPath ? path.resolve(getCwd(), projectPath) : getCwd();
const projectConfig = await getProjectConfig(cwd);
Expand All @@ -80,13 +85,13 @@ exports.handler = async options => {

const tempFile = tmp.fileSync({ postfix: '.zip' });

logger.log(`Compressing build files to '${tempFile.name}'`);
logger.debug(`Compressing build files to '${tempFile.name}'`);

const output = fs.createWriteStream(tempFile.name);
const archive = archiver('zip');

output.on('close', async function() {
logger.log(`Project files compressed: ${archive.pointer()} bytes`);
logger.debug(`Project files compressed: ${archive.pointer()} bytes`);

await uploadProjectFiles(accountId, projectConfig.name, tempFile.name);

Expand Down
88 changes: 87 additions & 1 deletion packages/cli/lib/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,24 @@ const path = require('path');
const chalk = require('chalk');
const findup = require('findup-sync');
const { prompt } = require('inquirer');
const Spinnies = require('spinnies');
Copy link
Contributor

@miketalley miketalley Aug 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using ora for loading spinners elsewhere in the CLI. Is there a reason to have both or should we use ora here also?

It looks like having multiple spinners displayed simultaneously for the purpose of displaying the status of the subbuilds is not possible using ora. I think we should work on updating uses of ora to use spinnies in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's exactly it and I agree

const { logger } = require('@hubspot/cli-lib/logger');
const { getEnv } = require('@hubspot/cli-lib/lib/config');
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
const {
ENVIRONMENTS,
POLLING_DELAY,
PROJECT_BUILD_STATUS,
PROJECT_BUILD_STATUS_TEXT,
} = require('@hubspot/cli-lib/lib/constants');
const { getBuildStatus } = require('@hubspot/cli-lib/api/dfs');

const isBuildComplete = build => {
return (
build.status === PROJECT_BUILD_STATUS.SUCCESS ||
build.status === PROJECT_BUILD_STATUS.FAILURE
);
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😀


const writeProjectConfig = (configPath, config) => {
try {
Expand Down Expand Up @@ -113,10 +127,82 @@ const showWelcomeMessage = (projectName, accountId) => {
);
};

const pollBuildStatus = async (accountId, name, buildId) => {
const buildStatus = await getBuildStatus(accountId, name, buildId);
const spinnies = new Spinnies();

logger.log(`Building project '${name}'...`);
for (let subBuild of buildStatus.subbuildStatuses) {
spinnies.add(subBuild.buildName, {
text: `'${subBuild.buildName}' ${
PROJECT_BUILD_STATUS_TEXT[PROJECT_BUILD_STATUS.ENQUEUED]
}`,
});
}

return new Promise((resolve, reject) => {
const pollInterval = setInterval(async () => {
const buildStatus = await getBuildStatus(accountId, name, buildId).catch(
reject
);
const { status, subbuildStatuses } = buildStatus;

if (Object.keys(spinnies.spinners).length) {
subbuildStatuses.forEach(subBuild => {
const updatedText = `'${subBuild.buildName}' ${
PROJECT_BUILD_STATUS_TEXT[subBuild.status]
}`;

switch (subBuild.status) {
case PROJECT_BUILD_STATUS.SUCCESS:
spinnies.succeed(subBuild.buildName, {
text: updatedText,
});
break;
case PROJECT_BUILD_STATUS.FAILURE:
spinnies.fail(subBuild.buildName, {
text: updatedText,
});
break;
default:
spinnies.update(subBuild.buildName, {
text: updatedText,
});
break;
}
});
}

if (isBuildComplete(buildStatus)) {
clearInterval(pollInterval);

if (status === PROJECT_BUILD_STATUS.SUCCESS) {
logger.success(
`Your project '${name}' ${PROJECT_BUILD_STATUS_TEXT[status]}.`
);
} else if (status === PROJECT_BUILD_STATUS.FAILURE) {
logger.error(
`Your project '${name}' ${PROJECT_BUILD_STATUS_TEXT[status]}.`
);
subbuildStatuses.forEach(subBuild => {
if (subBuild.status === PROJECT_BUILD_STATUS.FAILURE) {
logger.error(
`${subBuild.buildName} failed to build. ${subBuild.errorMessage}.`
);
}
});
}
resolve(buildStatus);
}
}, POLLING_DELAY);
});
};

module.exports = {
writeProjectConfig,
getProjectConfig,
getOrCreateProjectConfig,
validateProjectConfig,
showWelcomeMessage,
pollBuildStatus,
};
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"open": "^7.0.3",
"ora": "^4.0.3",
"shelljs": "0.8.3",
"spinnies": "^0.5.1",
"tmp": "^0.2.1",
"update-notifier": "3.0.1",
"yargs": "15.4.1"
Expand Down
Loading