Skip to content

Commit

Permalink
feat: git-client refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Chih-Ying committed Mar 23, 2020
1 parent 25bac7e commit 394fe82
Show file tree
Hide file tree
Showing 9 changed files with 492 additions and 219 deletions.
255 changes: 147 additions & 108 deletions lib/clients/git-client.js
Original file line number Diff line number Diff line change
@@ -1,137 +1,176 @@
const { execSync } = require('child_process');
const fs = require('fs-extra');
const git = require('simple-git');
const path = require('path');

const CLiError = require('@src/exceptions/cli-error');
const Messenger = require('@src/view/messenger');

module.exports = {
init,
configureCredentialHelper,
addOrigin,
fetchAll,
checkoutBranch,
add,
clone,
setupGitIgnore
};

const isWindows = process.platform === 'win32';
// eslint-disable-next-line quotes
const QUOTES = isWindows ? `"` : `'`;

function init(projectPath, verbosityOptions) {
const commands = [`git init ${QUOTES}${projectPath}${QUOTES}${verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = { showStdOut: verbosityOptions.showOutput, showStdErr: true, showCmd: verbosityOptions.showCommand };
for (const command of commands) {
try {
_execChildProcessSync(command, options);
} catch (ex) {
throw new Error(`${command} Failed.`);
}
/**
* Class for git commands management
*/
module.exports = class GitClient {
/**
* Constructor for GitClient with projectPath and verbosity options
* @param {string} projectPath the path of a project
* @param {object} verbosityOptions | showOutput
* | showCommand
*/
constructor(projectPath, verbosityOptions) {
this.projectPath = projectPath;
this.verbosityOptions = verbosityOptions;
}
}

function configureCredentialHelper(projectPath, credentialHelperPath, verbosityOptions) {
const commands = [`git config --local credential.helper ${QUOTES}${QUOTES}`,
`git config --local --add credential.helper ${QUOTES}!${credentialHelperPath}${QUOTES}`,
'git config --local credential.UseHttpPath true'];
const options = { showStdOut: verbosityOptions.showOutput, showStdErr: true, showCmd: verbosityOptions.showCommand, workingDir: projectPath };
for (const command of commands) {
try {
_execChildProcessSync(command, options);
} catch (ex) {
throw new Error(`${command} Failed.`);
}
/**
* Execute git-init to create an empty Git repository or reinitialize an existing one
*/
init() {
const commands = [`git init ${QUOTES}${this.projectPath}${QUOTES}${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: true,
showCmd: this.verbosityOptions.showCommand
};
this._execCommands(commands, options);
}
}

function addOrigin(projectPath, repoUrl, verbosityOptions) {
const commands = [`git remote add origin ${repoUrl}`];
const options = { showStdOut: verbosityOptions.showOutput, showStdErr: true, showCmd: verbosityOptions.showCommand, workingDir: projectPath };
for (const command of commands) {
try {
_execChildProcessSync(command, options);
} catch (ex) {
throw new Error(`${command} Failed.`);
}
/**
* Config local git credential helper with credentialHelperPath
* @param {string} credentialHelperPath the path of git credential helper
*/
configureCredentialHelper(credentialHelperPath) {
const commands = [`git config --local credential.helper ${QUOTES}${QUOTES}`,
`git config --local --add credential.helper ${QUOTES}!${credentialHelperPath}${QUOTES}`,
'git config --local credential.UseHttpPath true'];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: true,
showCmd: this.verbosityOptions.showCommand,
workingDir: this.projectPath
};
this._execCommands(commands, options);
}
}

