def install_shell_profile(ctx): config.CONFIG_DIR.mkdir(parents=True, exist_ok=True) profile = config.CONFIG_DIR / 'profile' with open(profile, 'w') as fh: fh.write(shell_profile_template) todo = actionable('TODO:') get_logger().info(f'{todo} Please add the following line to your shell config file, if you haven\'t already ') get_logger().info(' done so. The default shell config file is ~/.profile. If you\'re using a') get_logger().info(' different shell, e.g. zsh, you may have a different config file, e.g. ~/.zshrc') get_logger().info('') get_logger().info(actionable(' source %s'), str(profile))
def ssh_keys(ctx): ssh_dir = config.HOME / '.ssh' ssh_dir.mkdir(exist_ok=True) if config.SSH_KEY_FILE.is_file(): get_logger().info( 'Found existing key ~/.ssh/id_rsa, skipping setting up ssh keys') get_logger().info( 'Please ensure your keys are added to your GitHub account') return else: get_logger().info('Did not find existing ssh key in ~/.ssh/id_rsa') with open(ssh_dir / 'config', 'w') as fh: get_logger().info('Disabling host key checking for github.com in %s', str(ssh_dir / 'config')) fh.write('Host github.com\n\tStrictHostKeyChecking no\n') res = req_input( 'Opening browser for instructions to setting up ssh keys in GitHub, ' 'press any key to continue, enter "skip" to skip: ') if res != 'skip': webbrowser.open(config.GITHUB_SSH_HELP_URL) get_logger().info( actionable( 'Once you\'ve generated SSH keys and added them to GitHub, ' 'please rerun `workflow setup.macos`')) sys.exit(0) else: get_logger().info('Skipping adding SSH Keys to GitHub')
def install_githooks(ctx): hooks_dir = config.HOME / '.githooks' # Dupe of path defined in REQUIRED_REPO. kernel_tools_dir = config.REPO_ROOT / 'kernel-tools' mongo_dir = config.REPO_ROOT / 'mongo' hooks_dir.mkdir(exist_ok=True, parents=True) if not (hooks_dir / 'mongo').exists(): ctx.run( f'ln -s {str(kernel_tools_dir / "githooks")} {str(hooks_dir / "mongo")}' ) with ctx.cd(str(mongo_dir)): ctx.run(f'source buildscripts/install-hooks -f') todo = actionable('TODO:') get_logger().info( f'{todo} Please consult with your mentor on which githooks are needed. Some hooks may be' ) get_logger().info( ' unnecessarily cumbersome for your project. You can delete any unneeded hooks in ' ) get_logger().info(f' `%s` and rerun `workflow macos.setup`', str(kernel_tools_dir / "githooks" / "pre-push"))
def username(self): if not self._username: self._username = input( actionable( 'Please enter your Evergreen username (firstname.lastname): ' )) return self._username
def zzz(ctx, force=False): """ Step 6: Cleanup. Remove local branches and close Jira ticket :param force: Force delete the local branch even if it's not fully merged """ helpers.check_mongo_repo_root() ticket_conf = helpers.get_ticket_conf(ctx) feature_branch = git.cur_branch_name(ctx) base_branch = ticket_conf.base_branch get_logger().info(f'🍦 Congrats on completing {feature_branch.upper()}! 🍦') input(actionable(f'Press any key to remove local branches and close the Jira ticket')) config.Config().in_progress_tickets.pop(feature_branch) ctx.run(f'git checkout {base_branch}') try: if force: ctx.run(f'git branch -D {feature_branch}') else: ctx.run(f'git branch --delete {feature_branch}') except UnexpectedExit as e: get_logger().error(e) get_logger().error(f'Failed to delete branch, please manually delete your local branch {feature_branch}') jira.transition_ticket( feature_branch.upper(), 'In Code Review', 'Close Issue' )
def evergreen_yaml(conf): if config.EVG_CONFIG_FILE.exists(): get_logger().info( 'Found existing ~/.evergreen.yml, skipping adding Evergreen configuration') get_logger().info( 'Please ensure your ~/.evergreen.yml was generated by this tool. If not, ' 'make sure you know what\'s in there') else: settings_url = 'https://evergreen.mongodb.com/login/key' while True: pwd = getpass.getpass(prompt=actionable('Please enter your Evergreen password: '******'username': conf.username, 'password': pwd}) if res.status_code != 200: get_logger().error('Failed to fetch API key from evergreen. Error: %s', str(res)) req_input('Press any key to retry...') continue res_json = res.json() evg_config = evergreen_yaml_template.format(res_json['user'], res_json['api_key']) with open(config.EVG_CONFIG_FILE, 'w') as fh: fh.write(evg_config) break return True
def jira_pwd(self): if not self._jira_pwd: jira_pwd = keyring.get_password(JIRA_URL, self.username) if not jira_pwd: jira_pwd = getpass.getpass( prompt=actionable('Please enter your Jira password: ')) keyring.set_password(JIRA_URL, self.username, jira_pwd) self._jira_pwd = jira_pwd return self._jira_pwd
def get_sudo_pwd(self, ctx): if not self._sudo_pwd: while True: sudo_pwd = getpass.getpass( prompt=actionable('Please enter your sudo password: '******'ls', warn=False, hide='both', password=sudo_pwd) except invoke.exceptions.AuthFailure as e: get_logger().error(str(e)) continue self._sudo_pwd = sudo_pwd break return self._sudo_pwd
def post_task_instructions(): lines = [ actionable('Note on Using "compiledb":'), ' A Clang JSON Compilation Database (compiledb) can be generated for the mongo repository by running', '', ' `ninja compiledb` (from the mongo repo at ~/mongodb/mongo)', '', ' It enables features like jump to definition and semantic code completion in code editors. Please refer', ' to the following web page on how to integrate compiledb with your favorite editor', '', ' https://sarcasm.github.io/notes/dev/compilation-database.html#text-editors-and-ides', '', ' When you switch branches or add/remove files, compiledb needs to be updated by running `ninja compiledb`', '', ' If you\'d like to use an editor that "just works", The CLion IDE is a good option. You just need', ' to install it and open the "mongo" directory. Code completion and jumping to definitions will', ' automatically work', '', ' To install CLion, run: `brew cask install clion`' ] log_multiline(get_logger().info, lines)
def ship(ctx): """ Step 5: Provide instructions on pushing your changes to master Also add the URL of the latest patch build to the Jira ticket. """ helpers.check_mongo_repo_root() ticket_conf = helpers.get_ticket_conf(ctx) cur_branch = git.cur_branch_name(ctx) if ticket_conf.patch_ids: jira.add_comment( cur_branch.upper(), f'Patch Build: {urllib.parse.urljoin(config.EVG_PATCH_URL_BASE, ticket_conf.patch_ids[-1])}', visibility={'type': 'role', 'value': 'Developers'} ) else: get_logger().warning('No patch builds were created for this ticket, not adding patch build URL to Jira') # This will implicitly check for uncommitted changes on the feature branch git.refresh_repos(ctx, ticket_conf.base_branch) lines = [ actionable('Please run the following commands to push your changes to the upstream MongoDB repository:'), '', f' git rebase --interactive {ticket_conf.base_branch}', f' git checkout {ticket_conf.base_branch}', f' git merge --ff-only {cur_branch}', f' git push origin {ticket_conf.base_branch} --dry-run', f' git push origin {ticket_conf.base_branch}', '', 'As part of `git rebase --interactive`, you should squash your local commits into one commit. Please refer to', 'this guide for an intro to interactive rebase: https://git-scm.com/docs/git-rebase#_interactive_mode', '', 'If you encounter errors during any of the above steps, please ask your mentor for advice.', '', 'Finally, when you\'ve pushed your changes, run `workflow zzz` to delete your local branches' ] log.log_multiline(get_logger().info, lines)
def review(ctx): """ Step 4: Open a new code review (CR) or put up a new patch to an existing code review """ helpers.check_mongo_repo_root() ticket_conf = helpers.get_ticket_conf(ctx) if not ticket_conf.commits: get_logger().warning('Did not find any commits on this branch. Please make sure you run `commit` ' 'before `review`') with ctx.prefix(helpers.virtualenv): def upload(existing_cr, repo_name): has_changes = ctx.run(f'git diff {ticket_conf.base_branch}').stdout.strip() if has_changes: get_logger().info(f'Submitting code review for the {repo_name} repo') else: get_logger().info(f'There are no changes in the {repo_name} repository, skipping code review') return cmd = f'python {str(config.UPLOAD_PY)} --rev {ticket_conf.base_branch}...' # Yes three dots. cmd += ' --nojira -y --git_similarity 90 --check-clang-format --check-eslint' if existing_cr is not None: # Continue an existing CR. cmd += f' -i {existing_cr}' else: # Start a new CR. commit_msg = ctx.run('git log -1 --pretty=%B').stdout.strip() cr_title = input(f'Please enter the title for this code review (without the ticket number). ' f'Default: {commit_msg}') if not cr_title: cr_title = commit_msg else: # Add the ticket number to the description. cr_title = f'{git.cur_branch_name(ctx).upper()} {commit_msg}' cmd += f' -t "{cr_title}"' get_logger().info('Opening browser to authenticate with OAuth2... ') # Simulate some newline inputs to get the browser to open. sim_stdin = io.StringIO(initial_value='\n\n') res = ctx.run(cmd, in_stream=sim_stdin) if existing_cr is None: cr_url = re.search('Issue created. URL: (.*)', res.stdout.strip()).group(1) cr_issue_number = cr_url.split('/')[-1] get_logger().info(f'Code review created: {cr_url}') ticket_number = git.cur_branch_name(ctx).upper() jira.transition_ticket(ticket_number, 'In Progress', 'Start Code Review') jira.add_comment( ticket_number, f'Code Review: {cr_url}', visibility={'type': 'role', 'value': 'Developers'} ) return cr_issue_number else: get_logger().info(f'Code review updated') return existing_cr ticket_conf.cr_info.community = upload(ticket_conf.cr_info.community, 'community') with ctx.cd(git.ent_repo_rel_path): ticket_conf.cr_info.enterprise = upload(ticket_conf.cr_info.enterprise, 'enterprise') note = actionable('Note:') get_logger().info('') get_logger().info(f'{note} Step 3 (patch build) and Step 4 (code review) may need to be repeated to address') get_logger().info(' CR feedback or to validate new changes. Please consult with your mentor on the exact') get_logger().info(' workflow for your team.') get_logger().info('') req_input('Press any key to open the code review app...') webbrowser.open('https://mongodbcr.appspot.com')