diff --git a/src/create-branch.js b/src/create-branch.js new file mode 100644 index 0000000..2650842 --- /dev/null +++ b/src/create-branch.js @@ -0,0 +1,100 @@ +import { Command } from 'commander'; +import axios from 'axios'; +import logSymbols from 'log-symbols'; + +const getSuccessLog = (props) => ` +${logSymbols.success} ${props.repo} + Branch Name: ${props.branchName} + Author: ${props.commitAuthor} + Commit: ${props.commitHash} + Commit Link: ${props.commitLink} +`.trim(); + +const getErrorLog = (props) => ` +${logSymbols.error} ${props.repo} + Error Message: ${props.errorMessage.trim()} +`.trim(); + +export const makeCreateBranchCommand = (config) => { + const program = new Command(); + + program + .name('create-branch') + .description('Create a branch on all repositories.') + .argument('', 'Branch to create from.') + .argument('', 'Branch name to create.') + .action(async(fromBranch, branchName) => { + if (!config) { + console.log('Missing configuration file. Please run "init" command and try again.'); + return; + } + + const repos = config.repos || []; + + if (repos.length === 0) { + console.log('No repositories setup yet. Please run "repo add" command and try again.'); + return; + } + + const axiosInstance = axios.create({ + baseURL: 'https://api.bitbucket.org/2.0', + headers: { + 'Content-Type': 'application/json', + }, + auth: { + username: config.auth.username, + password: config.auth.appPassword, + }, + }); + + for (const repo of repos) { + + const logProps = { + repo: repo, + branchName: branchName, + commitHash: '', + commitLink: '', + errorMessage: '', + }; + + let wasSuccessful = false; + + try { + const response = await axiosInstance.request({ + method: 'post', + url: `repositories/${repo}/refs/branches`, + data: JSON.stringify({ + name: branchName, + target: { + hash: fromBranch, + }, + }), + }); + + wasSuccessful = true; + + const data = response.data; + + logProps.commitHash = data.target.hash; + logProps.commitLink = data.target.links.html.href; + logProps.commitAuthor = data.target.author.raw; + } catch (error) { + if (error.response.data.error.message) { + logProps.errorMessage = error.response.data.error.message; + } else if (error.response) { + // The request was made and the server responded with a status code + // that falls out of the range of 2xx + logProps.errorMessage = error.response.data; + } else { + logProps.errorMessage = error; + } + } + + console.log((wasSuccessful ? getSuccessLog(logProps) : getErrorLog(logProps)) + '\n'); + } + + }) + ; + + return program; +}; \ No newline at end of file diff --git a/src/init.js b/src/init.js new file mode 100644 index 0000000..e14d57b --- /dev/null +++ b/src/init.js @@ -0,0 +1,77 @@ +import { Command } from 'commander'; +import inquirer from 'inquirer'; +import fs from 'fs'; +import logSymbols from 'log-symbols'; + +export const makeInitCommand = (config, configFileLocation) => { + const program = new Command(); + + program + .name('init') + .description('Create the configuration file with your bitbucket username and app password.') + .action(async() => { + + if (config) { + const existingConfigAnswers = await inquirer.prompt([ + { + type: 'confirm', + name: 'shouldOverwriteConfig', + message: `Existing configuration file at ${configFileLocation}. Would you like to overwrite it?`, + default: false, + }, + ]); + + if (!existingConfigAnswers.shouldOverwriteConfig) { + return; + } + } + + console.log(` +${logSymbols.info} Note: Do not use your own bitbucket password. Create an app password with repositories read + write access. + +To setup a bitbucket app password, checkout the following documentation: +https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/ + `); + + const answers = await inquirer.prompt([ + { + type: 'input', + name: 'username', + message: 'Bitbucket Username:', + validate: (input) => { + if (!input.trim()) { + return 'Username cannot be empty.'; + } + return true; + }, + }, + { + type: 'password', + name: 'appPassword', + message: 'App Password:', + validate: (input) => { + if (!input.trim()) { + return 'App password cannot be empty.'; + } + return true; + }, + }, + ]); + + if (!config) { + config = {}; + } + + config.auth = config.auth || {}; + + config.auth.username = answers.username.trim(); + config.auth.appPassword = answers.appPassword.trim(); + + fs.writeFileSync(configFileLocation, JSON.stringify(config)); + + console.log(`${logSymbols.success} Created ${configFileLocation}.`); + }) + ; + + return program; +}; \ No newline at end of file diff --git a/src/repo.js b/src/repo.js new file mode 100644 index 0000000..489009f --- /dev/null +++ b/src/repo.js @@ -0,0 +1,86 @@ +import { Command } from 'commander'; +import fs from 'fs'; +import logSymbols from 'log-symbols'; + +export const makeRepoCommand = (config, configFileLocation) => { + const program = new Command(); + + program + .name('repo') + .description('Add, remove and list repositories.') + ; + + program + .command('add') + .description('Add a new repository to the list.') + .argument('', 'Repository name you\'d like to add. This should include the user/org.') + .action((repository) => { + if (!config) { + console.log('Missing configuration file. Please run "init" command and try again.'); + return; + } + + config.repos = config.repos || []; + + const hasExistingRepo = config.repos.some((repo) => repo === repository); + + if (hasExistingRepo) { + console.log(`${logSymbols.warning} Repository "${repository}" is already added, skipping.`); + return; + } + + config.repos.push(repository); + + fs.writeFileSync(configFileLocation, JSON.stringify(config)); + + console.log(`${logSymbols.success} Repository "${repository}" successfully added.`); + }) + ; + + program + .command('remove') + .description('Remove an existing repository from the list.') + .argument('', 'Repository name you\'d like to add. This should include the user/org.') + .action((repository) => { + if (!config) { + console.log('Missing configuration file. Please run "init" command and try again.'); + return; + } + + config.repos = config.repos || []; + + const hasExistingRepo = config.repos.some((repo) => repo === repository); + + if (!hasExistingRepo) { + console.log(`Repository "${repository}" doesn't exist, skipping.`); + return; + } + + config.repos = config.repos.filter((repo) => repo !== repository); + + fs.writeFileSync(configFileLocation, JSON.stringify(config)); + + console.log(`Repository "${repository}" successfully removed.`); + }) + ; + + program + .command('list') + .description('List all existing repositories.') + .action(() => { + + const repos = (config.repos || []); + + if (repos.length === 0) { + console.log('No repositories added yet. Try adding one with "repo add "'); + return; + } + + repos.forEach((repo) => { + console.log(repo); + }); + }) + ; + + return program; +}; \ No newline at end of file