function fetchAll(projectPath, verbosityOptions) {
const commands = [`git fetch --all${verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = { showStdOut: verbosityOptions.showOutput, showStdErr: false, showCmd: verbosityOptions.showCommand, workingDir: projectPath };
for (const command of commands) {
try {
_execChildProcessSync(command, options);
} catch (ex) {
throw new Error(`${command} Failed.`);
}
/**
* Add a new remote in the directory your repository is stored at
* @param {String} repoUrl a remote url
*/
addOrigin(repoUrl) {
const commands = [`git remote add origin ${repoUrl}`];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: true,
showCmd: this.verbosityOptions.showCommand,
workingDir: this.projectPath
};
this._execCommands(commands, options);
}
}

function checkoutBranch(projectPath, branch, verbosityOptions) {
const commands = [`git checkout ${branch}${verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = { showStdOut: verbosityOptions.showOutput, showStdErr: true, showCmd: verbosityOptions.showCommand, workingDir: projectPath };
for (const command of commands) {
try {
_execChildProcessSync(command, options);
} catch (ex) {
throw new Error(`${command} Failed.`);
}
/**
* Execute git fetch to fetch all remotes
*/
fetchAll() {
const commands = [`git fetch --all${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: false,
showCmd: this.verbosityOptions.showCommand,
workingDir: this.projectPath
};
this._execCommands(commands, options);
}

/**
* Execute git checkout to switch branch or restore working tree files
* @param {string} branch the branch name
*/
checkoutBranch(branch) {
const commands = [`git checkout ${branch}${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: true,
showCmd: this.verbosityOptions.showCommand,
workingDir: this.projectPath
};
this._execCommands(commands, options);
}

/**
* Execute git clone to clone a repository into a new directory
* @param {string} cloneUrl the clone url
* @param {string} branch the branch name
* @param {string} folderName the directory folder name
*/
clone(cloneUrl, branch, folderName) {
const commands = [`git clone --branch ${branch} ${cloneUrl} ${folderName} ${this.verbosityOptions.showOutput === false ? ' --quiet' : ''}`];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: true,
showCmd: this.verbosityOptions.showCommand
};
this._execCommands(commands, options);
}
}

function add(repoDir, projectPath, verbosityOptions) {
const commands = [`git add ${QUOTES}${projectPath}${QUOTES}`];
const options = { showStdOut: verbosityOptions.showOutput, showStdErr: true, showCmd: verbosityOptions.showCommand, workingDir: repoDir };
for (const command of commands) {
try {
_execChildProcessSync(command, options);
} catch (ex) {
throw new Error(`${command} Failed.`);
/**
* Set up or update a gitignore file
* @param {Array} filesToIgnore the list of files to ignore for git
*/
setupGitIgnore(filesToIgnore) {
const gitignorePath = path.join(this.projectPath, '.gitignore');
if (fs.existsSync(gitignorePath) === false) {
fs.writeFileSync(gitignorePath, `${filesToIgnore.join('\n')}`);
} else {
const gitignoreFile = fs.readFileSync(gitignorePath).toString();
filesToIgnore.forEach((file) => {
if (gitignoreFile.indexOf(file) === -1) {
fs.appendFileSync(gitignorePath, `\n${file}`);
}
});
}
this.add('.gitignore');
}
}

function clone(cloneUrl, branch, cloneDir, callback) {
const cloneOption = [];
if (branch) {
cloneOption.push('-b');
cloneOption.push(branch);
/**
* Execute git add to add file contents to the index
* @param {string} file the file to add content from
*/
add(file) {
const commands = [`git add ${QUOTES}${file}${QUOTES}`];
const options = {
showStdOut: this.verbosityOptions.showOutput,
showStdErr: true,
showCmd: this.verbosityOptions.showCommand,
workingDir: this.projectPath
};
this._execCommands(commands, options);
}
git().silent(true).clone(cloneUrl, cloneDir, cloneOption, (err) => {
callback(err);
});
}

function setupGitIgnore(projectPath, filesToIgnore, verbosityOptions) {
const gitignorePath = path.join(projectPath, '.gitignore');
if (fs.existsSync(gitignorePath) === false) {
fs.writeFileSync(gitignorePath, `${filesToIgnore.join('\n')}`);
} else {
const gitignoreFile = fs.readFileSync(gitignorePath);
filesToIgnore.forEach((file) => {
if (gitignoreFile.toString().indexOf(file) === -1) {
fs.appendFileSync(gitignorePath, `\n${file}`);
_execCommands(commands, options) {
for (const command of commands) {
try {
this._execChildProcessSync(command, options);
} catch (ex) {
throw new CLiError(`${ex}`);
}
});
}
}
add(projectPath, '.gitignore', verbosityOptions);
}

function _execChildProcessSync(command, options) {
const { showStdOut, showStdErr, showCmd, workingDir } = options;
const execOptions = {
stdio: [null, showStdOut ? 1 : null, showStdErr ? 2 : null],
shell: true,
windowsHide: true
};
if (workingDir) {
execOptions.cwd = options.workingDir;
}
if (showCmd) {
Messenger.getInstance().info(command);
_execChildProcessSync(command, options) {
const { showOutput, showStdErr, showCommand, workingDir } = options;
const execOptions = {
stdio: [null, showOutput ? 1 : null, showStdErr ? 2 : null],
shell: true,
windowsHide: true
};
if (workingDir) {
execOptions.cwd = options.workingDir;
}
if (showCommand) {
Messenger.getInstance().info(command);
}
return execSync(command, execOptions);
}
return execSync(command, execOptions);
}
};
13 changes: 5 additions & 8 deletions lib/commands/v2new/helper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const fs = require('fs-extra');
const path = require('path');

const gitClient = require('@src/clients/git-client');
const GitClient = require('@src/clients/git-client');
const SkillInfrastructureController = require('@src/controllers/skill-infrastructure-controller');
const Manifest = require('@src/model/manifest');
const ResourcesConfig = require('@src/model/resources-config');
Expand Down Expand Up @@ -45,14 +45,11 @@ function initializeDeployDelegate(deploymentType, projectFolderPath, profile, do
* @param {Object} userInput user input initialization setting
* @param {Function} callback (error, projectFolderPath)
*/
function downloadTemplateFromGit(userInput, callback) {
function downloadTemplateFromGit(userInput, doDebug, callback) {
const projectFolderPath = path.join(process.cwd(), userInput.projectFolderName);
gitClient.clone(userInput.templateInfo.templateUrl, CONSTANTS.TEMPLATES.TEMPLATE_BRANCH_NAME, projectFolderPath, (cloneErr) => {
if (cloneErr) {
return callback(cloneErr);
}
callback(null, projectFolderPath);
});
const gitClient = new GitClient(projectFolderPath, { showOutput: !!doDebug, showCommand: !!doDebug });
gitClient.clone(userInput.templateInfo.templateUrl, CONSTANTS.TEMPLATES.TEMPLATE_BRANCH_NAME, projectFolderPath);
callback(null, projectFolderPath);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions lib/commands/v2new/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class NewCommand extends AbstractCommand {
cb();
});
} else {
createNonHostedSkill(cmd, profile, userInput, (nonHostedErr) => {
createNonHostedSkill(cmd, profile, cmd.debug, userInput, (nonHostedErr) => {
if (nonHostedErr) {
Messenger.getInstance().error(nonHostedErr);
return cb(nonHostedErr);
Expand Down Expand Up @@ -85,9 +85,9 @@ function createHostedSkill(cmd, profile, vendorId, userInput, callback) {
});
}

function createNonHostedSkill(cmd, profile, userInput, callback) {
function createNonHostedSkill(cmd, profile, doDebug, userInput, callback) {
// 1.download skill project templates
helper.downloadTemplateFromGit(userInput, (projectErr, projectFolderPath) => {
helper.downloadTemplateFromGit(userInput, doDebug, (projectErr, projectFolderPath) => {
if (projectErr) {
return callback(projectErr);
}
Expand Down
23 changes: 12 additions & 11 deletions lib/controllers/hosted-skill-controller/clone-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const jsonfile = require('jsonfile');
const path = require('path');
const R = require('ramda');

const gitClient = require('@src/clients/git-client');
const GitClient = require('@src/clients/git-client');
const ResourcesConfig = require('@src/model/resources-config');
const CONSTANTS = require('@src/utils/constants');
const Messenger = require('@src/view/messenger');
Expand Down Expand Up @@ -72,10 +72,11 @@ function _gitCloneWorkflow(projectPath, repositoryUrl, verbosityOptions, profile
if (verbose) {
Messenger.getInstance().info('- Setting up git repo...');
}
gitClient.init(projectPath, verbosityOptions);
const gitClient = new GitClient(projectPath, verbosityOptions);
gitClient.init();
const credentialHelperPath = `askx util git-credentials-helper --profile ${profile}`;
gitClient.configureCredentialHelper(projectPath, credentialHelperPath, verbosityOptions);
gitClient.addOrigin(projectPath, repositoryUrl, verbosityOptions);
gitClient.configureCredentialHelper(credentialHelperPath);
gitClient.addOrigin(repositoryUrl);

if (verbose) {
Messenger.getInstance().info('- Fetching git repo...');
Expand All @@ -85,28 +86,28 @@ function _gitCloneWorkflow(projectPath, repositoryUrl, verbosityOptions, profile
if (verbose) {
Messenger.getInstance().info('- Checking out dev branch...');
}
gitClient.checkoutBranch(projectPath, 'dev', verbosityOptions);
gitClient.checkoutBranch('dev');

if (verbose) {
Messenger.getInstance().info('- Setting up .gitignore...');
}
const filesToIgnore = ['lambda/node_modules'];
gitClient.setupGitIgnore(projectPath, filesToIgnore, verbosityOptions);
const filesToIgnore = ['lambda/node_modules', '.ask/'];
gitClient.setupGitIgnore(filesToIgnore);

if (verbose) {
Messenger.getInstance().info('- Git repo successfully setup');
}
_makeBranchesVisible(projectPath, verbosityOptions);
_makeBranchesVisible(gitClient);

if (!verbose) {
progressSpinner.terminate();
}
}

function _makeBranchesVisible(projectPath, verbosityOptions) {
const branchesToCheckout = ['master', 'dev'];
function _makeBranchesVisible(gitClient) {
const branchesToCheckout = ['master', 'dev', 'prod'];
branchesToCheckout.forEach((branch) => {
gitClient.checkoutBranch(projectPath, branch, verbosityOptions);
gitClient.checkoutBranch(branch);
});
}

Expand Down
Loading

0 comments on commit 394fe82

Please sign in to comment.